diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 21:39:35 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 21:39:35 +0000 |
| commit | 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a (patch) | |
| tree | c6e220ba61db3a6ea4052e6841296d829654e664 /os/pxa/devuart.c | |
| parent | 46439007cf417cbd9ac8049bb4122c890097a0fa (diff) | |
20060303
Diffstat (limited to 'os/pxa/devuart.c')
| -rw-r--r-- | os/pxa/devuart.c | 1070 |
1 files changed, 1070 insertions, 0 deletions
diff --git a/os/pxa/devuart.c b/os/pxa/devuart.c new file mode 100644 index 00000000..4b8cc140 --- /dev/null +++ b/os/pxa/devuart.c @@ -0,0 +1,1070 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "../port/error.h" + +#include "../port/netif.h" + +/* + * Driver for the uart. + * TO DO: replace by uartpxa.c + */ +enum +{ + /* + * register numbers + */ + Data= 0, /* xmit/rcv buffer */ + Iena= 1, /* interrupt enable */ + Ircv= (1<<0), /* for char rcv'd */ + Ixmt= (1<<1), /* for xmit buffer empty */ + Irstat=(1<<2), /* for change in rcv'er status */ + Imstat=(1<<3), /* for change in modem status */ + Rtoie= 1<<4, /* character timeout indication */ + Nrze= 1<<5, /* NRZ encoding enabled */ + Uue= 1<<6, /* Uart unit enabled */ + Dmae= 1<<7, /* DMA requests enabled */ + Istat= 2, /* interrupt flag (read) */ + Ipend= 1, /* interrupt pending (not) */ + Fenabd=(3<<6), /* on if fifo's enabled */ + Fifoctl=2, /* fifo control (write) */ + Fena= (1<<0), /* enable xmit/rcv fifos */ + Fdma= (1<<3), /* dma on */ + Ftrig= (1<<6), /* trigger after 4 input characters */ + Fclear=(3<<1), /* clear xmit & rcv fifos */ + Format= 3, /* byte format */ + Bits8= (3<<0), /* 8 bits/byte */ + Stop2= (1<<2), /* 2 stop bits */ + Pena= (1<<3), /* generate parity */ + Peven= (1<<4), /* even parity */ + Pforce=(1<<5), /* force parity */ + Break= (1<<6), /* generate a break */ + Dra= (1<<7), /* address the divisor */ + Mctl= 4, /* modem control */ + Dtr= (1<<0), /* data terminal ready */ + Rts= (1<<1), /* request to send */ + Ri= (1<<2), /* ring */ + Inton= (1<<3), /* turn on interrupts */ + Loop= (1<<4), /* loop back */ + Lstat= 5, /* line status */ + Inready=(1<<0), /* receive buffer full */ + Oerror=(1<<1), /* receiver overrun */ + Perror=(1<<2), /* receiver parity error */ + Ferror=(1<<3), /* rcv framing error */ + Berror=(1<<4), /* break alarm */ + Outready=(1<<5), /* output buffer full */ + Mstat= 6, /* modem status */ + Ctsc= (1<<0), /* clear to send changed */ + Dsrc= (1<<1), /* data set ready changed */ + Rire= (1<<2), /* rising edge of ring indicator */ + Dcdc= (1<<3), /* data carrier detect changed */ + Cts= (1<<4), /* complement of clear to send line */ + Dsr= (1<<5), /* complement of data set ready line */ + Ringl= (1<<6), /* complement of ring indicator line */ + Dcd= (1<<7), /* complement of data carrier detect line */ + Scratch=7, /* scratchpad */ + Dlsb= 0, /* divisor lsb */ + Dmsb= 1, /* divisor msb */ + + CTLS= 023, + CTLQ= 021, + + Stagesize= 1024, + Nuart= 4, /* max per machine */ +}; + +typedef struct Uart Uart; +struct Uart +{ + QLock; + int opens; + + int enabled; + Uart *elist; /* next enabled interface */ + char name[KNAMELEN]; + + ulong sticky[8]; /* sticky write register values */ + void* regs; + ulong port; + ulong freq; /* clock frequency */ + uchar mask; /* bits/char */ + int dev; + int baud; /* baud rate */ + + uchar istat; /* last istat read */ + int frame; /* framing errors */ + int overrun; /* rcvr overruns */ + + /* buffers */ + int (*putc)(Queue*, int); + Queue *iq; + Queue *oq; + + Lock flock; /* fifo */ + uchar fifoon; /* fifo's enabled */ + uchar nofifo; /* earlier chip version with nofifo */ + + Lock rlock; /* receive */ + uchar istage[Stagesize]; + uchar *ip; + uchar *ie; + + int haveinput; + + Lock tlock; /* transmit */ + uchar ostage[Stagesize]; + uchar *op; + uchar *oe; + + int modem; /* hardware flow control on */ + int xonoff; /* software flow control on */ + int blocked; + int cts, dsr, dcd; /* keep track of modem status */ + int ctsbackoff; + int hup_dsr, hup_dcd; /* send hangup upstream? */ + int dohup; + + Rendez r; +}; + +static Uart *uart[Nuart]; +static int nuart; + +struct Uartalloc { + Lock; + Uart *elist; /* list of enabled interfaces */ +} uartalloc; + +static void uartintr(Uart*); + +/* + * pick up architecture specific routines and definitions + */ +#include "uart.h" + +/* + * set the baud rate by calculating and setting the baudrate + * generator constant. This will work with fairly non-standard + * baud rates. + */ +static void +uartsetbaud(Uart *p, int rate) +{ + ulong brconst; + + if(rate <= 0) + return; + + p->freq = archuartclock(p->port, rate); + if(p->freq == 0) + return; + + brconst = (p->freq+8*rate-1)/(16*rate); + + uartwrreg(p, Format, Dra); + uartwr(p, Dmsb, (brconst>>8) & 0xff); + uartwr(p, Dlsb, brconst & 0xff); + uartwrreg(p, Format, 0); + + p->baud = rate; +} + +/* + * decide if we should hangup when dsr or dcd drops. + */ +static void +uartdsrhup(Uart *p, int n) +{ + p->hup_dsr = n; +} + +static void +uartdcdhup(Uart *p, int n) +{ + p->hup_dcd = n; +} + +static void +uartparity(Uart *p, char type) +{ + switch(type){ + case 'e': + p->sticky[Format] |= Pena|Peven; + break; + case 'o': + p->sticky[Format] &= ~Peven; + p->sticky[Format] |= Pena; + break; + default: + p->sticky[Format] &= ~(Pena|Peven); + break; + } + uartwrreg(p, Format, 0); +} + +/* + * set bits/character, default 8 + */ +void +uartbits(Uart *p, int bits) +{ + if(bits < 5 || bits > 8) + error(Ebadarg); + + p->sticky[Format] &= ~3; + p->sticky[Format] |= bits-5; + + uartwrreg(p, Format, 0); +} + + +/* + * toggle DTR + */ +void +uartdtr(Uart *p, int n) +{ + if(n) + p->sticky[Mctl] |= Dtr; + else + p->sticky[Mctl] &= ~Dtr; + + uartwrreg(p, Mctl, 0); +} + +/* + * toggle RTS + */ +void +uartrts(Uart *p, int n) +{ + if(n) + p->sticky[Mctl] |= Rts; + else + p->sticky[Mctl] &= ~Rts; + + uartwrreg(p, Mctl, 0); +} + +/* + * send break + */ +static void +uartbreak(Uart *p, int ms) +{ + if(ms == 0) + ms = 200; + + uartwrreg(p, Format, Break); + tsleep(&up->sleep, return0, 0, ms); + uartwrreg(p, Format, 0); +} + +static void +uartfifoon(Uart *p) +{ + ulong i, x; + + if(p->nofifo || uartrdreg(p, Istat) & Fenabd) + return; + + x = splhi(); + + /* reset fifos */ + p->sticky[Fifoctl] = 0; + uartwrreg(p, Fifoctl, Fclear); + + /* empty buffer and interrupt conditions */ + for(i = 0; i < 16; i++){ + if(uartrdreg(p, Istat)){ + /* nothing to do */ + } + if(uartrdreg(p, Data)){ + /* nothing to do */ + } + } + + /* turn on fifo */ + p->fifoon = 1; + p->sticky[Fifoctl] = Fena|Ftrig; + uartwrreg(p, Fifoctl, 0); + p->istat = uartrdreg(p, Istat); + if((p->istat & Fenabd) == 0) { + /* didn't work, must be an earlier chip type */ + p->nofifo = 1; + } + + splx(x); +} + +/* + * modem flow control on/off (rts/cts) + */ +static void +uartmflow(Uart *p, int n) +{ + ilock(&p->tlock); + if(n){ + p->sticky[Iena] |= Imstat; + uartwrreg(p, Iena, 0); + p->modem = 1; + p->cts = uartrdreg(p, Mstat) & Cts; + } else { + p->sticky[Iena] &= ~Imstat; + uartwrreg(p, Iena, 0); + p->modem = 0; + p->cts = 1; + } + iunlock(&p->tlock); + +// ilock(&p->flock); +// if(1) +// /* turn on fifo's */ +// uartfifoon(p); +// else { +// /* turn off fifo's */ +// p->fifoon = 0; +// p->sticky[Fifoctl] = 0; +// uartwrreg(p, Fifoctl, Fclear); +// } +// iunlock(&p->flock); +} + +/* + * turn on a port's interrupts. set DTR and RTS + */ +static void +uartenable(Uart *p) +{ + Uart **l; + + if(p->enabled) + return; + + uartportpower(p, 1); + + p->hup_dsr = p->hup_dcd = 0; + p->cts = p->dsr = p->dcd = 0; + + /* + * turn on interrupts + */ + p->sticky[Iena] = Ircv | Ixmt | Irstat | Uue; + uartwrreg(p, Iena, 0); + + /* + * turn on DTR and RTS + */ + uartdtr(p, 1); + uartrts(p, 1); + + uartfifoon(p); + + /* + * assume we can send + */ + ilock(&p->tlock); + p->cts = 1; + p->blocked = 0; + iunlock(&p->tlock); + + /* + * set baud rate to the last used + */ + uartsetbaud(p, p->baud); + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p) + break; + } + if(*l == 0){ + p->elist = uartalloc.elist; + uartalloc.elist = p; + } + p->enabled = 1; + unlock(&uartalloc); +} + +/* + * turn off a port's interrupts. reset DTR and RTS + */ +static void +uartdisable(Uart *p) +{ + Uart **l; + + /* + * turn off interrupts + */ + p->sticky[Iena] = Uue; + uartwrreg(p, Iena, 0); + + /* + * revert to default settings + */ + p->sticky[Format] = Bits8; + uartwrreg(p, Format, 0); + + /* + * turn off DTR, RTS, hardware flow control & fifo's + */ + uartdtr(p, 0); + uartrts(p, 0); + uartmflow(p, 0); + ilock(&p->tlock); + p->xonoff = p->blocked = 0; + iunlock(&p->tlock); + + uartportpower(p, 0); + + lock(&uartalloc); + for(l = &uartalloc.elist; *l; l = &(*l)->elist){ + if(*l == p){ + *l = p->elist; + break; + } + } + p->enabled = 0; + unlock(&uartalloc); +} + +/* + * put some bytes into the local queue to avoid calling + * qconsume for every character + */ +static int +stageoutput(Uart *p) +{ + int n; + + n = qconsume(p->oq, p->ostage, Stagesize); + if(n <= 0) + return 0; + p->op = p->ostage; + p->oe = p->ostage + n; + return n; +} + +/* + * (re)start output + */ +static void +uartkick0(Uart *p) +{ + int i; + if((p->modem && (p->cts == 0)) || p->blocked) + return; + + /* + * 128 here is an arbitrary limit to make sure + * we don't stay in this loop too long. If the + * chips output queue is longer than 128, too + * bad -- presotto + */ + for(i = 0; i < 128; i++){ + if(!(uartrdreg(p, Lstat) & Outready)) + break; + if(p->op >= p->oe && stageoutput(p) == 0) + break; + uartwr(p, Data, *p->op++); + } +} + +static void +uartkick(void *v) +{ + Uart *p; + + p = v; + ilock(&p->tlock); + uartkick0(p); + iunlock(&p->tlock); +} + +/* + * restart input if it's off + */ +static void +uartflow(void *v) +{ + Uart *p; + + p = v; + if(p->modem) + uartrts(p, 1); + ilock(&p->rlock); + p->haveinput = 1; + iunlock(&p->rlock); +} + +/* + * default is 9600 baud, 1 stop bit, 8 bit chars, no interrupts, + * transmit and receive enabled, interrupts disabled. + */ +static void +uartsetup0(Uart *p) +{ + memset(p->sticky, 0, sizeof(p->sticky)); + /* + * set rate to 9600 baud. + * 8 bits/character. + * 1 stop bit. + * interrupts enabled. + */ + p->sticky[Format] = Bits8; + uartwrreg(p, Format, 0); + p->sticky[Mctl] |= Inton; + uartwrreg(p, Mctl, 0x0); + +// uartsetbaud(p, 9600); + uartsetbaud(p, 38400); + + p->iq = qopen(4*1024, 0, uartflow, p); + p->oq = qopen(4*1024, 0, uartkick, p); + if(p->iq == nil || p->oq == nil) + panic("uartsetup0"); + + p->ip = p->istage; + p->ie = &p->istage[Stagesize]; + p->op = p->ostage; + p->oe = p->ostage; +} + +/* + * called by uartinstall() to create a new duart + */ +void +uartsetup(ulong port, void *regs, ulong freq, char *name) +{ + Uart *p; + + if(nuart >= Nuart) + return; + + p = xalloc(sizeof(Uart)); + uart[nuart] = p; + strcpy(p->name, name); + p->dev = nuart; + nuart++; + p->port = port; + p->regs = regs; + p->freq = freq; + uartsetup0(p); +} + +/* + * called by main() to configure a duart port as a console or a mouse + */ +void +uartspecial(int port, int baud, Queue **in, Queue **out, int (*putc)(Queue*, int)) +{ + Uart *p = uart[port]; + uartenable(p); + if(baud) + uartsetbaud(p, baud); + p->putc = putc; + if(in) + *in = p->iq; + if(out) + *out = p->oq; + p->opens++; +} + +/* + * handle an interrupt to a single uart + */ +static void +uartintr(Uart *p) +{ + uchar ch; + int s, l; + + for (s = uartrdreg(p, Istat); !(s&Ipend); s = uartrdreg(p, Istat)) { + switch(s&0x3f){ + case 4: /* received data available */ + case 6: /* receiver line status (alarm or error) */ + case 12: /* character timeout indication */ + while ((l = uartrdreg(p, Lstat)) & Inready) { + if(l & Ferror) + p->frame++; + if(l & Oerror) + p->overrun++; + ch = uartrdreg(p, Data) & 0xff; + if (l & (Berror|Perror|Ferror)) { + /* ch came with break, parity or framing error - consume */ + continue; + } + if (ch == CTLS || ch == CTLQ) { + ilock(&p->tlock); + if(p->xonoff){ + if(ch == CTLS) + p->blocked = 1; + else + p->blocked = 0; /* clock gets output going again */ + } + iunlock(&p->tlock); + } + if(p->putc) + p->putc(p->iq, ch); + else { + ilock(&p->rlock); + if(p->ip < p->ie) + *p->ip++ = ch; + else + p->overrun++; + p->haveinput = 1; + iunlock(&p->rlock); + } + } + break; + + case 2: /* transmitter not full */ + uartkick(p); + break; + + case 0: /* modem status */ + ch = uartrdreg(p, Mstat); + if(ch & Ctsc){ + ilock(&p->tlock); + l = p->cts; + p->cts = ch & Cts; + if(l == 0 && p->cts) + p->ctsbackoff = 2; /* clock gets output going again */ + iunlock(&p->tlock); + } + if (ch & Dsrc) { + l = ch & Dsr; + if(p->hup_dsr && p->dsr && !l){ + ilock(&p->rlock); + p->dohup = 1; + iunlock(&p->rlock); + } + p->dsr = l; + } + if (ch & Dcdc) { + l = ch & Dcd; + if(p->hup_dcd && p->dcd && !l){ + ilock(&p->rlock); + p->dohup = 1; + iunlock(&p->rlock); + } + p->dcd = l; + } + break; + + default: + iprint("weird uart interrupt #%2.2ux\n", s); + break; + } + } + p->istat = s; +} + +/* + * we save up input characters till clock time + * + * There's also a bit of code to get a stalled print going. + * It shouldn't happen, but it does. Obviously I don't + * understand something. Since it was there, I bundled a + * restart after flow control with it to give some hysteresis + * to the hardware flow control. This makes compressing + * modems happier but will probably bother something else. + * -- presotto + */ +void +uartclock(void) +{ + int n; + Uart *p; + + for(p = uartalloc.elist; p; p = p->elist){ + + /* this amortizes cost of qproduce to many chars */ + if(p->haveinput){ + ilock(&p->rlock); + if(p->haveinput){ + n = p->ip - p->istage; + if(n > 0 && p->iq){ + if(n > Stagesize) + panic("uartclock"); + if(qproduce(p->iq, p->istage, n) < 0) + uartrts(p, 0); + else + p->ip = p->istage; + } + p->haveinput = 0; + } + iunlock(&p->rlock); + } + if(p->dohup){ + ilock(&p->rlock); + if(p->dohup){ + qhangup(p->iq, 0); + qhangup(p->oq, 0); + } + p->dohup = 0; + iunlock(&p->rlock); + } + + /* this adds hysteresis to hardware flow control */ + if(p->ctsbackoff){ + ilock(&p->tlock); + if(p->ctsbackoff){ + if(--(p->ctsbackoff) == 0) + uartkick0(p); + } + iunlock(&p->tlock); + } + } +} + +Dirtab *uartdir; +int ndir; + +static void +setlength(int i) +{ + Uart *p; + + if(i >= 0){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+3*i].length = qlen(p->iq); + } else for(i = 0; i < nuart; i++){ + p = uart[i]; + if(p && p->opens && p->iq) + uartdir[1+3*i].length = qlen(p->iq); + } +} + +/* + * all uarts must be uartsetup() by this point or inside of uartinstall() + */ +static void +uartreset(void) +{ + int i; + Dirtab *dp; + uartinstall(); /* architecture specific */ + + ndir = 1+3*nuart; + uartdir = xalloc(ndir * sizeof(Dirtab)); + dp = uartdir; + strcpy(dp->name, "."); + mkqid(&dp->qid, 0, 0, QTDIR); + dp->length = 0; + dp->perm = DMDIR|0555; + dp++; + for(i = 0; i < nuart; i++){ + /* 3 directory entries per port */ + strcpy(dp->name, uart[i]->name); + dp->qid.path = NETQID(i, Ndataqid); + dp->perm = 0666; + dp++; + sprint(dp->name, "%sctl", uart[i]->name); + dp->qid.path = NETQID(i, Nctlqid); + dp->perm = 0666; + dp++; + sprint(dp->name, "%sstatus", uart[i]->name); + dp->qid.path = NETQID(i, Nstatqid); + dp->perm = 0444; + dp++; + } +} + +static Chan* +uartattach(char *spec) +{ + return devattach('t', spec); +} + +static Walkqid* +uartwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, uartdir, ndir, devgen); +} + +static int +uartstat(Chan *c, uchar *dp, int n) +{ + if(NETTYPE(c->qid.path) == Ndataqid) + setlength(NETID(c->qid.path)); + return devstat(c, dp, n, uartdir, ndir, devgen); +} + +static Chan* +uartopen(Chan *c, int omode) +{ + Uart *p; + + c = devopen(c, omode, uartdir, ndir, devgen); + + switch(NETTYPE(c->qid.path)){ + case Nctlqid: + case Ndataqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(p->opens++ == 0){ + uartenable(p); + qreopen(p->iq); + qreopen(p->oq); + } + qunlock(p); + break; + } + + return c; +} + +static void +uartclose(Chan *c) +{ + Uart *p; + + if(c->qid.type & QTDIR) + return; + if((c->flag & COPEN) == 0) + return; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + case Nctlqid: + p = uart[NETID(c->qid.path)]; + qlock(p); + if(--(p->opens) == 0){ + uartdisable(p); + qclose(p->iq); + qclose(p->oq); + p->ip = p->istage; + p->dcd = p->dsr = p->dohup = 0; + } + qunlock(p); + break; + } +} + +static long +uartstatus(Chan*, Uart *p, void *buf, long n, long offset) +{ + uchar mstat, fstat, istat, tstat; + char str[256]; + + str[0] = 0; + tstat = p->sticky[Mctl]; + mstat = uartrdreg(p, Mstat); + istat = p->sticky[Iena]; + fstat = p->sticky[Format]; + snprint(str, sizeof str, + "b%d c%d d%d e%d l%d m%d p%c r%d s%d\n" + "%d %d %d%s%s%s%s%s\n", + + p->baud, + p->hup_dcd, + (tstat & Dtr) != 0, + p->hup_dsr, + (fstat & Bits8) + 5, + (istat & Imstat) != 0, + (fstat & Pena) ? ((fstat & Peven) ? 'e' : 'o') : 'n', + (tstat & Rts) != 0, + (fstat & Stop2) ? 2 : 1, + + p->dev, + p->frame, + p->overrun, + uartrdreg(p, Istat) & Fenabd ? " fifo" : "", + (mstat & Cts) ? " cts" : "", + (mstat & Dsr) ? " dsr" : "", + (mstat & Dcd) ? " dcd" : "", + (mstat & Ringl) ? " ring" : "" + ); + return readstr(offset, buf, n, str); +} + +static long +uartread(Chan *c, void *buf, long n, vlong off) +{ + Uart *p; + ulong offset = off; + + if(c->qid.type & QTDIR){ + setlength(-1); + return devdirread(c, buf, n, uartdir, ndir, devgen); + } + + p = uart[NETID(c->qid.path)]; + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qread(p->iq, buf, n); + case Nctlqid: + return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE); + case Nstatqid: + return uartstatus(c, p, buf, n, offset); + } + + return 0; +} + +static void +uartctl(Uart *p, char *cmd) +{ + int i, n; + + /* let output drain for a while */ + for(i = 0; i < 16 && qlen(p->oq); i++) + tsleep(&p->r, (int(*)(void*))qlen, p->oq, 125); + + if(strncmp(cmd, "break", 5) == 0){ + uartbreak(p, 0); + return; + } + + + n = atoi(cmd+1); + switch(*cmd){ + case 'B': + case 'b': + uartsetbaud(p, n); + break; + case 'C': + case 'c': + uartdcdhup(p, n); + break; + case 'D': + case 'd': + uartdtr(p, n); + break; + case 'E': + case 'e': + uartdsrhup(p, n); + break; + case 'f': + case 'F': + qflush(p->oq); + break; + case 'H': + case 'h': + qhangup(p->iq, 0); + qhangup(p->oq, 0); + break; + case 'L': + case 'l': + uartbits(p, n); + break; + case 'm': + case 'M': + uartmflow(p, n); + break; + case 'n': + case 'N': + qnoblock(p->oq, n); + break; + case 'P': + case 'p': + uartparity(p, *(cmd+1)); + break; + case 'K': + case 'k': + uartbreak(p, n); + break; + case 'R': + case 'r': + uartrts(p, n); + break; + case 'Q': + case 'q': + qsetlimit(p->iq, n); + qsetlimit(p->oq, n); + break; + case 'W': + case 'w': + /* obsolete */ + break; + case 'X': + case 'x': + ilock(&p->tlock); + p->xonoff = n; + iunlock(&p->tlock); + break; + } +} + +static long +uartwrite(Chan *c, void *buf, long n, vlong) +{ + Uart *p; + char cmd[32]; + + if(c->qid.type & QTDIR) + error(Eperm); + + p = uart[NETID(c->qid.path)]; + + /* + * The fifo's turn themselves off sometimes. + * It must be something I don't understand. -- presotto + */ + lock(&p->flock); + if((p->istat & Fenabd) == 0 && p->fifoon && p->nofifo == 0) + uartfifoon(p); + unlock(&p->flock); + + switch(NETTYPE(c->qid.path)){ + case Ndataqid: + return qwrite(p->oq, buf, n); + case Nctlqid: + if(n >= sizeof(cmd)) + n = sizeof(cmd)-1; + memmove(cmd, buf, n); + cmd[n] = 0; + uartctl(p, cmd); + return n; + } +} + +static int +uartwstat(Chan *c, uchar *dp, int n) +{ + Dir d; + Dirtab *dt; + + if(!iseve()) + error(Eperm); + if(c->qid.type & QTDIR) + error(Eperm); + if(NETTYPE(c->qid.path) == Nstatqid) + error(Eperm); + + dt = &uartdir[1+3 * NETID(c->qid.path)]; + n = convM2D(dp, n, &d, nil); + if(n == 0) + error(Eshortstat); + if(d.mode != ~0UL){ + d.mode &= 0666; + dt[0].perm = dt[1].perm = d.mode; + } + return n; +} + +Dev uartdevtab = { + 't', + "uart", + + uartreset, + devinit, + devshutdown, + uartattach, + uartwalk, + uartstat, + uartopen, + devcreate, + uartclose, + uartread, + devbread, + uartwrite, + devbwrite, + devremove, + uartwstat, +}; |
