13de6a9c0SDavid du Colombier #include "u.h"
23de6a9c0SDavid du Colombier #include "../port/lib.h"
33de6a9c0SDavid du Colombier #include "mem.h"
43de6a9c0SDavid du Colombier #include "dat.h"
53de6a9c0SDavid du Colombier #include "fns.h"
63de6a9c0SDavid du Colombier #include "io.h"
73de6a9c0SDavid du Colombier #include "../port/error.h"
83de6a9c0SDavid du Colombier
93de6a9c0SDavid du Colombier #include "../port/netif.h"
103de6a9c0SDavid du Colombier
113de6a9c0SDavid du Colombier enum
123de6a9c0SDavid du Colombier {
133de6a9c0SDavid du Colombier /* soft flow control chars */
143de6a9c0SDavid du Colombier CTLS= 023,
153de6a9c0SDavid du Colombier CTLQ= 021,
163de6a9c0SDavid du Colombier };
173de6a9c0SDavid du Colombier
183de6a9c0SDavid du Colombier extern Dev uartdevtab;
193de6a9c0SDavid du Colombier extern PhysUart* physuart[];
203de6a9c0SDavid du Colombier
213de6a9c0SDavid du Colombier static Uart* uartlist;
223de6a9c0SDavid du Colombier static Uart** uart;
233de6a9c0SDavid du Colombier static int uartnuart;
243de6a9c0SDavid du Colombier static Dirtab *uartdir;
253de6a9c0SDavid du Colombier static int uartndir;
263de6a9c0SDavid du Colombier static Timer *uarttimer;
273de6a9c0SDavid du Colombier
283de6a9c0SDavid du Colombier struct Uartalloc {
293de6a9c0SDavid du Colombier Lock;
303de6a9c0SDavid du Colombier Uart *elist; /* list of enabled interfaces */
313de6a9c0SDavid du Colombier } uartalloc;
323de6a9c0SDavid du Colombier
333de6a9c0SDavid du Colombier static void uartclock(void);
343de6a9c0SDavid du Colombier static void uartflow(void*);
353de6a9c0SDavid du Colombier
363de6a9c0SDavid du Colombier /*
373de6a9c0SDavid du Colombier * enable/disable uart and add/remove to list of enabled uarts
383de6a9c0SDavid du Colombier */
393de6a9c0SDavid du Colombier //static
403de6a9c0SDavid du Colombier Uart*
uartenable(Uart * p)413de6a9c0SDavid du Colombier uartenable(Uart *p)
423de6a9c0SDavid du Colombier {
433de6a9c0SDavid du Colombier Uart **l;
443de6a9c0SDavid du Colombier
453de6a9c0SDavid du Colombier if (up == nil)
463de6a9c0SDavid du Colombier return p; /* too soon; try again later */
473de6a9c0SDavid du Colombier // return nil;
483de6a9c0SDavid du Colombier
493de6a9c0SDavid du Colombier if(p->iq == nil){
503de6a9c0SDavid du Colombier if((p->iq = qopen(8*1024, 0, uartflow, p)) == nil)
513de6a9c0SDavid du Colombier return nil;
523de6a9c0SDavid du Colombier }
533de6a9c0SDavid du Colombier else
543de6a9c0SDavid du Colombier qreopen(p->iq);
553de6a9c0SDavid du Colombier if(p->oq == nil){
563de6a9c0SDavid du Colombier if((p->oq = qopen(8*1024, 0, uartkick, p)) == nil){
573de6a9c0SDavid du Colombier qfree(p->iq);
583de6a9c0SDavid du Colombier p->iq = nil;
593de6a9c0SDavid du Colombier return nil;
603de6a9c0SDavid du Colombier }
613de6a9c0SDavid du Colombier }
623de6a9c0SDavid du Colombier else
633de6a9c0SDavid du Colombier qreopen(p->oq);
643de6a9c0SDavid du Colombier
653de6a9c0SDavid du Colombier p->ir = p->istage;
663de6a9c0SDavid du Colombier p->iw = p->istage;
673de6a9c0SDavid du Colombier p->ie = &p->istage[Stagesize];
683de6a9c0SDavid du Colombier p->op = p->ostage;
693de6a9c0SDavid du Colombier p->oe = p->ostage;
703de6a9c0SDavid du Colombier
713de6a9c0SDavid du Colombier p->hup_dsr = p->hup_dcd = 0;
723de6a9c0SDavid du Colombier p->dsr = p->dcd = 0;
733de6a9c0SDavid du Colombier
743de6a9c0SDavid du Colombier /* assume we can send */
753de6a9c0SDavid du Colombier p->cts = 1;
763de6a9c0SDavid du Colombier p->ctsbackoff = 0;
773de6a9c0SDavid du Colombier
783de6a9c0SDavid du Colombier if (up) {
793de6a9c0SDavid du Colombier if(p->bits == 0)
803de6a9c0SDavid du Colombier uartctl(p, "l8");
813de6a9c0SDavid du Colombier if(p->stop == 0)
823de6a9c0SDavid du Colombier uartctl(p, "s1");
833de6a9c0SDavid du Colombier if(p->parity == 0)
843de6a9c0SDavid du Colombier uartctl(p, "pn");
853de6a9c0SDavid du Colombier if(p->baud == 0)
863de6a9c0SDavid du Colombier uartctl(p, "b9600");
873de6a9c0SDavid du Colombier (*p->phys->enable)(p, 1);
883de6a9c0SDavid du Colombier }
893de6a9c0SDavid du Colombier
903de6a9c0SDavid du Colombier /*
913de6a9c0SDavid du Colombier * use ilock because uartclock can otherwise interrupt here
923de6a9c0SDavid du Colombier * and would hang on an attempt to lock uartalloc.
933de6a9c0SDavid du Colombier */
943de6a9c0SDavid du Colombier ilock(&uartalloc);
953de6a9c0SDavid du Colombier for(l = &uartalloc.elist; *l; l = &(*l)->elist){
963de6a9c0SDavid du Colombier if(*l == p)
973de6a9c0SDavid du Colombier break;
983de6a9c0SDavid du Colombier }
993de6a9c0SDavid du Colombier if(*l == 0){
1003de6a9c0SDavid du Colombier p->elist = uartalloc.elist;
1013de6a9c0SDavid du Colombier uartalloc.elist = p;
1023de6a9c0SDavid du Colombier }
1033de6a9c0SDavid du Colombier p->enabled = 1;
1043de6a9c0SDavid du Colombier iunlock(&uartalloc);
1053de6a9c0SDavid du Colombier
1063de6a9c0SDavid du Colombier return p;
1073de6a9c0SDavid du Colombier }
1083de6a9c0SDavid du Colombier
1093de6a9c0SDavid du Colombier static void
uartdisable(Uart * p)1103de6a9c0SDavid du Colombier uartdisable(Uart *p)
1113de6a9c0SDavid du Colombier {
1123de6a9c0SDavid du Colombier Uart **l;
1133de6a9c0SDavid du Colombier
1143de6a9c0SDavid du Colombier (*p->phys->disable)(p);
1153de6a9c0SDavid du Colombier
1163de6a9c0SDavid du Colombier ilock(&uartalloc);
1173de6a9c0SDavid du Colombier for(l = &uartalloc.elist; *l; l = &(*l)->elist){
1183de6a9c0SDavid du Colombier if(*l == p){
1193de6a9c0SDavid du Colombier *l = p->elist;
1203de6a9c0SDavid du Colombier break;
1213de6a9c0SDavid du Colombier }
1223de6a9c0SDavid du Colombier }
1233de6a9c0SDavid du Colombier p->enabled = 0;
1243de6a9c0SDavid du Colombier iunlock(&uartalloc);
1253de6a9c0SDavid du Colombier }
1263de6a9c0SDavid du Colombier
1273de6a9c0SDavid du Colombier void
uartmouse(Uart * p,int (* putc)(Queue *,int),int setb1200)1283de6a9c0SDavid du Colombier uartmouse(Uart* p, int (*putc)(Queue*, int), int setb1200)
1293de6a9c0SDavid du Colombier {
1303de6a9c0SDavid du Colombier qlock(p);
1313de6a9c0SDavid du Colombier if(p->opens++ == 0 && uartenable(p) == nil){
1323de6a9c0SDavid du Colombier qunlock(p);
1333de6a9c0SDavid du Colombier error(Enodev);
1343de6a9c0SDavid du Colombier }
1353de6a9c0SDavid du Colombier if(setb1200)
1363de6a9c0SDavid du Colombier uartctl(p, "b1200");
1373de6a9c0SDavid du Colombier p->putc = putc;
1383de6a9c0SDavid du Colombier p->special = 1;
1393de6a9c0SDavid du Colombier qunlock(p);
1403de6a9c0SDavid du Colombier }
1413de6a9c0SDavid du Colombier
1423de6a9c0SDavid du Colombier void
uartsetmouseputc(Uart * p,int (* putc)(Queue *,int))1433de6a9c0SDavid du Colombier uartsetmouseputc(Uart* p, int (*putc)(Queue*, int))
1443de6a9c0SDavid du Colombier {
1453de6a9c0SDavid du Colombier qlock(p);
1463de6a9c0SDavid du Colombier if(p->opens == 0 || p->special == 0){
1473de6a9c0SDavid du Colombier qunlock(p);
1483de6a9c0SDavid du Colombier error(Enodev);
1493de6a9c0SDavid du Colombier }
1503de6a9c0SDavid du Colombier p->putc = putc;
1513de6a9c0SDavid du Colombier qunlock(p);
1523de6a9c0SDavid du Colombier }
1533de6a9c0SDavid du Colombier
1543de6a9c0SDavid du Colombier static void
setlength(int i)1553de6a9c0SDavid du Colombier setlength(int i)
1563de6a9c0SDavid du Colombier {
1573de6a9c0SDavid du Colombier Uart *p;
1583de6a9c0SDavid du Colombier
1593de6a9c0SDavid du Colombier if(i > 0){
1603de6a9c0SDavid du Colombier p = uart[i];
1613de6a9c0SDavid du Colombier if(p && p->opens && p->iq)
1623de6a9c0SDavid du Colombier uartdir[1+3*i].length = qlen(p->iq);
1633de6a9c0SDavid du Colombier } else for(i = 0; i < uartnuart; i++){
1643de6a9c0SDavid du Colombier p = uart[i];
1653de6a9c0SDavid du Colombier if(p && p->opens && p->iq)
1663de6a9c0SDavid du Colombier uartdir[1+3*i].length = qlen(p->iq);
1673de6a9c0SDavid du Colombier }
1683de6a9c0SDavid du Colombier }
1693de6a9c0SDavid du Colombier
1703de6a9c0SDavid du Colombier /*
1713de6a9c0SDavid du Colombier * set up the '#t' directory
1723de6a9c0SDavid du Colombier */
1733de6a9c0SDavid du Colombier static void
uartreset(void)1743de6a9c0SDavid du Colombier uartreset(void)
1753de6a9c0SDavid du Colombier {
1763de6a9c0SDavid du Colombier int i;
1773de6a9c0SDavid du Colombier Dirtab *dp;
1783de6a9c0SDavid du Colombier Uart *p, *tail;
1793de6a9c0SDavid du Colombier
1803de6a9c0SDavid du Colombier tail = nil;
1813de6a9c0SDavid du Colombier for(i = 0; physuart[i] != nil; i++){
1823de6a9c0SDavid du Colombier if(physuart[i]->pnp == nil)
1833de6a9c0SDavid du Colombier continue;
1843de6a9c0SDavid du Colombier if((p = physuart[i]->pnp()) == nil)
1853de6a9c0SDavid du Colombier continue;
1863de6a9c0SDavid du Colombier if(uartlist != nil)
1873de6a9c0SDavid du Colombier tail->next = p;
1883de6a9c0SDavid du Colombier else
1893de6a9c0SDavid du Colombier uartlist = p;
1903de6a9c0SDavid du Colombier for(tail = p; tail->next != nil; tail = tail->next)
1913de6a9c0SDavid du Colombier uartnuart++;
1923de6a9c0SDavid du Colombier uartnuart++;
1933de6a9c0SDavid du Colombier }
1943de6a9c0SDavid du Colombier
1953de6a9c0SDavid du Colombier if(uartnuart)
1963de6a9c0SDavid du Colombier uart = xalloc(uartnuart*sizeof(Uart*));
1973de6a9c0SDavid du Colombier
1983de6a9c0SDavid du Colombier uartndir = 1 + 3*uartnuart;
1993de6a9c0SDavid du Colombier uartdir = xalloc(uartndir * sizeof(Dirtab));
2003de6a9c0SDavid du Colombier if (uart == nil || uartdir == nil)
2013de6a9c0SDavid du Colombier panic("uartreset: no memory");
2023de6a9c0SDavid du Colombier dp = uartdir;
2033de6a9c0SDavid du Colombier strcpy(dp->name, ".");
2043de6a9c0SDavid du Colombier mkqid(&dp->qid, 0, 0, QTDIR);
2053de6a9c0SDavid du Colombier dp->length = 0;
2063de6a9c0SDavid du Colombier dp->perm = DMDIR|0555;
2073de6a9c0SDavid du Colombier dp++;
2083de6a9c0SDavid du Colombier p = uartlist;
2093de6a9c0SDavid du Colombier for(i = 0; i < uartnuart; i++){
2103de6a9c0SDavid du Colombier /* 3 directory entries per port */
2113de6a9c0SDavid du Colombier snprint(dp->name, sizeof dp->name, "eia%d", i);
2123de6a9c0SDavid du Colombier dp->qid.path = NETQID(i, Ndataqid);
2133de6a9c0SDavid du Colombier dp->perm = 0660;
2143de6a9c0SDavid du Colombier dp++;
2153de6a9c0SDavid du Colombier snprint(dp->name, sizeof dp->name, "eia%dctl", i);
2163de6a9c0SDavid du Colombier dp->qid.path = NETQID(i, Nctlqid);
2173de6a9c0SDavid du Colombier dp->perm = 0660;
2183de6a9c0SDavid du Colombier dp++;
2193de6a9c0SDavid du Colombier snprint(dp->name, sizeof dp->name, "eia%dstatus", i);
2203de6a9c0SDavid du Colombier dp->qid.path = NETQID(i, Nstatqid);
2213de6a9c0SDavid du Colombier dp->perm = 0444;
2223de6a9c0SDavid du Colombier dp++;
2233de6a9c0SDavid du Colombier
2243de6a9c0SDavid du Colombier uart[i] = p;
2253de6a9c0SDavid du Colombier p->dev = i;
2263de6a9c0SDavid du Colombier if(p->console || p->special){
2273de6a9c0SDavid du Colombier if(uartenable(p) != nil){
2283de6a9c0SDavid du Colombier if(p->console && up){
2293de6a9c0SDavid du Colombier kbdq = p->iq;
2303de6a9c0SDavid du Colombier serialoq = p->oq;
2313de6a9c0SDavid du Colombier p->putc = kbdcr2nl;
2323de6a9c0SDavid du Colombier }
2333de6a9c0SDavid du Colombier p->opens++;
2343de6a9c0SDavid du Colombier }
2353de6a9c0SDavid du Colombier }
2363de6a9c0SDavid du Colombier p = p->next;
2373de6a9c0SDavid du Colombier }
2383de6a9c0SDavid du Colombier
2393de6a9c0SDavid du Colombier if(uartnuart){
2403de6a9c0SDavid du Colombier /*
2413de6a9c0SDavid du Colombier * at 115200 baud, the 1024 char buffer takes 56 ms to process,
2423de6a9c0SDavid du Colombier * processing it every 22 ms should be fine.
2433de6a9c0SDavid du Colombier */
2443de6a9c0SDavid du Colombier uarttimer = addclock0link(uartclock, 22);
2453de6a9c0SDavid du Colombier }
2463de6a9c0SDavid du Colombier }
2473de6a9c0SDavid du Colombier
2483de6a9c0SDavid du Colombier
2493de6a9c0SDavid du Colombier static Chan*
uartattach(char * spec)2503de6a9c0SDavid du Colombier uartattach(char *spec)
2513de6a9c0SDavid du Colombier {
2523de6a9c0SDavid du Colombier return devattach('t', spec);
2533de6a9c0SDavid du Colombier }
2543de6a9c0SDavid du Colombier
2553de6a9c0SDavid du Colombier static Walkqid*
uartwalk(Chan * c,Chan * nc,char ** name,int nname)2563de6a9c0SDavid du Colombier uartwalk(Chan *c, Chan *nc, char **name, int nname)
2573de6a9c0SDavid du Colombier {
2583de6a9c0SDavid du Colombier return devwalk(c, nc, name, nname, uartdir, uartndir, devgen);
2593de6a9c0SDavid du Colombier }
2603de6a9c0SDavid du Colombier
2613de6a9c0SDavid du Colombier static int
uartstat(Chan * c,uchar * dp,int n)2623de6a9c0SDavid du Colombier uartstat(Chan *c, uchar *dp, int n)
2633de6a9c0SDavid du Colombier {
2643de6a9c0SDavid du Colombier if(NETTYPE(c->qid.path) == Ndataqid)
2653de6a9c0SDavid du Colombier setlength(NETID(c->qid.path));
2663de6a9c0SDavid du Colombier return devstat(c, dp, n, uartdir, uartndir, devgen);
2673de6a9c0SDavid du Colombier }
2683de6a9c0SDavid du Colombier
2693de6a9c0SDavid du Colombier static Chan*
uartopen(Chan * c,int omode)2703de6a9c0SDavid du Colombier uartopen(Chan *c, int omode)
2713de6a9c0SDavid du Colombier {
2723de6a9c0SDavid du Colombier Uart *p;
2733de6a9c0SDavid du Colombier
2743de6a9c0SDavid du Colombier c = devopen(c, omode, uartdir, uartndir, devgen);
2753de6a9c0SDavid du Colombier
2763de6a9c0SDavid du Colombier switch(NETTYPE(c->qid.path)){
2773de6a9c0SDavid du Colombier case Nctlqid:
2783de6a9c0SDavid du Colombier case Ndataqid:
2793de6a9c0SDavid du Colombier p = uart[NETID(c->qid.path)];
2803de6a9c0SDavid du Colombier qlock(p);
2813de6a9c0SDavid du Colombier if(p->opens++ == 0 && uartenable(p) == nil){
2823de6a9c0SDavid du Colombier qunlock(p);
2833de6a9c0SDavid du Colombier c->flag &= ~COPEN;
2843de6a9c0SDavid du Colombier error(Enodev);
2853de6a9c0SDavid du Colombier }
2863de6a9c0SDavid du Colombier qunlock(p);
2873de6a9c0SDavid du Colombier break;
2883de6a9c0SDavid du Colombier }
2893de6a9c0SDavid du Colombier
2903de6a9c0SDavid du Colombier c->iounit = qiomaxatomic;
2913de6a9c0SDavid du Colombier return c;
2923de6a9c0SDavid du Colombier }
2933de6a9c0SDavid du Colombier
2943de6a9c0SDavid du Colombier static int
uartdrained(void * arg)2953de6a9c0SDavid du Colombier uartdrained(void* arg)
2963de6a9c0SDavid du Colombier {
2973de6a9c0SDavid du Colombier Uart *p;
2983de6a9c0SDavid du Colombier
2993de6a9c0SDavid du Colombier p = arg;
3003de6a9c0SDavid du Colombier return qlen(p->oq) == 0 && p->op == p->oe;
3013de6a9c0SDavid du Colombier }
3023de6a9c0SDavid du Colombier
3033de6a9c0SDavid du Colombier static void
uartdrainoutput(Uart * p)3043de6a9c0SDavid du Colombier uartdrainoutput(Uart *p)
3053de6a9c0SDavid du Colombier {
3063de6a9c0SDavid du Colombier if(!p->enabled || up == nil)
3073de6a9c0SDavid du Colombier return;
3083de6a9c0SDavid du Colombier
3093de6a9c0SDavid du Colombier p->drain = 1;
3103de6a9c0SDavid du Colombier if(waserror()){
3113de6a9c0SDavid du Colombier p->drain = 0;
3123de6a9c0SDavid du Colombier nexterror();
3133de6a9c0SDavid du Colombier }
3143de6a9c0SDavid du Colombier sleep(&p->r, uartdrained, p);
3153de6a9c0SDavid du Colombier poperror();
3163de6a9c0SDavid du Colombier }
3173de6a9c0SDavid du Colombier
3183de6a9c0SDavid du Colombier static void
uartclose(Chan * c)3193de6a9c0SDavid du Colombier uartclose(Chan *c)
3203de6a9c0SDavid du Colombier {
3213de6a9c0SDavid du Colombier Uart *p;
3223de6a9c0SDavid du Colombier
3233de6a9c0SDavid du Colombier if(c->qid.type & QTDIR)
3243de6a9c0SDavid du Colombier return;
3253de6a9c0SDavid du Colombier if((c->flag & COPEN) == 0)
3263de6a9c0SDavid du Colombier return;
3273de6a9c0SDavid du Colombier switch(NETTYPE(c->qid.path)){
3283de6a9c0SDavid du Colombier case Ndataqid:
3293de6a9c0SDavid du Colombier case Nctlqid:
3303de6a9c0SDavid du Colombier p = uart[NETID(c->qid.path)];
3313de6a9c0SDavid du Colombier qlock(p);
3323de6a9c0SDavid du Colombier if(--(p->opens) == 0){
3333de6a9c0SDavid du Colombier qclose(p->iq);
3343de6a9c0SDavid du Colombier ilock(&p->rlock);
3353de6a9c0SDavid du Colombier p->ir = p->iw = p->istage;
3363de6a9c0SDavid du Colombier iunlock(&p->rlock);
3373de6a9c0SDavid du Colombier
3383de6a9c0SDavid du Colombier /*
3393de6a9c0SDavid du Colombier */
3403de6a9c0SDavid du Colombier qhangup(p->oq, nil);
3413de6a9c0SDavid du Colombier if(!waserror()){
3423de6a9c0SDavid du Colombier uartdrainoutput(p);
3433de6a9c0SDavid du Colombier poperror();
3443de6a9c0SDavid du Colombier }
3453de6a9c0SDavid du Colombier qclose(p->oq);
3463de6a9c0SDavid du Colombier uartdisable(p);
3473de6a9c0SDavid du Colombier p->dcd = p->dsr = p->dohup = 0;
3483de6a9c0SDavid du Colombier }
3493de6a9c0SDavid du Colombier qunlock(p);
3503de6a9c0SDavid du Colombier break;
3513de6a9c0SDavid du Colombier }
3523de6a9c0SDavid du Colombier }
3533de6a9c0SDavid du Colombier
3543de6a9c0SDavid du Colombier static long
uartread(Chan * c,void * buf,long n,vlong off)3553de6a9c0SDavid du Colombier uartread(Chan *c, void *buf, long n, vlong off)
3563de6a9c0SDavid du Colombier {
3573de6a9c0SDavid du Colombier Uart *p;
3583de6a9c0SDavid du Colombier ulong offset = off;
3593de6a9c0SDavid du Colombier
3603de6a9c0SDavid du Colombier if(c->qid.type & QTDIR){
3613de6a9c0SDavid du Colombier setlength(-1);
3623de6a9c0SDavid du Colombier return devdirread(c, buf, n, uartdir, uartndir, devgen);
3633de6a9c0SDavid du Colombier }
3643de6a9c0SDavid du Colombier
3653de6a9c0SDavid du Colombier p = uart[NETID(c->qid.path)];
3663de6a9c0SDavid du Colombier switch(NETTYPE(c->qid.path)){
3673de6a9c0SDavid du Colombier case Ndataqid:
3683de6a9c0SDavid du Colombier return qread(p->iq, buf, n);
3693de6a9c0SDavid du Colombier case Nctlqid:
3703de6a9c0SDavid du Colombier return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
3713de6a9c0SDavid du Colombier case Nstatqid:
3723de6a9c0SDavid du Colombier return (*p->phys->status)(p, buf, n, offset);
3733de6a9c0SDavid du Colombier }
3743de6a9c0SDavid du Colombier
3753de6a9c0SDavid du Colombier return 0;
3763de6a9c0SDavid du Colombier }
3773de6a9c0SDavid du Colombier
3783de6a9c0SDavid du Colombier int
uartctl(Uart * p,char * cmd)3793de6a9c0SDavid du Colombier uartctl(Uart *p, char *cmd)
3803de6a9c0SDavid du Colombier {
3813de6a9c0SDavid du Colombier char *f[16];
3823de6a9c0SDavid du Colombier int i, n, nf;
3833de6a9c0SDavid du Colombier
3843de6a9c0SDavid du Colombier nf = tokenize(cmd, f, nelem(f));
3853de6a9c0SDavid du Colombier for(i = 0; i < nf; i++){
3863de6a9c0SDavid du Colombier if(strncmp(f[i], "break", 5) == 0){
3873de6a9c0SDavid du Colombier (*p->phys->dobreak)(p, 0);
3883de6a9c0SDavid du Colombier continue;
3893de6a9c0SDavid du Colombier }
3903de6a9c0SDavid du Colombier
3913de6a9c0SDavid du Colombier n = atoi(f[i]+1);
3923de6a9c0SDavid du Colombier switch(*f[i]){
3933de6a9c0SDavid du Colombier case 'B':
3943de6a9c0SDavid du Colombier case 'b':
3953de6a9c0SDavid du Colombier uartdrainoutput(p);
3963de6a9c0SDavid du Colombier if((*p->phys->baud)(p, n) < 0)
3973de6a9c0SDavid du Colombier return -1;
3983de6a9c0SDavid du Colombier break;
3993de6a9c0SDavid du Colombier case 'C':
4003de6a9c0SDavid du Colombier case 'c':
4013de6a9c0SDavid du Colombier p->hup_dcd = n;
4023de6a9c0SDavid du Colombier break;
4033de6a9c0SDavid du Colombier case 'D':
4043de6a9c0SDavid du Colombier case 'd':
4053de6a9c0SDavid du Colombier uartdrainoutput(p);
4063de6a9c0SDavid du Colombier (*p->phys->dtr)(p, n);
4073de6a9c0SDavid du Colombier break;
4083de6a9c0SDavid du Colombier case 'E':
4093de6a9c0SDavid du Colombier case 'e':
4103de6a9c0SDavid du Colombier p->hup_dsr = n;
4113de6a9c0SDavid du Colombier break;
4123de6a9c0SDavid du Colombier case 'f':
4133de6a9c0SDavid du Colombier case 'F':
4143de6a9c0SDavid du Colombier if(p->oq != nil)
4153de6a9c0SDavid du Colombier qflush(p->oq);
4163de6a9c0SDavid du Colombier break;
4173de6a9c0SDavid du Colombier case 'H':
4183de6a9c0SDavid du Colombier case 'h':
4193de6a9c0SDavid du Colombier if(p->iq != nil)
4203de6a9c0SDavid du Colombier qhangup(p->iq, 0);
4213de6a9c0SDavid du Colombier if(p->oq != nil)
4223de6a9c0SDavid du Colombier qhangup(p->oq, 0);
4233de6a9c0SDavid du Colombier break;
4243de6a9c0SDavid du Colombier case 'i':
4253de6a9c0SDavid du Colombier case 'I':
4263de6a9c0SDavid du Colombier uartdrainoutput(p);
4273de6a9c0SDavid du Colombier (*p->phys->fifo)(p, n);
4283de6a9c0SDavid du Colombier break;
4293de6a9c0SDavid du Colombier case 'K':
4303de6a9c0SDavid du Colombier case 'k':
4313de6a9c0SDavid du Colombier uartdrainoutput(p);
4323de6a9c0SDavid du Colombier (*p->phys->dobreak)(p, n);
4333de6a9c0SDavid du Colombier break;
4343de6a9c0SDavid du Colombier case 'L':
4353de6a9c0SDavid du Colombier case 'l':
4363de6a9c0SDavid du Colombier uartdrainoutput(p);
4373de6a9c0SDavid du Colombier if((*p->phys->bits)(p, n) < 0)
4383de6a9c0SDavid du Colombier return -1;
4393de6a9c0SDavid du Colombier break;
4403de6a9c0SDavid du Colombier case 'm':
4413de6a9c0SDavid du Colombier case 'M':
4423de6a9c0SDavid du Colombier uartdrainoutput(p);
4433de6a9c0SDavid du Colombier (*p->phys->modemctl)(p, n);
4443de6a9c0SDavid du Colombier break;
4453de6a9c0SDavid du Colombier case 'n':
4463de6a9c0SDavid du Colombier case 'N':
4473de6a9c0SDavid du Colombier if(p->oq != nil)
4483de6a9c0SDavid du Colombier qnoblock(p->oq, n);
4493de6a9c0SDavid du Colombier break;
4503de6a9c0SDavid du Colombier case 'P':
4513de6a9c0SDavid du Colombier case 'p':
4523de6a9c0SDavid du Colombier uartdrainoutput(p);
4533de6a9c0SDavid du Colombier if((*p->phys->parity)(p, *(f[i]+1)) < 0)
4543de6a9c0SDavid du Colombier return -1;
4553de6a9c0SDavid du Colombier break;
4563de6a9c0SDavid du Colombier case 'Q':
4573de6a9c0SDavid du Colombier case 'q':
4583de6a9c0SDavid du Colombier if(p->iq != nil)
4593de6a9c0SDavid du Colombier qsetlimit(p->iq, n);
4603de6a9c0SDavid du Colombier if(p->oq != nil)
4613de6a9c0SDavid du Colombier qsetlimit(p->oq, n);
4623de6a9c0SDavid du Colombier break;
4633de6a9c0SDavid du Colombier case 'R':
4643de6a9c0SDavid du Colombier case 'r':
4653de6a9c0SDavid du Colombier uartdrainoutput(p);
4663de6a9c0SDavid du Colombier (*p->phys->rts)(p, n);
4673de6a9c0SDavid du Colombier break;
4683de6a9c0SDavid du Colombier case 'S':
4693de6a9c0SDavid du Colombier case 's':
4703de6a9c0SDavid du Colombier uartdrainoutput(p);
4713de6a9c0SDavid du Colombier if((*p->phys->stop)(p, n) < 0)
4723de6a9c0SDavid du Colombier return -1;
4733de6a9c0SDavid du Colombier break;
4743de6a9c0SDavid du Colombier case 'W':
4753de6a9c0SDavid du Colombier case 'w':
4763de6a9c0SDavid du Colombier if(uarttimer == nil || n < 1)
4773de6a9c0SDavid du Colombier return -1;
4783de6a9c0SDavid du Colombier uarttimer->tns = (vlong)n * 100000LL;
4793de6a9c0SDavid du Colombier break;
4803de6a9c0SDavid du Colombier case 'X':
4813de6a9c0SDavid du Colombier case 'x':
4823de6a9c0SDavid du Colombier if(p->enabled){
4833de6a9c0SDavid du Colombier ilock(&p->tlock);
4843de6a9c0SDavid du Colombier p->xonoff = n;
4853de6a9c0SDavid du Colombier iunlock(&p->tlock);
4863de6a9c0SDavid du Colombier }
4873de6a9c0SDavid du Colombier break;
4883de6a9c0SDavid du Colombier }
4893de6a9c0SDavid du Colombier }
4903de6a9c0SDavid du Colombier return 0;
4913de6a9c0SDavid du Colombier }
4923de6a9c0SDavid du Colombier
4933de6a9c0SDavid du Colombier static long
uartwrite(Chan * c,void * buf,long n,vlong)4943de6a9c0SDavid du Colombier uartwrite(Chan *c, void *buf, long n, vlong)
4953de6a9c0SDavid du Colombier {
4963de6a9c0SDavid du Colombier Uart *p;
4973de6a9c0SDavid du Colombier char *cmd;
4983de6a9c0SDavid du Colombier
4993de6a9c0SDavid du Colombier if(c->qid.type & QTDIR)
5003de6a9c0SDavid du Colombier error(Eperm);
5013de6a9c0SDavid du Colombier
5023de6a9c0SDavid du Colombier p = uart[NETID(c->qid.path)];
5033de6a9c0SDavid du Colombier
5043de6a9c0SDavid du Colombier switch(NETTYPE(c->qid.path)){
5053de6a9c0SDavid du Colombier case Ndataqid:
5063de6a9c0SDavid du Colombier qlock(p);
5073de6a9c0SDavid du Colombier if(waserror()){
5083de6a9c0SDavid du Colombier qunlock(p);
5093de6a9c0SDavid du Colombier nexterror();
5103de6a9c0SDavid du Colombier }
5113de6a9c0SDavid du Colombier
5123de6a9c0SDavid du Colombier n = qwrite(p->oq, buf, n);
5133de6a9c0SDavid du Colombier
5143de6a9c0SDavid du Colombier qunlock(p);
5153de6a9c0SDavid du Colombier poperror();
5163de6a9c0SDavid du Colombier break;
5173de6a9c0SDavid du Colombier case Nctlqid:
5183de6a9c0SDavid du Colombier cmd = malloc(n+1);
5193de6a9c0SDavid du Colombier memmove(cmd, buf, n);
5203de6a9c0SDavid du Colombier cmd[n] = 0;
5213de6a9c0SDavid du Colombier qlock(p);
5223de6a9c0SDavid du Colombier if(waserror()){
5233de6a9c0SDavid du Colombier qunlock(p);
5243de6a9c0SDavid du Colombier free(cmd);
5253de6a9c0SDavid du Colombier nexterror();
5263de6a9c0SDavid du Colombier }
5273de6a9c0SDavid du Colombier
5283de6a9c0SDavid du Colombier /* let output drain */
5293de6a9c0SDavid du Colombier if(uartctl(p, cmd) < 0)
5303de6a9c0SDavid du Colombier error(Ebadarg);
5313de6a9c0SDavid du Colombier
5323de6a9c0SDavid du Colombier qunlock(p);
5333de6a9c0SDavid du Colombier poperror();
5343de6a9c0SDavid du Colombier free(cmd);
5353de6a9c0SDavid du Colombier break;
5363de6a9c0SDavid du Colombier }
5373de6a9c0SDavid du Colombier
5383de6a9c0SDavid du Colombier return n;
5393de6a9c0SDavid du Colombier }
5403de6a9c0SDavid du Colombier
5413de6a9c0SDavid du Colombier static int
uartwstat(Chan * c,uchar * dp,int n)5423de6a9c0SDavid du Colombier uartwstat(Chan *c, uchar *dp, int n)
5433de6a9c0SDavid du Colombier {
5443de6a9c0SDavid du Colombier Dir d;
5453de6a9c0SDavid du Colombier Dirtab *dt;
5463de6a9c0SDavid du Colombier
5473de6a9c0SDavid du Colombier if(!iseve())
5483de6a9c0SDavid du Colombier error(Eperm);
5493de6a9c0SDavid du Colombier if(QTDIR & c->qid.type)
5503de6a9c0SDavid du Colombier error(Eperm);
5513de6a9c0SDavid du Colombier if(NETTYPE(c->qid.path) == Nstatqid)
5523de6a9c0SDavid du Colombier error(Eperm);
5533de6a9c0SDavid du Colombier
5543de6a9c0SDavid du Colombier dt = &uartdir[1 + 3 * NETID(c->qid.path)];
5553de6a9c0SDavid du Colombier n = convM2D(dp, n, &d, nil);
5563de6a9c0SDavid du Colombier if(n == 0)
5573de6a9c0SDavid du Colombier error(Eshortstat);
5583de6a9c0SDavid du Colombier if(d.mode != ~0UL)
5593de6a9c0SDavid du Colombier dt[0].perm = dt[1].perm = d.mode;
5603de6a9c0SDavid du Colombier return n;
5613de6a9c0SDavid du Colombier }
5623de6a9c0SDavid du Colombier
5633de6a9c0SDavid du Colombier void
uartpower(int on)5643de6a9c0SDavid du Colombier uartpower(int on)
5653de6a9c0SDavid du Colombier {
5663de6a9c0SDavid du Colombier Uart *p;
5673de6a9c0SDavid du Colombier
5683de6a9c0SDavid du Colombier for(p = uartlist; p != nil; p = p->next) {
5693de6a9c0SDavid du Colombier if(p->phys->power)
5703de6a9c0SDavid du Colombier (*p->phys->power)(p, on);
5713de6a9c0SDavid du Colombier }
5723de6a9c0SDavid du Colombier }
5733de6a9c0SDavid du Colombier
5743de6a9c0SDavid du Colombier Dev uartdevtab = {
5753de6a9c0SDavid du Colombier 't',
5763de6a9c0SDavid du Colombier "uart",
5773de6a9c0SDavid du Colombier
5783de6a9c0SDavid du Colombier uartreset,
5793de6a9c0SDavid du Colombier devinit,
5803de6a9c0SDavid du Colombier devshutdown,
5813de6a9c0SDavid du Colombier uartattach,
5823de6a9c0SDavid du Colombier uartwalk,
5833de6a9c0SDavid du Colombier uartstat,
5843de6a9c0SDavid du Colombier uartopen,
5853de6a9c0SDavid du Colombier devcreate,
5863de6a9c0SDavid du Colombier uartclose,
5873de6a9c0SDavid du Colombier uartread,
5883de6a9c0SDavid du Colombier devbread,
5893de6a9c0SDavid du Colombier uartwrite,
5903de6a9c0SDavid du Colombier devbwrite,
5913de6a9c0SDavid du Colombier devremove,
5923de6a9c0SDavid du Colombier uartwstat,
5933de6a9c0SDavid du Colombier uartpower,
5943de6a9c0SDavid du Colombier };
5953de6a9c0SDavid du Colombier
5963de6a9c0SDavid du Colombier /*
5973de6a9c0SDavid du Colombier * restart input if it's off
5983de6a9c0SDavid du Colombier */
5993de6a9c0SDavid du Colombier static void
uartflow(void * v)6003de6a9c0SDavid du Colombier uartflow(void *v)
6013de6a9c0SDavid du Colombier {
6023de6a9c0SDavid du Colombier Uart *p;
6033de6a9c0SDavid du Colombier
6043de6a9c0SDavid du Colombier p = v;
6053de6a9c0SDavid du Colombier if(p->modem)
6063de6a9c0SDavid du Colombier (*p->phys->rts)(p, 1);
6073de6a9c0SDavid du Colombier }
6083de6a9c0SDavid du Colombier
6093de6a9c0SDavid du Colombier /*
6103de6a9c0SDavid du Colombier * put some bytes into the local queue to avoid calling
6113de6a9c0SDavid du Colombier * qconsume for every character
6123de6a9c0SDavid du Colombier */
6133de6a9c0SDavid du Colombier int
uartstageoutput(Uart * p)6143de6a9c0SDavid du Colombier uartstageoutput(Uart *p)
6153de6a9c0SDavid du Colombier {
6163de6a9c0SDavid du Colombier int n;
6173de6a9c0SDavid du Colombier
6183de6a9c0SDavid du Colombier n = qconsume(p->oq, p->ostage, Stagesize);
6193de6a9c0SDavid du Colombier if(n <= 0)
6203de6a9c0SDavid du Colombier // n = 0; /* experiment */
6213de6a9c0SDavid du Colombier return 0;
6223de6a9c0SDavid du Colombier p->op = p->ostage;
6233de6a9c0SDavid du Colombier p->oe = p->ostage + n;
6243de6a9c0SDavid du Colombier return n;
6253de6a9c0SDavid du Colombier }
6263de6a9c0SDavid du Colombier
6273de6a9c0SDavid du Colombier /*
6283de6a9c0SDavid du Colombier * restart output
6293de6a9c0SDavid du Colombier */
6303de6a9c0SDavid du Colombier void
uartkick(void * v)6313de6a9c0SDavid du Colombier uartkick(void *v)
6323de6a9c0SDavid du Colombier {
6333de6a9c0SDavid du Colombier Uart *p = v;
6343de6a9c0SDavid du Colombier
6353de6a9c0SDavid du Colombier if(p->blocked)
6363de6a9c0SDavid du Colombier return;
6373de6a9c0SDavid du Colombier
6383de6a9c0SDavid du Colombier ilock(&p->tlock);
6393de6a9c0SDavid du Colombier (*p->phys->kick)(p);
6403de6a9c0SDavid du Colombier iunlock(&p->tlock);
6413de6a9c0SDavid du Colombier
6423de6a9c0SDavid du Colombier if(p->drain && uartdrained(p)){
6433de6a9c0SDavid du Colombier p->drain = 0;
6443de6a9c0SDavid du Colombier wakeup(&p->r);
6453de6a9c0SDavid du Colombier }
6463de6a9c0SDavid du Colombier }
6473de6a9c0SDavid du Colombier
6483de6a9c0SDavid du Colombier /*
6493de6a9c0SDavid du Colombier * Move data from the interrupt staging area to
6503de6a9c0SDavid du Colombier * the input Queue.
6513de6a9c0SDavid du Colombier */
6523de6a9c0SDavid du Colombier static void
uartstageinput(Uart * p)6533de6a9c0SDavid du Colombier uartstageinput(Uart *p)
6543de6a9c0SDavid du Colombier {
6553de6a9c0SDavid du Colombier int n;
6563de6a9c0SDavid du Colombier uchar *ir, *iw;
6573de6a9c0SDavid du Colombier
6583de6a9c0SDavid du Colombier while(p->ir != p->iw){
6593de6a9c0SDavid du Colombier ir = p->ir;
6603de6a9c0SDavid du Colombier if(p->ir > p->iw){
6613de6a9c0SDavid du Colombier iw = p->ie;
6623de6a9c0SDavid du Colombier p->ir = p->istage;
6633de6a9c0SDavid du Colombier }
6643de6a9c0SDavid du Colombier else{
6653de6a9c0SDavid du Colombier iw = p->iw;
6663de6a9c0SDavid du Colombier p->ir = p->iw;
6673de6a9c0SDavid du Colombier }
6683de6a9c0SDavid du Colombier if((n = qproduce(p->iq, ir, iw - ir)) < 0){
6693de6a9c0SDavid du Colombier p->serr++;
6703de6a9c0SDavid du Colombier (*p->phys->rts)(p, 0);
6713de6a9c0SDavid du Colombier }
6723de6a9c0SDavid du Colombier else if(n == 0)
6733de6a9c0SDavid du Colombier p->berr++;
6743de6a9c0SDavid du Colombier }
6753de6a9c0SDavid du Colombier }
6763de6a9c0SDavid du Colombier
6773de6a9c0SDavid du Colombier /*
6783de6a9c0SDavid du Colombier * receive a character at interrupt time
6793de6a9c0SDavid du Colombier */
6803de6a9c0SDavid du Colombier void
uartrecv(Uart * p,char ch)6813de6a9c0SDavid du Colombier uartrecv(Uart *p, char ch)
6823de6a9c0SDavid du Colombier {
6833de6a9c0SDavid du Colombier uchar *next;
6843de6a9c0SDavid du Colombier
6853de6a9c0SDavid du Colombier /* software flow control */
6863de6a9c0SDavid du Colombier if(p->xonoff){
6873de6a9c0SDavid du Colombier if(ch == CTLS){
6883de6a9c0SDavid du Colombier p->blocked = 1;
6893de6a9c0SDavid du Colombier }else if(ch == CTLQ){
6903de6a9c0SDavid du Colombier p->blocked = 0;
6913de6a9c0SDavid du Colombier p->ctsbackoff = 2; /* clock gets output going again */
6923de6a9c0SDavid du Colombier }
6933de6a9c0SDavid du Colombier }
6943de6a9c0SDavid du Colombier
6953de6a9c0SDavid du Colombier /* receive the character */
6963de6a9c0SDavid du Colombier if(p->putc)
6973de6a9c0SDavid du Colombier p->putc(p->iq, ch);
6983de6a9c0SDavid du Colombier else if (p->iw) { /* maybe the line isn't enabled yet */
6993de6a9c0SDavid du Colombier ilock(&p->rlock);
7003de6a9c0SDavid du Colombier next = p->iw + 1;
7013de6a9c0SDavid du Colombier if(next == p->ie)
7023de6a9c0SDavid du Colombier next = p->istage;
7033de6a9c0SDavid du Colombier if(next == p->ir)
7043de6a9c0SDavid du Colombier uartstageinput(p);
7053de6a9c0SDavid du Colombier if(next != p->ir){
7063de6a9c0SDavid du Colombier *p->iw = ch;
7073de6a9c0SDavid du Colombier p->iw = next;
7083de6a9c0SDavid du Colombier }
7093de6a9c0SDavid du Colombier iunlock(&p->rlock);
7103de6a9c0SDavid du Colombier }
7113de6a9c0SDavid du Colombier }
7123de6a9c0SDavid du Colombier
7133de6a9c0SDavid du Colombier /*
7143de6a9c0SDavid du Colombier * we save up input characters till clock time to reduce
7153de6a9c0SDavid du Colombier * per character interrupt overhead.
7163de6a9c0SDavid du Colombier */
7173de6a9c0SDavid du Colombier static void
uartclock(void)7183de6a9c0SDavid du Colombier uartclock(void)
7193de6a9c0SDavid du Colombier {
7203de6a9c0SDavid du Colombier Uart *p;
7213de6a9c0SDavid du Colombier
7223de6a9c0SDavid du Colombier ilock(&uartalloc);
7233de6a9c0SDavid du Colombier for(p = uartalloc.elist; p; p = p->elist){
7243de6a9c0SDavid du Colombier
7253de6a9c0SDavid du Colombier /* this hopefully amortizes cost of qproduce to many chars */
7263de6a9c0SDavid du Colombier if(p->iw != p->ir){
7273de6a9c0SDavid du Colombier ilock(&p->rlock);
7283de6a9c0SDavid du Colombier uartstageinput(p);
7293de6a9c0SDavid du Colombier iunlock(&p->rlock);
7303de6a9c0SDavid du Colombier }
7313de6a9c0SDavid du Colombier
7323de6a9c0SDavid du Colombier /* hang up if requested */
7333de6a9c0SDavid du Colombier if(p->dohup){
7343de6a9c0SDavid du Colombier qhangup(p->iq, 0);
7353de6a9c0SDavid du Colombier qhangup(p->oq, 0);
7363de6a9c0SDavid du Colombier p->dohup = 0;
7373de6a9c0SDavid du Colombier }
7383de6a9c0SDavid du Colombier
7393de6a9c0SDavid du Colombier /* this adds hysteresis to hardware/software flow control */
7403de6a9c0SDavid du Colombier if(p->ctsbackoff){
7413de6a9c0SDavid du Colombier ilock(&p->tlock);
7423de6a9c0SDavid du Colombier if(p->ctsbackoff){
7433de6a9c0SDavid du Colombier if(--(p->ctsbackoff) == 0)
7443de6a9c0SDavid du Colombier (*p->phys->kick)(p);
7453de6a9c0SDavid du Colombier }
7463de6a9c0SDavid du Colombier iunlock(&p->tlock);
7473de6a9c0SDavid du Colombier }
7483de6a9c0SDavid du Colombier uartkick(p); /* keep it moving */
7493de6a9c0SDavid du Colombier }
7503de6a9c0SDavid du Colombier iunlock(&uartalloc);
7513de6a9c0SDavid du Colombier }
7523de6a9c0SDavid du Colombier
7533de6a9c0SDavid du Colombier /*
7543de6a9c0SDavid du Colombier * polling console input, output
7553de6a9c0SDavid du Colombier */
7563de6a9c0SDavid du Colombier
7573de6a9c0SDavid du Colombier Uart* consuart;
7583de6a9c0SDavid du Colombier
7593de6a9c0SDavid du Colombier int
uartgetc(void)7603de6a9c0SDavid du Colombier uartgetc(void)
7613de6a9c0SDavid du Colombier {
7623de6a9c0SDavid du Colombier if(consuart == nil || consuart->phys->getc == nil)
7633de6a9c0SDavid du Colombier return -1;
7643de6a9c0SDavid du Colombier return consuart->phys->getc(consuart);
7653de6a9c0SDavid du Colombier }
7663de6a9c0SDavid du Colombier
7673de6a9c0SDavid du Colombier void
uartputc(int c)7683de6a9c0SDavid du Colombier uartputc(int c)
7693de6a9c0SDavid du Colombier {
7703de6a9c0SDavid du Colombier char c2;
7713de6a9c0SDavid du Colombier
7723de6a9c0SDavid du Colombier if(consuart == nil || consuart->phys->putc == nil) {
7733de6a9c0SDavid du Colombier c2 = c;
774*696c1e60SDavid du Colombier if (lprint)
775*696c1e60SDavid du Colombier (*lprint)(&c2, 1);
7763de6a9c0SDavid du Colombier return;
7773de6a9c0SDavid du Colombier }
7783de6a9c0SDavid du Colombier consuart->phys->putc(consuart, c);
7793de6a9c0SDavid du Colombier }
7803de6a9c0SDavid du Colombier
7813de6a9c0SDavid du Colombier void
uartputs(char * s,int n)7823de6a9c0SDavid du Colombier uartputs(char *s, int n)
7833de6a9c0SDavid du Colombier {
7843de6a9c0SDavid du Colombier char *e;
7853de6a9c0SDavid du Colombier
7863de6a9c0SDavid du Colombier if(consuart == nil || consuart->phys->putc == nil) {
787*696c1e60SDavid du Colombier if (lprint)
788*696c1e60SDavid du Colombier (*lprint)(s, n);
7893de6a9c0SDavid du Colombier return;
7903de6a9c0SDavid du Colombier }
7913de6a9c0SDavid du Colombier
7923de6a9c0SDavid du Colombier e = s+n;
7933de6a9c0SDavid du Colombier for(; s<e; s++){
7943de6a9c0SDavid du Colombier if(*s == '\n')
7953de6a9c0SDavid du Colombier consuart->phys->putc(consuart, '\r');
7963de6a9c0SDavid du Colombier consuart->phys->putc(consuart, *s);
7973de6a9c0SDavid du Colombier }
7983de6a9c0SDavid du Colombier }
799