xref: /plan9/sys/src/9/omap/devuart.c (revision 696c1e60c910958ad30cdfb7404030ea707cbbf1)
193631029SDavid du Colombier #include	"u.h"
293631029SDavid du Colombier #include	"../port/lib.h"
393631029SDavid du Colombier #include	"mem.h"
493631029SDavid du Colombier #include	"dat.h"
593631029SDavid du Colombier #include	"fns.h"
693631029SDavid du Colombier #include	"io.h"
793631029SDavid du Colombier #include	"../port/error.h"
893631029SDavid du Colombier 
993631029SDavid du Colombier #include	"../port/netif.h"
1093631029SDavid du Colombier 
1193631029SDavid du Colombier enum
1293631029SDavid du Colombier {
1393631029SDavid du Colombier 	/* soft flow control chars */
1493631029SDavid du Colombier 	CTLS= 023,
1593631029SDavid du Colombier 	CTLQ= 021,
1693631029SDavid du Colombier };
1793631029SDavid du Colombier 
1893631029SDavid du Colombier extern Dev uartdevtab;
1993631029SDavid du Colombier extern PhysUart* physuart[];
2093631029SDavid du Colombier 
2193631029SDavid du Colombier static Uart* uartlist;
2293631029SDavid du Colombier static Uart** uart;
2393631029SDavid du Colombier static int uartnuart;
2493631029SDavid du Colombier static Dirtab *uartdir;
2593631029SDavid du Colombier static int uartndir;
2693631029SDavid du Colombier static Timer *uarttimer;
2793631029SDavid du Colombier 
2893631029SDavid du Colombier struct Uartalloc {
2993631029SDavid du Colombier 	Lock;
3093631029SDavid du Colombier 	Uart *elist;	/* list of enabled interfaces */
3193631029SDavid du Colombier } uartalloc;
3293631029SDavid du Colombier 
3393631029SDavid du Colombier static void	uartclock(void);
3493631029SDavid du Colombier static void	uartflow(void*);
3593631029SDavid du Colombier 
3693631029SDavid du Colombier /*
3793631029SDavid du Colombier  *  enable/disable uart and add/remove to list of enabled uarts
3893631029SDavid du Colombier  */
3993631029SDavid du Colombier //static
4093631029SDavid du Colombier Uart*
uartenable(Uart * p)4193631029SDavid du Colombier uartenable(Uart *p)
4293631029SDavid du Colombier {
4393631029SDavid du Colombier 	Uart **l;
4493631029SDavid du Colombier 
4593631029SDavid du Colombier 	if (up == nil)
4693631029SDavid du Colombier 		return p;		/* too soon; try again later */
4793631029SDavid du Colombier //		return nil;
4893631029SDavid du Colombier 
4993631029SDavid du Colombier 	if(p->iq == nil){
5093631029SDavid du Colombier 		if((p->iq = qopen(8*1024, 0, uartflow, p)) == nil)
5193631029SDavid du Colombier 			return nil;
5293631029SDavid du Colombier 	}
5393631029SDavid du Colombier 	else
5493631029SDavid du Colombier 		qreopen(p->iq);
5593631029SDavid du Colombier 	if(p->oq == nil){
5693631029SDavid du Colombier 		if((p->oq = qopen(8*1024, 0, uartkick, p)) == nil){
5793631029SDavid du Colombier 			qfree(p->iq);
5893631029SDavid du Colombier 			p->iq = nil;
5993631029SDavid du Colombier 			return nil;
6093631029SDavid du Colombier 		}
6193631029SDavid du Colombier 	}
6293631029SDavid du Colombier 	else
6393631029SDavid du Colombier 		qreopen(p->oq);
6493631029SDavid du Colombier 
6593631029SDavid du Colombier 	p->ir = p->istage;
6693631029SDavid du Colombier 	p->iw = p->istage;
6793631029SDavid du Colombier 	p->ie = &p->istage[Stagesize];
6893631029SDavid du Colombier 	p->op = p->ostage;
6993631029SDavid du Colombier 	p->oe = p->ostage;
7093631029SDavid du Colombier 
7193631029SDavid du Colombier 	p->hup_dsr = p->hup_dcd = 0;
7293631029SDavid du Colombier 	p->dsr = p->dcd = 0;
7393631029SDavid du Colombier 
7493631029SDavid du Colombier 	/* assume we can send */
7593631029SDavid du Colombier 	p->cts = 1;
7693631029SDavid du Colombier 	p->ctsbackoff = 0;
7793631029SDavid du Colombier 
7893631029SDavid du Colombier 	if (up) {
7993631029SDavid du Colombier 		if(p->bits == 0)
8093631029SDavid du Colombier 			uartctl(p, "l8");
8193631029SDavid du Colombier 		if(p->stop == 0)
8293631029SDavid du Colombier 			uartctl(p, "s1");
8393631029SDavid du Colombier 		if(p->parity == 0)
8493631029SDavid du Colombier 			uartctl(p, "pn");
8593631029SDavid du Colombier 		if(p->baud == 0)
8693631029SDavid du Colombier 			uartctl(p, "b9600");
8793631029SDavid du Colombier 		(*p->phys->enable)(p, 1);
8893631029SDavid du Colombier 	}
8993631029SDavid du Colombier 
9093631029SDavid du Colombier 	/*
9193631029SDavid du Colombier 	 * use ilock because uartclock can otherwise interrupt here
9293631029SDavid du Colombier 	 * and would hang on an attempt to lock uartalloc.
9393631029SDavid du Colombier 	 */
9493631029SDavid du Colombier 	ilock(&uartalloc);
9593631029SDavid du Colombier 	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
9693631029SDavid du Colombier 		if(*l == p)
9793631029SDavid du Colombier 			break;
9893631029SDavid du Colombier 	}
9993631029SDavid du Colombier 	if(*l == 0){
10093631029SDavid du Colombier 		p->elist = uartalloc.elist;
10193631029SDavid du Colombier 		uartalloc.elist = p;
10293631029SDavid du Colombier 	}
10393631029SDavid du Colombier 	p->enabled = 1;
10493631029SDavid du Colombier 	iunlock(&uartalloc);
10593631029SDavid du Colombier 
10693631029SDavid du Colombier 	return p;
10793631029SDavid du Colombier }
10893631029SDavid du Colombier 
10993631029SDavid du Colombier static void
uartdisable(Uart * p)11093631029SDavid du Colombier uartdisable(Uart *p)
11193631029SDavid du Colombier {
11293631029SDavid du Colombier 	Uart **l;
11393631029SDavid du Colombier 
11493631029SDavid du Colombier 	(*p->phys->disable)(p);
11593631029SDavid du Colombier 
11693631029SDavid du Colombier 	ilock(&uartalloc);
11793631029SDavid du Colombier 	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
11893631029SDavid du Colombier 		if(*l == p){
11993631029SDavid du Colombier 			*l = p->elist;
12093631029SDavid du Colombier 			break;
12193631029SDavid du Colombier 		}
12293631029SDavid du Colombier 	}
12393631029SDavid du Colombier 	p->enabled = 0;
12493631029SDavid du Colombier 	iunlock(&uartalloc);
12593631029SDavid du Colombier }
12693631029SDavid du Colombier 
12793631029SDavid du Colombier void
uartmouse(Uart * p,int (* putc)(Queue *,int),int setb1200)12893631029SDavid du Colombier uartmouse(Uart* p, int (*putc)(Queue*, int), int setb1200)
12993631029SDavid du Colombier {
13093631029SDavid du Colombier 	qlock(p);
13193631029SDavid du Colombier 	if(p->opens++ == 0 && uartenable(p) == nil){
13293631029SDavid du Colombier 		qunlock(p);
13393631029SDavid du Colombier 		error(Enodev);
13493631029SDavid du Colombier 	}
13593631029SDavid du Colombier 	if(setb1200)
13693631029SDavid du Colombier 		uartctl(p, "b1200");
13793631029SDavid du Colombier 	p->putc = putc;
13893631029SDavid du Colombier 	p->special = 1;
13993631029SDavid du Colombier 	qunlock(p);
14093631029SDavid du Colombier }
14193631029SDavid du Colombier 
14293631029SDavid du Colombier void
uartsetmouseputc(Uart * p,int (* putc)(Queue *,int))14393631029SDavid du Colombier uartsetmouseputc(Uart* p, int (*putc)(Queue*, int))
14493631029SDavid du Colombier {
14593631029SDavid du Colombier 	qlock(p);
14693631029SDavid du Colombier 	if(p->opens == 0 || p->special == 0){
14793631029SDavid du Colombier 		qunlock(p);
14893631029SDavid du Colombier 		error(Enodev);
14993631029SDavid du Colombier 	}
15093631029SDavid du Colombier 	p->putc = putc;
15193631029SDavid du Colombier 	qunlock(p);
15293631029SDavid du Colombier }
15393631029SDavid du Colombier 
15493631029SDavid du Colombier static void
setlength(int i)15593631029SDavid du Colombier setlength(int i)
15693631029SDavid du Colombier {
15793631029SDavid du Colombier 	Uart *p;
15893631029SDavid du Colombier 
15993631029SDavid du Colombier 	if(i > 0){
16093631029SDavid du Colombier 		p = uart[i];
16193631029SDavid du Colombier 		if(p && p->opens && p->iq)
16293631029SDavid du Colombier 			uartdir[1+3*i].length = qlen(p->iq);
16393631029SDavid du Colombier 	} else for(i = 0; i < uartnuart; i++){
16493631029SDavid du Colombier 		p = uart[i];
16593631029SDavid du Colombier 		if(p && p->opens && p->iq)
16693631029SDavid du Colombier 			uartdir[1+3*i].length = qlen(p->iq);
16793631029SDavid du Colombier 	}
16893631029SDavid du Colombier }
16993631029SDavid du Colombier 
17093631029SDavid du Colombier /*
17193631029SDavid du Colombier  *  set up the '#t' directory
17293631029SDavid du Colombier  */
17393631029SDavid du Colombier static void
uartreset(void)17493631029SDavid du Colombier uartreset(void)
17593631029SDavid du Colombier {
17693631029SDavid du Colombier 	int i;
17793631029SDavid du Colombier 	Dirtab *dp;
17893631029SDavid du Colombier 	Uart *p, *tail;
17993631029SDavid du Colombier 
18093631029SDavid du Colombier 	tail = nil;
18193631029SDavid du Colombier 	for(i = 0; physuart[i] != nil; i++){
18293631029SDavid du Colombier 		if(physuart[i]->pnp == nil)
18393631029SDavid du Colombier 			continue;
18493631029SDavid du Colombier 		if((p = physuart[i]->pnp()) == nil)
18593631029SDavid du Colombier 			continue;
18693631029SDavid du Colombier 		if(uartlist != nil)
18793631029SDavid du Colombier 			tail->next = p;
18893631029SDavid du Colombier 		else
18993631029SDavid du Colombier 			uartlist = p;
19093631029SDavid du Colombier 		for(tail = p; tail->next != nil; tail = tail->next)
19193631029SDavid du Colombier 			uartnuart++;
19293631029SDavid du Colombier 		uartnuart++;
19393631029SDavid du Colombier 	}
19493631029SDavid du Colombier 
19593631029SDavid du Colombier 	if(uartnuart)
19693631029SDavid du Colombier 		uart = xalloc(uartnuart*sizeof(Uart*));
19793631029SDavid du Colombier 
19893631029SDavid du Colombier 	uartndir = 1 + 3*uartnuart;
19993631029SDavid du Colombier 	uartdir = xalloc(uartndir * sizeof(Dirtab));
20093631029SDavid du Colombier 	if (uart == nil || uartdir == nil)
20193631029SDavid du Colombier 		panic("uartreset: no memory");
20293631029SDavid du Colombier 	dp = uartdir;
20393631029SDavid du Colombier 	strcpy(dp->name, ".");
20493631029SDavid du Colombier 	mkqid(&dp->qid, 0, 0, QTDIR);
20593631029SDavid du Colombier 	dp->length = 0;
20693631029SDavid du Colombier 	dp->perm = DMDIR|0555;
20793631029SDavid du Colombier 	dp++;
20893631029SDavid du Colombier 	p = uartlist;
20993631029SDavid du Colombier 	for(i = 0; i < uartnuart; i++){
21093631029SDavid du Colombier 		/* 3 directory entries per port */
21157d98441SDavid du Colombier 		snprint(dp->name, sizeof dp->name, "eia%d", i);
21293631029SDavid du Colombier 		dp->qid.path = NETQID(i, Ndataqid);
21393631029SDavid du Colombier 		dp->perm = 0660;
21493631029SDavid du Colombier 		dp++;
21557d98441SDavid du Colombier 		snprint(dp->name, sizeof dp->name, "eia%dctl", i);
21693631029SDavid du Colombier 		dp->qid.path = NETQID(i, Nctlqid);
21793631029SDavid du Colombier 		dp->perm = 0660;
21893631029SDavid du Colombier 		dp++;
21957d98441SDavid du Colombier 		snprint(dp->name, sizeof dp->name, "eia%dstatus", i);
22093631029SDavid du Colombier 		dp->qid.path = NETQID(i, Nstatqid);
22193631029SDavid du Colombier 		dp->perm = 0444;
22293631029SDavid du Colombier 		dp++;
22393631029SDavid du Colombier 
22493631029SDavid du Colombier 		uart[i] = p;
22593631029SDavid du Colombier 		p->dev = i;
22693631029SDavid du Colombier 		if(p->console || p->special){
22793631029SDavid du Colombier 			if(uartenable(p) != nil){
22893631029SDavid du Colombier 				if(p->console && up){
22993631029SDavid du Colombier 					kbdq = p->iq;
23093631029SDavid du Colombier 					serialoq = p->oq;
23193631029SDavid du Colombier 					p->putc = kbdcr2nl;
23293631029SDavid du Colombier 				}
23393631029SDavid du Colombier 				p->opens++;
23493631029SDavid du Colombier 			}
23593631029SDavid du Colombier 		}
23693631029SDavid du Colombier 		p = p->next;
23793631029SDavid du Colombier 	}
23893631029SDavid du Colombier 
23993631029SDavid du Colombier 	if(uartnuart){
24093631029SDavid du Colombier 		/*
24193631029SDavid du Colombier 		 * at 115200 baud, the 1024 char buffer takes 56 ms to process,
24293631029SDavid du Colombier 		 * processing it every 22 ms should be fine.
24393631029SDavid du Colombier 		 */
24493631029SDavid du Colombier 		uarttimer = addclock0link(uartclock, 22);
24593631029SDavid du Colombier 	}
24693631029SDavid du Colombier }
24793631029SDavid du Colombier 
24893631029SDavid du Colombier 
24993631029SDavid du Colombier static Chan*
uartattach(char * spec)25093631029SDavid du Colombier uartattach(char *spec)
25193631029SDavid du Colombier {
25293631029SDavid du Colombier 	return devattach('t', spec);
25393631029SDavid du Colombier }
25493631029SDavid du Colombier 
25593631029SDavid du Colombier static Walkqid*
uartwalk(Chan * c,Chan * nc,char ** name,int nname)25693631029SDavid du Colombier uartwalk(Chan *c, Chan *nc, char **name, int nname)
25793631029SDavid du Colombier {
25893631029SDavid du Colombier 	return devwalk(c, nc, name, nname, uartdir, uartndir, devgen);
25993631029SDavid du Colombier }
26093631029SDavid du Colombier 
26193631029SDavid du Colombier static int
uartstat(Chan * c,uchar * dp,int n)26293631029SDavid du Colombier uartstat(Chan *c, uchar *dp, int n)
26393631029SDavid du Colombier {
26493631029SDavid du Colombier 	if(NETTYPE(c->qid.path) == Ndataqid)
26593631029SDavid du Colombier 		setlength(NETID(c->qid.path));
26693631029SDavid du Colombier 	return devstat(c, dp, n, uartdir, uartndir, devgen);
26793631029SDavid du Colombier }
26893631029SDavid du Colombier 
26993631029SDavid du Colombier static Chan*
uartopen(Chan * c,int omode)27093631029SDavid du Colombier uartopen(Chan *c, int omode)
27193631029SDavid du Colombier {
27293631029SDavid du Colombier 	Uart *p;
27393631029SDavid du Colombier 
27493631029SDavid du Colombier 	c = devopen(c, omode, uartdir, uartndir, devgen);
27593631029SDavid du Colombier 
27693631029SDavid du Colombier 	switch(NETTYPE(c->qid.path)){
27793631029SDavid du Colombier 	case Nctlqid:
27893631029SDavid du Colombier 	case Ndataqid:
27993631029SDavid du Colombier 		p = uart[NETID(c->qid.path)];
28093631029SDavid du Colombier 		qlock(p);
28193631029SDavid du Colombier 		if(p->opens++ == 0 && uartenable(p) == nil){
28293631029SDavid du Colombier 			qunlock(p);
28393631029SDavid du Colombier 			c->flag &= ~COPEN;
28493631029SDavid du Colombier 			error(Enodev);
28593631029SDavid du Colombier 		}
28693631029SDavid du Colombier 		qunlock(p);
28793631029SDavid du Colombier 		break;
28893631029SDavid du Colombier 	}
28993631029SDavid du Colombier 
29093631029SDavid du Colombier 	c->iounit = qiomaxatomic;
29193631029SDavid du Colombier 	return c;
29293631029SDavid du Colombier }
29393631029SDavid du Colombier 
29493631029SDavid du Colombier static int
uartdrained(void * arg)29593631029SDavid du Colombier uartdrained(void* arg)
29693631029SDavid du Colombier {
29793631029SDavid du Colombier 	Uart *p;
29893631029SDavid du Colombier 
29993631029SDavid du Colombier 	p = arg;
30093631029SDavid du Colombier 	return qlen(p->oq) == 0 && p->op == p->oe;
30193631029SDavid du Colombier }
30293631029SDavid du Colombier 
30393631029SDavid du Colombier static void
uartdrainoutput(Uart * p)30493631029SDavid du Colombier uartdrainoutput(Uart *p)
30593631029SDavid du Colombier {
30693631029SDavid du Colombier 	if(!p->enabled || up == nil)
30793631029SDavid du Colombier 		return;
30893631029SDavid du Colombier 
30993631029SDavid du Colombier 	p->drain = 1;
31093631029SDavid du Colombier 	if(waserror()){
31193631029SDavid du Colombier 		p->drain = 0;
31293631029SDavid du Colombier 		nexterror();
31393631029SDavid du Colombier 	}
31493631029SDavid du Colombier 	sleep(&p->r, uartdrained, p);
31593631029SDavid du Colombier 	poperror();
31693631029SDavid du Colombier }
31793631029SDavid du Colombier 
31893631029SDavid du Colombier static void
uartclose(Chan * c)31993631029SDavid du Colombier uartclose(Chan *c)
32093631029SDavid du Colombier {
32193631029SDavid du Colombier 	Uart *p;
32293631029SDavid du Colombier 
32393631029SDavid du Colombier 	if(c->qid.type & QTDIR)
32493631029SDavid du Colombier 		return;
32593631029SDavid du Colombier 	if((c->flag & COPEN) == 0)
32693631029SDavid du Colombier 		return;
32793631029SDavid du Colombier 	switch(NETTYPE(c->qid.path)){
32893631029SDavid du Colombier 	case Ndataqid:
32993631029SDavid du Colombier 	case Nctlqid:
33093631029SDavid du Colombier 		p = uart[NETID(c->qid.path)];
33193631029SDavid du Colombier 		qlock(p);
33293631029SDavid du Colombier 		if(--(p->opens) == 0){
33393631029SDavid du Colombier 			qclose(p->iq);
33493631029SDavid du Colombier 			ilock(&p->rlock);
33593631029SDavid du Colombier 			p->ir = p->iw = p->istage;
33693631029SDavid du Colombier 			iunlock(&p->rlock);
33793631029SDavid du Colombier 
33893631029SDavid du Colombier 			/*
33993631029SDavid du Colombier 			 */
34093631029SDavid du Colombier 			qhangup(p->oq, nil);
34193631029SDavid du Colombier 			if(!waserror()){
34293631029SDavid du Colombier 				uartdrainoutput(p);
34393631029SDavid du Colombier 				poperror();
34493631029SDavid du Colombier 			}
34593631029SDavid du Colombier 			qclose(p->oq);
34693631029SDavid du Colombier 			uartdisable(p);
34793631029SDavid du Colombier 			p->dcd = p->dsr = p->dohup = 0;
34893631029SDavid du Colombier 		}
34993631029SDavid du Colombier 		qunlock(p);
35093631029SDavid du Colombier 		break;
35193631029SDavid du Colombier 	}
35293631029SDavid du Colombier }
35393631029SDavid du Colombier 
35493631029SDavid du Colombier static long
uartread(Chan * c,void * buf,long n,vlong off)35593631029SDavid du Colombier uartread(Chan *c, void *buf, long n, vlong off)
35693631029SDavid du Colombier {
35793631029SDavid du Colombier 	Uart *p;
35893631029SDavid du Colombier 	ulong offset = off;
35993631029SDavid du Colombier 
36093631029SDavid du Colombier 	if(c->qid.type & QTDIR){
36193631029SDavid du Colombier 		setlength(-1);
36293631029SDavid du Colombier 		return devdirread(c, buf, n, uartdir, uartndir, devgen);
36393631029SDavid du Colombier 	}
36493631029SDavid du Colombier 
36593631029SDavid du Colombier 	p = uart[NETID(c->qid.path)];
36693631029SDavid du Colombier 	switch(NETTYPE(c->qid.path)){
36793631029SDavid du Colombier 	case Ndataqid:
36893631029SDavid du Colombier 		return qread(p->iq, buf, n);
36993631029SDavid du Colombier 	case Nctlqid:
37093631029SDavid du Colombier 		return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
37193631029SDavid du Colombier 	case Nstatqid:
37293631029SDavid du Colombier 		return (*p->phys->status)(p, buf, n, offset);
37393631029SDavid du Colombier 	}
37493631029SDavid du Colombier 
37593631029SDavid du Colombier 	return 0;
37693631029SDavid du Colombier }
37793631029SDavid du Colombier 
37893631029SDavid du Colombier int
uartctl(Uart * p,char * cmd)37993631029SDavid du Colombier uartctl(Uart *p, char *cmd)
38093631029SDavid du Colombier {
38193631029SDavid du Colombier 	char *f[16];
38293631029SDavid du Colombier 	int i, n, nf;
38393631029SDavid du Colombier 
38493631029SDavid du Colombier 	nf = tokenize(cmd, f, nelem(f));
38593631029SDavid du Colombier 	for(i = 0; i < nf; i++){
38693631029SDavid du Colombier 		if(strncmp(f[i], "break", 5) == 0){
38793631029SDavid du Colombier 			(*p->phys->dobreak)(p, 0);
38893631029SDavid du Colombier 			continue;
38993631029SDavid du Colombier 		}
39093631029SDavid du Colombier 
39193631029SDavid du Colombier 		n = atoi(f[i]+1);
39293631029SDavid du Colombier 		switch(*f[i]){
39393631029SDavid du Colombier 		case 'B':
39493631029SDavid du Colombier 		case 'b':
39593631029SDavid du Colombier 			uartdrainoutput(p);
39693631029SDavid du Colombier 			if((*p->phys->baud)(p, n) < 0)
39793631029SDavid du Colombier 				return -1;
39893631029SDavid du Colombier 			break;
39993631029SDavid du Colombier 		case 'C':
40093631029SDavid du Colombier 		case 'c':
40193631029SDavid du Colombier 			p->hup_dcd = n;
40293631029SDavid du Colombier 			break;
40393631029SDavid du Colombier 		case 'D':
40493631029SDavid du Colombier 		case 'd':
40593631029SDavid du Colombier 			uartdrainoutput(p);
40693631029SDavid du Colombier 			(*p->phys->dtr)(p, n);
40793631029SDavid du Colombier 			break;
40893631029SDavid du Colombier 		case 'E':
40993631029SDavid du Colombier 		case 'e':
41093631029SDavid du Colombier 			p->hup_dsr = n;
41193631029SDavid du Colombier 			break;
41293631029SDavid du Colombier 		case 'f':
41393631029SDavid du Colombier 		case 'F':
41493631029SDavid du Colombier 			if(p->oq != nil)
41593631029SDavid du Colombier 				qflush(p->oq);
41693631029SDavid du Colombier 			break;
41793631029SDavid du Colombier 		case 'H':
41893631029SDavid du Colombier 		case 'h':
41993631029SDavid du Colombier 			if(p->iq != nil)
42093631029SDavid du Colombier 				qhangup(p->iq, 0);
42193631029SDavid du Colombier 			if(p->oq != nil)
42293631029SDavid du Colombier 				qhangup(p->oq, 0);
42393631029SDavid du Colombier 			break;
42493631029SDavid du Colombier 		case 'i':
42593631029SDavid du Colombier 		case 'I':
42693631029SDavid du Colombier 			uartdrainoutput(p);
42793631029SDavid du Colombier 			(*p->phys->fifo)(p, n);
42893631029SDavid du Colombier 			break;
42993631029SDavid du Colombier 		case 'K':
43093631029SDavid du Colombier 		case 'k':
43193631029SDavid du Colombier 			uartdrainoutput(p);
43293631029SDavid du Colombier 			(*p->phys->dobreak)(p, n);
43393631029SDavid du Colombier 			break;
43493631029SDavid du Colombier 		case 'L':
43593631029SDavid du Colombier 		case 'l':
43693631029SDavid du Colombier 			uartdrainoutput(p);
43793631029SDavid du Colombier 			if((*p->phys->bits)(p, n) < 0)
43893631029SDavid du Colombier 				return -1;
43993631029SDavid du Colombier 			break;
44093631029SDavid du Colombier 		case 'm':
44193631029SDavid du Colombier 		case 'M':
44293631029SDavid du Colombier 			uartdrainoutput(p);
44393631029SDavid du Colombier 			(*p->phys->modemctl)(p, n);
44493631029SDavid du Colombier 			break;
44593631029SDavid du Colombier 		case 'n':
44693631029SDavid du Colombier 		case 'N':
44793631029SDavid du Colombier 			if(p->oq != nil)
44893631029SDavid du Colombier 				qnoblock(p->oq, n);
44993631029SDavid du Colombier 			break;
45093631029SDavid du Colombier 		case 'P':
45193631029SDavid du Colombier 		case 'p':
45293631029SDavid du Colombier 			uartdrainoutput(p);
45393631029SDavid du Colombier 			if((*p->phys->parity)(p, *(f[i]+1)) < 0)
45493631029SDavid du Colombier 				return -1;
45593631029SDavid du Colombier 			break;
45693631029SDavid du Colombier 		case 'Q':
45793631029SDavid du Colombier 		case 'q':
45893631029SDavid du Colombier 			if(p->iq != nil)
45993631029SDavid du Colombier 				qsetlimit(p->iq, n);
46093631029SDavid du Colombier 			if(p->oq != nil)
46193631029SDavid du Colombier 				qsetlimit(p->oq, n);
46293631029SDavid du Colombier 			break;
46393631029SDavid du Colombier 		case 'R':
46493631029SDavid du Colombier 		case 'r':
46593631029SDavid du Colombier 			uartdrainoutput(p);
46693631029SDavid du Colombier 			(*p->phys->rts)(p, n);
46793631029SDavid du Colombier 			break;
46893631029SDavid du Colombier 		case 'S':
46993631029SDavid du Colombier 		case 's':
47093631029SDavid du Colombier 			uartdrainoutput(p);
47193631029SDavid du Colombier 			if((*p->phys->stop)(p, n) < 0)
47293631029SDavid du Colombier 				return -1;
47393631029SDavid du Colombier 			break;
47493631029SDavid du Colombier 		case 'W':
47593631029SDavid du Colombier 		case 'w':
47693631029SDavid du Colombier 			if(uarttimer == nil || n < 1)
47793631029SDavid du Colombier 				return -1;
47893631029SDavid du Colombier 			uarttimer->tns = (vlong)n * 100000LL;
47993631029SDavid du Colombier 			break;
48093631029SDavid du Colombier 		case 'X':
48193631029SDavid du Colombier 		case 'x':
48293631029SDavid du Colombier 			if(p->enabled){
48393631029SDavid du Colombier 				ilock(&p->tlock);
48493631029SDavid du Colombier 				p->xonoff = n;
48593631029SDavid du Colombier 				iunlock(&p->tlock);
48693631029SDavid du Colombier 			}
48793631029SDavid du Colombier 			break;
48893631029SDavid du Colombier 		}
48993631029SDavid du Colombier 	}
49093631029SDavid du Colombier 	return 0;
49193631029SDavid du Colombier }
49293631029SDavid du Colombier 
49393631029SDavid du Colombier static long
uartwrite(Chan * c,void * buf,long n,vlong)49493631029SDavid du Colombier uartwrite(Chan *c, void *buf, long n, vlong)
49593631029SDavid du Colombier {
49693631029SDavid du Colombier 	Uart *p;
49793631029SDavid du Colombier 	char *cmd;
49893631029SDavid du Colombier 
49993631029SDavid du Colombier 	if(c->qid.type & QTDIR)
50093631029SDavid du Colombier 		error(Eperm);
50193631029SDavid du Colombier 
50293631029SDavid du Colombier 	p = uart[NETID(c->qid.path)];
50393631029SDavid du Colombier 
50493631029SDavid du Colombier 	switch(NETTYPE(c->qid.path)){
50593631029SDavid du Colombier 	case Ndataqid:
50693631029SDavid du Colombier 		qlock(p);
50793631029SDavid du Colombier 		if(waserror()){
50893631029SDavid du Colombier 			qunlock(p);
50993631029SDavid du Colombier 			nexterror();
51093631029SDavid du Colombier 		}
51193631029SDavid du Colombier 
51293631029SDavid du Colombier 		n = qwrite(p->oq, buf, n);
51393631029SDavid du Colombier 
51493631029SDavid du Colombier 		qunlock(p);
51593631029SDavid du Colombier 		poperror();
51693631029SDavid du Colombier 		break;
51793631029SDavid du Colombier 	case Nctlqid:
51893631029SDavid du Colombier 		cmd = malloc(n+1);
51993631029SDavid du Colombier 		memmove(cmd, buf, n);
52093631029SDavid du Colombier 		cmd[n] = 0;
52193631029SDavid du Colombier 		qlock(p);
52293631029SDavid du Colombier 		if(waserror()){
52393631029SDavid du Colombier 			qunlock(p);
52493631029SDavid du Colombier 			free(cmd);
52593631029SDavid du Colombier 			nexterror();
52693631029SDavid du Colombier 		}
52793631029SDavid du Colombier 
52893631029SDavid du Colombier 		/* let output drain */
52993631029SDavid du Colombier 		if(uartctl(p, cmd) < 0)
53093631029SDavid du Colombier 			error(Ebadarg);
53193631029SDavid du Colombier 
53293631029SDavid du Colombier 		qunlock(p);
53393631029SDavid du Colombier 		poperror();
53493631029SDavid du Colombier 		free(cmd);
53593631029SDavid du Colombier 		break;
53693631029SDavid du Colombier 	}
53793631029SDavid du Colombier 
53893631029SDavid du Colombier 	return n;
53993631029SDavid du Colombier }
54093631029SDavid du Colombier 
54193631029SDavid du Colombier static int
uartwstat(Chan * c,uchar * dp,int n)54293631029SDavid du Colombier uartwstat(Chan *c, uchar *dp, int n)
54393631029SDavid du Colombier {
54493631029SDavid du Colombier 	Dir d;
54593631029SDavid du Colombier 	Dirtab *dt;
54693631029SDavid du Colombier 
54793631029SDavid du Colombier 	if(!iseve())
54893631029SDavid du Colombier 		error(Eperm);
54993631029SDavid du Colombier 	if(QTDIR & c->qid.type)
55093631029SDavid du Colombier 		error(Eperm);
55193631029SDavid du Colombier 	if(NETTYPE(c->qid.path) == Nstatqid)
55293631029SDavid du Colombier 		error(Eperm);
55393631029SDavid du Colombier 
55493631029SDavid du Colombier 	dt = &uartdir[1 + 3 * NETID(c->qid.path)];
55593631029SDavid du Colombier 	n = convM2D(dp, n, &d, nil);
55693631029SDavid du Colombier 	if(n == 0)
55793631029SDavid du Colombier 		error(Eshortstat);
55893631029SDavid du Colombier 	if(d.mode != ~0UL)
55993631029SDavid du Colombier 		dt[0].perm = dt[1].perm = d.mode;
56093631029SDavid du Colombier 	return n;
56193631029SDavid du Colombier }
56293631029SDavid du Colombier 
56393631029SDavid du Colombier void
uartpower(int on)56493631029SDavid du Colombier uartpower(int on)
56593631029SDavid du Colombier {
56693631029SDavid du Colombier 	Uart *p;
56793631029SDavid du Colombier 
56893631029SDavid du Colombier 	for(p = uartlist; p != nil; p = p->next) {
56993631029SDavid du Colombier 		if(p->phys->power)
57093631029SDavid du Colombier 			(*p->phys->power)(p, on);
57193631029SDavid du Colombier 	}
57293631029SDavid du Colombier }
57393631029SDavid du Colombier 
57493631029SDavid du Colombier Dev uartdevtab = {
57593631029SDavid du Colombier 	't',
57693631029SDavid du Colombier 	"uart",
57793631029SDavid du Colombier 
57893631029SDavid du Colombier 	uartreset,
57993631029SDavid du Colombier 	devinit,
58093631029SDavid du Colombier 	devshutdown,
58193631029SDavid du Colombier 	uartattach,
58293631029SDavid du Colombier 	uartwalk,
58393631029SDavid du Colombier 	uartstat,
58493631029SDavid du Colombier 	uartopen,
58593631029SDavid du Colombier 	devcreate,
58693631029SDavid du Colombier 	uartclose,
58793631029SDavid du Colombier 	uartread,
58893631029SDavid du Colombier 	devbread,
58993631029SDavid du Colombier 	uartwrite,
59093631029SDavid du Colombier 	devbwrite,
59193631029SDavid du Colombier 	devremove,
59293631029SDavid du Colombier 	uartwstat,
59393631029SDavid du Colombier 	uartpower,
59493631029SDavid du Colombier };
59593631029SDavid du Colombier 
59693631029SDavid du Colombier /*
59793631029SDavid du Colombier  *  restart input if it's off
59893631029SDavid du Colombier  */
59993631029SDavid du Colombier static void
uartflow(void * v)60093631029SDavid du Colombier uartflow(void *v)
60193631029SDavid du Colombier {
60293631029SDavid du Colombier 	Uart *p;
60393631029SDavid du Colombier 
60493631029SDavid du Colombier 	p = v;
60593631029SDavid du Colombier 	if(p->modem)
60693631029SDavid du Colombier 		(*p->phys->rts)(p, 1);
60793631029SDavid du Colombier }
60893631029SDavid du Colombier 
60993631029SDavid du Colombier /*
61093631029SDavid du Colombier  *  put some bytes into the local queue to avoid calling
61193631029SDavid du Colombier  *  qconsume for every character
61293631029SDavid du Colombier  */
61393631029SDavid du Colombier int
uartstageoutput(Uart * p)61493631029SDavid du Colombier uartstageoutput(Uart *p)
61593631029SDavid du Colombier {
61693631029SDavid du Colombier 	int n;
61793631029SDavid du Colombier 
61893631029SDavid du Colombier 	n = qconsume(p->oq, p->ostage, Stagesize);
61993631029SDavid du Colombier 	if(n <= 0)
62093631029SDavid du Colombier //		n = 0;			/* experiment */
62193631029SDavid du Colombier 		return 0;
62293631029SDavid du Colombier 	p->op = p->ostage;
62393631029SDavid du Colombier 	p->oe = p->ostage + n;
62493631029SDavid du Colombier 	return n;
62593631029SDavid du Colombier }
62693631029SDavid du Colombier 
62793631029SDavid du Colombier /*
62893631029SDavid du Colombier  *  restart output
62993631029SDavid du Colombier  */
63093631029SDavid du Colombier void
uartkick(void * v)63193631029SDavid du Colombier uartkick(void *v)
63293631029SDavid du Colombier {
63393631029SDavid du Colombier 	Uart *p = v;
63493631029SDavid du Colombier 
63593631029SDavid du Colombier 	if(p->blocked)
63693631029SDavid du Colombier 		return;
63793631029SDavid du Colombier 
63893631029SDavid du Colombier 	ilock(&p->tlock);
63993631029SDavid du Colombier 	(*p->phys->kick)(p);
64093631029SDavid du Colombier 	iunlock(&p->tlock);
64193631029SDavid du Colombier 
64293631029SDavid du Colombier 	if(p->drain && uartdrained(p)){
64393631029SDavid du Colombier 		p->drain = 0;
64493631029SDavid du Colombier 		wakeup(&p->r);
64593631029SDavid du Colombier 	}
64693631029SDavid du Colombier }
64793631029SDavid du Colombier 
64893631029SDavid du Colombier /*
64993631029SDavid du Colombier  * Move data from the interrupt staging area to
65093631029SDavid du Colombier  * the input Queue.
65193631029SDavid du Colombier  */
65293631029SDavid du Colombier static void
uartstageinput(Uart * p)65393631029SDavid du Colombier uartstageinput(Uart *p)
65493631029SDavid du Colombier {
65593631029SDavid du Colombier 	int n;
65693631029SDavid du Colombier 	uchar *ir, *iw;
65793631029SDavid du Colombier 
65893631029SDavid du Colombier 	while(p->ir != p->iw){
65993631029SDavid du Colombier 		ir = p->ir;
66093631029SDavid du Colombier 		if(p->ir > p->iw){
66193631029SDavid du Colombier 			iw = p->ie;
66293631029SDavid du Colombier 			p->ir = p->istage;
66393631029SDavid du Colombier 		}
66493631029SDavid du Colombier 		else{
66593631029SDavid du Colombier 			iw = p->iw;
66693631029SDavid du Colombier 			p->ir = p->iw;
66793631029SDavid du Colombier 		}
66893631029SDavid du Colombier 		if((n = qproduce(p->iq, ir, iw - ir)) < 0){
66993631029SDavid du Colombier 			p->serr++;
67093631029SDavid du Colombier 			(*p->phys->rts)(p, 0);
67193631029SDavid du Colombier 		}
67293631029SDavid du Colombier 		else if(n == 0)
67393631029SDavid du Colombier 			p->berr++;
67493631029SDavid du Colombier 	}
67593631029SDavid du Colombier }
67693631029SDavid du Colombier 
67793631029SDavid du Colombier /*
67893631029SDavid du Colombier  *  receive a character at interrupt time
67993631029SDavid du Colombier  */
68093631029SDavid du Colombier void
uartrecv(Uart * p,char ch)68193631029SDavid du Colombier uartrecv(Uart *p,  char ch)
68293631029SDavid du Colombier {
68393631029SDavid du Colombier 	uchar *next;
68493631029SDavid du Colombier 
68593631029SDavid du Colombier 	/* software flow control */
68693631029SDavid du Colombier 	if(p->xonoff){
68793631029SDavid du Colombier 		if(ch == CTLS){
68893631029SDavid du Colombier 			p->blocked = 1;
68993631029SDavid du Colombier 		}else if(ch == CTLQ){
69093631029SDavid du Colombier 			p->blocked = 0;
69193631029SDavid du Colombier 			p->ctsbackoff = 2; /* clock gets output going again */
69293631029SDavid du Colombier 		}
69393631029SDavid du Colombier 	}
69493631029SDavid du Colombier 
69593631029SDavid du Colombier 	/* receive the character */
69693631029SDavid du Colombier 	if(p->putc)
69793631029SDavid du Colombier 		p->putc(p->iq, ch);
69893631029SDavid du Colombier 	else if (p->iw) {		/* maybe the line isn't enabled yet */
69993631029SDavid du Colombier 		ilock(&p->rlock);
70093631029SDavid du Colombier 		next = p->iw + 1;
70193631029SDavid du Colombier 		if(next == p->ie)
70293631029SDavid du Colombier 			next = p->istage;
70393631029SDavid du Colombier 		if(next == p->ir)
70493631029SDavid du Colombier 			uartstageinput(p);
70593631029SDavid du Colombier 		if(next != p->ir){
70693631029SDavid du Colombier 			*p->iw = ch;
70793631029SDavid du Colombier 			p->iw = next;
70893631029SDavid du Colombier 		}
70993631029SDavid du Colombier 		iunlock(&p->rlock);
71093631029SDavid du Colombier 	}
71193631029SDavid du Colombier }
71293631029SDavid du Colombier 
71393631029SDavid du Colombier /*
71493631029SDavid du Colombier  *  we save up input characters till clock time to reduce
71593631029SDavid du Colombier  *  per character interrupt overhead.
71693631029SDavid du Colombier  */
71793631029SDavid du Colombier static void
uartclock(void)71893631029SDavid du Colombier uartclock(void)
71993631029SDavid du Colombier {
72093631029SDavid du Colombier 	Uart *p;
72193631029SDavid du Colombier 
72293631029SDavid du Colombier 	ilock(&uartalloc);
72393631029SDavid du Colombier 	for(p = uartalloc.elist; p; p = p->elist){
72493631029SDavid du Colombier 
72593631029SDavid du Colombier 		/* this hopefully amortizes cost of qproduce to many chars */
72693631029SDavid du Colombier 		if(p->iw != p->ir){
72793631029SDavid du Colombier 			ilock(&p->rlock);
72893631029SDavid du Colombier 			uartstageinput(p);
72993631029SDavid du Colombier 			iunlock(&p->rlock);
73093631029SDavid du Colombier 		}
73193631029SDavid du Colombier 
73293631029SDavid du Colombier 		/* hang up if requested */
73393631029SDavid du Colombier 		if(p->dohup){
73493631029SDavid du Colombier 			qhangup(p->iq, 0);
73593631029SDavid du Colombier 			qhangup(p->oq, 0);
73693631029SDavid du Colombier 			p->dohup = 0;
73793631029SDavid du Colombier 		}
73893631029SDavid du Colombier 
73993631029SDavid du Colombier 		/* this adds hysteresis to hardware/software flow control */
74093631029SDavid du Colombier 		if(p->ctsbackoff){
74193631029SDavid du Colombier 			ilock(&p->tlock);
74293631029SDavid du Colombier 			if(p->ctsbackoff){
74393631029SDavid du Colombier 				if(--(p->ctsbackoff) == 0)
74493631029SDavid du Colombier 					(*p->phys->kick)(p);
74593631029SDavid du Colombier 			}
74693631029SDavid du Colombier 			iunlock(&p->tlock);
74793631029SDavid du Colombier 		}
748bacfa46cSDavid du Colombier 		uartkick(p);		/* keep it moving */
74993631029SDavid du Colombier 	}
75093631029SDavid du Colombier 	iunlock(&uartalloc);
75193631029SDavid du Colombier }
75293631029SDavid du Colombier 
75393631029SDavid du Colombier /*
75493631029SDavid du Colombier  * polling console input, output
75593631029SDavid du Colombier  */
75693631029SDavid du Colombier 
75793631029SDavid du Colombier Uart* consuart;
75893631029SDavid du Colombier 
75993631029SDavid du Colombier int
uartgetc(void)76093631029SDavid du Colombier uartgetc(void)
76193631029SDavid du Colombier {
76293631029SDavid du Colombier 	if(consuart == nil || consuart->phys->getc == nil)
76393631029SDavid du Colombier 		return -1;
76493631029SDavid du Colombier 	return consuart->phys->getc(consuart);
76593631029SDavid du Colombier }
76693631029SDavid du Colombier 
76793631029SDavid du Colombier void
uartputc(int c)76893631029SDavid du Colombier uartputc(int c)
76993631029SDavid du Colombier {
770*696c1e60SDavid du Colombier 	char c2;
771*696c1e60SDavid du Colombier 
772*696c1e60SDavid du Colombier 	if(consuart == nil || consuart->phys->putc == nil) {
773*696c1e60SDavid du Colombier 		c2 = c;
774*696c1e60SDavid du Colombier 		if (lprint)
775*696c1e60SDavid du Colombier 			(*lprint)(&c2, 1);
77693631029SDavid du Colombier 		return;
777*696c1e60SDavid du Colombier 	}
77893631029SDavid du Colombier 	consuart->phys->putc(consuart, c);
77993631029SDavid du Colombier }
78093631029SDavid du Colombier 
78193631029SDavid du Colombier void
uartputs(char * s,int n)78293631029SDavid du Colombier uartputs(char *s, int n)
78393631029SDavid du Colombier {
78493631029SDavid du Colombier 	char *e;
78593631029SDavid du Colombier 
786*696c1e60SDavid du Colombier 	if(consuart == nil || consuart->phys->putc == nil) {
787*696c1e60SDavid du Colombier 		if (lprint)
788*696c1e60SDavid du Colombier 			(*lprint)(s, n);
78993631029SDavid du Colombier 		return;
790*696c1e60SDavid du Colombier 	}
79193631029SDavid du Colombier 
79293631029SDavid du Colombier 	e = s+n;
79393631029SDavid du Colombier 	for(; s<e; s++){
79493631029SDavid du Colombier 		if(*s == '\n')
79593631029SDavid du Colombier 			consuart->phys->putc(consuart, '\r');
79693631029SDavid du Colombier 		consuart->phys->putc(consuart, *s);
79793631029SDavid du Colombier 	}
79893631029SDavid du Colombier }
799