From 00f9c62276b2a26a2832f7612178a7b04cb59c55 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Sat, 6 Oct 2001 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 2001-10-06 --- pc/devusb.c | 496 ++++++++++++++++++++++++++++------------------------ 1 file changed, 271 insertions(+), 225 deletions(-) diff --git a/pc/devusb.c b/pc/devusb.c index 4d9a7af057105054b4a283ca9d756a53551cd44e..c6a0d2dd39b93c5888e39963bf72d506cd82993b 100644 --- a/pc/devusb.c +++ b/pc/devusb.c @@ -116,7 +116,7 @@ enum { DevicePresent = 1<<0, FRAMESIZE= 4096, /* fixed by hardware; aligned to same */ - NFRAME = FRAMESIZE/4, + NFRAME = (FRAMESIZE/4), NISOTD = 4, /* number of TDs for isochronous io per frame */ Vf = 1<<2, /* TD only */ @@ -146,6 +146,8 @@ enum { /* TD.flags (software) */ CancelTD= 1<<0, + IsoLock= 1<<1, + IsoClean= 1<<2, }; #define GET2(p) ((((p)[1]&0xFF)<<8)|((p)[0]&0xFF)) @@ -207,9 +209,11 @@ struct Endpt { byte isopen; /* ep operations forbidden on open endpoints */ byte mode; /* OREAD, OWRITE, ORDWR */ byte nbuf; /* number of buffers allowed */ - byte iso; + volatile byte iso; byte debug; byte active; /* listed for examination by interrupts */ + byte * tdalloc; + byte * bpalloc; int hz; int remain; int samplesz; @@ -217,14 +221,16 @@ struct Endpt { int setin; int pollms; /* polling interval in msec */ int psize; /* size of this packet */ - Block *curblock; /* half full block between iso writes */ int curbytes; /* # of bytes in that block */ QH* epq; /* queue of TDs for this endpoint */ - TD* etd; /* pointer into circular list of TDs for isochronous ept */ + TD* td0; /* pointer to list of TDs for isochronous ept */ + volatile TD* etd; /* reader/writer pointer into list of TDs for isochronous ept */ + volatile TD* xtd; /* next td to be cleaned */ QLock rlock; Rendez rr; Queue* rq; QLock wlock; + int isolock; Rendez wr; Queue* wq; @@ -675,79 +681,6 @@ queueqh(QH *qh) { ub->recvq->entries = PADDR(qh) | IsQH; } -static int -tdisready(void *arg) -{ - return (((TD*)arg)->status & Active) == 0; -} - -static Block * -isoxmit(Endpt *e, Block *b, int pid) -{ - Block *ob; - static int ioc, n, id; - TD *td; - - td = e->etd; - if (td == nil) - panic("usb: isoxmit"); - while(td->status & Active){ - XPRINT("isoxmit: sleep %lux\n", &e->wr); - sleep(&e->wr, tdisready, e->etd); - XPRINT("isoxmit: awake\n"); - } - if (td->status & AnyError) - iprint("usbisoerror 0x%lux\n", td->status); - ob = td->bp; - if (ob == nil) - panic("isoxmit: null block"); - td->buffer = PADDR(b->rp); - td->bp = b; - n = BLEN(b); - id = (e->x<<7)|(e->dev->x&0x7F); - td->dev = ((n-1)<<21) | ((id&0x7FF)<<8) | pid; - if ((ioc++ & 0xff) == 0) - td->status = ErrLimit3 | Active | IsoSelect | IOC; - else - td->status = ErrLimit3 | Active | IsoSelect; - e->etd = td->next; - return ob; -} - -static Block* -isorecv(Endpt *e, Block *b, int pid) -{ - TD *td; - int n, id; - Block *ob; - static int ioc; - - td = e->etd; - if (td == nil) - panic("usb: isorecv"); - while(td->status & Active){ - XPRINT("isorecv: sleep %lux\n", &e->wr); - sleep(&e->wr, tdisready, e->etd); - XPRINT("isorecv: awake\n"); - } - if (td->status & AnyError) - iprint("usbisoerror 0x%lux\n", td->status); - ob = td->bp; - if (ob == nil) - panic("isorecv: null block"); - n = (td->status + 1) & 0x7FF; - if(n > ob->lim - b->wp) - n = 0; - ob->wp = ob->rp + n; - td->buffer = PADDR(b->rp); - td->bp = b; - id = (e->x<<7)|(e->dev->x&0x7F); - td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | pid; - td->status = ErrLimit3 | Active | IsoSelect | IOC; - e->etd = td->next; - return ob; -} - static QH* qxmit(Endpt *e, Block *b, int pid) { @@ -836,7 +769,7 @@ flog2(int n) static int usbsched( Ctlr *ub, int pollms, ulong load) { - int i, j, d, q; + int i, d, q; ulong best, worst; best = 1000000; @@ -844,13 +777,6 @@ usbsched( Ctlr *ub, int pollms, ulong load) for (d = 0; d < pollms; d++){ worst = 0; for (i = d; i < NFRAME; i++){ - for(j = 0; j < NISOTD; j++) - if(ub->tdpool[NISOTD*i + j].ep == nil) - break; - if(j == NISOTD){ - worst = 1000000; /* No free TDs */ - break; - } if (ub->frameld[i] + load > worst) worst = ub->frameld[i] + load; } @@ -866,8 +792,9 @@ static int schedendpt(Endpt *e) { Ctlr *ub; - TD *td, **prev, *first; - int i, j; + TD *td; + uchar *bp; + int i, id, ix, size, frnum; if(!e->iso || e->sched >= 0) return 0; @@ -876,10 +803,6 @@ schedendpt(Endpt *e) if (e->isopen){ return -1; } - if (e->curblock == nil) - e->curblock = allocb(e->maxpkt); - if (e->curblock == nil) - panic("schedept: allocb"); e->curbytes = 0; e->sched = usbsched(ub, e->pollms, e->maxpkt); if(e->sched < 0){ @@ -887,45 +810,48 @@ schedendpt(Endpt *e) return -1; } - /* Allocate TDs from the NISOTD entries on each of the - * frames. Link them circularly so they can be walked easily - */ - - prev = nil; - first = nil; - td = nil; + if (e->tdalloc || e->bpalloc) + panic("usb: tdalloc/bpalloc"); + e->tdalloc = mallocz(0x10 + NFRAME*sizeof(TD), 1); + e->bpalloc = mallocz(0x10 + e->maxpkt*NFRAME/e->pollms, 1); + e->td0 = (TD*)(((ulong)e->tdalloc + 0xf) & ~0xf); + bp = (byte *)(((ulong)e->bpalloc + 0xf) & ~0xf); + frnum = (IN(Frnum) + 1) & 0x3ff; + frnum = (frnum & ~(e->pollms - 1)) + e->sched; + e->isolock = 1; + e->xtd = &e->td0[frnum]; /* Next td to finish */ + e->etd = nil; /* Writer is idle */ + e->remain = 0; + td = e->td0; for(i = e->sched; i < NFRAME; i += e->pollms){ - for(j = 0; j < NISOTD; j++){ - td = &ub->tdpool[i*NISOTD + j]; - if(td->ep == nil) - break; - } - if(j == NISOTD) - panic("usb: no td entries despite schedulability test"); + td->bp = (Block *)(bp + e->maxpkt*i/e->pollms); + td->buffer = PADDR(td->bp); td->ep = e; - if(td->bp) - panic("usb: schedendpt: block not empty"); - td->bp = allocb(e->maxpkt); - if(td->bp == nil) - panic("schedept: allocb"); - if (e->mode == OREAD){ - int id; + td->next = &td[1]; + ub->frameld[i] += e->maxpkt; + td++; + } + td[-1].next = e->td0; + for(i = e->sched; i < NFRAME; i += e->pollms){ + ix = (frnum+i)%NFRAME; + td = &e->td0[ix]; + + id = (e->x<<7)|(e->dev->x&0x7F); + if (e->mode == OREAD) /* enable receive on this entry */ - td->buffer = PADDR(td->bp->rp); - id = (e->x<<7)|(e->dev->x&0x7F); td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | TokIN; - td->status = ErrLimit3 | Active | IsoSelect | IOC; + else{ + size = (e->hz + e->remain)*e->pollms/1000; + e->remain = (e->hz + e->remain)*e->pollms%1000; + size *= e->samplesz; + memset((byte*)td->bp, 0, size); + td->dev = ((size-1)<<21) | ((id&0x7FF)<<8) | TokOUT; } - if(i == e->sched){ - first = td; - e->etd = td; - }else{ - *prev = td; - } - prev = &td->next; - ub->frameld[i] += e->maxpkt; + td->status = ErrLimit3 | Active | IsoSelect | IOC; + td->link = ub->frames[ix]; + ub->frames[ix] = PADDR(td); } - *prev = first; /* complete circular link */ + e->isolock = 0; return 0; } @@ -933,23 +859,33 @@ static void unschedendpt(Endpt *e) { Ctlr *ub; - TD *td, *next; + TD *td; + ulong *addr; int q; ub = &ubus; - if(!e->iso || (q = e->sched) < 0) + if(!e->iso || e->sched < 0) return; - for (td = e->etd; q < NFRAME; q += e->pollms){ + if (e->tdalloc == nil) + panic("tdalloc"); + for (q = e->sched; q < NFRAME; q += e->pollms){ + td = e->td0++; + addr = &ub->frames[q]; + while (*addr != PADDR(td)){ + if (*addr & IsQH) + panic("usb: TD expected"); + addr = &TFOL(*addr)->link; + } + *addr = td->link; ub->frameld[q] -= e->maxpkt; - next = td->next; - td->ep = nil; - td->next = nil; - freeb(td->bp); - td->bp = nil; - td = next; } - freeb(e->curblock); + free(e->tdalloc); + free(e->bpalloc); + e->tdalloc = nil; + e->bpalloc = nil; + e->etd = nil; + e->td0 = nil; e->sched = -1; } @@ -1002,9 +938,9 @@ freept(Endpt *e) { if(e != nil && decref(e) == 0){ XPRINT("freept(%d,%d)\n", e->dev->x, e->x); + eptdeactivate(e); unschedendpt(e); e->dev->ep[e->x] = nil; - eptdeactivate(e); if(e->epq != nil) freeqh(&ubus, e->epq); free(e); @@ -1127,12 +1063,53 @@ portinfo(Ctlr *ub, int *p0, int *p1) return m; } +static void +cleaniso(Endpt *e, int frnum) +{ + TD *td; + int tok, id, size; + + if (e->xtd->status & Active){ + td = e->td0 + ((frnum -1)&0x3ff); + if (td->status & Active) + return; + iprint("%ld active -> %ld", e->xtd - e->td0, td - e->td0); + e->xtd = td; + } + tok = (e->mode == OREAD)?TokIN:TokOUT; + id = (e->x<<7)|(e->dev->x&0x7F); + for(td = e->xtd; (td->status & Active) == 0; td = td->next){ + if (td->flags & IsoLock) + iprint("usbinterrupt: locked?\n"); + if (e->isolock == 0 && td == e->etd){ + /* if isolock is set, don't touch e->etd */ + if (e->xtd == e->etd) + wakeup(&e->wr); /* writer had to wait */ + else + e->etd = nil; /* writer was overtaken */ + } + if (td->status & AnyError) + iprint("usbisoerror 0x%lux\n", td->status); + size = (e->hz + e->remain)*e->pollms/1000; + e->remain = (e->hz + e->remain)*e->pollms%1000; + size *= e->samplesz; + if ((td->flags & IsoClean) == 0){ + memset((byte*)td->bp, 0, e->maxpkt); + td->flags |= IsoClean; + } + td->dev = ((size-1)<<21) | ((id&0x7FF)<<8) | tok; + td->status = ErrLimit3 | Active | IsoSelect | IOC; + } + e->xtd = td; + if (e->etd) wakeup(&e->wr); +} + static void interrupt(Ureg*, void *a) { Ctlr *ub; Endpt *e; - int s; + int s, frnum; QH *q; ub = a; @@ -1140,7 +1117,8 @@ interrupt(Ureg*, void *a) OUT(Status, s); if ((s & 0x1f) == 0) return; - XPRINT("usbint: #%x f%d\n", s, IN(Frnum)); + frnum = IN(Frnum) & 0x3ff; + XPRINT("usbint: #%x f%d\n", s, frnum); if (s & 0x1a) { XPRINT("cmd #%x sofmod #%x\n", IN(Cmd), inb(ub->io+SOFMod)); XPRINT("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1)); @@ -1161,8 +1139,9 @@ interrupt(Ureg*, void *a) XPRINT("cleanq(e->epq, 0, 0)\n"); cleanq(e->epq, 0, 0); } - if(e->iso && e->etd != nil) { - wakeup(&e->wr); + if(e->iso) { + XPRINT("cleaniso(e)\n"); + cleaniso(e, frnum); } } iunlock(&activends); @@ -1268,7 +1247,7 @@ static void usbreset(void) { Pcidev *cfg; - int i, j; + int i; ulong port; TD *t; Ctlr *ub; @@ -1314,8 +1293,8 @@ usbreset(void) intrenable(cfg->intl, interrupt, ub, cfg->tbdf, "usb"); ub->io = port; - ub->tdpool = xspanalloc((NFRAME*NISOTD+128)*sizeof(TD), 16, 0); - for(i=NFRAME*NISOTD+128; --i>=NFRAME*NISOTD;){ + ub->tdpool = xspanalloc(128*sizeof(TD), 16, 0); + for(i=128; --i>=0;){ ub->tdpool[i].next = ub->freetd; ub->freetd = &ub->tdpool[i]; } @@ -1352,26 +1331,8 @@ usbreset(void) ub->frames = xspanalloc(FRAMESIZE, FRAMESIZE, 0); ub->frameld = mallocz(FRAMESIZE, 1); - for (i = 0; i < NFRAME; i++){ - j = NISOTD*i; - ub->frames[i] = PADDR(&ub->tdpool[j]); /* breadth 1st, is TD, no termin. */ - ub->tdpool[j+0].link = PADDR(&ub->tdpool[j+1]); - ub->tdpool[j+0].status = IsoSelect; - ub->tdpool[j+0].dev = 0; - ub->tdpool[j+0].buffer = 0; - ub->tdpool[j+1].link = PADDR(&ub->tdpool[j+2]); - ub->tdpool[j+1].status = IsoSelect; - ub->tdpool[j+1].dev = 0; - ub->tdpool[j+1].buffer = 0; - ub->tdpool[j+2].link = PADDR(&ub->tdpool[j+3]); - ub->tdpool[j+2].status = IsoSelect; - ub->tdpool[j+2].dev = 0; - ub->tdpool[j+2].buffer = 0; - ub->tdpool[j+3].link = PADDR(ub->ctlq) | IsQH; - ub->tdpool[j+3].status = IsoSelect; - ub->tdpool[j+3].dev = 0; - ub->tdpool[j+3].buffer = 0; - } + for (i = 0; i < NFRAME; i++) + ub->frames[i] = PADDR(ub->ctlq) | IsQH; outl(port+Flbaseadd, PADDR(ub->frames)); OUT(Frnum, 0); @@ -1613,6 +1574,10 @@ usbclose(Chan *c) error(Enodev); } e->isopen--; + if (e->isopen == 0){ + eptdeactivate(e); + unschedendpt(e); + } } if(QID(c->qid) == Qctl) d->busy = 0; @@ -1631,12 +1596,43 @@ eptinput(void *arg) return e->eof || e->err || qcanread(e->rq); } +static int +isorready(void *arg) +{ + int frnum; + Ctlr *ub; + Endpt *e; + + e = arg; + e->isolock = 1; + ub = &ubus; + frnum = IN(Frnum) & 0x3ff; + if (e->etd == nil){ + /* reader was idle, find a place to start */ + e->etd = e->td0 + (frnum & 0x3ff); + } + if (e->etd->status & Active){ + /* not yet ready */ + e->isolock = 0; + return 0; + } + /* leave locked if succesful */ + e->psize = (e->etd->status + 1) & 0x7FF; + e->curbytes = 0; + e->nbytes += e->psize; + e->nblocks++; + if (e->psize > e->maxpkt) + panic("packet size > maximum"); + return 1; +} + static long readusb(Endpt *e, void *a, long n) { Block *b; - uchar *p; - long l, i; + uchar *p, *q; + long l, i, id; + TD *td; XPRINT("qlock(%p)\n", &e->rlock); qlock(&e->rlock); @@ -1647,37 +1643,52 @@ readusb(Endpt *e, void *a, long n) nexterror(); } p = a; - do { - if(e->eof) { - XPRINT("e->eof\n"); - break; - } - if(e->err) - error(e->err); - if (e->iso){ - if (e->curbytes == 0){ - /* calculate size of next receive buffer */ - /* put empty buffer on receive queue and get next full one */ - b = isorecv(e, e->curblock, TokIN); - /* bytes to consume in b: */ - e->curbytes = BLEN(b); - b->wp = b->rp; - e->nbytes += e->curbytes; - e->nblocks++; - }else{ - b = e->curblock; + if (e->iso){ + do { + if (e->psize == 0){ + while(isorready(e) == 0){ + XPRINT("readusb: sleep %lux\n", &e->wr); + sleep(&e->wr, isorready, e); + XPRINT("readusb: awake\n"); + } } - if (b == nil) - panic("readusb: block"); - if((i = n) >= e->curbytes) - i = e->curbytes; - memmove(p, b->wp, i); - b->wp += i; - p += i; - n -= i; - e->curbytes -= i; - e->curblock = b; - }else{ + td = e->etd; + e->isolock = 0; + + if (e->psize > 0){ + q = (byte*)td->bp + e->curbytes; + if((i = n) >= e->psize) + i = e->psize; + memmove(p, q, i); + p += i; + n -= i; + e->curbytes += i; + e->psize -= i; + if (e->psize > 0){ + /* Read done, return */ + if (n != 0) + panic("usb iso: can't happen"); + break; + } + } + e->isolock = 1; + /* Finished a packet */ + id = (e->x<<7)|(e->dev->x&0x7F); + td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | TokIN; + td->status = ErrLimit3 | Active | IsoSelect | IOC; + if (e->xtd == td) + e->xtd = td->next; + /* Get next buffer, wait for it to become ready */ + e->etd = td->next; + } while (n > 0); + }else{ + do { + if(e->eof) { + XPRINT("e->eof\n"); + break; + } + if(e->err) + error(e->err); qrcv(e); if(!e->iso) e->data01 ^= 1; @@ -1705,8 +1716,8 @@ readusb(Endpt *e, void *a, long n) n -= i; if (l != e->maxpkt) break; - } - } while (n > 0); + } while (n > 0); + } poperror(); qunlock(&e->rlock); return p-(uchar*)a; @@ -1790,13 +1801,41 @@ qisempty(void *arg) return ((QH*)arg)->entries & Terminate; } +static int +isowready(void *arg) +{ + int frnum; + Ctlr *ub; + Endpt *e; + + e = arg; + e->isolock = 1; + ub = &ubus; + frnum = IN(Frnum) & 0x3ff; + if (e->etd == nil){ + /* writer was idle, find a place to start */ + e->etd = e->td0 + ((frnum + 4) & 0x3ff); + e->curbytes = 0; + e->psize = ((e->etd->dev >> 21) & 0x7ff) + 1; + if (e->psize > e->maxpkt) + panic("packet size > maximum"); + } + if (e->etd != e->xtd && e->etd->next != e->xtd){ + /* leave locked if succesful */ + return 1; + } + e->isolock = 0; + return 0; +} + static long writeusb(Endpt *e, void *a, long n, int tok) { - long i; + int i; Block *b; - uchar *p; + uchar *p, *q; QH *qh; + TD *td; p = a; qlock(&e->wlock); @@ -1805,45 +1844,52 @@ writeusb(Endpt *e, void *a, long n, int tok) eptcancel(e); nexterror(); } - do { - if(e->err) - error(e->err); - if (e->iso){ - if (e->curbytes == 0){ - e->psize = (e->hz + e->remain)*e->pollms/1000; - e->remain = (e->hz + e->remain)*e->pollms%1000; - e->psize *= e->samplesz; + e->td0 = (TD*)(((ulong)e->tdalloc + 0xf) & ~0xf); + if (e->iso){ + if(waserror()){ + e->isolock = 0; + if (td = e->etd) + td->flags &= ~IsoLock; + nexterror(); + } + do { + while (isowready(e) == 0){ + XPRINT("writeusb: sleep %lux\n", &e->wr); + sleep(&e->wr, isowready, e); + XPRINT("writeusb: awake\n"); } - if (e->psize > e->maxpkt) - panic("packet size > maximum"); - b = e->curblock; - if (b == nil) - panic("writeusb: block"); + td = e->etd; + e->isolock = 0; + td->flags |= IsoLock; + td->flags &= ~IsoClean; + q = (uchar*)td->bp + e->curbytes; if((i = n) >= e->psize - e->curbytes) i = e->psize - e->curbytes; - if (e->curbytes != BLEN(b)) - iprint("mismatch: curbytes %d, blen %ld\n", e->curbytes, BLEN(b)); - memmove(b->wp, p, i); - b->wp += i; + memmove(q, p, i); p += i; n -= i; e->curbytes += i; + td->flags &= ~IsoLock; if (e->curbytes < e->psize){ - e->curblock = b; if (n != 0) panic("usb iso: can't happen"); break; } - if (BLEN(b) != e->psize) - panic("usbwrite: length mismatch"); - e->curblock = isoxmit(e, b, TokOUT); + e->etd = td->next; e->curbytes = 0; - e->curblock->wp = e->curblock->rp; + e->psize = ((e->etd->dev >> 21) & 0x7ff) + 1; + if (e->psize > e->maxpkt) + panic("packet size > maximum"); e->nbytes += e->psize; e->nblocks++; - }else{ + } while(n > 0); + poperror(); + }else{ + do { int j; + if(e->err) + error(e->err); if((i = n) >= e->maxpkt) i = e->maxpkt; b = allocb(i); @@ -1851,7 +1897,7 @@ writeusb(Endpt *e, void *a, long n, int tok) freeb(b); nexterror(); } - XPRINT("out [%ld]", i); + XPRINT("out [%d]", i); for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]); XPRINT("\n"); memmove(b->wp, p, i); @@ -1868,8 +1914,8 @@ writeusb(Endpt *e, void *a, long n, int tok) sleep(&e->wr, qisempty, qh); XPRINT("writeusb: awake\n"); } - } - } while(n > 0); + } while(n > 0); + } poperror(); qunlock(&e->wlock); return p-(uchar*)a; @@ -1964,7 +2010,7 @@ usbwrite(Chan *c, void *a, long n, vlong) e->err = nil; }else if(nf == 6 && strcmp(fields[0], "ep") == 0){ /* ep n `bulk' mode maxpkt nbuf OR - * ep n period mode samplesize hz + * ep n period mode samplesize KHz */ i = strtoul(fields[1], nil, 0); if(i < 0 || i >= nelem(d->ep)) {