/*
 * Generic serial driver interface.
 *
 * The operating system dependent code must
 * provide the four functions detailed below.
 * All functions should error() rather than returning
 * an error condition.
 *
 * eiaopenport(n, omode):
 *	open port #n for omode.
 *	return a pointer to an Eiaport p.
 * eiacloseport(p):
 *	close the device returned by eiaopenport.
 *	note that there may be multiple connections
 *	to a given port at any time, so some
 *	reference counting may be needed here.
 *	eiacloseport must not error.
 * eiardwr(p, a, n, iswrite):
 *	read or write n bytes of data at a on port p.
 *	if iswrite!=0, the call is a write.
 * eiactl(p, ctl):
 *	process the control message ctl.
 *	messages are typically a single letter 
 *	immediate followed by a number, e.g. b9600.
 *	messages are case-insensitive, so B9600 and b9600 
 *	mean the same thing.
 *	the following are messages supported by native Plan 9:
 *		b# - set baud rate to #.
 *		l# - set byte size to # bits/byte (5, 6, 7, or 8).
 *		d# - set dtr if # is non-zero; else clear it.
 *		k# - send a # millisecond break.
 *		r# - set rts if # is non-zero; else clear it.
 *		m# - obey cts signal if # is non-zero; else don't.
 *		i# - enable input fifo if # is non-zero; else disable.
 *		pc - set parity odd if c is 'o', even if c is 'e',
 *			else no parity.
 *		s# - set stop bits to # (1 or 2).
 *	the b and l messages are the most important.  the others
 * 	can safely go unimplemented for most uses.
 */

#include <9pm/u.h>
#include <9pm/libc.h>
#include "dat.h"
#include "fns.h" 

enum
{
	Qdir,
	Qeia0,
	Qeia0ctl,
	Qeia1,
	Qeia1ctl,
	Qeia2,
	Qeia2ctl,
	Qeia3,
	Qeia3ctl,
	Neia		= 4
};
#define PORT(qp)	(((qp)-Qeia0)/2)

Dirtab eiatab[]=
{
	".",		{Qdir, 0, QTDIR},	0,	0555,
	"eia0",		{Qeia0},		0,	0600,
	"eia1",		{Qeia1},		0,	0600,
	"eia2",		{Qeia2},		0,	0600,
	"eia3",		{Qeia3},		0,	0600,
	"eia0ctl",	{Qeia0ctl},		0,	0600,
	"eia1ctl",	{Qeia1ctl},		0,	0600,
	"eia2ctl",	{Qeia2ctl},		0,	0600,
	"eia3ctl",	{Qeia3ctl},		0,	0600,
};
#define Neiatab (sizeof(eiatab)/sizeof(Dirtab))

void
eiareset(void)
{
}

Chan*
eiaattach(char *spec)
{
	return devattach('t', spec);
}

Walkqid*
eiawalk(Chan *c, Chan *nc, char **name, int nname)
{
	return devwalk(c, nc, name, nname, eiatab, Neiatab, devgen);
}

int
eiastat(Chan *c, uchar *db, int n)
{
	return devstat(c, db, n, eiatab, Neiatab, devgen);
}

Chan*
eiaopen(Chan *c, int omode)
{
	c = devopen(c, omode, eiatab, Neiatab, devgen);
	if(waserror()){
		c->flag &= ~COPEN;
		nexterror();
	}
	switch(c->qid.path) {
	case Qeia0:
	case Qeia0ctl:
	case Qeia1:
	case Qeia1ctl:
	case Qeia2:
	case Qeia2ctl:
	case Qeia3:
	case Qeia3ctl:
		c->aux = eiaopenport(PORT(c->qid.path), omode);
		break;
	}
	poperror();
	return c;
}

void
eiaclose(Chan *c)
{
	switch(c->qid.path) {
	case Qeia0:
	case Qeia1:
	case Qeia2:
	case Qeia3:
	case Qeia0ctl:
	case Qeia1ctl:
	case Qeia2ctl:
	case Qeia3ctl:
		if(c->aux){
			eiacloseport(c->aux);
			c->aux = nil;
		}
		break;
	}
}

long
eiaread(Chan *c, void *a, long n, vlong offset)
{
	switch(c->qid.path){
	case Qdir:
		return devdirread(c, a, n, eiatab, Neiatab, devgen);
	case Qeia0:
	case Qeia1:
	case Qeia2:
	case Qeia3:
		n = eiardwr(c->aux, a, n, 0);
		break;
	case Qeia0ctl:
	case Qeia1ctl:
	case Qeia2ctl:
	case Qeia3ctl:
	default:
		error(Ebadusefd);
	}
	return n;
}

long
eiawrite(Chan *c, char *a, long n, vlong offset)
{
	char buf[128];

	switch(c->qid.path){
	case Qeia0:
	case Qeia1:
	case Qeia2:
	case Qeia3:
		n = eiardwr(c->aux, a, n, 1);
		break;
	case Qeia0ctl:
	case Qeia1ctl:
	case Qeia2ctl:
	case Qeia3ctl:
		if(n >= sizeof buf || n < 0)
			error(Ebadarg);
		memmove(buf, a, n);
		buf[n] = '\0';
		eiactl(c->aux, buf);
		break;
	default:
		error(Ebadusefd);
	}
	return n;
}

Dev eiadevtab = {
	't',
	"eia",

	devreset,
	eiaattach,
	eiawalk,
	eiastat,
	eiaopen,
	devcreate,
	eiaclose,
	eiaread,
	devbread,
	eiawrite,
	devbwrite,
	devremove,
	devwstat
};
