ref: 6b84c3a6548f1c577948763b0a09a7b6e2460e04
dir: /os/mpc/devuart.c/
#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" enum { Nbuf= 2, /* double buffered */ Rbufsize= 512, Bufsize= (Rbufsize+CACHELINESZ-1)&~(CACHELINESZ-1), Nuart= 2+4, /* max in any 8xx architecture (2xSMC, 4xSCC) */ CTLS= 's'&037, CTLQ= 'q'&037, }; enum { /* status bits in SCC receive buffer descriptors */ RxBRK= 1<<7, /* break ended frame (async hdlc) */ RxDE= 1<<7, /* DPLL error (hdlc) */ RxBOF= 1<<6, /* BOF ended frame (async hdlc) */ RxLG= 1<<5, /* frame too large (hdlc) */ RxNO= 1<<4, /* bad bit alignment (hdlc) */ RxBR= 1<<5, /* break received during frame (uart) */ RxFR= 1<<4, /* framing error (uart) */ RxPR= 1<<3, /* parity error (uart) */ RxAB= 1<<3, /* frame aborted (hdlc, async hdlc) */ RxCR= 1<<2, /* bad CRC (hdlc, async hdlc) */ RxOV= 1<<1, /* receiver overrun (all) */ RxCD= 1<<0, /* CD lost (all) */ /* hdlc-specific Rx/Tx BDs */ TxTC= 1<<10, }; /* * SMC in UART mode */ typedef struct Uartsmc Uartsmc; struct Uartsmc { IOCparam; ushort maxidl; ushort idlc; ushort brkln; ushort brkec; ushort brkcr; ushort rmask; }; /* * SCC2 UART parameters */ enum { /* special mode bits */ SccAHDLC = 1<<0, SccHDLC = 1<<1, SccIR = 1<<2, SccPPP = 1<<3, }; typedef struct Uartscc Uartscc; struct Uartscc { SCCparam; uchar rsvd[8]; ushort max_idl; ushort idlc; ushort brkcr; ushort parec; ushort frmec; ushort nosec; ushort brkec; ushort brkln; ushort uaddr1; ushort uaddr2; ushort rtemp; ushort toseq; ushort character[8]; ushort rccm; ushort rccrp; ushort rlbc; }; typedef struct UartAHDLC UartAHDLC; struct UartAHDLC { SCCparam; ulong rsvd1; ulong c_mask; ulong c_pres; ushort bof; ushort eof; ushort esc; ushort rsvd2[2]; ushort zero; ushort rsvd3; ushort rfthr; ushort resvd4[2]; ulong txctl_tbl; ulong rxctl_tbl; ushort nof; ushort rsvd5; }; typedef struct UartHDLC UartHDLC; struct UartHDLC { SCCparam; ulong rsvd1; ulong c_mask; ulong c_pres; ushort disfc; ushort crcec; ushort abtsc; ushort nmarc; ushort retrc; ushort mflr; ushort max_cnt; ushort rfthr; ushort rfcnt; ushort hmask; ushort haddr[4]; ushort tmp; ushort tmp_mb; }; enum { /* SCC events of possible interest here eventually */ AB= 1<<9, /* autobaud detected */ GRA= 1<<7, /* graceful stop completed */ CCR= 1<<3, /* control character detected */ /* SCC, SMC common interrupt events */ BSY= 1<<2, /* receive buffer was busy (overrun) */ TXB= 1<<1, /* block sent */ RXB= 1<<0, /* block received */ /* SCC events */ TXE = 1<<4, /* transmission error */ RXF = 1<<3, /* final block received */ /* gsmr_l */ ENR = 1<<5, /* enable receiver */ ENT = 1<<4, /* enable transmitter */ /* port A */ RXD1= SIBIT(15), TXD1= SIBIT(14), /* port B */ RTS1B= IBIT(19), /* port C */ RTS1C= SIBIT(15), CTS1= SIBIT(11), CD1= SIBIT(10), }; typedef struct Uart Uart; struct Uart { QLock; Uart *elist; /* next enabled interface */ char name[KNAMELEN]; int x; /* index: x in SMCx or SCCx */ int cpmid; /* eg, SCC1ID, SMC1ID */ CPMdev* cpm; int opens; uchar bpc; /* bits/char */ uchar parity; uchar stopb; uchar setup; uchar enabled; int dev; ulong frame; /* framing errors */ ulong perror; ulong overrun; /* rcvr overruns */ ulong crcerr; ulong interrupts; int baud; /* baud rate */ /* flow control */ int xonoff; /* software flow control on */ int blocked; int modem; /* hardware flow control on */ int cts; /* ... cts state */ int rts; /* ... rts state */ Rendez r; /* buffers */ int (*putc)(Queue*, int); Queue *iq; Queue *oq; /* staging areas to avoid some of the per character costs */ /* TO DO: should probably use usual Ring */ Block* istage[Nbuf]; /* double buffered */ int rdrx; /* last buffer read */ Lock plock; /* for output variables */ Block* outb; /* currently transmitting Block */ BD* rxb; BD* txb; SMC* smc; SCC* scc; IOCparam* param; ushort* brkcr; /* brkcr location in appropriate block */ int brgc; int mode; Block* partial; int loopback; }; static Uart *uart[Nuart]; static int nuart; struct Uartalloc { Lock; Uart *elist; /* list of enabled interfaces */ } uartalloc; static void uartintr(Uart*, int); static void smcuintr(Ureg*, void*); static void sccuintr(Ureg*, void*); static void uartsetbuf(Uart *up) { IOCparam *p; BD *bd; int i; Block *bp; p = up->param; p->rfcr = 0x18; p->tfcr = 0x18; p->mrblr = Rbufsize; if((bd = up->rxb) == nil){ bd = bdalloc(Nbuf); up->rxb = bd; } p->rbase = (ushort)bd; for(i=0; i<Nbuf; i++){ bd->status = BDEmpty|BDInt; bd->length = 0; if((bp = up->istage[i]) == nil) up->istage[i] = bp = allocb(Bufsize); bd->addr = PADDR(bp->wp); dcflush(bp->wp, Bufsize); bd++; } (bd-1)->status |= BDWrap; up->rdrx = 0; if((bd = up->txb) == nil){ bd = bdalloc(1); up->txb = bd; } p->tbase = (ushort)bd; bd->status = BDWrap|BDInt; bd->length = 0; bd->addr = 0; } static void smcsetup(Uart *up) { IMM *io; Uartsmc *p; SMC *smc; ulong txrx; archdisableuart(up->cpmid); up->brgc = brgalloc(); if(up->brgc < 0) error(Eio); m->iomem->brgc[up->brgc] = baudgen(up->baud, 16) | BaudEnable; smcnmsi(up->x, up->brgc); archenableuart(up->cpmid, 0); if(up->x == 1) txrx = IBIT(24)|IBIT(25); /* SMC1 RX/TX */ else txrx = IBIT(20)|IBIT(21); /* SMC2 */ io = ioplock(); io->pbpar |= txrx; io->pbdir &= ~txrx; iopunlock(); up->param = up->cpm->param; uartsetbuf(up); cpmop(up->cpm, InitRxTx, 0); /* SMC protocol parameters */ p = (Uartsmc*)up->param; up->brkcr = &p->brkcr; p->maxidl = 1; /* non-zero so buffer closes when idle before mrblr reached */ p->brkln = 0; p->brkec = 0; p->brkcr = 1; smc = up->cpm->regs; smc->smce = 0xff; /* clear events */ smc->smcm = BSY|RXB|TXB; /* enable all possible interrupts */ up->smc = smc; smc->smcmr = ((1+8+1-1)<<11)|(2<<4); /* 8-bit, 1 stop, no parity; UART mode */ intrenable(VectorCPIC+up->cpm->irq, smcuintr, up, BUSUNKNOWN, up->name); /* enable when device opened */ } static void smcuintr(Ureg*, void *a) { Uart *up; int events; up = a; events = up->smc->smce; eieio(); up->smc->smce = events; uartintr(up, events&(BSY|RXB|TXB)); } /* * set the IO ports to enable the control signals for SCCx */ static void sccuartpins(int x, int mode) { IMM *io; int i, w; x--; io = ioplock(); i = 2*x; w = (TXD1|RXD1)<<i; /* TXDn and RXDn in port A */ io->papar |= w; /* enable TXDn and RXDn pins */ io->padir &= ~w; if((mode & SccIR) == 0) io->paodr |= TXD1<<i; else io->paodr &= ~w; /* not open drain */ w = (CD1|CTS1)<<i; /* CDn and CTSn in port C */ io->pcpar &= ~w; io->pcdir &= ~w; if(conf.nocts2 || mode) io->pcso &= ~w; /* force CTS and CD on */ else io->pcso |= w; w = RTS1B<<x; io->pbpar &= ~w; io->pbdir &= ~w; w = RTS1C<<x; /* RTSn~ */ if((mode & SccIR) == 0) io->pcpar |= w; else io->pcpar &= ~w; /* don't use for IR */ iopunlock(); } static void sccsetup(Uart *up) { SCC *scc; int i; scc = up->cpm->regs; up->scc = scc; up->param = up->cpm->param; sccxstop(up->cpm); archdisableuart(up->cpmid); if(up->brgc < 0){ up->brgc = brgalloc(); if(up->brgc < 0) error(Eio); } m->iomem->brgc[up->brgc] = baudgen(up->baud, 16) | BaudEnable; sccnmsi(up->x, up->brgc, up->brgc); sccuartpins(up->x, up->mode); uartsetbuf(up); cpmop(up->cpm, InitRxTx, 0); /* SCC protocol parameters */ if((up->mode & (SccAHDLC|SccHDLC)) == 0){ Uartscc *sp; sp = (Uartscc*)up->param; sp->max_idl = 1; sp->brkcr = 1; sp->parec = 0; sp->frmec = 0; sp->nosec = 0; sp->brkec = 0; sp->brkln = 0; sp->brkec = 0; sp->uaddr1 = 0; sp->uaddr2 = 0; sp->toseq = 0; for(i=0; i<8; i++) sp->character[i] = 0x8000; sp->rccm = 0xC0FF; up->brkcr = &sp->brkcr; scc->irmode = 0; scc->dsr = ~0; scc->gsmrh = 1<<5; /* 8-bit oriented receive fifo */ scc->gsmrl = 0x28004; /* UART mode */ }else{ UartAHDLC *hp; hp = (UartAHDLC*)up->param; hp->c_mask = 0x0000F0B8; hp->c_pres = 0x0000FFFF; if(up->mode & SccIR){ hp->bof = 0xC0; hp->eof = 0xC1; //scc->dsr = 0xC0C0; scc->dsr = 0x7E7E; }else{ hp->bof = 0x7E; hp->eof = 0x7E; scc->dsr = 0x7E7E; } hp->esc = 0x7D; hp->zero = 0; if(up->mode & SccHDLC) hp->rfthr = 1; else hp->rfthr = 0; /* receive threshold of 1 doesn't work properly for Async HDLC */ hp->txctl_tbl = 0; hp->rxctl_tbl = 0; if(up->mode & SccIR){ /* low-speed infrared */ hp->nof = 12-1; /* 12 flags */ scc->irsip = 0; scc->irmode = (2<<8) | 1; archsetirxcvr(0); if(up->loopback) scc->irmode = (3<<4)|1; /* loopback */ }else{ scc->irmode = 0; hp->txctl_tbl = ~0; hp->rxctl_tbl = ~0; hp->nof = 1-1; /* one opening flag */ } up->brkcr = nil; scc->gsmrh = 1<<5; /* 8-bit oriented receive fifo */ if(up->mode & SccHDLC) scc->gsmrl = 0x28000; /* HDLC */ else scc->gsmrl = 0x28006; /* async HDLC/IrDA */ } archenableuart(up->cpmid, (up->mode&SccIR)!=0); scc->scce = ~0; /* clear events */ scc->sccm = TXE|BSY|RXF|TXB|RXB; /* enable all interesting interrupts */ intrenable(VectorCPIC+up->cpm->irq, sccuintr, up, BUSUNKNOWN, up->name); scc->psmr = 3<<12; /* 8-bit, 1 stop, no parity; UART mode */ if(up->loopback && (up->mode & SccIR) == 0) scc->gsmrl |= 1<<6; /* internal loop back */ scc->gsmrl |= ENT|ENR; /* enable rx/tx */ if(0){ print("gsmrl=%8.8lux gsmrh=%8.8lux dsr=%4.4ux irmode=%4.4ux\n", scc->gsmrl, scc->gsmrh, scc->dsr, scc->irmode); for(i=0; i<sizeof(Uartscc); i+=4) print("%2.2ux %8.8lux\n", i, *(ulong*)((uchar*)up->param+i)); } } static void sccuintr(Ureg*, void *a) { Uart *up; int events; up = a; if(up->scc == nil) return; events = up->scc->scce; eieio(); up->scc->scce = events; if(up->enabled){ if(0) print("#%ux|", events); uartintr(up, events); } } static void uartsetbaud(Uart *p, int rate) { if(rate <= 0 || p->brgc < 0) return; p->baud = rate; m->iomem->brgc[p->brgc] = baudgen(rate, 16) | BaudEnable; } static void uartsetmode(Uart *p) { int r, clen; ilock(&p->plock); clen = p->bpc; if(p->parity == 'e' || p->parity == 'o') clen++; clen++; /* stop bit */ if(p->stopb == 2) clen++; if(p->smc){ r = p->smc->smcmr & 0x3F; /* keep mode, enable bits */ r |= (clen<<11); if(p->parity == 'e') r |= 3<<8; else if(p->parity == 'o') r |= 2<<8; if(p->stopb == 2) r |= 1<<10; eieio(); p->smc->smcmr = r; }else if(p->scc && p->mode == 0){ r = p->scc->psmr & 0x8FE0; /* keep mode bits */ r |= ((p->bpc-5)&3)<<12; if(p->parity == 'e') r |= (6<<2)|2; else if(p->parity == 'o') r |= (4<<2)|0; if(p->stopb == 2) r |= 1<<14; eieio(); p->scc->psmr = r; } iunlock(&p->plock); } static void uartparity(Uart *p, char type) { ilock(&p->plock); p->parity = type; iunlock(&p->plock); uartsetmode(p); } /* * set bits/character */ static void uartbits(Uart *p, int bits) { if(bits < 5 || bits > 14 || bits > 8 && p->scc) error(Ebadarg); ilock(&p->plock); p->bpc = bits; iunlock(&p->plock); uartsetmode(p); } /* * toggle DTR */ static void uartdtr(Uart *p, int n) { if(p->scc == nil) return; /* not possible */ USED(n); /* not possible on FADS */ } /* * toggle RTS */ static void uartrts(Uart *p, int n) { p->rts = n; if(p->scc == nil) return; /* not possible */ USED(n); /* not possible on FADS */ } /* * send break */ static void uartbreak(Uart *p, int ms) { if(p->brkcr == nil) return; if(ms <= 0) ms = 200; if(waserror()){ ilock(&p->plock); *p->brkcr = 1; cpmop(p->cpm, RestartTx, 0); iunlock(&p->plock); nexterror(); } ilock(&p->plock); *p->brkcr = ((p->baud/(p->bpc+2))*ms+500)/1000; cpmop(p->cpm, StopTx, 0); iunlock(&p->plock); tsleep(&up->sleep, return0, 0, ms); poperror(); ilock(&p->plock); *p->brkcr = 1; cpmop(p->cpm, RestartTx, 0); iunlock(&p->plock); } /* * modem flow control on/off (rts/cts) */ static void uartmflow(Uart *p, int n) { if(p->scc == nil) return; /* not possible */ if(n){ p->modem = 1; /* enable status interrupts ... */ p->scc->psmr |= 1<<15; /* enable async flow control */ p->cts = 1; /* could change maxidl */ }else{ p->modem = 0; /* stop status interrupts ... */ p->scc->psmr &= ~(1<<15); p->cts = 1; } } /* * turn on a port's interrupts. set DTR and RTS */ void uartenable(Uart *p) { Uart **l; if(p->enabled) return; if(p->setup == 0){ if(p->cpmid == CPsmc1 || p->cpmid == CPsmc2) smcsetup(p); else sccsetup(p); p->setup = 1; } /* * turn on interrupts */ if(p->smc){ cpmop(p->cpm, RestartTx, 0); p->smc->smcmr |= 3; p->smc->smcm = BSY|TXB|RXB; eieio(); }else if(p->scc){ cpmop(p->cpm, RestartTx, 0); p->scc->gsmrl |= ENT|ENR; p->scc->sccm = BSY|TXB|RXB; eieio(); } /* * turn on DTR and RTS */ uartdtr(p, 1); uartrts(p, 1); /* * assume we can send */ p->cts = 1; p->blocked = 0; /* * 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); p->cts = 1; p->blocked = 0; p->xonoff = 0; p->enabled = 1; } /* * turn off a port's interrupts. reset DTR and RTS */ void uartdisable(Uart *p) { Uart **l; /* * turn off interrpts */ if(p->smc) smcxstop(p->cpm); else if(p->scc) sccxstop(p->cpm); /* * revert to default settings */ p->bpc = 8; p->parity = 0; p->stopb = 0; /* * turn off DTR, RTS, hardware flow control & fifo's */ uartdtr(p, 0); uartrts(p, 0); uartmflow(p, 0); p->xonoff = p->blocked = 0; lock(&uartalloc); for(l = &uartalloc.elist; *l; l = &(*l)->elist){ if(*l == p){ *l = p->elist; break; } } p->enabled = 0; unlock(&uartalloc); } /* * set the next output buffer going */ static void txstart(Uart *p) { Block *b; int n, flags; if(!p->cts || p->blocked || p->txb->status & BDReady) return; if((b = p->outb) == nil){ if((b = qget(p->oq)) == nil) return; if(p->mode & SccPPP && p->mode & SccAHDLC && BLEN(b) >= 8){ /* strip framing data */ UartAHDLC *hp; hp = (UartAHDLC*)p->param; if(hp != nil && (p->mode & SccIR) == 0){ hp->txctl_tbl = nhgetl(b->rp); hp->rxctl_tbl = nhgetl(b->rp+4); } b->rp += 8; if(0) print("tx #%lux rx #%lux\n", hp->txctl_tbl, hp->rxctl_tbl); } } n = BLEN(b); if(n <= 0) print("txstart: 0\n"); if(p->bpc > 8){ /* half-word alignment and length if chars are long */ if(PADDR(b->rp)&1){ /* must be even if chars are long */ memmove(b->base, b->rp, n); b->rp = b->base; b->wp = b->rp+n; } if(n & 1) n++; } dcflush(b->rp, n); p->outb = b; if(n > 0xFFFF) n = 0xFFFE; if(p->mode & SccHDLC) flags = BDLast | TxTC; else if(p->mode) flags = BDLast; else flags = 0; p->txb->addr = PADDR(b->rp); p->txb->length = n; eieio(); p->txb->status = (p->txb->status & BDWrap) | flags | BDReady|BDInt; eieio(); } /* * (re)start output */ static void uartkick(void *v) { Uart *p; p = v; ilock(&p->plock); if(p->outb == nil) txstart(p); iunlock(&p->plock); } /* * restart input if it's off */ static void uartflow(void *v) { Uart *p; p = v; if(p->modem) uartrts(p, 1); } static void uartsetup(int x, int lid, char *name) { Uart *p; if(nuart >= Nuart) return; p = xalloc(sizeof(Uart)); uart[nuart] = p; strcpy(p->name, name); p->dev = nuart; nuart++; p->x = x; p->cpmid = lid; p->cpm = cpmdev(lid); p->brgc = -1; p->mode = 0; /* * set rate to 9600 baud. * 8 bits/character. * 1 stop bit. * interrupts enabled. */ p->bpc = 8; p->parity = 0; p->baud = 9600; p->iq = qopen(4*1024, Qcoalesce, uartflow, p); p->oq = qopen(4*1024, 0, uartkick, 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; if(port < 0 || port >= nuart || (p = uart[port]) == nil) return; /* specified port not implemented */ uartenable(p); if(baud) uartsetbaud(p, baud); p->putc = putc; if(in) *in = p->iq; if(out) *out = p->oq; p->opens++; } static int uartinput(Uart *p, BD *bd) { int ch, dokick, i, l; uchar *bp; dokick = 0; if(bd->status & RxFR) p->frame++; if(bd->status & RxOV) p->overrun++; l = bd->length; if(bd->status & RxPR){ p->perror++; l--; /* it's the last character */ } bp = KADDR(bd->addr); if(p->xonoff || p->putc && p->opens==1){ for(i=0; i<l; i++){ ch = bp[i]; if(p->xonoff){ if(ch == CTLS){ p->blocked = 1; cpmop(p->cpm, StopTx, 0); }else if (ch == CTLQ){ p->blocked = 0; dokick = 1; } /* BUG? should discard on/off char? */ } if(p->putc) (*p->putc)(p->iq, ch); } } if(l > 0 && (p->putc == nil || p->opens>1)) qproduce(p->iq, bp, l); return dokick; } static void framedinput(Uart *p, BD *bd) { Block *pkt; int l; pkt = p->partial; p->partial = nil; if(bd->status & RxOV){ p->overrun++; goto Discard; } if(bd->status & (RxAB|RxCR|RxCD|RxLG|RxNO|RxDE|RxBOF|RxBRK)){ if(bd->status & RxCR) p->crcerr++; else p->frame++; goto Discard; } if(pkt == nil){ pkt = iallocb(1500); /* TO DO: allocate less if possible */ if(pkt == nil) return; } l = bd->length; if(bd->status & BDLast) l -= BLEN(pkt); /* last one gives size of entire frame */ if(l > 0){ if(pkt->wp+l > pkt->lim) goto Discard; memmove(pkt->wp, KADDR(bd->addr), l); pkt->wp += l; } if(0) print("#%ux|", bd->status); if(bd->status & BDLast){ if(p->mode & (SccHDLC|SccAHDLC)){ if(BLEN(pkt) <= 2){ p->frame++; goto Discard; } pkt->wp -= 2; /* strip CRC */ } qpass(p->iq, pkt); }else p->partial = pkt; return; Discard: if(pkt != nil) freeb(pkt); } /* * handle an interrupt to a single uart */ static void uartintr(Uart *p, int events) { int dokick; BD *bd; Block *b; if(events & BSY) p->overrun++; p->interrupts++; dokick = 0; while(p->rxb != nil && ((bd = &p->rxb[p->rdrx])->status & BDEmpty) == 0){ dcinval(KADDR(bd->addr), bd->length); if(p->mode) framedinput(p, bd); else if(uartinput(p, bd)) dokick = 1; bd->status = (bd->status & BDWrap) | BDEmpty|BDInt; eieio(); if(++p->rdrx >= Nbuf) p->rdrx = 0; } if((bd = p->txb) != nil){ if((bd->status & BDReady) == 0){ ilock(&p->plock); if((b = p->outb) != nil){ b->rp += bd->length; if(b->rp >= b->wp){ p->outb = nil; freeb(b); } } txstart(p); iunlock(&p->plock); } } eieio(); /* TO DO: modem status isn't available on 82xFADS */ if(dokick && p->cts && !p->blocked){ if(p->outb == nil){ ilock(&p->plock); txstart(p); iunlock(&p->plock); } cpmop(p->cpm, RestartTx, 0); } else if (events & TXE) cpmop(p->cpm, RestartTx, 0); } /* * used to ensure uart console output when debugging */ void uartwait(void) { Uart *p = uart[0]; int s; while(p && (p->outb||qlen(p->oq))){ if(islo()) continue; s = splhi(); if((p->txb->status & BDReady) == 0){ p->blocked = 0; p->cts = 1; if(p->scc == nil) smcuintr(nil, p); else sccuintr(nil, p); } splx(s); } } static Dirtab *uartdir; static int ndir; static void setlength(int i) { Uart *p; if(i >= 0){ p = uart[i]; if(p && p->opens && p->iq) uartdir[1+4*i].length = qlen(p->iq); } else for(i = 0; i < nuart; i++){ p = uart[i]; if(p && p->opens && p->iq) uartdir[1+4*i].length = qlen(p->iq); } } void uartinstall(void) { static int already; int i, n; char name[2*KNAMELEN]; if(already) return; already = 1; n = 0; for(i=0; i<2; i++) if(conf.smcuarts & (1<<i)){ snprint(name, sizeof(name), "eia%d", n++); uartsetup(i+1, CPsmc1+i, name); } n = 2; for(i=0; i<conf.nscc; i++) if(conf.sccuarts & (1<<i)){ snprint(name, sizeof(name), "eia%d", n++); uartsetup(i+1, CPscc1+i, name); } } /* * 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+4*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++){ /* 4 directory entries per port */ strcpy(dp->name, uart[i]->name); dp->qid.path = NETQID(i, Ndataqid); dp->perm = 0660; dp++; sprint(dp->name, "%sctl", uart[i]->name); dp->qid.path = NETQID(i, Nctlqid); dp->perm = 0660; dp++; sprint(dp->name, "%sstatus", uart[i]->name); dp->qid.path = NETQID(i, Nstatqid); dp->perm = 0444; dp++; sprint(dp->name, "%smode", uart[i]->name); dp->qid.path = NETQID(i, Ntypeqid); dp->perm = 0660; 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); } qunlock(p); break; } } static long uartstatus(Chan*, Uart *p, void *buf, long n, long offset) { IMM *io; char str[256]; // TO DO: change to standard format for first line: //"b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n" sprint(str, "opens %d ferr %lud oerr %lud crcerr %lud baud %ud perr %lud intr %lud", p->opens, p->frame, p->overrun, p->crcerr, p->baud, p->perror, p->interrupts); /* TO DO: cts, dsr, ring, dcd, dtr, rts aren't all available on 82xFADS */ io = m->iomem; if(p->scc){ if((io->pcdat & SIBIT(9)) == 0) strcat(str, " cts"); if((io->pcdat & SIBIT(8)) == 0) strcat(str, " dcd"); if((io->pbdat & IBIT(22)) == 0) strcat(str, " dtr"); }else if(p->smc){ if((io->pbdat & IBIT(23)) == 0) strcat(str, " dtr"); } strcat(str, "\n"); return readstr(offset, buf, n, str); } static long uartread(Chan *c, void *buf, long n, vlong offset) { Uart *p; 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); case Ntypeqid: return readnum(offset, buf, n, p->mode, NUMSIZE); } return 0; } static Block* uartbread(Chan *c, long n, ulong offset) { if(c->qid.type & QTDIR || NETTYPE(c->qid.path) != Ndataqid) return devbread(c, n, offset); return qbread(uart[NETID(c->qid.path)]->iq, n); } 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 'D': case 'd': uartdtr(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': p->xonoff = n; break; case 'Z': case 'z': p->loopback = n; break; } } static long uartwrite(Chan *c, void *buf, long n, vlong offset) { Uart *p; char cmd[32]; int m, inuse; USED(offset); if(c->qid.type & QTDIR) error(Eperm); p = uart[NETID(c->qid.path)]; 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; case Ntypeqid: if(p->smc || p->putc) error(Ebadarg); if(n >= sizeof(cmd)) n = sizeof(cmd)-1; memmove(cmd, buf, n); cmd[n] = 0; m = strtoul(cmd, nil, 0); inuse = 0; qlock(p); if(p->opens == 0){ p->mode = m & 0x7F; p->loopback = (m&0x80)!=0; p->setup = 0; }else inuse = 1; qunlock(p); if(inuse) error(Einuse); return n; } } static long uartbwrite(Chan *c, Block *bp, ulong offset) { if(c->qid.type & QTDIR || NETTYPE(c->qid.path) != Ndataqid) return devbwrite(c, bp, offset); return qbwrite(uart[NETID(c->qid.path)]->oq, bp); } 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+4 * NETID(c->qid.path)]; n = convM2D(dp, n, &d, nil); 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, uartbread, uartwrite, uartbwrite, devremove, uartwstat, };