123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "io.h"
- #include "../port/error.h"
- enum {
- Qdir = 0,
- Qdata,
- Qctl,
- Qstat,
- };
- #define UARTTYPE(x) (((unsigned)x)&0x1f)
- #define UARTID(x) ((((unsigned)x))>>5)
- #define UARTQID(i, t) ((((unsigned)i)<<5)|(t))
- enum
- {
- /* soft flow control chars */
- CTLS= 023,
- CTLQ= 021,
- };
- extern Dev uartdevtab;
- extern PhysUart* physuart[];
- static Uart* uartlist;
- static Uart** uart;
- static int uartnuart;
- static Dirtab *uartdir;
- static int uartndir;
- static Timer *uarttimer;
- struct Uartalloc {
- Lock;
- Uart *elist; /* list of enabled interfaces */
- } uartalloc;
- static void uartclock(void);
- static void uartflow(void*);
- /*
- * enable/disable uart and add/remove to list of enabled uarts
- */
- static Uart*
- uartenable(Uart *p)
- {
- Uart **l;
- if(p->iq == nil){
- if((p->iq = qopen(8*1024, 0, uartflow, p)) == nil)
- return nil;
- }
- else
- qreopen(p->iq);
- if(p->oq == nil){
- if((p->oq = qopen(8*1024, 0, uartkick, p)) == nil){
- qfree(p->iq);
- p->iq = nil;
- return nil;
- }
- }
- else
- qreopen(p->oq);
- p->ir = p->istage;
- p->iw = p->istage;
- p->ie = &p->istage[Stagesize];
- p->op = p->ostage;
- p->oe = p->ostage;
- p->hup_dsr = p->hup_dcd = 0;
- p->dsr = p->dcd = 0;
- /* assume we can send */
- p->cts = 1;
- p->ctsbackoff = 0;
- if(p->bits == 0)
- uartctl(p, "l8");
- if(p->stop == 0)
- uartctl(p, "s1");
- if(p->parity == 0)
- uartctl(p, "pn");
- if(p->baud == 0)
- uartctl(p, "b9600");
- (*p->phys->enable)(p, 1);
- 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);
- return p;
- }
- static void
- uartdisable(Uart *p)
- {
- Uart **l;
- (*p->phys->disable)(p);
- lock(&uartalloc);
- for(l = &uartalloc.elist; *l; l = &(*l)->elist){
- if(*l == p){
- *l = p->elist;
- break;
- }
- }
- p->enabled = 0;
- unlock(&uartalloc);
- }
- static void
- uartsetlength(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 < uartnuart; i++){
- p = uart[i];
- if(p && p->opens && p->iq)
- uartdir[1+3*i].length = qlen(p->iq);
- }
- }
- /*
- * set up the '#t' directory
- */
- static void
- uartreset(void)
- {
- int i;
- Dirtab *dp;
- Uart *p, *tail;
- tail = nil;
- for(i = 0; physuart[i] != nil; i++){
- if(physuart[i]->pnp == nil)
- continue;
- if((p = physuart[i]->pnp()) == nil)
- continue;
- if(uartlist != nil)
- tail->next = p;
- else
- uartlist = p;
- for(tail = p; tail->next != nil; tail = tail->next)
- uartnuart++;
- uartnuart++;
- }
- if(uartnuart)
- uart = malloc(uartnuart*sizeof(Uart*));
- uartndir = 1 + 3*uartnuart;
- uartdir = malloc(uartndir * sizeof(Dirtab));
- if(uartnuart > 0 && uart == nil || uartdir == nil)
- panic("uartreset: no memory");
- dp = uartdir;
- strcpy(dp->name, ".");
- mkqid(&dp->qid, 0, 0, QTDIR);
- dp->length = 0;
- dp->perm = DMDIR|0555;
- dp++;
- p = uartlist;
- for(i = 0; i < uartnuart; i++){
- /* 3 directory entries per port */
- sprint(dp->name, "eia%d", i);
- dp->qid.path = UARTQID(i, Qdata);
- dp->perm = 0660;
- dp++;
- sprint(dp->name, "eia%dctl", i);
- dp->qid.path = UARTQID(i, Qctl);
- dp->perm = 0660;
- dp++;
- sprint(dp->name, "eia%dstatus", i);
- dp->qid.path = UARTQID(i, Qstat);
- dp->perm = 0444;
- dp++;
- uart[i] = p;
- p->dev = i;
- p = p->next;
- }
- if(uartnuart){
- /*
- * at 115200 baud, the 1024 char buffer takes 56 ms to process,
- * processing it every 22 ms should be fine.
- */
- uarttimer = addclock0link(uartclock, 22);
- }
- }
- 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, uartndir, devgen);
- }
- static int32_t
- uartstat(Chan *c, uint8_t *dp, int32_t n)
- {
- if(UARTTYPE(c->qid.path) == Qdata)
- uartsetlength(UARTID(c->qid.path));
- return devstat(c, dp, n, uartdir, uartndir, devgen);
- }
- static Chan*
- uartopen(Chan *c, int omode)
- {
- Uart *p;
- c = devopen(c, omode, uartdir, uartndir, devgen);
- switch(UARTTYPE(c->qid.path)){
- case Qctl:
- case Qdata:
- p = uart[UARTID(c->qid.path)];
- qlock(p);
- if(p->opens == 0 && uartenable(p) == nil){
- qunlock(p);
- c->flag &= ~COPEN;
- error(Enodev);
- }
- p->opens++;
- qunlock(p);
- break;
- }
- c->iounit = qiomaxatomic;
- return c;
- }
- static int
- uartdrained(void* arg)
- {
- Uart *p;
- p = arg;
- return qlen(p->oq) == 0 && p->op == p->oe;
- }
- static void
- uartdrainoutput(Uart *p)
- {
- Proc *up = externup();
- if(!p->enabled)
- return;
- p->drain = 1;
- if(waserror()){
- p->drain = 0;
- nexterror();
- }
- sleep(&p->r, uartdrained, p);
- poperror();
- }
- static void
- uartclose(Chan *c)
- {
- Proc *up = externup();
- Uart *p;
- if(c->qid.type & QTDIR)
- return;
- if((c->flag & COPEN) == 0)
- return;
- switch(UARTTYPE(c->qid.path)){
- case Qdata:
- case Qctl:
- p = uart[UARTID(c->qid.path)];
- qlock(p);
- if(--(p->opens) == 0){
- qclose(p->iq);
- ilock(&p->rlock);
- p->ir = p->iw = p->istage;
- iunlock(&p->rlock);
- /*
- */
- qhangup(p->oq, nil);
- if(!waserror()){
- uartdrainoutput(p);
- poperror();
- }
- qclose(p->oq);
- uartdisable(p);
- p->dcd = p->dsr = p->dohup = 0;
- }
- qunlock(p);
- break;
- }
- }
- static int32_t
- uartread(Chan *c, void *buf, int32_t n, int64_t off)
- {
- Uart *p;
- uint32_t offset = off;
- if(c->qid.type & QTDIR){
- uartsetlength(-1);
- return devdirread(c, buf, n, uartdir, uartndir, devgen);
- }
- p = uart[UARTID(c->qid.path)];
- switch(UARTTYPE(c->qid.path)){
- case Qdata:
- return qread(p->iq, buf, n);
- case Qctl:
- return readnum(offset, buf, n, UARTID(c->qid.path), NUMSIZE);
- case Qstat:
- return (*p->phys->status)(p, buf, n, offset);
- }
- return 0;
- }
- int
- uartctl(Uart *p, char *cmd)
- {
- char *f[16];
- int i, n, nf;
- nf = tokenize(cmd, f, nelem(f));
- for(i = 0; i < nf; i++){
- if(strncmp(f[i], "break", 5) == 0){
- (*p->phys->dobreak)(p, 0);
- continue;
- }
- n = atoi(f[i]+1);
- switch(*f[i]){
- case 'B':
- case 'b':
- uartdrainoutput(p);
- if((*p->phys->baud)(p, n) < 0)
- return -1;
- break;
- case 'C':
- case 'c':
- p->hup_dcd = n;
- break;
- case 'D':
- case 'd':
- uartdrainoutput(p);
- (*p->phys->dtr)(p, n);
- break;
- case 'E':
- case 'e':
- p->hup_dsr = n;
- break;
- case 'F':
- case 'f':
- if(p->oq != nil)
- qflush(p->oq);
- break;
- case 'H':
- case 'h':
- if(p->iq != nil)
- qhangup(p->iq, 0);
- if(p->oq != nil)
- qhangup(p->oq, 0);
- break;
- case 'I':
- case 'i':
- uartdrainoutput(p);
- (*p->phys->fifo)(p, n);
- break;
- case 'K':
- case 'k':
- uartdrainoutput(p);
- (*p->phys->dobreak)(p, n);
- break;
- case 'L':
- case 'l':
- uartdrainoutput(p);
- if((*p->phys->bits)(p, n) < 0)
- return -1;
- break;
- case 'M':
- case 'm':
- uartdrainoutput(p);
- (*p->phys->modemctl)(p, n);
- break;
- case 'N':
- case 'n':
- if(p->oq != nil)
- qnoblock(p->oq, n);
- break;
- case 'P':
- case 'p':
- uartdrainoutput(p);
- if((*p->phys->parity)(p, *(f[i]+1)) < 0)
- return -1;
- break;
- case 'Q':
- case 'q':
- if(p->iq != nil)
- qsetlimit(p->iq, n);
- if(p->oq != nil)
- qsetlimit(p->oq, n);
- break;
- case 'R':
- case 'r':
- uartdrainoutput(p);
- (*p->phys->rts)(p, n);
- break;
- case 'S':
- case 's':
- uartdrainoutput(p);
- if((*p->phys->stop)(p, n) < 0)
- return -1;
- break;
- case 'W':
- case 'w':
- if(uarttimer == nil || n < 1)
- return -1;
- uarttimer->tns = (int64_t)n * 100000LL;
- break;
- case 'X':
- case 'x':
- if(p->enabled){
- ilock(&p->tlock);
- p->xonoff = n;
- iunlock(&p->tlock);
- }
- break;
- }
- }
- return 0;
- }
- static int32_t
- uartwrite(Chan *c, void *buf, int32_t n, int64_t mm)
- {
- Proc *up = externup();
- Uart *p;
- char *cmd;
- if(c->qid.type & QTDIR)
- error(Eperm);
- p = uart[UARTID(c->qid.path)];
- switch(UARTTYPE(c->qid.path)){
- case Qdata:
- qlock(p);
- if(waserror()){
- qunlock(p);
- nexterror();
- }
- n = qwrite(p->oq, buf, n);
- qunlock(p);
- poperror();
- break;
- case Qctl:
- cmd = malloc(n+1);
- memmove(cmd, buf, n);
- cmd[n] = 0;
- qlock(p);
- if(waserror()){
- qunlock(p);
- free(cmd);
- nexterror();
- }
- /* let output drain */
- if(uartctl(p, cmd) < 0)
- error(Ebadarg);
- qunlock(p);
- poperror();
- free(cmd);
- break;
- }
- return n;
- }
- static int32_t
- uartwstat(Chan *c, uint8_t *dp, int32_t n)
- {
- Dir d;
- Dirtab *dt;
- if(!iseve())
- error(Eperm);
- if(QTDIR & c->qid.type)
- error(Eperm);
- if(UARTTYPE(c->qid.path) == Qstat)
- error(Eperm);
- dt = &uartdir[1 + 3 * UARTID(c->qid.path)];
- n = convM2D(dp, n, &d, nil);
- if(n == 0)
- error(Eshortstat);
- if(d.mode != (uint32_t)~0UL)
- dt[0].perm = dt[1].perm = d.mode;
- return n;
- }
- void
- uartpower(int on)
- {
- Uart *p;
- for(p = uartlist; p != nil; p = p->next) {
- if(p->phys->power)
- (*p->phys->power)(p, on);
- }
- }
- Dev uartdevtab = {
- 't',
- "uart",
- uartreset,
- devinit,
- devshutdown,
- uartattach,
- uartwalk,
- uartstat,
- uartopen,
- devcreate,
- uartclose,
- uartread,
- devbread,
- uartwrite,
- devbwrite,
- devremove,
- uartwstat,
- uartpower,
- };
- /*
- * restart input if it's off
- */
- static void
- uartflow(void *v)
- {
- Uart *p;
- p = v;
- if(p->modem)
- (*p->phys->rts)(p, 1);
- }
- /*
- * put some bytes into the local queue to avoid calling
- * qconsume for every character
- */
- int
- uartstageoutput(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;
- }
- /*
- * restart output
- */
- void
- uartkick(void *v)
- {
- Uart *p = v;
- if(p->blocked)
- return;
- ilock(&p->tlock);
- (*p->phys->kick)(p);
- iunlock(&p->tlock);
- if(p->drain && uartdrained(p)){
- p->drain = 0;
- wakeup(&p->r);
- }
- }
- /*
- * Move data from the interrupt staging area to
- * the input Queue.
- */
- static void
- uartstageinput(Uart *p)
- {
- int n;
- uint8_t *ir, *iw;
- while(p->ir != p->iw){
- ir = p->ir;
- if(p->ir > p->iw){
- iw = p->ie;
- p->ir = p->istage;
- }
- else{
- iw = p->iw;
- p->ir = p->iw;
- }
- if((n = qproduce(p->iq, ir, iw - ir)) < 0){
- p->serr++;
- (*p->phys->rts)(p, 0);
- }
- else if(n == 0)
- p->berr++;
- }
- }
- /*
- * receive a character at interrupt time
- */
- void
- uartrecv(Uart *p, char ch)
- {
- uint8_t *next;
- /* software flow control */
- if(p->xonoff){
- if(ch == CTLS){
- p->blocked = 1;
- }else if(ch == CTLQ){
- p->blocked = 0;
- p->ctsbackoff = 2; /* clock gets output going again */
- }
- }
- /* receive the character */
- if(p->putc)
- p->putc(p->iq, ch);
- else{
- ilock(&p->rlock);
- next = p->iw + 1;
- if(next == p->ie)
- next = p->istage;
- if(next == p->ir)
- uartstageinput(p);
- if(next != p->ir){
- *p->iw = ch;
- p->iw = next;
- }
- iunlock(&p->rlock);
- }
- }
- /*
- * we save up input characters till clock time to reduce
- * per character interrupt overhead.
- */
- static void
- uartclock(void)
- {
- Uart *p;
- lock(&uartalloc);
- for(p = uartalloc.elist; p; p = p->elist){
- if(p->phys->poll != nil)
- (*p->phys->poll)(p);
- /* this hopefully amortizes cost of qproduce to many chars */
- if(p->iw != p->ir){
- ilock(&p->rlock);
- uartstageinput(p);
- iunlock(&p->rlock);
- }
- /* hang up if requested */
- if(p->dohup){
- qhangup(p->iq, 0);
- qhangup(p->oq, 0);
- p->dohup = 0;
- }
- /* this adds hysteresis to hardware/software flow control */
- if(p->ctsbackoff){
- ilock(&p->tlock);
- if(p->ctsbackoff){
- if(--(p->ctsbackoff) == 0)
- (*p->phys->kick)(p);
- }
- iunlock(&p->tlock);
- }
- }
- unlock(&uartalloc);
- }
|