xref: /plan9/sys/src/9/port/devuart.c (revision 696c1e60c910958ad30cdfb7404030ea707cbbf1)
19a747e4fSDavid du Colombier #include	"u.h"
29a747e4fSDavid du Colombier #include	"../port/lib.h"
39a747e4fSDavid du Colombier #include	"mem.h"
49a747e4fSDavid du Colombier #include	"dat.h"
59a747e4fSDavid du Colombier #include	"fns.h"
69a747e4fSDavid du Colombier #include	"io.h"
79a747e4fSDavid du Colombier #include	"../port/error.h"
89a747e4fSDavid du Colombier 
99a747e4fSDavid du Colombier #include	"../port/netif.h"
109a747e4fSDavid du Colombier 
119a747e4fSDavid du Colombier enum
129a747e4fSDavid du Colombier {
139a747e4fSDavid du Colombier 	/* soft flow control chars */
149a747e4fSDavid du Colombier 	CTLS= 023,
159a747e4fSDavid du Colombier 	CTLQ= 021,
169a747e4fSDavid du Colombier };
179a747e4fSDavid du Colombier 
189a747e4fSDavid du Colombier extern Dev uartdevtab;
199a747e4fSDavid du Colombier extern PhysUart* physuart[];
209a747e4fSDavid du Colombier 
219a747e4fSDavid du Colombier static Uart* uartlist;
229a747e4fSDavid du Colombier static Uart** uart;
239a747e4fSDavid du Colombier static int uartnuart;
249a747e4fSDavid du Colombier static Dirtab *uartdir;
259a747e4fSDavid du Colombier static int uartndir;
265243b8d1SDavid du Colombier static Timer *uarttimer;
279a747e4fSDavid du Colombier 
289a747e4fSDavid du Colombier struct Uartalloc {
299a747e4fSDavid du Colombier 	Lock;
309a747e4fSDavid du Colombier 	Uart *elist;	/* list of enabled interfaces */
319a747e4fSDavid du Colombier } uartalloc;
329a747e4fSDavid du Colombier 
339a747e4fSDavid du Colombier static void	uartclock(void);
349a747e4fSDavid du Colombier static void	uartflow(void*);
359a747e4fSDavid du Colombier 
369a747e4fSDavid du Colombier /*
379a747e4fSDavid du Colombier  *  enable/disable uart and add/remove to list of enabled uarts
389a747e4fSDavid du Colombier  */
39*696c1e60SDavid du Colombier Uart*
uartenable(Uart * p)409a747e4fSDavid du Colombier uartenable(Uart *p)
419a747e4fSDavid du Colombier {
429a747e4fSDavid du Colombier 	Uart **l;
439a747e4fSDavid du Colombier 
44cb8c047aSDavid du Colombier 	if(p->enabled)
45cb8c047aSDavid du Colombier 		return p;
469a747e4fSDavid du Colombier 	if(p->iq == nil){
475243b8d1SDavid du Colombier 		if((p->iq = qopen(8*1024, 0, uartflow, p)) == nil)
489a747e4fSDavid du Colombier 			return nil;
499a747e4fSDavid du Colombier 	}
509a747e4fSDavid du Colombier 	else
519a747e4fSDavid du Colombier 		qreopen(p->iq);
529a747e4fSDavid du Colombier 	if(p->oq == nil){
535243b8d1SDavid du Colombier 		if((p->oq = qopen(8*1024, 0, uartkick, p)) == nil){
549a747e4fSDavid du Colombier 			qfree(p->iq);
559a747e4fSDavid du Colombier 			p->iq = nil;
569a747e4fSDavid du Colombier 			return nil;
579a747e4fSDavid du Colombier 		}
589a747e4fSDavid du Colombier 	}
599a747e4fSDavid du Colombier 	else
609a747e4fSDavid du Colombier 		qreopen(p->oq);
619a747e4fSDavid du Colombier 
629a747e4fSDavid du Colombier 	p->ir = p->istage;
639a747e4fSDavid du Colombier 	p->iw = p->istage;
649a747e4fSDavid du Colombier 	p->ie = &p->istage[Stagesize];
659a747e4fSDavid du Colombier 	p->op = p->ostage;
669a747e4fSDavid du Colombier 	p->oe = p->ostage;
679a747e4fSDavid du Colombier 
689a747e4fSDavid du Colombier 	p->hup_dsr = p->hup_dcd = 0;
699a747e4fSDavid du Colombier 	p->dsr = p->dcd = 0;
709a747e4fSDavid du Colombier 
719a747e4fSDavid du Colombier 	/* assume we can send */
729a747e4fSDavid du Colombier 	p->cts = 1;
739a747e4fSDavid du Colombier 	p->ctsbackoff = 0;
749a747e4fSDavid du Colombier 
759a747e4fSDavid du Colombier 	if(p->bits == 0)
769a747e4fSDavid du Colombier 		uartctl(p, "l8");
779a747e4fSDavid du Colombier 	if(p->stop == 0)
789a747e4fSDavid du Colombier 		uartctl(p, "s1");
799a747e4fSDavid du Colombier 	if(p->parity == 0)
809a747e4fSDavid du Colombier 		uartctl(p, "pn");
819a747e4fSDavid du Colombier 	if(p->baud == 0)
829a747e4fSDavid du Colombier 		uartctl(p, "b9600");
839a747e4fSDavid du Colombier 	(*p->phys->enable)(p, 1);
849a747e4fSDavid du Colombier 
858f92dc5cSDavid du Colombier 	/*
868f92dc5cSDavid du Colombier 	 * use ilock because uartclock can otherwise interrupt here
878f92dc5cSDavid du Colombier 	 * and would hang on an attempt to lock uartalloc.
888f92dc5cSDavid du Colombier 	 */
898f92dc5cSDavid du Colombier 	ilock(&uartalloc);
909a747e4fSDavid du Colombier 	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
919a747e4fSDavid du Colombier 		if(*l == p)
929a747e4fSDavid du Colombier 			break;
939a747e4fSDavid du Colombier 	}
949a747e4fSDavid du Colombier 	if(*l == 0){
959a747e4fSDavid du Colombier 		p->elist = uartalloc.elist;
969a747e4fSDavid du Colombier 		uartalloc.elist = p;
979a747e4fSDavid du Colombier 	}
989a747e4fSDavid du Colombier 	p->enabled = 1;
998f92dc5cSDavid du Colombier 	iunlock(&uartalloc);
1009a747e4fSDavid du Colombier 
1019a747e4fSDavid du Colombier 	return p;
1029a747e4fSDavid du Colombier }
1039a747e4fSDavid du Colombier 
1049a747e4fSDavid du Colombier static void
uartdisable(Uart * p)1059a747e4fSDavid du Colombier uartdisable(Uart *p)
1069a747e4fSDavid du Colombier {
1079a747e4fSDavid du Colombier 	Uart **l;
1089a747e4fSDavid du Colombier 
109cb8c047aSDavid du Colombier 	if(!p->enabled)
110cb8c047aSDavid du Colombier 		return;
1119a747e4fSDavid du Colombier 	(*p->phys->disable)(p);
1129a747e4fSDavid du Colombier 
1138f92dc5cSDavid du Colombier 	ilock(&uartalloc);
1149a747e4fSDavid du Colombier 	for(l = &uartalloc.elist; *l; l = &(*l)->elist){
1159a747e4fSDavid du Colombier 		if(*l == p){
1169a747e4fSDavid du Colombier 			*l = p->elist;
1179a747e4fSDavid du Colombier 			break;
1189a747e4fSDavid du Colombier 		}
1199a747e4fSDavid du Colombier 	}
1209a747e4fSDavid du Colombier 	p->enabled = 0;
1218f92dc5cSDavid du Colombier 	iunlock(&uartalloc);
1229a747e4fSDavid du Colombier }
1239a747e4fSDavid du Colombier 
124b7b24591SDavid du Colombier void
uartmouse(Uart * p,int (* putc)(Queue *,int),int setb1200)125b7b24591SDavid du Colombier uartmouse(Uart* p, int (*putc)(Queue*, int), int setb1200)
126b7b24591SDavid du Colombier {
127b7b24591SDavid du Colombier 	qlock(p);
128b7b24591SDavid du Colombier 	if(p->opens++ == 0 && uartenable(p) == nil){
129b7b24591SDavid du Colombier 		qunlock(p);
130b7b24591SDavid du Colombier 		error(Enodev);
131b7b24591SDavid du Colombier 	}
132b7b24591SDavid du Colombier 	if(setb1200)
133b7b24591SDavid du Colombier 		uartctl(p, "b1200");
134b7b24591SDavid du Colombier 	p->putc = putc;
135b7b24591SDavid du Colombier 	p->special = 1;
136b7b24591SDavid du Colombier 	qunlock(p);
137b7b24591SDavid du Colombier }
138b7b24591SDavid du Colombier 
139aa46331bSDavid du Colombier void
uartsetmouseputc(Uart * p,int (* putc)(Queue *,int))140aa46331bSDavid du Colombier uartsetmouseputc(Uart* p, int (*putc)(Queue*, int))
141aa46331bSDavid du Colombier {
142aa46331bSDavid du Colombier 	qlock(p);
143aa46331bSDavid du Colombier 	if(p->opens == 0 || p->special == 0){
144aa46331bSDavid du Colombier 		qunlock(p);
145aa46331bSDavid du Colombier 		error(Enodev);
146aa46331bSDavid du Colombier 	}
147aa46331bSDavid du Colombier 	p->putc = putc;
148aa46331bSDavid du Colombier 	qunlock(p);
149aa46331bSDavid du Colombier }
150aa46331bSDavid du Colombier 
15136d3592fSDavid du Colombier static void
setlength(int i)15236d3592fSDavid du Colombier setlength(int i)
15336d3592fSDavid du Colombier {
15436d3592fSDavid du Colombier 	Uart *p;
15536d3592fSDavid du Colombier 
15636d3592fSDavid du Colombier 	if(i > 0){
15736d3592fSDavid du Colombier 		p = uart[i];
15836d3592fSDavid du Colombier 		if(p && p->opens && p->iq)
15936d3592fSDavid du Colombier 			uartdir[1+3*i].length = qlen(p->iq);
16036d3592fSDavid du Colombier 	} else for(i = 0; i < uartnuart; i++){
16136d3592fSDavid du Colombier 		p = uart[i];
16236d3592fSDavid du Colombier 		if(p && p->opens && p->iq)
16336d3592fSDavid du Colombier 			uartdir[1+3*i].length = qlen(p->iq);
16436d3592fSDavid du Colombier 	}
16536d3592fSDavid du Colombier }
1669a747e4fSDavid du Colombier 
1679a747e4fSDavid du Colombier /*
1689a747e4fSDavid du Colombier  *  set up the '#t' directory
1699a747e4fSDavid du Colombier  */
1709a747e4fSDavid du Colombier static void
uartreset(void)1719a747e4fSDavid du Colombier uartreset(void)
1729a747e4fSDavid du Colombier {
1739a747e4fSDavid du Colombier 	int i;
1749a747e4fSDavid du Colombier 	Dirtab *dp;
1759a747e4fSDavid du Colombier 	Uart *p, *tail;
1769a747e4fSDavid du Colombier 
1779a747e4fSDavid du Colombier 	tail = nil;
1789a747e4fSDavid du Colombier 	for(i = 0; physuart[i] != nil; i++){
1799a747e4fSDavid du Colombier 		if(physuart[i]->pnp == nil)
1809a747e4fSDavid du Colombier 			continue;
1819a747e4fSDavid du Colombier 		if((p = physuart[i]->pnp()) == nil)
1829a747e4fSDavid du Colombier 			continue;
1839a747e4fSDavid du Colombier 		if(uartlist != nil)
1849a747e4fSDavid du Colombier 			tail->next = p;
1859a747e4fSDavid du Colombier 		else
1869a747e4fSDavid du Colombier 			uartlist = p;
1879a747e4fSDavid du Colombier 		for(tail = p; tail->next != nil; tail = tail->next)
1889a747e4fSDavid du Colombier 			uartnuart++;
1899a747e4fSDavid du Colombier 		uartnuart++;
1909a747e4fSDavid du Colombier 	}
1919a747e4fSDavid du Colombier 
1921f03e0e7SDavid du Colombier 	if(uartnuart) {
1933ff48bf5SDavid du Colombier 		uart = xalloc(uartnuart*sizeof(Uart*));
1941f03e0e7SDavid du Colombier 		if (uart == nil)
1951f03e0e7SDavid du Colombier 			panic("uartreset: no memory");
1961f03e0e7SDavid du Colombier 	}
1979a747e4fSDavid du Colombier 
1989a747e4fSDavid du Colombier 	uartndir = 1 + 3*uartnuart;
1999a747e4fSDavid du Colombier 	uartdir = xalloc(uartndir * sizeof(Dirtab));
2001f03e0e7SDavid du Colombier 	if (uartdir == nil)
2019c63691cSDavid du Colombier 		panic("uartreset: no memory");
2029a747e4fSDavid du Colombier 	dp = uartdir;
2039a747e4fSDavid du Colombier 	strcpy(dp->name, ".");
2049a747e4fSDavid du Colombier 	mkqid(&dp->qid, 0, 0, QTDIR);
2059a747e4fSDavid du Colombier 	dp->length = 0;
2069a747e4fSDavid du Colombier 	dp->perm = DMDIR|0555;
2079a747e4fSDavid du Colombier 	dp++;
2089a747e4fSDavid du Colombier 	p = uartlist;
2099a747e4fSDavid du Colombier 	for(i = 0; i < uartnuart; i++){
2109a747e4fSDavid du Colombier 		/* 3 directory entries per port */
211cb8c047aSDavid du Colombier 		snprint(dp->name, sizeof dp->name, "eia%d", i);
2129a747e4fSDavid du Colombier 		dp->qid.path = NETQID(i, Ndataqid);
2139a747e4fSDavid du Colombier 		dp->perm = 0660;
2149a747e4fSDavid du Colombier 		dp++;
215cb8c047aSDavid du Colombier 		snprint(dp->name, sizeof dp->name, "eia%dctl", i);
2169a747e4fSDavid du Colombier 		dp->qid.path = NETQID(i, Nctlqid);
2179a747e4fSDavid du Colombier 		dp->perm = 0660;
2189a747e4fSDavid du Colombier 		dp++;
219cb8c047aSDavid du Colombier 		snprint(dp->name, sizeof dp->name, "eia%dstatus", i);
2209a747e4fSDavid du Colombier 		dp->qid.path = NETQID(i, Nstatqid);
2219a747e4fSDavid du Colombier 		dp->perm = 0444;
2229a747e4fSDavid du Colombier 		dp++;
2239a747e4fSDavid du Colombier 
2249a747e4fSDavid du Colombier 		uart[i] = p;
2259a747e4fSDavid du Colombier 		p->dev = i;
2269a747e4fSDavid du Colombier 		if(p->console || p->special){
2279a747e4fSDavid du Colombier 			if(uartenable(p) != nil){
2289a747e4fSDavid du Colombier 				if(p->console){
2299a747e4fSDavid du Colombier 					kbdq = p->iq;
2309a747e4fSDavid du Colombier 					serialoq = p->oq;
2319a747e4fSDavid du Colombier 					p->putc = kbdcr2nl;
2329a747e4fSDavid du Colombier 				}
2339a747e4fSDavid du Colombier 				p->opens++;
2349a747e4fSDavid du Colombier 			}
2359a747e4fSDavid du Colombier 		}
2369a747e4fSDavid du Colombier 		p = p->next;
2379a747e4fSDavid du Colombier 	}
2389a747e4fSDavid du Colombier 
2393ff48bf5SDavid du Colombier 	if(uartnuart){
240d9306527SDavid du Colombier 		/*
241d9306527SDavid du Colombier 		 * at 115200 baud, the 1024 char buffer takes 56 ms to process,
2425243b8d1SDavid du Colombier 		 * processing it every 22 ms should be fine.
243d9306527SDavid du Colombier 		 */
2445243b8d1SDavid du Colombier 		uarttimer = addclock0link(uartclock, 22);
2459a747e4fSDavid du Colombier 	}
2463ff48bf5SDavid du Colombier }
2479a747e4fSDavid du Colombier 
2489a747e4fSDavid du Colombier 
2499a747e4fSDavid du Colombier static Chan*
uartattach(char * spec)2509a747e4fSDavid du Colombier uartattach(char *spec)
2519a747e4fSDavid du Colombier {
2529a747e4fSDavid du Colombier 	return devattach('t', spec);
2539a747e4fSDavid du Colombier }
2549a747e4fSDavid du Colombier 
2559a747e4fSDavid du Colombier static Walkqid*
uartwalk(Chan * c,Chan * nc,char ** name,int nname)2569a747e4fSDavid du Colombier uartwalk(Chan *c, Chan *nc, char **name, int nname)
2579a747e4fSDavid du Colombier {
2589a747e4fSDavid du Colombier 	return devwalk(c, nc, name, nname, uartdir, uartndir, devgen);
2599a747e4fSDavid du Colombier }
2609a747e4fSDavid du Colombier 
2619a747e4fSDavid du Colombier static int
uartstat(Chan * c,uchar * dp,int n)2629a747e4fSDavid du Colombier uartstat(Chan *c, uchar *dp, int n)
2639a747e4fSDavid du Colombier {
26436d3592fSDavid du Colombier 	if(NETTYPE(c->qid.path) == Ndataqid)
26536d3592fSDavid du Colombier 		setlength(NETID(c->qid.path));
2669a747e4fSDavid du Colombier 	return devstat(c, dp, n, uartdir, uartndir, devgen);
2679a747e4fSDavid du Colombier }
2689a747e4fSDavid du Colombier 
2699a747e4fSDavid du Colombier static Chan*
uartopen(Chan * c,int omode)2709a747e4fSDavid du Colombier uartopen(Chan *c, int omode)
2719a747e4fSDavid du Colombier {
2729a747e4fSDavid du Colombier 	Uart *p;
2739a747e4fSDavid du Colombier 
2749a747e4fSDavid du Colombier 	c = devopen(c, omode, uartdir, uartndir, devgen);
2759a747e4fSDavid du Colombier 
2769a747e4fSDavid du Colombier 	switch(NETTYPE(c->qid.path)){
2779a747e4fSDavid du Colombier 	case Nctlqid:
2789a747e4fSDavid du Colombier 	case Ndataqid:
2799a747e4fSDavid du Colombier 		p = uart[NETID(c->qid.path)];
2809a747e4fSDavid du Colombier 		qlock(p);
2819a747e4fSDavid du Colombier 		if(p->opens++ == 0 && uartenable(p) == nil){
2829a747e4fSDavid du Colombier 			qunlock(p);
2839a747e4fSDavid du Colombier 			c->flag &= ~COPEN;
2849a747e4fSDavid du Colombier 			error(Enodev);
2859a747e4fSDavid du Colombier 		}
2869a747e4fSDavid du Colombier 		qunlock(p);
2879a747e4fSDavid du Colombier 		break;
2889a747e4fSDavid du Colombier 	}
2899a747e4fSDavid du Colombier 
2909a747e4fSDavid du Colombier 	c->iounit = qiomaxatomic;
2919a747e4fSDavid du Colombier 	return c;
2929a747e4fSDavid du Colombier }
2939a747e4fSDavid du Colombier 
2949a747e4fSDavid du Colombier static int
uartdrained(void * arg)2959a747e4fSDavid du Colombier uartdrained(void* arg)
2969a747e4fSDavid du Colombier {
2979a747e4fSDavid du Colombier 	Uart *p;
2989a747e4fSDavid du Colombier 
2999a747e4fSDavid du Colombier 	p = arg;
3009a747e4fSDavid du Colombier 	return qlen(p->oq) == 0 && p->op == p->oe;
3019a747e4fSDavid du Colombier }
3029a747e4fSDavid du Colombier 
3039a747e4fSDavid du Colombier static void
uartdrainoutput(Uart * p)304e288d156SDavid du Colombier uartdrainoutput(Uart *p)
305e288d156SDavid du Colombier {
306e288d156SDavid du Colombier 	if(!p->enabled)
307e288d156SDavid du Colombier 		return;
308e288d156SDavid du Colombier 
309e288d156SDavid du Colombier 	p->drain = 1;
310e288d156SDavid du Colombier 	if(waserror()){
311e288d156SDavid du Colombier 		p->drain = 0;
312e288d156SDavid du Colombier 		nexterror();
313e288d156SDavid du Colombier 	}
314e288d156SDavid du Colombier 	sleep(&p->r, uartdrained, p);
315e288d156SDavid du Colombier 	poperror();
316e288d156SDavid du Colombier }
317e288d156SDavid du Colombier 
318e288d156SDavid du Colombier static void
uartclose(Chan * c)3199a747e4fSDavid du Colombier uartclose(Chan *c)
3209a747e4fSDavid du Colombier {
3219a747e4fSDavid du Colombier 	Uart *p;
3229a747e4fSDavid du Colombier 
3239a747e4fSDavid du Colombier 	if(c->qid.type & QTDIR)
3249a747e4fSDavid du Colombier 		return;
3259a747e4fSDavid du Colombier 	if((c->flag & COPEN) == 0)
3269a747e4fSDavid du Colombier 		return;
3279a747e4fSDavid du Colombier 	switch(NETTYPE(c->qid.path)){
3289a747e4fSDavid du Colombier 	case Ndataqid:
3299a747e4fSDavid du Colombier 	case Nctlqid:
3309a747e4fSDavid du Colombier 		p = uart[NETID(c->qid.path)];
3319a747e4fSDavid du Colombier 		qlock(p);
3329a747e4fSDavid du Colombier 		if(--(p->opens) == 0){
3339a747e4fSDavid du Colombier 			qclose(p->iq);
3345243b8d1SDavid du Colombier 			ilock(&p->rlock);
3359a747e4fSDavid du Colombier 			p->ir = p->iw = p->istage;
3365243b8d1SDavid du Colombier 			iunlock(&p->rlock);
3379a747e4fSDavid du Colombier 
3389a747e4fSDavid du Colombier 			/*
3399a747e4fSDavid du Colombier 			 */
3409a747e4fSDavid du Colombier 			qhangup(p->oq, nil);
3419a747e4fSDavid du Colombier 			if(!waserror()){
342e288d156SDavid du Colombier 				uartdrainoutput(p);
3439a747e4fSDavid du Colombier 				poperror();
3449a747e4fSDavid du Colombier 			}
3459a747e4fSDavid du Colombier 			qclose(p->oq);
3469a747e4fSDavid du Colombier 			uartdisable(p);
3479a747e4fSDavid du Colombier 			p->dcd = p->dsr = p->dohup = 0;
3489a747e4fSDavid du Colombier 		}
3499a747e4fSDavid du Colombier 		qunlock(p);
3509a747e4fSDavid du Colombier 		break;
3519a747e4fSDavid du Colombier 	}
3529a747e4fSDavid du Colombier }
3539a747e4fSDavid du Colombier 
3549a747e4fSDavid du Colombier static long
uartread(Chan * c,void * buf,long n,vlong off)3559a747e4fSDavid du Colombier uartread(Chan *c, void *buf, long n, vlong off)
3569a747e4fSDavid du Colombier {
3579a747e4fSDavid du Colombier 	Uart *p;
3589a747e4fSDavid du Colombier 	ulong offset = off;
3599a747e4fSDavid du Colombier 
3609a747e4fSDavid du Colombier 	if(c->qid.type & QTDIR){
36136d3592fSDavid du Colombier 		setlength(-1);
3629a747e4fSDavid du Colombier 		return devdirread(c, buf, n, uartdir, uartndir, devgen);
3639a747e4fSDavid du Colombier 	}
3649a747e4fSDavid du Colombier 
3659a747e4fSDavid du Colombier 	p = uart[NETID(c->qid.path)];
3669a747e4fSDavid du Colombier 	switch(NETTYPE(c->qid.path)){
3679a747e4fSDavid du Colombier 	case Ndataqid:
3689a747e4fSDavid du Colombier 		return qread(p->iq, buf, n);
3699a747e4fSDavid du Colombier 	case Nctlqid:
3709a747e4fSDavid du Colombier 		return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
3719a747e4fSDavid du Colombier 	case Nstatqid:
3729a747e4fSDavid du Colombier 		return (*p->phys->status)(p, buf, n, offset);
3739a747e4fSDavid du Colombier 	}
3749a747e4fSDavid du Colombier 
3759a747e4fSDavid du Colombier 	return 0;
3769a747e4fSDavid du Colombier }
3779a747e4fSDavid du Colombier 
3789a747e4fSDavid du Colombier int
uartctl(Uart * p,char * cmd)3799a747e4fSDavid du Colombier uartctl(Uart *p, char *cmd)
3809a747e4fSDavid du Colombier {
3819a747e4fSDavid du Colombier 	char *f[16];
3829a747e4fSDavid du Colombier 	int i, n, nf;
3839a747e4fSDavid du Colombier 
3849a747e4fSDavid du Colombier 	nf = tokenize(cmd, f, nelem(f));
3859a747e4fSDavid du Colombier 	for(i = 0; i < nf; i++){
3869a747e4fSDavid du Colombier 		if(strncmp(f[i], "break", 5) == 0){
3879a747e4fSDavid du Colombier 			(*p->phys->dobreak)(p, 0);
3889a747e4fSDavid du Colombier 			continue;
3899a747e4fSDavid du Colombier 		}
3909a747e4fSDavid du Colombier 
3919a747e4fSDavid du Colombier 		n = atoi(f[i]+1);
3929a747e4fSDavid du Colombier 		switch(*f[i]){
3939a747e4fSDavid du Colombier 		case 'B':
3949a747e4fSDavid du Colombier 		case 'b':
395e288d156SDavid du Colombier 			uartdrainoutput(p);
3969a747e4fSDavid du Colombier 			if((*p->phys->baud)(p, n) < 0)
3979a747e4fSDavid du Colombier 				return -1;
3989a747e4fSDavid du Colombier 			break;
3999a747e4fSDavid du Colombier 		case 'C':
4009a747e4fSDavid du Colombier 		case 'c':
4019a747e4fSDavid du Colombier 			p->hup_dcd = n;
4029a747e4fSDavid du Colombier 			break;
4039a747e4fSDavid du Colombier 		case 'D':
4049a747e4fSDavid du Colombier 		case 'd':
405e288d156SDavid du Colombier 			uartdrainoutput(p);
4069a747e4fSDavid du Colombier 			(*p->phys->dtr)(p, n);
4079a747e4fSDavid du Colombier 			break;
4089a747e4fSDavid du Colombier 		case 'E':
4099a747e4fSDavid du Colombier 		case 'e':
4109a747e4fSDavid du Colombier 			p->hup_dsr = n;
4119a747e4fSDavid du Colombier 			break;
4129a747e4fSDavid du Colombier 		case 'f':
4139a747e4fSDavid du Colombier 		case 'F':
4149a747e4fSDavid du Colombier 			if(p->oq != nil)
4159a747e4fSDavid du Colombier 				qflush(p->oq);
4169a747e4fSDavid du Colombier 			break;
4179a747e4fSDavid du Colombier 		case 'H':
4189a747e4fSDavid du Colombier 		case 'h':
4199a747e4fSDavid du Colombier 			if(p->iq != nil)
4209a747e4fSDavid du Colombier 				qhangup(p->iq, 0);
4219a747e4fSDavid du Colombier 			if(p->oq != nil)
4229a747e4fSDavid du Colombier 				qhangup(p->oq, 0);
4239a747e4fSDavid du Colombier 			break;
4249a747e4fSDavid du Colombier 		case 'i':
4259a747e4fSDavid du Colombier 		case 'I':
426e288d156SDavid du Colombier 			uartdrainoutput(p);
4279a747e4fSDavid du Colombier 			(*p->phys->fifo)(p, n);
4289a747e4fSDavid du Colombier 			break;
4299a747e4fSDavid du Colombier 		case 'K':
4309a747e4fSDavid du Colombier 		case 'k':
431e288d156SDavid du Colombier 			uartdrainoutput(p);
4329a747e4fSDavid du Colombier 			(*p->phys->dobreak)(p, n);
4339a747e4fSDavid du Colombier 			break;
4349a747e4fSDavid du Colombier 		case 'L':
4359a747e4fSDavid du Colombier 		case 'l':
436e288d156SDavid du Colombier 			uartdrainoutput(p);
4379a747e4fSDavid du Colombier 			if((*p->phys->bits)(p, n) < 0)
4389a747e4fSDavid du Colombier 				return -1;
4399a747e4fSDavid du Colombier 			break;
4409a747e4fSDavid du Colombier 		case 'm':
4419a747e4fSDavid du Colombier 		case 'M':
442e288d156SDavid du Colombier 			uartdrainoutput(p);
4439a747e4fSDavid du Colombier 			(*p->phys->modemctl)(p, n);
4449a747e4fSDavid du Colombier 			break;
4459a747e4fSDavid du Colombier 		case 'n':
4469a747e4fSDavid du Colombier 		case 'N':
4479a747e4fSDavid du Colombier 			if(p->oq != nil)
4489a747e4fSDavid du Colombier 				qnoblock(p->oq, n);
4499a747e4fSDavid du Colombier 			break;
4509a747e4fSDavid du Colombier 		case 'P':
4519a747e4fSDavid du Colombier 		case 'p':
452e288d156SDavid du Colombier 			uartdrainoutput(p);
4539a747e4fSDavid du Colombier 			if((*p->phys->parity)(p, *(f[i]+1)) < 0)
4549a747e4fSDavid du Colombier 				return -1;
4559a747e4fSDavid du Colombier 			break;
4569a747e4fSDavid du Colombier 		case 'Q':
4579a747e4fSDavid du Colombier 		case 'q':
4589a747e4fSDavid du Colombier 			if(p->iq != nil)
4599a747e4fSDavid du Colombier 				qsetlimit(p->iq, n);
4609a747e4fSDavid du Colombier 			if(p->oq != nil)
4619a747e4fSDavid du Colombier 				qsetlimit(p->oq, n);
4629a747e4fSDavid du Colombier 			break;
4639a747e4fSDavid du Colombier 		case 'R':
4649a747e4fSDavid du Colombier 		case 'r':
465e288d156SDavid du Colombier 			uartdrainoutput(p);
4669a747e4fSDavid du Colombier 			(*p->phys->rts)(p, n);
4679a747e4fSDavid du Colombier 			break;
4689a747e4fSDavid du Colombier 		case 'S':
4699a747e4fSDavid du Colombier 		case 's':
470e288d156SDavid du Colombier 			uartdrainoutput(p);
4719a747e4fSDavid du Colombier 			if((*p->phys->stop)(p, n) < 0)
4729a747e4fSDavid du Colombier 				return -1;
4739a747e4fSDavid du Colombier 			break;
4749a747e4fSDavid du Colombier 		case 'W':
4759a747e4fSDavid du Colombier 		case 'w':
4765243b8d1SDavid du Colombier 			if(uarttimer == nil || n < 1)
4775243b8d1SDavid du Colombier 				return -1;
4785243b8d1SDavid du Colombier 			uarttimer->tns = (vlong)n * 100000LL;
4799a747e4fSDavid du Colombier 			break;
4809a747e4fSDavid du Colombier 		case 'X':
4819a747e4fSDavid du Colombier 		case 'x':
4829a747e4fSDavid du Colombier 			if(p->enabled){
4839a747e4fSDavid du Colombier 				ilock(&p->tlock);
4849a747e4fSDavid du Colombier 				p->xonoff = n;
4859a747e4fSDavid du Colombier 				iunlock(&p->tlock);
4869a747e4fSDavid du Colombier 			}
4879a747e4fSDavid du Colombier 			break;
4889a747e4fSDavid du Colombier 		}
4899a747e4fSDavid du Colombier 	}
4909a747e4fSDavid du Colombier 	return 0;
4919a747e4fSDavid du Colombier }
4929a747e4fSDavid du Colombier 
4939a747e4fSDavid du Colombier static long
uartwrite(Chan * c,void * buf,long n,vlong)4949a747e4fSDavid du Colombier uartwrite(Chan *c, void *buf, long n, vlong)
4959a747e4fSDavid du Colombier {
4969a747e4fSDavid du Colombier 	Uart *p;
4979a747e4fSDavid du Colombier 	char *cmd;
4989a747e4fSDavid du Colombier 
4999a747e4fSDavid du Colombier 	if(c->qid.type & QTDIR)
5009a747e4fSDavid du Colombier 		error(Eperm);
5019a747e4fSDavid du Colombier 
5029a747e4fSDavid du Colombier 	p = uart[NETID(c->qid.path)];
5039a747e4fSDavid du Colombier 
5049a747e4fSDavid du Colombier 	switch(NETTYPE(c->qid.path)){
5059a747e4fSDavid du Colombier 	case Ndataqid:
5069a747e4fSDavid du Colombier 		qlock(p);
5079a747e4fSDavid du Colombier 		if(waserror()){
5089a747e4fSDavid du Colombier 			qunlock(p);
5099a747e4fSDavid du Colombier 			nexterror();
5109a747e4fSDavid du Colombier 		}
5119a747e4fSDavid du Colombier 
5129a747e4fSDavid du Colombier 		n = qwrite(p->oq, buf, n);
5139a747e4fSDavid du Colombier 
5149a747e4fSDavid du Colombier 		qunlock(p);
5159a747e4fSDavid du Colombier 		poperror();
5169a747e4fSDavid du Colombier 		break;
5179a747e4fSDavid du Colombier 	case Nctlqid:
5189a747e4fSDavid du Colombier 		cmd = malloc(n+1);
519aa72973aSDavid du Colombier 		if(cmd == nil)
520aa72973aSDavid du Colombier 			error(Enomem);
5219a747e4fSDavid du Colombier 		memmove(cmd, buf, n);
5229a747e4fSDavid du Colombier 		cmd[n] = 0;
5239a747e4fSDavid du Colombier 		qlock(p);
5249a747e4fSDavid du Colombier 		if(waserror()){
5259a747e4fSDavid du Colombier 			qunlock(p);
5269a747e4fSDavid du Colombier 			free(cmd);
5279a747e4fSDavid du Colombier 			nexterror();
5289a747e4fSDavid du Colombier 		}
5299a747e4fSDavid du Colombier 
5309a747e4fSDavid du Colombier 		/* let output drain */
5319a747e4fSDavid du Colombier 		if(uartctl(p, cmd) < 0)
5329a747e4fSDavid du Colombier 			error(Ebadarg);
5339a747e4fSDavid du Colombier 
5349a747e4fSDavid du Colombier 		qunlock(p);
5359a747e4fSDavid du Colombier 		poperror();
5369a747e4fSDavid du Colombier 		free(cmd);
5379a747e4fSDavid du Colombier 		break;
5389a747e4fSDavid du Colombier 	}
5399a747e4fSDavid du Colombier 
5409a747e4fSDavid du Colombier 	return n;
5419a747e4fSDavid du Colombier }
5429a747e4fSDavid du Colombier 
5439a747e4fSDavid du Colombier static int
uartwstat(Chan * c,uchar * dp,int n)5449a747e4fSDavid du Colombier uartwstat(Chan *c, uchar *dp, int n)
5459a747e4fSDavid du Colombier {
5469a747e4fSDavid du Colombier 	Dir d;
5479a747e4fSDavid du Colombier 	Dirtab *dt;
5489a747e4fSDavid du Colombier 
5499a747e4fSDavid du Colombier 	if(!iseve())
5509a747e4fSDavid du Colombier 		error(Eperm);
5519a747e4fSDavid du Colombier 	if(QTDIR & c->qid.type)
5529a747e4fSDavid du Colombier 		error(Eperm);
5539a747e4fSDavid du Colombier 	if(NETTYPE(c->qid.path) == Nstatqid)
5549a747e4fSDavid du Colombier 		error(Eperm);
5559a747e4fSDavid du Colombier 
5569a747e4fSDavid du Colombier 	dt = &uartdir[1 + 3 * NETID(c->qid.path)];
5579a747e4fSDavid du Colombier 	n = convM2D(dp, n, &d, nil);
5589a747e4fSDavid du Colombier 	if(n == 0)
5599a747e4fSDavid du Colombier 		error(Eshortstat);
5609a747e4fSDavid du Colombier 	if(d.mode != ~0UL)
5619a747e4fSDavid du Colombier 		dt[0].perm = dt[1].perm = d.mode;
5629a747e4fSDavid du Colombier 	return n;
5639a747e4fSDavid du Colombier }
5649a747e4fSDavid du Colombier 
5659a747e4fSDavid du Colombier void
uartpower(int on)5669a747e4fSDavid du Colombier uartpower(int on)
5679a747e4fSDavid du Colombier {
5689a747e4fSDavid du Colombier 	Uart *p;
5699a747e4fSDavid du Colombier 
5709a747e4fSDavid du Colombier 	for(p = uartlist; p != nil; p = p->next) {
5719a747e4fSDavid du Colombier 		if(p->phys->power)
5729a747e4fSDavid du Colombier 			(*p->phys->power)(p, on);
5739a747e4fSDavid du Colombier 	}
5749a747e4fSDavid du Colombier }
5759a747e4fSDavid du Colombier 
5769a747e4fSDavid du Colombier Dev uartdevtab = {
5779a747e4fSDavid du Colombier 	't',
5789a747e4fSDavid du Colombier 	"uart",
5799a747e4fSDavid du Colombier 
5809a747e4fSDavid du Colombier 	uartreset,
5819a747e4fSDavid du Colombier 	devinit,
5829a747e4fSDavid du Colombier 	devshutdown,
5839a747e4fSDavid du Colombier 	uartattach,
5849a747e4fSDavid du Colombier 	uartwalk,
5859a747e4fSDavid du Colombier 	uartstat,
5869a747e4fSDavid du Colombier 	uartopen,
5879a747e4fSDavid du Colombier 	devcreate,
5889a747e4fSDavid du Colombier 	uartclose,
5899a747e4fSDavid du Colombier 	uartread,
5909a747e4fSDavid du Colombier 	devbread,
5919a747e4fSDavid du Colombier 	uartwrite,
5929a747e4fSDavid du Colombier 	devbwrite,
5939a747e4fSDavid du Colombier 	devremove,
5949a747e4fSDavid du Colombier 	uartwstat,
5959a747e4fSDavid du Colombier 	uartpower,
5969a747e4fSDavid du Colombier };
5979a747e4fSDavid du Colombier 
5989a747e4fSDavid du Colombier /*
5999a747e4fSDavid du Colombier  *  restart input if it's off
6009a747e4fSDavid du Colombier  */
6019a747e4fSDavid du Colombier static void
uartflow(void * v)6029a747e4fSDavid du Colombier uartflow(void *v)
6039a747e4fSDavid du Colombier {
6049a747e4fSDavid du Colombier 	Uart *p;
6059a747e4fSDavid du Colombier 
6069a747e4fSDavid du Colombier 	p = v;
6079a747e4fSDavid du Colombier 	if(p->modem)
6089a747e4fSDavid du Colombier 		(*p->phys->rts)(p, 1);
6099a747e4fSDavid du Colombier }
6109a747e4fSDavid du Colombier 
6119a747e4fSDavid du Colombier /*
6129a747e4fSDavid du Colombier  *  put some bytes into the local queue to avoid calling
6139a747e4fSDavid du Colombier  *  qconsume for every character
6149a747e4fSDavid du Colombier  */
6159a747e4fSDavid du Colombier int
uartstageoutput(Uart * p)6169a747e4fSDavid du Colombier uartstageoutput(Uart *p)
6179a747e4fSDavid du Colombier {
6189a747e4fSDavid du Colombier 	int n;
6199a747e4fSDavid du Colombier 
6209a747e4fSDavid du Colombier 	n = qconsume(p->oq, p->ostage, Stagesize);
6219a747e4fSDavid du Colombier 	if(n <= 0)
6229a747e4fSDavid du Colombier 		return 0;
6239a747e4fSDavid du Colombier 	p->op = p->ostage;
6249a747e4fSDavid du Colombier 	p->oe = p->ostage + n;
6259a747e4fSDavid du Colombier 	return n;
6269a747e4fSDavid du Colombier }
6279a747e4fSDavid du Colombier 
6289a747e4fSDavid du Colombier /*
6299a747e4fSDavid du Colombier  *  restart output
6309a747e4fSDavid du Colombier  */
6319a747e4fSDavid du Colombier void
uartkick(void * v)6329a747e4fSDavid du Colombier uartkick(void *v)
6339a747e4fSDavid du Colombier {
6349a747e4fSDavid du Colombier 	Uart *p = v;
6359a747e4fSDavid du Colombier 
6369a747e4fSDavid du Colombier 	if(p->blocked)
6379a747e4fSDavid du Colombier 		return;
6389a747e4fSDavid du Colombier 
6399a747e4fSDavid du Colombier 	ilock(&p->tlock);
6409a747e4fSDavid du Colombier 	(*p->phys->kick)(p);
6419a747e4fSDavid du Colombier 	iunlock(&p->tlock);
6429a747e4fSDavid du Colombier 
643e288d156SDavid du Colombier 	if(p->drain && uartdrained(p)){
644e288d156SDavid du Colombier 		p->drain = 0;
6459a747e4fSDavid du Colombier 		wakeup(&p->r);
6469a747e4fSDavid du Colombier 	}
647e288d156SDavid du Colombier }
6489a747e4fSDavid du Colombier 
6499a747e4fSDavid du Colombier /*
6505243b8d1SDavid du Colombier  * Move data from the interrupt staging area to
6515243b8d1SDavid du Colombier  * the input Queue.
6525243b8d1SDavid du Colombier  */
6535243b8d1SDavid du Colombier static void
uartstageinput(Uart * p)6545243b8d1SDavid du Colombier uartstageinput(Uart *p)
6555243b8d1SDavid du Colombier {
6565243b8d1SDavid du Colombier 	int n;
6575243b8d1SDavid du Colombier 	uchar *ir, *iw;
6585243b8d1SDavid du Colombier 
6595243b8d1SDavid du Colombier 	while(p->ir != p->iw){
6605243b8d1SDavid du Colombier 		ir = p->ir;
6615243b8d1SDavid du Colombier 		if(p->ir > p->iw){
6625243b8d1SDavid du Colombier 			iw = p->ie;
6635243b8d1SDavid du Colombier 			p->ir = p->istage;
6645243b8d1SDavid du Colombier 		}
6655243b8d1SDavid du Colombier 		else{
6665243b8d1SDavid du Colombier 			iw = p->iw;
6675243b8d1SDavid du Colombier 			p->ir = p->iw;
6685243b8d1SDavid du Colombier 		}
6695243b8d1SDavid du Colombier 		if((n = qproduce(p->iq, ir, iw - ir)) < 0){
6705243b8d1SDavid du Colombier 			p->serr++;
6715243b8d1SDavid du Colombier 			(*p->phys->rts)(p, 0);
6725243b8d1SDavid du Colombier 		}
6735243b8d1SDavid du Colombier 		else if(n == 0)
6745243b8d1SDavid du Colombier 			p->berr++;
6755243b8d1SDavid du Colombier 	}
6765243b8d1SDavid du Colombier }
6775243b8d1SDavid du Colombier 
6785243b8d1SDavid du Colombier /*
6799a747e4fSDavid du Colombier  *  receive a character at interrupt time
6809a747e4fSDavid du Colombier  */
6819a747e4fSDavid du Colombier void
uartrecv(Uart * p,char ch)6829a747e4fSDavid du Colombier uartrecv(Uart *p,  char ch)
6839a747e4fSDavid du Colombier {
6849a747e4fSDavid du Colombier 	uchar *next;
6859a747e4fSDavid du Colombier 
6869a747e4fSDavid du Colombier 	/* software flow control */
6879a747e4fSDavid du Colombier 	if(p->xonoff){
6889a747e4fSDavid du Colombier 		if(ch == CTLS){
6899a747e4fSDavid du Colombier 			p->blocked = 1;
6909a747e4fSDavid du Colombier 		}else if(ch == CTLQ){
6919a747e4fSDavid du Colombier 			p->blocked = 0;
6929a747e4fSDavid du Colombier 			p->ctsbackoff = 2; /* clock gets output going again */
6939a747e4fSDavid du Colombier 		}
6949a747e4fSDavid du Colombier 	}
6959a747e4fSDavid du Colombier 
6969a747e4fSDavid du Colombier 	/* receive the character */
6979a747e4fSDavid du Colombier 	if(p->putc)
6989a747e4fSDavid du Colombier 		p->putc(p->iq, ch);
699cb82d19aSDavid du Colombier 	else if (p->iw) {		/* maybe the line isn't enabled yet */
7005243b8d1SDavid du Colombier 		ilock(&p->rlock);
7019a747e4fSDavid du Colombier 		next = p->iw + 1;
7029a747e4fSDavid du Colombier 		if(next == p->ie)
7039a747e4fSDavid du Colombier 			next = p->istage;
7045243b8d1SDavid du Colombier 		if(next == p->ir)
7055243b8d1SDavid du Colombier 			uartstageinput(p);
7069a747e4fSDavid du Colombier 		if(next != p->ir){
7079a747e4fSDavid du Colombier 			*p->iw = ch;
7089a747e4fSDavid du Colombier 			p->iw = next;
7099a747e4fSDavid du Colombier 		}
7105243b8d1SDavid du Colombier 		iunlock(&p->rlock);
7119a747e4fSDavid du Colombier 	}
7129a747e4fSDavid du Colombier }
7139a747e4fSDavid du Colombier 
7149a747e4fSDavid du Colombier /*
7159a747e4fSDavid du Colombier  *  we save up input characters till clock time to reduce
7169a747e4fSDavid du Colombier  *  per character interrupt overhead.
7179a747e4fSDavid du Colombier  */
7189a747e4fSDavid du Colombier static void
uartclock(void)7199a747e4fSDavid du Colombier uartclock(void)
7209a747e4fSDavid du Colombier {
7219a747e4fSDavid du Colombier 	Uart *p;
7229a747e4fSDavid du Colombier 
7238f92dc5cSDavid du Colombier 	ilock(&uartalloc);
7249a747e4fSDavid du Colombier 	for(p = uartalloc.elist; p; p = p->elist){
7259a747e4fSDavid du Colombier 
7265243b8d1SDavid du Colombier 		/* this hopefully amortizes cost of qproduce to many chars */
7279a747e4fSDavid du Colombier 		if(p->iw != p->ir){
7285243b8d1SDavid du Colombier 			ilock(&p->rlock);
7295243b8d1SDavid du Colombier 			uartstageinput(p);
7305243b8d1SDavid du Colombier 			iunlock(&p->rlock);
7319a747e4fSDavid du Colombier 		}
7329a747e4fSDavid du Colombier 
7339a747e4fSDavid du Colombier 		/* hang up if requested */
7349a747e4fSDavid du Colombier 		if(p->dohup){
7359a747e4fSDavid du Colombier 			qhangup(p->iq, 0);
7369a747e4fSDavid du Colombier 			qhangup(p->oq, 0);
7379a747e4fSDavid du Colombier 			p->dohup = 0;
7389a747e4fSDavid du Colombier 		}
7399a747e4fSDavid du Colombier 
7409a747e4fSDavid du Colombier 		/* this adds hysteresis to hardware/software flow control */
7419a747e4fSDavid du Colombier 		if(p->ctsbackoff){
7429a747e4fSDavid du Colombier 			ilock(&p->tlock);
7439a747e4fSDavid du Colombier 			if(p->ctsbackoff){
7449a747e4fSDavid du Colombier 				if(--(p->ctsbackoff) == 0)
7459a747e4fSDavid du Colombier 					(*p->phys->kick)(p);
7469a747e4fSDavid du Colombier 			}
7479a747e4fSDavid du Colombier 			iunlock(&p->tlock);
7489a747e4fSDavid du Colombier 		}
7499a747e4fSDavid du Colombier 	}
7508f92dc5cSDavid du Colombier 	iunlock(&uartalloc);
7519a747e4fSDavid du Colombier }
7529a747e4fSDavid du Colombier 
7539a747e4fSDavid du Colombier /*
7549a747e4fSDavid du Colombier  * polling console input, output
7559a747e4fSDavid du Colombier  */
7569a747e4fSDavid du Colombier 
7579a747e4fSDavid du Colombier Uart* consuart;
7589a747e4fSDavid du Colombier 
7599a747e4fSDavid du Colombier int
uartgetc(void)7609a747e4fSDavid du Colombier uartgetc(void)
7619a747e4fSDavid du Colombier {
7629a747e4fSDavid du Colombier 	if(consuart == nil || consuart->phys->getc == nil)
7639a747e4fSDavid du Colombier 		return -1;
7649a747e4fSDavid du Colombier 	return consuart->phys->getc(consuart);
7659a747e4fSDavid du Colombier }
7669a747e4fSDavid du Colombier 
7679a747e4fSDavid du Colombier void
uartputc(int c)7689a747e4fSDavid du Colombier uartputc(int c)
7699a747e4fSDavid 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);
7769a747e4fSDavid du Colombier 		return;
777*696c1e60SDavid du Colombier 	}
7789a747e4fSDavid du Colombier 	consuart->phys->putc(consuart, c);
7799a747e4fSDavid du Colombier }
7809a747e4fSDavid du Colombier 
7819a747e4fSDavid du Colombier void
uartputs(char * s,int n)7829a747e4fSDavid du Colombier uartputs(char *s, int n)
7839a747e4fSDavid du Colombier {
7849a747e4fSDavid du Colombier 	char *e;
7859a747e4fSDavid 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);
7899a747e4fSDavid du Colombier 		return;
790*696c1e60SDavid du Colombier 	}
7919a747e4fSDavid du Colombier 	e = s+n;
7929a747e4fSDavid du Colombier 	for(; s<e; s++){
7939a747e4fSDavid du Colombier 		if(*s == '\n')
7949a747e4fSDavid du Colombier 			consuart->phys->putc(consuart, '\r');
7959a747e4fSDavid du Colombier 		consuart->phys->putc(consuart, *s);
7969a747e4fSDavid du Colombier 	}
7979a747e4fSDavid du Colombier }
798