summaryrefslogtreecommitdiff
path: root/os/mpc/devuart.c
diff options
context:
space:
mode:
Diffstat (limited to 'os/mpc/devuart.c')
-rw-r--r--os/mpc/devuart.c1450
1 files changed, 1450 insertions, 0 deletions
diff --git a/os/mpc/devuart.c b/os/mpc/devuart.c
new file mode 100644
index 00000000..13adf351
--- /dev/null
+++ b/os/mpc/devuart.c
@@ -0,0 +1,1450 @@
+#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,
+};