| plan 9 kernel history: overview | file list | diff list |
1991/0607/power/devduart.c (diff list | history)
| power/devduart.c on 1991/0607 | ||
| 1991/0607 | #include "u.h" #include "lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" int duartacr; int duartimr; void duartputs(IOQ*, char*, int); void iprint(char*, ...); #define PAD 15 /* registers are well-spaced */ /* * Register set for half the duart. There are really two sets. */ struct Duart{ uchar mr1_2, /* Mode Register Channels 1 & 2 */ pad0[PAD]; uchar sr_csr, /* Status Register/Clock Select Register */ pad1[PAD]; uchar cmnd, /* Command Register */ pad2[PAD]; uchar data, /* RX Holding / TX Holding Register */ pad3[PAD]; uchar ipc_acr, /* Input Port Change/Aux. Control Register */ pad4[PAD]; uchar is_imr, /* Interrupt Status/Interrupt Mask Register */ pad5[PAD]; uchar ctur, /* Counter/Timer Upper Register */ pad6[PAD]; uchar ctlr, /* Counter/Timer Lower Register */ pad7[PAD]; }; #define ppcr is_imr /* in the second register set */ #define DBD75 0 #define DBD110 1 #define DBD38400 2 #define DBD150 3 #define DBD300 4 #define DBD600 5 #define DBD1200 6 #define DBD2000 7 #define DBD2400 8 #define DBD4800 9 #define DBD1800 10 #define DBD9600 11 #define DBD19200 12 enum{ CHAR_ERR =0x00, /* MR1x - Mode Register 1 */ EVEN_PAR =0x00, ODD_PAR =0x04, NO_PAR =0x10, CBITS8 =0x03, CBITS7 =0x02, CBITS6 =0x01, CBITS5 =0x00, NORM_OP =0x00, /* MR2x - Mode Register 2 */ TWOSTOPB =0x0F, ONESTOPB =0x07, ENB_RX =0x01, /* CRx - Command Register */ DIS_RX =0x02, ENB_TX =0x04, DIS_TX =0x08, RESET_MR =0x10, RESET_RCV =0x20, RESET_TRANS =0x30, RESET_ERR =0x40, RESET_BCH =0x50, STRT_BRK =0x60, STOP_BRK =0x70, RCV_RDY =0x01, /* SRx - Channel Status Register */ FIFOFULL =0x02, XMT_RDY =0x04, XMT_EMT =0x08, OVR_ERR =0x10, PAR_ERR =0x20, FRM_ERR =0x40, RCVD_BRK =0x80, IM_IPC =0x80, /* IMRx/ISRx - Interrupt Mask/Interrupt Status */ IM_DBB =0x40, IM_RRDYB =0x20, IM_XRDYB =0x10, IM_CRDY =0x08, IM_DBA =0x04, IM_RRDYA =0x02, IM_XRDYA =0x01, BD38400 =0xCC|0x0000, BD19200 =0xCC|0x0100, BD9600 =0xBB|0x0000, BD4800 =0x99|0x0000, BD2400 =0x88|0x0000, BD1200 =0x66|0x0000, BD300 =0x44|0x0000, Maxport =8, }; /* * requests to perform on a duart */ enum { Dnone= 0, Dbaud, Dbreak, Ddtr, Dprint, Dena, Dstate, }; /* * software info for a serial duart interface */ typedef struct Duartport Duartport; struct Duartport { QLock; int printing; /* true if printing */ Duart *duart; /* device */ int inited; int c; /* character to restart output */ int op; /* operation requested */ int val; /* value of operation */ Rendez opr; /* waiot here for op to complete */ /* console interface */ int nostream; /* can't use the stream interface */ IOQ *iq; /* input character queue */ IOQ *oq; /* output character queue */ /* stream interface */ Queue *wq; /* write queue */ Rendez r; /* kproc waiting for input */ Alarm *a; /* alarm for waking the kernel process */ int delay; /* between character input and waking kproc */ int kstarted; /* kproc started */ uchar delim[256/8]; /* characters that act as delimiters */ }; Duartport duartport[Maxport]; /* max possible */ /* * configure a duart port, default is 9600 baud, 8 bits/char, 1 stop bit, * no parity */ void duartsetup(Duartport *dp) { Duart *duart; duart = dp->duart; duart->cmnd = RESET_RCV|DIS_TX|DIS_RX; duart->cmnd = RESET_TRANS; duart->cmnd = RESET_ERR; duart->cmnd = STOP_BRK; duart->ipc_acr = 0x80; /* baud-rate set 2 */ duart->cmnd = RESET_MR; duart->mr1_2 = NO_PAR|CBITS8; duart->mr1_2 = ONESTOPB; duart->sr_csr = (DBD9600<<4)|DBD9600; duart->is_imr = IM_RRDYA|IM_XRDYA; duart->cmnd = ENB_TX|ENB_RX; } /* * init the duart on the current processor */ void duartinit(void) { Duartport *dp; dp = &duartport[2*m->machno]; if(dp->inited) return; dp->inited = 1; dp->duart = DUARTREG; duartsetup(dp); dp++; dp->duart = DUARTREG+1; duartsetup(dp); } /* * enable a duart port */ void duartenable(Duartport *dp) { dp->duart->cmnd = ENB_TX|ENB_RX; } void duartenable0(void) { DUARTREG->cmnd = ENB_TX|ENB_RX; } void duartbaud(Duartport *dp, int b) { int x = 0; switch(b){ case 38400: x = BD38400; break; case 19200: x = BD19200; break; case 9600: x = BD9600; break; case 4800: x = BD4800; break; case 2400: x = BD2400; break; case 1200: x = BD1200; break; case 300: x = BD300; break; default: errors("bad baud rate"); } if(x & 0x0100) dp->duart->ipc_acr = duartacr |= 0x80; else dp->duart->ipc_acr = duartacr &= ~0x80; dp->duart->sr_csr = x; } void duartdtr(Duartport *dp, int val) { if (val) dp->duart->ctlr=0x01; else dp->duart->ctur=0x01; } void duartbreak(Duartport *dp, int val) { Duart *duart; duart = dp->duart; if (val){ duart->is_imr = duartimr &= ~IM_XRDYB; duart->cmnd = STRT_BRK|ENB_TX; } else { duart->cmnd = STOP_BRK|ENB_TX; duart->is_imr = duartimr |= IM_XRDYB; } } /* * do anything requested for this CPU's duarts */ void duartslave0(Duartport *dp) { switch(dp->op){ case Ddtr: duartbaud(dp, dp->val); break; case Dbaud: duartdtr(dp, dp->val); break; case Dbreak: duartbreak(dp, dp->val); break; case Dprint: dp->duart->cmnd = ENB_TX; dp->duart->data = dp->val; break; case Dena: duartenable(dp); break; case Dstate: dp->val = dp->duart->is_imr; break; } dp->op = Dnone; wakeup(&dp->opr); } void duartslave(void) { Duartport *dp; dp = &duartport[2*m->machno]; if(dp->op != Dnone) duartslave0(dp); dp++; if(dp->op != Dnone) duartslave0(dp); } duartrintr(Duartport *dp) { Duart *duart; IOQ *cq; int status; char ch; duart = dp->duart; status = duart->sr_csr; ch = duart->data; if(status & (FRM_ERR|OVR_ERR|PAR_ERR)) duart->cmnd = RESET_ERR; cq = dp->iq; if(cq->putc) (*cq->putc)(cq, ch); else { putc(cq, ch); if(dp->delim[ch/8] & (1<<(ch&7)) ) wakeup(&cq->r); } } duartxintr(Duartport *dp) { Duart *duart; IOQ *cq; char ch; cq = dp->oq; lock(cq); ch = getc(cq); duart = dp->duart; if(ch < 0){ dp->printing = 0; wakeup(&cq->r); duart->cmnd = DIS_TX; } else duart->data = ch; unlock(cq); } void duartintr(void) { int cause, status, c; Duart *duart; Duartport *dp; dp = &duartport[2*m->machno]; duart = dp->duart; cause = duart->is_imr; /* * I can guess your interrupt. */ /* * Is it 1? */ if(cause & IM_RRDYA) duartrintr(dp); /* * Is it 2? */ if(cause & IM_XRDYA) duartxintr(dp); /* * Is it 3? */ if(cause & IM_RRDYB) duartrintr(dp+1); /* * Is it 4? */ if(cause & IM_XRDYB) duartxintr(dp+1); } /* * processor 0 only */ int duartrawputc(int c) { Duart *duart; int i; duart = DUARTREG; if(c == '\n') duartrawputc('\r'); duart->cmnd = ENB_TX; i = 0; while((duart->sr_csr&XMT_RDY) == 0) if(++i >= 1000000){ duartsetup(&duartport[0]); for(i=0; i<100000; i++) ; break; } duart->data = c; if(c == '\n') for(i=0; i<100000; i++) ; return c; } void duartrawputs(char *s) { int i; while(*s){ duartrawputc(*s++); } for(i=0; i < 1000000; i++) ; } void iprint(char *fmt, ...) { char buf[1024]; long *arg; arg = (long*)(&fmt+1); sprint(buf, fmt, *arg, *(arg+1), *(arg+2), *(arg+3)); duartrawputs(buf); } /* * Queue n characters for output; if queue is full, we lose characters. * Get the output going if it isn't already. */ void duartputs(IOQ *cq, char *s, int n) { int ch, x; Duartport *dp; Duart *duart; x = splhi(); lock(cq); puts(cq, s, n); dp = cq->ptr; if(dp->printing == 0){ ch = getc(cq); if(ch >= 0){ dp->printing = 1; dp->val = ch; dp->op = Dprint; } } unlock(cq); splx(x); } /* * set up an duart port as something other than a stream */ void duartspecial(int port, IOQ *oq, IOQ *iq, int baud) { Duartport *dp = &duartport[port]; dp->nostream = 1; if(oq){ dp->oq = oq; dp->oq->puts = duartputs; dp->oq->ptr = dp; } if(iq){ dp->iq = iq; dp->iq->ptr = dp; } duartenable(dp); duartbaud(dp, baud); } static void duarttimer(Alarm*); static int duartputc(IOQ *, int); static void duartstopen(Queue*, Stream*); static void duartstclose(Queue*); static void duartoput(Queue*, Block*); static void duartkproc(void *); Qinfo duartinfo = { nullput, duartoput, duartstopen, duartstclose, "duart" }; /* * wakeup the helper process to do input */ static void duarttimer(Alarm *a) { Duartport *dp = a->arg; cancel(a); dp->a = 0; wakeup(&dp->iq->r); } static int duartputc(IOQ *cq, int ch) { Duartport *dp = cq->ptr; int r; r = putc(cq, ch); /* * pass upstream within dp->delay milliseconds */ if(dp->a==0){ if(dp->delay == 0) wakeup(&cq->r); else dp->a = alarm(dp->delay, duarttimer, dp); } return r; } static void duartstopen(Queue *q, Stream *s) { Duartport *dp; char name[NAMELEN]; dp = &duartport[s->id]; qlock(dp); dp->wq = WR(q); WR(q)->ptr = dp; RD(q)->ptr = dp; dp->delay = 64; dp->iq->putc = duartputc; qunlock(dp); /* start with all characters as delimiters */ memset(dp->delim, 1, sizeof(dp->delim)); if(dp->kstarted == 0){ dp->kstarted = 1; sprint(name, "duart%d", s->id); kproc(name, duartkproc, dp); } } static void duartstclose(Queue *q) { Duartport *dp = q->ptr; qlock(dp); dp->wq = 0; dp->iq->putc = 0; WR(q)->ptr = 0; RD(q)->ptr = 0; qunlock(dp); } static int opdone(void *x) { Duartport *dp = x; return dp->op == Dnone; } static void duartoput(Queue *q, Block *bp) { Duartport *dp = q->ptr; IOQ *cq; int n, m; if(dp == 0){ freeb(bp); return; } cq = dp->oq; if(waserror()){ freeb(bp); nexterror(); } if(bp->type == M_CTL){ qlock(dp); while (cangetc(cq)) /* let output drain */ sleep(&cq->r, cangetc, cq); n = strtoul((char *)(bp->rptr+1), 0, 0); switch(*bp->rptr){ case 'B': case 'b': dp->val = n; dp->op = Dbaud; sleep(&dp->opr, opdone, dp); break; case 'D': case 'd': dp->val = n; dp->op = Ddtr; sleep(&dp->opr, opdone, dp); break; case 'K': case 'k': dp->val = 1; dp->op = Dbreak; sleep(&dp->opr, opdone, dp); tsleep(&dp->opr, return0, 0, n); dp->val = 0; dp->op = Dbreak; sleep(&dp->opr, opdone, dp); break; case 'R': case 'r': /* can't control? */ break; case 'W': case 'w': if(n>=0 && n<1000) dp->delay = n; break; } qunlock(dp); }else while((m = BLEN(bp)) > 0){ while ((n = canputc(cq)) == 0){ kprint(" duartoput: sleeping\n"); sleep(&cq->r, canputc, cq); } if(n > m) n = m; (*cq->puts)(cq, bp->rptr, n); bp->rptr += n; } freeb(bp); poperror(); } /* * process to send bytes upstream for a port */ static void duartkproc(void *a) { Duartport *dp = a; IOQ *cq = dp->iq; Block *bp; int n; loop: while ((n = cangetc(cq)) == 0) sleep(&cq->r, cangetc, cq); qlock(dp); if(dp->wq == 0){ cq->out = cq->in; }else{ bp = allocb(n); bp->flags |= S_DELIM; bp->wptr += gets(cq, bp->wptr, n); PUTNEXT(RD(dp->wq), bp); } qunlock(dp); goto loop; } Dirtab *duartdir; int nduartport; /* * allocate the queues if no one else has */ void duartreset(void) { Duartport *dp; int i; /* * allocate the directory and fill it in */ nduartport = 2*conf.nmach; duartdir = ialloc(nduartport*2*sizeof(Dirtab), 0); for(i = 0; i < nduartport; i++){ sprint(duartdir[2*i].name, "tty%d", i); sprint(duartdir[2*i+1].name, "tty%dctl", i); duartdir[2*i].length = duartdir[2*i+1].length = 0; duartdir[2*i].perm = duartdir[2*i+1].perm = 0666; duartdir[2*i].qid.path = STREAMQID(i, Sdataqid); duartdir[2*i+1].qid.path = STREAMQID(i, Sctlqid); } /* * allocate queues for any stream interfaces */ for(dp = duartport; dp < &duartport[nduartport]; dp++){ if(dp->nostream) continue; dp->iq = ialloc(sizeof(IOQ), 0); initq(dp->iq); dp->iq->ptr = dp; dp->oq = ialloc(sizeof(IOQ), 0); initq(dp->oq); dp->oq->ptr = dp; dp->oq->puts = duartputs; } } Chan* duartattach(char *spec) { return devattach('t', spec); } Chan* duartclone(Chan *c, Chan *nc) { return devclone(c, nc); } int duartwalk(Chan *c, char *name) { return devwalk(c, name, duartdir, 2*nduartport, devgen); } void duartstat(Chan *c, char *dp) { switch(STREAMTYPE(c->qid.path)){ case Sdataqid: streamstat(c, dp, "tty0"); break; default: devstat(c, dp, duartdir, 2*nduartport, devgen); break; } } Chan* duartopen(Chan *c, int omode) { Duartport *dp; switch(STREAMTYPE(c->qid.path)){ case Sdataqid: case Sctlqid: dp = &duartport[STREAMID(c->qid.path)]; break; default: dp = 0; break; } if(dp && dp->nostream) errors("in use"); if((c->qid.path & CHDIR) == 0) streamopen(c, &duartinfo); return devopen(c, omode, duartdir, 2*nduartport, devgen); } void duartcreate(Chan *c, char *name, int omode, ulong perm) { errors("only God can create a tty"); } void duartclose(Chan *c) { if(c->stream) streamclose(c); } long duartread(Chan *c, void *buf, long n, ulong offset) { Duartport *dp; if(c->qid.path&CHDIR) return devdirread(c, buf, n, duartdir, 2*nduartport, devgen); switch(STREAMTYPE(c->qid.path)){ case Sdataqid: return streamread(c, buf, n); case Sctlqid: if(offset) return 0; dp = &duartport[STREAMID(c->qid.path)]; qlock(dp); dp->op = Dstate; sleep(&dp->opr, opdone, dp); *(uchar *)buf = dp->val; qunlock(dp); return 1; } errors("no such duart type"); } long duartwrite(Chan *c, void *va, long n, ulong offset) { return streamwrite(c, va, n, 0); } void duartremove(Chan *c) { errors("can't remove a duart"); } void duartwstat(Chan *c, char *dp) { errors("can't write a duart"); } int duartactive(void) { int i; for(i = 0; i < nduartport; i++) if(duartport[i].printing) return 1; return 0; } | |