From 5079ac3025f884563b293d0e832ad2fa5d579bf1 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Tue, 19 Feb 2002 00:00:00 +0000 Subject: [PATCH] Plan 9 from Bell Labs 2002-02-19 --- pc/devusb.c | 2216 +++++++++----------------------------------------- pc/usbuhci.c | 99 +-- 2 files changed, 435 insertions(+), 1880 deletions(-) diff --git a/pc/devusb.c b/pc/devusb.c index f4e3f388c214b8a5fef7e144373cbc1a7cf54e6b..9ea30d9e41c5874b9badf443381e2429a22cada7 100644 --- a/pc/devusb.c +++ b/pc/devusb.c @@ -1,17 +1,3 @@ -/* - * UHCI USB driver - * (c) 1998, 1999 C H Forsyth, forsyth@caldo.demon.co.uk - * to do: - * endpoint open/close - * build Endpt on open from attributes stored in Udev? - * build data0/data1 rings for bulk and interrupt endpoints - * endpoint TD rings (can there be prefetch?) - * hubs? - * special handling of isochronous traffic? - * is use of Queues justified? (could have client clean TD rings on wakeup) - * bandwidth check - */ - #include "u.h" #include "../port/lib.h" #include "mem.h" @@ -20,284 +6,80 @@ #include "io.h" #include "../port/error.h" +#include "usb.h" + +static int debug = 0; + #define Chatty 1 #define DPRINT if(Chatty)print #define XPRINT if(debug)iprint -static int debug = 0; +Usbhost* usbhost[MaxUsb]; -/* - * USB packet definitions - */ -enum { - TokIN = 0x69, - TokOUT = 0xE1, - TokSETUP = 0x2D, - - /* request type */ - RH2D = 0<<7, - RD2H = 1<<7, - Rstandard = 0<<5, - Rclass = 1<<5, - Rvendor = 2<<5, - Rdevice = 0, - Rinterface = 1, - Rendpt = 2, - Rother = 3, +static char *devstates[] = { + [Disabled] "Disabled", + [Attached] "Attached", + [Enabled] "Enabled", + [Assigned] "Assigned", + [Configured] "Configured", }; -typedef struct Ctlr Ctlr; -typedef struct Endpt Endpt; -typedef struct Udev Udev; +static char Ebadusbmsg[] = "invalid parameters to USB ctl message"; -/* - * UHCI hardware structures, aligned on 16-byte boundary - */ -typedef struct QH QH; -typedef struct TD TD; - -#define Class(csp) ((csp)&0xff) -#define Subclass(csp) (((csp)>>8)&0xff) -#define Proto(csp) (((csp)>>16)&0xff) -#define CSP(c, s, p) ((c) | ((s)<<8) | ((p)<<16)) - -struct TD { - ulong link; - ulong status; /* controller r/w */ - ulong dev; - ulong buffer; - - /* software */ - ulong flags; - union{ - Block* bp; /* non-iso */ - ulong offset; /* iso */ - }; - Endpt* ep; - TD* next; -}; -#define TFOL(p) ((TD*)KADDR((ulong)(p) & ~0xF)) - -struct QH { - ulong head; - ulong entries; /* address of next TD or QH to process (updated by controller) */ - - /* software */ - QH* hlink; - TD* first; - QH* next; /* free list */ - TD* last; - ulong _d1; /* fillers */ - ulong _d2; +enum +{ + Qtopdir = 0, + Q2nd, + Qnew, + Qport, + Q3rd, + Qctl, + Qstatus, + Qep0, + /* other endpoint files */ }; -#define QFOL(p) ((QH*)KADDR((ulong)(p) & ~0xF)) /* - * UHCI interface registers and bits + * Qid path is: + * 8 bits of file type (qids above) + * 8 bits of slot number; default address 0 used for per-controller files + * 4 bits of controller number */ enum { - /* i/o space */ - Cmd = 0, - Status = 2, - Usbintr = 4, - Frnum = 6, - Flbaseadd = 8, - SOFMod = 0xC, - Portsc0 = 0x10, - Portsc1 = 0x12, - - /* port status */ - Suspend = 1<<12, - PortReset = 1<<9, - SlowDevice = 1<<8, - ResumeDetect = 1<<6, - PortChange = 1<<3, /* write 1 to clear */ - PortEnable = 1<<2, - StatusChange = 1<<1, /* write 1 to clear */ - DevicePresent = 1<<0, - - NFRAME = 1024, - FRAMESIZE= NFRAME*sizeof(ulong), /* fixed by hardware; aligned to same */ - FRAMEMASK= FRAMESIZE-1, - NISOTD = 4, /* number of TDs for isochronous io per frame */ - - Vf = 1<<2, /* TD only */ - IsQH = 1<<1, - Terminate = 1<<0, - - /* TD.status */ - SPD = 1<<29, - ErrLimit0 = 0<<27, - ErrLimit1 = 1<<27, - ErrLimit2 = 2<<27, - ErrLimit3 = 3<<27, - LowSpeed = 1<<26, - IsoSelect = 1<<25, - IOC = 1<<24, - Active = 1<<23, - Stalled = 1<<22, - DataBufferErr = 1<<21, - Babbling = 1<<20, - NAKed = 1<<19, - CRCorTimeout = 1<<18, - BitstuffErr = 1<<17, - AnyError = (Stalled | DataBufferErr | Babbling | NAKed | CRCorTimeout | BitstuffErr), - - /* TD.dev */ - IsDATA1 = 1<<19, - - /* TD.flags (software) */ - CancelTD= 1<<0, - IsoClean= 1<<2, -}; + TYPEBITS = 8, + SLOTBITS = 8, + CTLRBITS = 4, -#define GET2(p) ((((p)[1]&0xFF)<<8)|((p)[0]&0xFF)) -#define PUT2(p,v) (((p)[0] = (v)), ((p)[1] = (v)>>8)) + SLOTSHIFT = TYPEBITS, + CTLRSHIFT = SLOTSHIFT+SLOTBITS, -/* - * active USB device - */ -struct Udev { - Ref; - Lock; - int x; /* index in usbdev[] */ - int busy; - int state; - int id; - uchar port; /* port number on connecting hub */ - ulong csp; - int ls; - int npt; - Endpt* ep[16]; /* active end points */ - Udev* ports; /* active ports, if hub */ - Udev* next; /* next device on this hub */ + TYPEMASK = (1<>SLOTSHIFT)&SLOTMASK) +#define CTLR(q) ((((ulong)(q).path)>>CTLRSHIFT)&CTLRMASK) +#define PATH(t, s, c) ((t)|((s)<io+(x)) -#define OUT(x, v) outs(ub->io+(x), (v)) - -static long usbints; -static long framenumber; -static long frameptr; -static long usbbogus; - -static Ctlr ubus; -static char Estalled[] = "usb endpoint stalled"; -static char Ebadusbmsg[] = "invalid parameters to USB ctl message"; - -static QLock usbstate; /* protects name space state */ -static Udev* usbdev[32]; -static struct { - Lock; - Endpt* f; -} activends; enum { - BCMdisable, - BCMenable, - BCMreset, + PMdisable, + PMenable, + PMreset, }; enum @@ -312,11 +94,11 @@ enum CMunstall, }; -static Cmdtab usbbusctlmsg[] = +static Cmdtab usbportmsg[] = { - BCMdisable, "disable", 2, - BCMenable, "enable", 2, - BCMreset, "reset", 2, + PMdisable, "disable", 2, + PMenable, "enable", 2, + PMreset, "reset", 2, }; static Cmdtab usbctlmsg[] = @@ -331,634 +113,60 @@ static Cmdtab usbctlmsg[] = CMunstall, "unstall", 2, }; -static long readusb(Endpt*, void*, long); -static long writeusb(Endpt*, void*, long, int); - -static TD * -alloctd(Ctlr *ub) -{ - TD *t; - - ilock(ub); - t = ub->freetd; - if(t == nil) - panic("alloctd"); /* TO DO */ - ub->freetd = t->next; - t->next = nil; - iunlock(ub); - t->ep = nil; - t->bp = nil; - t->status = 0; - t->link = Terminate; - t->buffer = 0; - t->flags = 0; - return t; -} - -static void -freetd(TD *t) -{ - Ctlr *ub; - - ub = &ubus; - t->ep = nil; - if(t->bp) - freeb(t->bp); - t->bp = nil; - ilock(ub); - t->buffer = 0xdeadbeef; - t->next = ub->freetd; - ub->freetd = t; - iunlock(ub); -} - -static void -dumpdata(Block *b, int n) -{ - int i; - - XPRINT("\tb %8.8lux[%d]: ", (ulong)b->rp, n); - if(n > 16) - n = 16; - for(i=0; irp[i]); - XPRINT("\n"); -} - -static void -dumptd(TD *t, int follow) -{ - int i, n; - char buf[20], *s; - TD *t0; - - t0 = t; - while(t){ - i = t->dev & 0xFF; - if(i == TokOUT || i == TokSETUP) - n = ((t->dev>>21) + 1) & 0x7FF; - else if((t->status & Active) == 0) - n = (t->status + 1) & 0x7FF; - else - n = 0; - s = buf; - if(t->status & Active) - *s++ = 'A'; - if(t->status & Stalled) - *s++ = 'S'; - if(t->status & DataBufferErr) - *s++ = 'D'; - if(t->status & Babbling) - *s++ = 'B'; - if(t->status & NAKed) - *s++ = 'N'; - if(t->status & CRCorTimeout) - *s++ = 'T'; - if(t->status & BitstuffErr) - *s++ = 'b'; - if(t->status & LowSpeed) - *s++ = 'L'; - *s = 0; - XPRINT("td %8.8lux: ", t); - XPRINT("l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n", - t->link, t->status, t->dev, t->buffer, t->bp?(ulong)t->bp->rp:0, t->flags); - XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n", - buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1); - if(debug && t->bp && (t->flags & CancelTD) == 0) - dumpdata(t->bp, n); - if(!follow || t->link & Terminate || t->link & IsQH) - break; - t = TFOL(t->link); - if(t == t0) - break; /* looped */ - } -} - -static TD * -alloctde(Endpt *e, int pid, int n) -{ - TD *t; - int tog, id; - - t = alloctd(&ubus); - id = (e->x<<7)|(e->dev->x&0x7F); - tog = 0; - if(e->data01 && pid != TokSETUP) - tog = IsDATA1; - t->ep = e; - t->status = ErrLimit3 | Active | IOC; /* or put IOC only on last? */ - if(e->dev->ls) - t->status |= LowSpeed; - t->dev = ((n-1)<<21) | ((id&0x7FF)<<8) | pid | tog; - return t; -} - -static QH * -allocqh(Ctlr *ub) -{ - QH *qh; - - ilock(ub); - qh = ub->freeqh; - if(qh == nil) - panic("allocqh"); /* TO DO */ - ub->freeqh = qh->next; - qh->next = nil; - iunlock(ub); - qh->head = Terminate; - qh->entries = Terminate; - qh->hlink = nil; - qh->first = nil; - qh->last = nil; - return qh; -} - -static void -freeqh(Ctlr *ub, QH *qh) -{ - ilock(ub); - qh->next = ub->freeqh; - ub->freeqh = qh; - iunlock(ub); -} - -static void -dumpqh(QH *q) +static struct { - int i; - QH *q0; - - q0 = q; - for(i = 0; q != nil && i < 10; i++){ - XPRINT("qh %8.8lux: %8.8lux %8.8lux\n", q, q->head, q->entries); - if((q->entries & Terminate) == 0) - dumptd(TFOL(q->entries), 1); - if(q->head & Terminate) - break; - if((q->head & IsQH) == 0){ - XPRINT("head:"); - dumptd(TFOL(q->head), 1); - break; - } - q = QFOL(q->head); - if(q == q0) - break; /* looped */ - } -} - -static void -queuetd(Ctlr *ub, QH *q, TD *t, int vf, char *why) -{ - TD *lt; - - for(lt = t; lt->next != nil; lt = lt->next) - lt->link = PADDR(lt->next) | vf; - lt->link = Terminate; - ilock(ub); - XPRINT("queuetd %s: t=%p lt=%p q=%p first=%p last=%p entries=%.8lux\n", - why, t, lt, q, q->first, q->last, q->entries); - if(q->first != nil){ - q->last->link = PADDR(t) | vf; - q->last->next = t; - }else{ - q->first = t; - q->entries = PADDR(t); - } - q->last = lt; - XPRINT(" t=%p q=%p first=%p last=%p entries=%.8lux\n", - t, q, q->first, q->last, q->entries); - dumpqh(q); - iunlock(ub); -} - -static void -cleantd(TD *t, int discard) -{ - Block *b; - int n, err; - - XPRINT("cleanTD: %8.8lux %8.8lux %8.8lux %8.8lux\n", - t->link, t->status, t->dev, t->buffer); - if(t->ep != nil && t->ep->debug) - dumptd(t, 0); - if(t->status & Active) - panic("cleantd Active"); - err = t->status & (AnyError&~NAKed); - /* TO DO: on t->status&AnyError, q->entries will not have advanced */ - if (err) - XPRINT("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", - t->link, t->status, t->dev, t->buffer); - switch(t->dev&0xFF){ - case TokIN: - if(discard || (t->flags & CancelTD) || t->ep == nil || t->ep->x!=0&&err){ - if(t->ep != nil){ - if(err != 0) - t->ep->err = err==Stalled? Estalled: Eio; - wakeup(&t->ep->rr); /* in case anyone cares */ - } - break; - } - b = t->bp; - n = (t->status + 1) & 0x7FF; - if(n > b->lim - b->wp) - n = 0; - b->wp += n; - if(Chatty) - dumpdata(b, n); - t->bp = nil; - t->ep->nbytes += n; - t->ep->nblocks++; - qpass(t->ep->rq, b); /* TO DO: flow control */ - wakeup(&t->ep->rr); /* TO DO */ - break; - case TokSETUP: - XPRINT("cleanTD: TokSETUP %lux\n", &t->ep); - /* don't really need to wakeup: subsequent IN or OUT gives status */ - if(t->ep != nil) { - wakeup(&t->ep->wr); /* TO DO */ - XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr); - } - break; - case TokOUT: - /* TO DO: mark it done somewhere */ - XPRINT("cleanTD: TokOut %lux\n", &t->ep); - if(t->ep != nil){ - if(t->bp){ - n = BLEN(t->bp); - t->ep->nbytes += n; - t->ep->nblocks++; - } - if(t->ep->x!=0 && err != 0) - t->ep->err = err==Stalled? Estalled: Eio; - if(--t->ep->ntd < 0) - panic("cleantd ntd"); - wakeup(&t->ep->wr); /* TO DO */ - XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr); - } - break; - } - freetd(t); -} + char* type; + int (*reset)(Usbhost*); +} usbtypes[MaxUsb+1]; -static void -cleanq(QH *q, int discard, int vf) -{ - TD *t, *tp; - Ctlr *ub; - - ub = &ubus; - ilock(ub); - tp = nil; - for(t = q->first; t != nil;){ - XPRINT("cleanq: %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux\n", - t->link, t->status, t->dev, t->buffer, t->flags, t->next); - if(t->status & Active){ - if(t->status & NAKed){ - t->status = (t->status & ~NAKed) | IOC; /* ensure interrupt next frame */ - tp = t; - t = t->next; - continue; - } - if(t->flags & CancelTD){ - XPRINT("cancelTD: %8.8lux\n", (ulong)t); - t->status = (t->status & ~Active) | IOC; /* ensure interrupt next frame */ - tp = t; - t = t->next; - continue; - } - tp = t; - t = t->next; - continue; - break; - } - t->status &= ~IOC; - if (tp == nil) { - q->first = t->next; - if(q->first != nil) - q->entries = PADDR(q->first); - else - q->entries = Terminate; - } else { - tp->next = t->next; - if (t->next != nil) - tp->link = PADDR(t->next) | vf; - else - tp->link = Terminate; - } - if (q->last == t) - q->last = tp; - iunlock(ub); - cleantd(t, discard); - ilock(ub); - if (tp) - t = tp->next; - else - t = q->first; - XPRINT("t = %8.8lux\n", t); - dumpqh(q); - } - if(q->first && q->entries != PADDR(q->first)){ - usbbogus++; - q->entries = PADDR(q->first); - } - iunlock(ub); -} - -static void -canceltds(Ctlr *ub, QH *q, Endpt *e) -{ - TD *t; - - if(q != nil){ - ilock(ub); - for(t = q->first; t != nil; t = t->next) - if(t->ep == e) - t->flags |= CancelTD; - iunlock(ub); - XPRINT("cancel:\n"); - dumpqh(q); - } -} - -static void -eptcancel(Endpt *e) -{ - Ctlr *ub; - - if(e == nil) - return; - ub = &ubus; - canceltds(ub, e->epq, e); - canceltds(ub, ub->ctlq, e); - canceltds(ub, ub->bulkq, e); -} - -static void -eptactivate(Endpt *e) -{ - ilock(&activends); - if(e->active == 0){ - XPRINT("activate 0x%p\n", e); - e->active = 1; - e->activef = activends.f; - activends.f = e; - } - iunlock(&activends); -} - -static void -eptdeactivate(Endpt *e) -{ - Endpt **l; - - /* could be O(1) but not worth it yet */ - ilock(&activends); - if(e->active){ - e->active = 0; - XPRINT("deactivate 0x%p\n", e); - for(l = &activends.f; *l != e; l = &(*l)->activef) - if(*l == nil){ - iunlock(&activends); - panic("usb eptdeactivate"); - } - *l = e->activef; - } - iunlock(&activends); -} - -static void -queueqh(QH *qh) { - QH *q; - Ctlr *ub; - - ub = &ubus; - // See if it's already queued - for (q = ub->recvq->next; q; q = q->hlink) - if (q == qh) - return; - if ((qh->hlink = ub->recvq->next) == nil) - qh->head = Terminate; - else - qh->head = PADDR(ub->recvq->next) | IsQH; - ub->recvq->next = qh; - ub->recvq->entries = PADDR(qh) | IsQH; -} - -static QH* -qxmit(Endpt *e, Block *b, int pid) -{ - TD *t; - int n, vf; - Ctlr *ub; - QH *qh; - - if(b != nil){ - n = BLEN(b); - t = alloctde(e, pid, n); - t->bp = b; - t->buffer = PADDR(b->rp); - }else - t = alloctde(e, pid, 0); - ub = &ubus; - ilock(ub); - e->ntd++; - iunlock(ub); - if(e->debug) pprint("QTD: %8.8lux n=%ld\n", t, b?BLEN(b): 0); - vf = 0; - if(e->x == 0){ - qh = ub->ctlq; - vf = 0; - }else if((qh = e->epq) == nil || e->mode != OWRITE){ - qh = ub->bulkq; - vf = Vf; - } - queuetd(ub, qh, t, vf, "qxmit"); - return qh; -} - -static QH* -qrcv(Endpt *e) -{ - TD *t; - Block *b; - Ctlr *ub; - QH *qh; - int vf; - - t = alloctde(e, TokIN, e->maxpkt); - b = allocb(e->maxpkt); - t->bp = b; - t->buffer = PADDR(b->wp); - ub = &ubus; - vf = 0; - if(e->x == 0){ - qh = ub->ctlq; - }else if((qh = e->epq) == nil || e->mode != OREAD){ - qh = ub->bulkq; - vf = Vf; - } - queuetd(ub, qh, t, vf, "qrcv"); - return qh; -} - -static Block * -usbreq(int type, int req, int value, int offset, int count) -{ - Block *b; - - b = allocb(8); - b->wp[0] = type; - b->wp[1] = req; - PUT2(b->wp+2, value); - PUT2(b->wp+4, offset); - PUT2(b->wp+6, count); - b->wp += 8; - return b; -} - -/* - * return smallest power of 2 >= n - */ -static int -flog2(int n) +void +addusbtype(char* t, int (*r)(Usbhost*)) { - int i; + static int ntype; - for(i=0; (1<frameld[i] + load > worst) - worst = ub->frameld[i] + load; - } - if (worst < best){ - best = worst; - q = d; - } - } - return q; -} - -static int -schedendpt(Endpt *e) -{ - Ctlr *ub; - TD *td; - uchar *bp; - int i, id, ix, size, frnum; - - if(!e->iso || e->sched >= 0) - return 0; - ub = &ubus; - - if (e->active){ - return -1; - } - e->off = 0; - e->sched = usbsched(ub, e->pollms, e->maxpkt); - if(e->sched < 0) - return -1; - - 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); - e->bp0 = (uchar *)(((ulong)e->bpalloc + 0xf) & ~0xf); - frnum = (IN(Frnum) + 1) & 0x3ff; - frnum = (frnum & ~(e->pollms - 1)) + e->sched; - e->xtd = &e->td0[(frnum+8)&0x3ff]; /* Next td to finish */ - e->etd = nil; - e->remain = 0; - e->nbytes = 0; - td = e->td0; - for(i = e->sched; i < NFRAME; i += e->pollms){ - bp = e->bp0 + e->maxpkt*i/e->pollms; - td->buffer = PADDR(bp); - td->ep = e; - 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) & 0x3ff; - td = &e->td0[ix]; - - id = (e->x<<7)|(e->dev->x&0x7F); - if (e->mode == OREAD) - /* enable receive on this entry */ - td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | TokIN; - else{ - size = (e->hz + e->remain)*e->pollms/1000; - e->remain = (e->hz + e->remain)*e->pollms%1000; - size *= e->samplesz; - td->dev = ((size-1)<<21) | ((id&0x7FF)<<8) | TokOUT; - } - td->status = ErrLimit1 | Active | IsoSelect | IOC; - td->link = ub->frames[ix]; - td->flags |= IsoClean; - ub->frames[ix] = PADDR(td); - } - return 0; + if(s < 0 || s > nelem(uh->dev)) + return nil; + return uh->dev[s]; } -static void -unschedendpt(Endpt *e) +static Udev* +usbdevice(Chan *c) { - Ctlr *ub; - TD *td; - ulong *addr; - int q; - - ub = &ubus; - if(!e->iso || e->sched < 0) - return; + int bus; + Udev *d; + Usbhost *uh; - 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; + bus = CTLR(c->qid); + if(bus > nelem(usbhost) || (uh = usbhost[bus]) == nil) { + error(Egreg); + return nil; /* for compiler */ } - free(e->tdalloc); - free(e->bpalloc); - e->tdalloc = nil; - e->bpalloc = nil; - e->etd = nil; - e->td0 = nil; - e->sched = -1; + d = usbdeviceofslot(uh, SLOT(c->qid)); + if(d == nil || d->id != c->qid.vers || d->state == Disabled) + error(Ehungup); + return d; } static Endpt * devendpt(Udev *d, int id, int add) { + Usbhost *uh; Endpt *e, **p; - Ctlr *ub; - ub = &ubus; p = &d->ep[id&0xF]; lock(d); - if((e = *p) != nil){ + e = *p; + if(e != nil){ incref(e); XPRINT("incref(0x%p) in devendpt, new value %ld\n", e, e->ref); unlock(d); @@ -967,6 +175,7 @@ devendpt(Udev *d, int id, int add) unlock(d); if(!add) return nil; + e = mallocz(sizeof(*e), 1); e->ref = 1; e->x = id&0xF; @@ -976,563 +185,136 @@ devendpt(Udev *d, int id, int add) e->nbuf = 1; e->dev = d; e->active = 0; - e->epq = allocqh(ub); - if(e->epq == nil) - panic("devendpt"); + + uh = d->uh; + uh->epalloc(uh, e); lock(d); if(*p != nil){ incref(*p); XPRINT("incref(0x%p) in devendpt, new value %ld\n", *p, (*p)->ref); unlock(d); + uh->epfree(uh, e); free(e); return *p; } *p = e; unlock(d); - e->rq = qopen(8*1024, 0, nil, e); - e->wq = qopen(8*1024, 0, nil, e); - return e; -} - -static int -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; - if(e->epq != nil) - freeqh(&ubus, e->epq); - free(e); - return 1; - } - if (e) XPRINT("decref(0x%p) in freept, new value %ld\n", e, e->ref); - return 0; -} - -static void -usbdevreset(Udev *d) -{ - d->state = Disabled; - if(Class(d->csp) == Hubclass) - for(d = d->ports; d != nil; d = d->next) - usbdevreset(d); -} - -static void -freedev(Udev *d, int ept) -{ - int i; - Endpt *e; - - if(d != nil && decref(d) == 0){ - XPRINT("freedev 0x%p, 0\n", d); - for(i=0; iep); i++) - freept(d->ep[i]); - if(d->x >= 0) - usbdev[d->x] = nil; - free(d); - } else { - if(ept >= 0 && ept < nelem(d->ep)){ - e = d->ep[ept]; - XPRINT("freedev, freept 0x%p\n", e); - if(e != nil){ - eptdeactivate(e); - unschedendpt(e); - } - } - } -} - -static void -hubportreset(Udev *h, int p) -{ - USED(h, p); - /* reset state of each attached device? */ -} - -static int ioports[] = {-1, Portsc0, Portsc1}; - -static void -portreset(int port) -{ - Ctlr *ub; - int i, p; - - /* should check that device not being configured on other port? */ - p = ioports[port]; - ub = &ubus; - qlock(&ub->resetl); - if(waserror()){ - qunlock(&ub->resetl); - nexterror(); - } - XPRINT("r: %x\n", IN(p)); - ilock(ub); - OUT(p, PortReset); - delay(12); /* BUG */ - XPRINT("r2: %x\n", IN(p)); - OUT(p, IN(p) & ~PortReset); - XPRINT("r3: %x\n", IN(p)); - OUT(p, IN(p) | PortEnable); - microdelay(64); - for(i=0; i<1000 && (IN(p) & PortEnable) == 0; i++) - ; - XPRINT("r': %x %d\n", IN(p), i); - OUT(p, (IN(p) & ~PortReset)|PortEnable); - iunlock(ub); - hubportreset(nil, port); - poperror(); - qunlock(&ub->resetl); -} - -static void -portenable(int port, int on) -{ - Ctlr *ub; - int w, p; - - /* should check that device not being configured on other port? */ - p = ioports[port]; - ub = &ubus; - qlock(&ub->resetl); - if(waserror()){ - qunlock(&ub->resetl); - nexterror(); - } - ilock(ub); - w = IN(p); - if(on) - w |= PortEnable; - else - w &= ~PortEnable; - OUT(p, w); - microdelay(64); - iunlock(ub); - XPRINT("e: %x\n", IN(p)); - if(!on) - hubportreset(nil, port); - poperror(); - qunlock(&ub->resetl); -} - -static int -portinfo(Ctlr *ub, int *p0, int *p1) -{ - int m, v; - - ilock(ub); - m = 0; - if((v = IN(Portsc0)) & PortChange){ - OUT(Portsc0, v); - m |= 1<<0; - } - *p0 = v; - if((v = IN(Portsc1)) & PortChange){ - OUT(Portsc1, v); - m |= 1<<1; - } - *p1 = v; - iunlock(ub); - return m; -} - -static void -cleaniso(Endpt *e, int frnum) -{ - TD *td; - int id, n, i; - uchar *bp; - - td = e->xtd; - if (td->status & Active) - return; - id = (e->x<<7)|(e->dev->x&0x7F); - do { - if (td->status & AnyError) - iprint("usbisoerror 0x%lux\n", td->status); - n = (td->status + 1) & 0x3ff; - e->nbytes += n; - if ((td->flags & IsoClean) == 0) - e->nblocks++; - if (e->mode == OREAD){ - e->buffered += n; - e->poffset += (td->status + 1) & 0x3ff; - td->offset = e->poffset; - td->dev = ((e->maxpkt -1)<<21) | ((id&0x7FF)<<8) | TokIN; - e->toffset = td->offset; - }else{ - if ((td->flags & IsoClean) == 0){ - e->buffered -= n; - if (e->buffered < 0){ - print("e->buffered %d?\n", e->buffered); - e->buffered = 0; - } - } - e->toffset = td->offset; - n = (e->hz + e->remain)*e->pollms/1000; - e->remain = (e->hz + e->remain)*e->pollms%1000; - n *= e->samplesz; - td->dev = ((n -1)<<21) | ((id&0x7FF)<<8) | TokOUT; - td->offset = e->poffset; - e->poffset += n; - } - td = td->next; - if (e->xtd == td){ - XPRINT("@"); - break; - } - } while ((td->status & Active) == 0); - e->time = todget(nil); - e->xtd = td; - for (n = 2; n < 4; n++){ - i = ((frnum + n)&0x3ff); - td = e->td0 + i; - bp = e->bp0 + e->maxpkt*i/e->pollms; - if (td->status & Active) - continue; - - if (e->mode == OWRITE){ - if (td == e->etd) { - XPRINT("*"); - memset(bp+e->off, 0, e->maxpkt-e->off); - if (e->off == 0) - td->flags |= IsoClean; - else - e->buffered += (((td->dev>>21) +1) & 0x3ff) - e->off; - e->etd = nil; - }else if ((td->flags & IsoClean) == 0){ - XPRINT("-"); - memset(bp, 0, e->maxpkt); - td->flags |= IsoClean; - } - } else { - /* Unread bytes are now lost */ - e->buffered -= (td->status + 1) & 0x3ff; - } - td->status = ErrLimit1 | Active | IsoSelect | IOC; - } - wakeup(&e->wr); -} - -static void -usbinterrupt(Ureg*, void *a) -{ - Ctlr *ub; - Endpt *e; - int s, frnum; - QH *q; - - ub = a; - s = IN(Status); - - frameptr = inl(ub->io+Flbaseadd); - framenumber = IN(Frnum) & 0x3ff; - OUT(Status, s); - if ((s & 0x1f) == 0) - return; - usbints++; - frnum = IN(Frnum) & 0x3ff; - 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)); - } - - ilock(&activends); - for(e = activends.f; e != nil; e = e->activef){ - if(!e->iso && e->epq != nil) { - XPRINT("cleanq(e->epq, 0, 0)\n"); - cleanq(e->epq, 0, 0); - } - if(e->iso) { - XPRINT("cleaniso(e)\n"); - cleaniso(e, frnum); - } - } - iunlock(&activends); - XPRINT("cleanq(ub->ctlq, 0, 0)\n"); - cleanq(ub->ctlq, 0, 0); - XPRINT("cleanq(ub->bulkq, 0, Vf)\n"); - cleanq(ub->bulkq, 0, Vf); - XPRINT("clean recvq\n"); - for (q = ub->recvq->next; q; q = q->hlink) { - XPRINT("cleanq(q, 0, Vf)\n"); - cleanq(q, 0, Vf); - } -} - -enum -{ - Qtopdir = 0, - Q2nd, - Qbusctl, - Qnew, - Qport, - Q3rd, - Qctl, - Qsetup, - Qdebug, - Qstatus, - Qep0, - /* other endpoint files */ -}; - -/* - * Qid path is: - * 8 bits of file type (qids above) - * 10 bits of slot number +1; 0 means not attached to device - */ -#define QSHIFT 8 /* location in qid of device # */ -#define QMASK ((1<>QSHIFT) - -static Dirtab usbdir2[] = { - "new", {Qnew}, 0, 0666, - "ctl", {Qbusctl}, 0, 0666, - "port", {Qport}, 0, 0444, -}; - -static Dirtab usbdir3[]={ - "ctl", {Qctl}, 0, 0666, - "setup", {Qsetup}, 0, 0666, - "status", {Qstatus}, 0, 0444, - "debug", {Qdebug}, 0, 0666, - /* epNdata names are generated on demand */ -}; - -static Udev * -usbdeviceofpath(ulong path) -{ - int s; - - s = DEVPATH(path); - if(s == 0) - return nil; - return usbdev[s-1]; + e->rq = qopen(8*1024, 0, nil, e); + e->wq = qopen(8*1024, 0, nil, e); + return e; } -static Udev * -usbdevice(Chan *c) +static void +freept(Endpt *e) { - Udev *d; + Usbhost *uh; - d = usbdeviceofpath(c->qid.path); - if(d == nil || d->id != c->qid.vers || d->state == Disabled) - error(Ehungup); - return d; + if(e != nil && decref(e) == 0){ + XPRINT("freept(%d,%d)\n", e->dev->x, e->x); + uh = e->dev->uh; + uh->epclose(uh, e); + e->dev->ep[e->x] = nil; + uh->epfree(uh, e); + free(e); + } } -static Udev * -usbnewdevice(void) +static Udev* +usbnewdevice(Usbhost *uh) { + int i; Udev *d; Endpt *e; - int i; d = nil; - qlock(&usbstate); + qlock(uh); if(waserror()){ - qunlock(&usbstate); + qunlock(uh); nexterror(); } - for(i=0; idev); i++) + if(uh->dev[i] == nil){ + uh->idgen++; d = mallocz(sizeof(*d), 1); + d->uh = uh; d->ref = 1; d->x = i; - d->id = (ubus.idgen << 8) | i; + d->id = (uh->idgen << 8) | i; d->state = Enabled; XPRINT("calling devendpt in usbnewdevice\n"); e = devendpt(d, 0, 1); /* always provide control endpoint 0 */ e->mode = ORDWR; e->iso = 0; e->sched = -1; - usbdev[i] = d; + uh->dev[i] = d; break; } poperror(); - qunlock(&usbstate); + qunlock(uh); return d; } static void -usbreset(void) +freedev(Udev *d, int ept) { - Pcidev *cfg; int i; - ulong port; - TD *t; - Ctlr *ub; - ISAConf isa; - - if(isaconfig("usb", 0, &isa) == 0) { - XPRINT("usb not in plan9.ini\n"); - return; - } - ub = &ubus; - cfg = nil; - while(cfg = pcimatch(cfg, 0, 0)){ - /* - * Look for devices with the correct class and - * sub-class code and known device and vendor ID. - */ - if(cfg->ccrb != 0x0C || cfg->ccru != 0x03) - continue; - if(cfg->did == 0x2482 || cfg->did == 0x2487) - continue; -// switch(cfg->vid | cfg->did<<16){ -// default: -// continue; -// case 0x8086 | 0x7112<<16: /* 82371[AE]B (PIIX4[E]) */ -// case 0x8086 | 0x719A<<16: /* 82443MX */ -// case 0x0586 | 0x1106<<16: /* VIA 82C586 */ -// break; -// } - if((cfg->mem[4].bar & ~0x0F) != 0) - break; - } - if(cfg == nil) { - DPRINT("No USB device found\n"); - return; - } - port = cfg->mem[4].bar & ~0x0F; - - DPRINT("USB: %x/%x port 0x%lux size 0x%x irq %d\n", - cfg->vid, cfg->did, port, cfg->mem[4].size, cfg->intl); - - i = inb(port+SOFMod); -if(0){ - OUT(Cmd, 4); /* global reset */ - delay(15); - OUT(Cmd, 0); /* end reset */ - delay(4); - } - outb(port+SOFMod, i); - /* - * Interrupt handler. - * Bail out if no IRQ assigned by the BIOS. - */ - if(cfg->intl == 0xFF || cfg->intl == 0) - return; - intrenable(cfg->intl, usbinterrupt, ub, cfg->tbdf, "usb"); - - ub->io = port; - ub->tdpool = (TD*)(((ulong)xalloc(128*sizeof(TD) + 0x10) + 0xf) & ~0xf); - for(i=128; --i>=0;){ - ub->tdpool[i].next = ub->freetd; - ub->freetd = &ub->tdpool[i]; - } - ub->qhpool = (QH*)(((ulong)xalloc(32*sizeof(QH) + 0x10) + 0xf) & ~0xf); - for(i=32; --i>=0;){ - ub->qhpool[i].next = ub->freeqh; - ub->freeqh = &ub->qhpool[i]; - } - - /* - * the last entries of the periodic (interrupt & isochronous) scheduling TD entries - * point to the control queue and the bandwidth sop for bulk traffic. - * this is looped following the instructions in PIIX4 errata 29773804.pdf: - * a QH links to a looped but inactive TD as its sole entry, - * with its head entry leading on to the bulk traffic, the last QH of which - * links back to the empty QH. - */ - ub->ctlq = allocqh(ub); - ub->bwsop = allocqh(ub); - ub->bulkq = allocqh(ub); - ub->recvq = allocqh(ub); - t = alloctd(ub); /* inactive TD, looped */ - t->link = PADDR(t); - - ub->ctlq->head = PADDR(ub->bulkq) | IsQH; - ub->bulkq->head = PADDR(ub->recvq) | IsQH; - ub->recvq->head = PADDR(ub->bwsop) | IsQH; - ub->bwsop->head = Terminate; /* loop back */ -// ub->bwsop->head = PADDR(ub->bwsop) | IsQH; /* loop back */ - ub->bwsop->entries = PADDR(t); - - XPRINT("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n", - IN(Cmd), IN(Status), IN(Usbintr), inb(port+Frnum)); - XPRINT("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n", - IN(Flbaseadd), inb(port+SOFMod), IN(Portsc0), IN(Portsc1)); - OUT(Cmd, 0); /* stop */ - ub->frames = (ulong*)(((ulong)xalloc(2*FRAMESIZE) + FRAMEMASK) & ~FRAMEMASK); - ub->frameld = xallocz(FRAMESIZE, 1); - - for (i = 0; i < NFRAME; i++) - ub->frames[i] = PADDR(ub->ctlq) | IsQH; - - outl(port+Flbaseadd, PADDR(ub->frames)); - OUT(Frnum, 0); - OUT(Usbintr, 0xF); /* enable all interrupts */ - XPRINT("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(port+SOFMod)); - XPRINT("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1)); -} - -void -usbinit(void) -{ - Udev *d; - - if(ubus.io != 0 && usbdev[0] == nil){ - d = usbnewdevice(); /* reserve device 0 for configuration */ - incref(d); - d->state = Attached; - } -} - -Chan * -usbattach(char *spec) -{ - Ctlr *ub; + Endpt *e; + Usbhost *uh; - ub = &ubus; - if(ub->io == 0) { - XPRINT("usbattach failed\n"); - error(Enodev); - } - if((IN(Cmd)&1)==0 || *spec) - OUT(Cmd, 1); /* run */ -// pprint("at: c=%x s=%x c0=%x\n", IN(Cmd), IN(Status), IN(Portsc0)); - return devattach('U', spec); + uh = d->uh; + if(decref(d) == 0){ + XPRINT("freedev 0x%p, 0\n", d); + for(i=0; iep); i++) + freept(d->ep[i]); + if(d->x >= 0) + uh->dev[d->x] = nil; + free(d); + } else { + if(ept >= 0 && ept < nelem(d->ep)){ + e = d->ep[ept]; + XPRINT("freedev, freept 0x%p\n", e); + if(e != nil) + uh->epclose(uh, e); + } + } } static int usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) { - int t; Qid q; - ulong path; Udev *d; - Dirtab *tab; Endpt *e; - vlong len; + Dirtab *tab; + Usbhost *uh; + int t, bus, slot, perm; /* - * Top level directory contains the name of the device. + * Top level directory contains the controller names. */ - if(QID(c->qid) == Qtopdir){ + if(c->qid.path == Qtopdir){ if(s == DEVDOTDOT){ mkqid(&q, Qtopdir, 0, QTDIR); devdir(c, q, "#U", 0, eve, 0555, dp); return 1; } - if(s == 0){ - mkqid(&q, Q2nd, 0, QTDIR); - devdir(c, q, "usb", 0, eve, 0555, dp); - return 1; - } - return -1; + if(s >= nelem(usbhost) || usbhost[s] == nil) + return -1; + mkqid(&q, PATH(Q2nd, 0, s), 0, QTDIR); + snprint(up->genbuf, sizeof up->genbuf, "usb%d", s); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); + return 1; } + bus = CTLR(c->qid); + if(bus >= nelem(usbhost) || (uh = usbhost[bus]) == nil) + return -1; /* - * Second level contains "new" plus all the clients. + * Second level contains "new", "port", and a numbered + * directory for each enumerated device on the bus. */ - t = QID(c->qid); + t = TYPE(c->qid); if(t < Q3rd){ if(s == DEVDOTDOT){ mkqid(&q, Qtopdir, 0, QTDIR); @@ -1540,17 +322,21 @@ usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) return 1; } if(s < nelem(usbdir2)){ + d = uh->dev[0]; + if(d == nil) + return -1; tab = &usbdir2[s]; - devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); + mkqid(&q, PATH(tab->qid.path, 0, bus), d->id, QTFILE); + devdir(c, q, tab->name, tab->length, eve, tab->perm, dp); return 1; } s -= nelem(usbdir2); - if(s >= 0 && s < nelem(usbdev)){ - d = usbdev[s]; + if(s >= 0 && s < nelem(uh->dev)) { + d = uh->dev[s]; if(d == nil) - return -1; + return 0; sprint(up->genbuf, "%d", s); - mkqid(&q, ((s+1)<id, QTDIR); + mkqid(&q, PATH(Q3rd, s, bus), d->id, QTDIR); devdir(c, q, up->genbuf, 0, eve, 0555, dp); return 1; } @@ -1560,36 +346,122 @@ usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) /* * Third level. */ - len = 0; - path = c->qid.path & ~QMASK; /* slot component */ - if(s == DEVDOTDOT){ - mkqid(&q, Q2nd, c->qid.vers, QTDIR); - devdir(c, q, "usb", 0, eve, 0555, dp); + slot = SLOT(c->qid); + if(s == DEVDOTDOT) { + mkqid(&q, PATH(Q2nd, 0, bus), c->qid.vers, QTDIR); + snprint(up->genbuf, sizeof up->genbuf, "usb%d", bus); + devdir(c, q, up->genbuf, 0, eve, 0555, dp); return 1; } - if(s < nelem(usbdir3)){ - Dirtab *tab = &usbdir3[s]; - mkqid(&q, path | tab->qid.path, c->qid.vers, QTFILE); + if(s < nelem(usbdir3)) { + tab = &usbdir3[s]; + mkqid(&q, PATH(tab->qid.path, slot, bus), c->qid.vers, QTFILE); devdir(c, q, tab->name, tab->length, eve, tab->perm, dp); return 1; } + s -= nelem(usbdir3); /* active endpoints */ - d = usbdeviceofpath(path); - if(d == nil) - return -1; - s -= nelem(usbdir3); - if(s < 0 || s >= nelem(d->ep)) + d = usbdeviceofslot(uh, slot); + if(d == nil || s >= nelem(d->ep)) return -1; if(s == 0 || (e = d->ep[s]) == nil) /* ep0data is called "setup" */ return 0; sprint(up->genbuf, "ep%ddata", s); - mkqid(&q, path | (Qep0+s), c->qid.vers, QTFILE); - len = e->buffered; - devdir(c, q, up->genbuf, len, eve, e->mode==OREAD? 0444: e->mode==OWRITE? 0222: 0666, dp); + mkqid(&q, PATH(Qep0+s, slot, bus), c->qid.vers, QTFILE); + switch(e->mode) { + case OREAD: + perm = 0444; + break; + case OWRITE: + perm = 0222; + break; + default: + perm = 0666; + break; + } + devdir(c, q, up->genbuf, e->buffered, eve, perm, dp); return 1; } +static void +usbreset(void) +{ + int n, ctlrno; + Usbhost *uh; + char name[64], buf[128], *p, *ebuf, *type; + + uh = nil; + for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){ + if(uh == nil) + uh = malloc(sizeof(Usbhost)); + memset(uh, 0, sizeof(Usbhost)); + uh->tbdf = BUSUNKNOWN; + if(isaconfig("usb", ctlrno, uh) == 0) + continue; + for(n = 0; usbtypes[n].type; n++){ + type = uh->type; + if(type == nil || *type == '\0') + type = "uhci"; + if(cistrcmp(usbtypes[n].type, type)) + continue; + if(usbtypes[n].reset(uh)) + break; + + /* + * IRQ2 doesn't really exist, it's used to gang the interrupt + * controllers together. A device set to IRQ2 will appear on + * the second interrupt controller as IRQ9. + */ + if(uh->irq == 2) + uh->irq = 9; + snprint(name, sizeof(name), "usb%d", ctlrno); + intrenable(uh->irq, uh->interrupt, uh, uh->tbdf, name); + + ebuf = buf + sizeof buf; + p = seprint(buf, ebuf, "#U/usb%d: %s: port 0x%luX irq %lud", ctlrno, type, uh->port, uh->irq); + if(uh->mem) + p = seprint(p, ebuf, " addr 0x%luX", PADDR(uh->mem)); + if(uh->size) + seprint(p, ebuf, " size 0x%luX", uh->size); + print("%s\n", buf); + + usbhost[ctlrno] = uh; + uh = nil; + break; + } + } + if(uh != nil) + free(uh); +} + +void +usbinit(void) +{ + Udev *d; + int ctlrno; + Usbhost *uh; + + for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){ + uh = usbhost[ctlrno]; + if(uh == nil) + continue; + if(uh->init != 0) + uh->init(uh); + + /* reserve device for configuration */ + d = usbnewdevice(uh); + incref(d); + d->state = Attached; + } +} + +Chan * +usbattach(char *spec) +{ + return devattach('U', spec); +} + static Walkqid* usbwalk(Chan *c, Chan *nc, char **name, int nname) { @@ -1602,42 +474,47 @@ usbstat(Chan *c, uchar *db, int n) return devstat(c, db, n, nil, 0, usbgen); } -Chan * +Chan* usbopen(Chan *c, int omode) { Udev *d; - int f, s; + Endpt *e; + int f, s, type; + Usbhost *uh; if(c->qid.type == QTDIR) return devopen(c, omode, nil, 0, usbgen); f = 0; - if(QID(c->qid) == Qnew){ - d = usbnewdevice(); + type = TYPE(c->qid); + if(type == Qnew){ + d = usbdevice(c); + d = usbnewdevice(d->uh); XPRINT("usbopen, new dev 0x%p\n", d); if(d == nil) { XPRINT("usbopen failed (usbnewdevice)\n"); error(Enodev); } - c->qid.path = Qctl|((d->x+1)<qid.vers = d->id; + type = Qctl; + mkqid(&c->qid, PATH(type, d->x, CTLR(c->qid)), d->id, QTFILE); f = 1; } - if(QID(c->qid) < Q3rd){ + if(type < Q3rd){ XPRINT("usbopen, devopen < Q3rd\n"); return devopen(c, omode, nil, 0, usbgen); } - qlock(&usbstate); + d = usbdevice(c); + uh = d->uh; + qlock(uh); if(waserror()){ - qunlock(&usbstate); + qunlock(uh); nexterror(); } - switch(QID(c->qid)){ + switch(type){ case Qctl: - d = usbdevice(c); if(0&&d->busy) error(Einuse); d->busy = 1; @@ -1647,437 +524,142 @@ usbopen(Chan *c, int omode) break; default: - d = usbdevice(c); - s = QID(c->qid) - Qep0; + s = type - Qep0; XPRINT("usbopen, default 0x%p, %d\n", d, s); if(s >= 0 && s < nelem(d->ep)){ - Endpt *e; if((e = d->ep[s]) == nil) { XPRINT("usbopen failed (endpoint)\n"); error(Enodev); } XPRINT("usbopen: dev 0x%p, ept 0x%p\n", d, e); - if(schedendpt(e) < 0){ - if (e->active) - error("can't schedule USB endpoint, active"); - else - error("can't schedule USB endpoint"); - } + uh->epopen(uh, e); e->foffset = 0; e->toffset = 0; e->poffset = 0; e->buffered = 0; - eptactivate(e); } incref(d); break; } poperror(); - qunlock(&usbstate); + qunlock(uh); c->mode = openmode(omode); c->flag |= COPEN; c->offset = 0; return c; } -void -usbcreate(Chan *c, char *name, int omode, ulong perm) -{ - USED(c, name, omode, perm); - error(Eperm); -} - -void -usbremove(Chan*) -{ - error(Eperm); -} - void usbclose(Chan *c) { Udev *d; - int ept; + int ept, type; + Usbhost *uh; - if(c->qid.type == QTDIR || QID(c->qid) < Q3rd) + type = TYPE(c->qid); + if(c->qid.type == QTDIR || type < Q3rd) return; - qlock(&usbstate); - if(waserror()){ /* usbdevice can error */ - qunlock(&usbstate); + d = usbdevice(c); + uh = d->uh; + qlock(uh); + if(waserror()){ + qunlock(uh); nexterror(); } - d = usbdevice(c); - if(QID(c->qid) == Qctl) + if(type == Qctl) d->busy = 0; XPRINT("usbclose: dev 0x%p\n", d); if(c->flag & COPEN){ - ept = (QID(c->qid) != Qctl) ? QID(c->qid) - Qep0 : -1; + ept = (type != Qctl) ? type - Qep0 : -1; XPRINT("usbclose: freedev 0x%p\n", d); freedev(d, ept); } poperror(); - qunlock(&usbstate); -} - -static int -eptinput(void *arg) -{ - Endpt *e; - - e = arg; - return e->eof || e->err || qcanread(e->rq); -} - -static int -isoready(void *arg) -{ - Endpt *e; - - e = arg; - return e->etd == nil || (e->etd != e->xtd && (e->etd->status & Active) == 0); -} - -static long -isoio(Endpt *e, void *a, long n, ulong offset, int w) -{ - int i, frnum; - volatile int isolock; - uchar *p, *q, *bp; - Ctlr *ub; - TD *td; - - qlock(&e->rlock); - isolock = 0; - if(waserror()){ - if (isolock){ - isolock = 0; - iunlock(&activends); - } - qunlock(&e->rlock); - eptcancel(e); - nexterror(); - } - p = a; - ub = &ubus; - if (offset != 0 && offset != e->foffset){ - iprint("offset %lud, foffset %lud\n", offset, e->foffset); - /* Seek to a specific position */ - frnum = (IN(Frnum) + 8) & 0x3ff; - td = e->td0 +frnum; - if (offset < td->offset) - error("ancient history"); - while (offset > e->toffset){ - tsleep(&e->wr, return0, 0, 500); - } - while (offset >= td->offset + ((w?(td->dev >> 21):td->status) + 1) & 0x7ff){ - td = td->next; - if (td == e->xtd) - iprint("trouble\n"); - } - ilock(&activends); - isolock = 1; - e->off = td->offset - offset; - if (e->off >= e->maxpkt){ - iprint("I can't program: %d\n", e->off); - e->off = 0; - } - e->etd = td; - e->foffset = offset; - } - do { - if (isolock == 0){ - ilock(&activends); - isolock = 1; - } - td = e->etd; - if (td == nil || e->off == 0){ - if (td == nil){ - XPRINT("0"); - if (w){ - frnum = (IN(Frnum) + 1) & 0x3ff; - td = e->td0 + frnum; - while(td->status & Active) - td = td->next; - }else{ - frnum = (IN(Frnum) - 4) & 0x3ff; - td = e->td0 + frnum; - while(td->next != e->xtd) - td = td->next; - } - e->etd = td; - e->off = 0; - }else{ - /* New td, make sure it's ready */ - isolock = 0; - iunlock(&activends); - while (isoready(e) == 0){ - sleep(&e->wr, isoready, e); - } - ilock(&activends); - isolock = 1; - if (e->etd == nil){ - XPRINT("!"); - continue; - } - } - if (w) - e->psize = ((td->dev >> 21) + 1) & 0x7ff; - else - e->psize = (e->etd->status + 1) & 0x7ff; - if (e->psize > e->maxpkt) - panic("packet size > maximum"); - } - if((i = n) >= e->psize) - i = e->psize; - if (w) - e->buffered += i; - else{ - e->buffered -= i; - if (e->buffered < 0) - e->buffered = 0; - } - isolock = 0; - iunlock(&activends); - td->flags &= ~IsoClean; - bp = e->bp0 + (td - e->td0) * e->maxpkt / e->pollms; - q = bp + e->off; - if (w){ - memmove(q, p, i); - }else{ - memmove(p, q, i); - } - p += i; - n -= i; - e->off += i; - e->psize -= i; - if (e->psize){ - if (n != 0) - panic("usb iso: can't happen"); - break; - } - if(w) - td->offset = offset + (p-(uchar*)a) - (((td->dev >> 21) + 1) & 0x7ff); - td->status = ErrLimit3 | Active | IsoSelect | IOC; - e->etd = td->next; - e->off = 0; - } while(n > 0); - n = p-(uchar*)a; - e->foffset += n; - poperror(); - if (isolock) - iunlock(&activends); - qunlock(&e->rlock); - return n; + qunlock(uh); } -static long -readusb(Endpt *e, void *a, long n) +static char * +epstatus(char *s, char *se, Endpt *e, int i) { - Block *b; - uchar *p; - int l, i; - - XPRINT("qlock(%p)\n", &e->rlock); - qlock(&e->rlock); - XPRINT("got qlock(%p)\n", &e->rlock); - if(waserror()){ - qunlock(&e->rlock); - eptcancel(e); - nexterror(); - } - p = a; - do { - if(e->eof) { - XPRINT("e->eof\n"); - break; - } - if(e->err) - error(e->err); - qrcv(e); - if(!e->iso) - e->data01 ^= 1; - sleep(&e->rr, eptinput, e); - if(e->err) - error(e->err); - b = qget(e->rq); /* TO DO */ - if(b == nil) { - XPRINT("b == nil\n"); - break; - } - if(waserror()){ - freeb(b); - nexterror(); - } - l = BLEN(b); - if((i = l) > n) - i = n; - if(i > 0){ - memmove(p, b->rp, i); - p += i; - } - poperror(); - freeb(b); - n -= i; - if (l != e->maxpkt) - break; - } while (n > 0); - poperror(); - qunlock(&e->rlock); - return p-(uchar*)a; -} + char *p; -int -epstatus(char *s, int n, Endpt *e, int i) -{ - int l; - - l = 0; - l += snprint(s+l, n-l, "%2d %#6.6lux %10lud bytes %10lud blocks\n", - i, e->csp, e->nbytes, e->nblocks); - if (e->iso){ - l += snprint(s+l, n-l, "bufsize %6d buffered %6d", - e->maxpkt, e->buffered); + p = seprint(s, se, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks); + if(e->iso){ + p = seprint(p, se, "bufsize %6d buffered %6d", e->maxpkt, e->buffered); if(e->toffset) - l += snprint(s+l, n-l, " offset %10lud time %19lld\n", - e->toffset, e->time); - if (n-l > 0) - s[l++] = '\n'; - s[l] = '\0'; + p = seprint(p, se, " offset %10lud time %19lld\n", e->toffset, e->time); + p = seprint(p, se, "\n"); } - return l; + return p; } long usbread(Chan *c, void *a, long n, vlong offset) { - Endpt *e; + int t, i; Udev *d; - char buf[48], *s; - int t, w0, w1, ps, l, i; + Endpt *e; + Usbhost *uh; + char *s, *se, *p; if(c->qid.type == QTDIR) return devdirread(c, a, n, nil, 0, usbgen); - t = QID(c->qid); - switch(t){ - case Qbusctl: - snprint(buf, sizeof(buf), "%11d %11d ", 0, 0); - return readstr(offset, a, n, buf); - - case Qport: - ps = portinfo(&ubus, &w0, &w1); - snprint(buf, sizeof(buf), "0x%ux 0x%ux 0x%ux ", ps, w0, w1); - return readstr(offset, a, n, buf); - - case Qctl: - d = usbdevice(c); - sprint(buf, "%11d %11d ", d->x, d->id); - return readstr(offset, a, n, buf); + d = usbdevice(c); + uh = d->uh; + t = TYPE(c->qid); - case Qsetup: /* endpoint 0 */ - d = usbdevice(c); - if((e = d->ep[0]) == nil) - error(Eio); /* can't happen */ - e->data01 = 1; - n = readusb(e, a, n); - if(e->setin){ - e->setin = 0; + if(t >= Qep0) { + t -= Qep0; + if(t >= nelem(d->ep)) + error(Eio); + e = d->ep[t]; + if(e == nil || e->mode == OWRITE) + error(Egreg); + if(t == 0) { + if(e->iso) + error(Egreg); e->data01 = 1; - writeusb(e, "", 0, TokOUT); + n = uh->read(uh, e, a, n, 0LL); + if(e->setin){ + e->setin = 0; + e->data01 = 1; + uh->write(uh, e, "", 0, 0LL, TokOUT); + } + return n; } - break; - - case Qdebug: - n=0; - break; + return uh->read(uh, e, a, n, offset); + } - case Qstatus: - d = usbdevice(c); - s = smalloc(READSTR); - if(waserror()){ - free(s); - nexterror(); - } - l = snprint(s, READSTR, "%s %#6.6lux\n", devstates[d->state], d->csp); - for(i=0; iep); i++) - if((e = d->ep[i]) != nil){ /* TO DO: freeze e */ - l += epstatus(s+l, READSTR-l, e, i); - } - n = readstr(offset, a, n, s); - poperror(); + s = smalloc(READSTR); + se = s+READSTR; + if(waserror()){ free(s); + nexterror(); + } + switch(t){ + case Qport: + uh->portinfo(uh, s, se); break; - default: - d = usbdevice(c); - if((t -= Qep0) < 0 || t >= nelem(d->ep)) - error(Eio); - if((e = d->ep[t]) == nil || e->mode == OWRITE) - error(Eio); /* can't happen */ - if (e->iso) - n=isoio(e, a, n, (ulong)offset, 0); - else - n=readusb(e, a, n); + case Qctl: + seprint(s, se, "%11d %11d\n", d->x, d->id); break; - } - return n; -} - -static int -qisempty(void *arg) -{ - return ((QH*)arg)->entries & Terminate; -} - -static long -writeusb(Endpt *e, void *a, long n, int tok) -{ - int i; - Block *b; - uchar *p; - QH *qh; - qlock(&e->wlock); - if(waserror()){ - qunlock(&e->wlock); - eptcancel(e); - nexterror(); - } - p = a; - do { - int j; - - if(e->err) - error(e->err); - if((i = n) >= e->maxpkt) - i = e->maxpkt; - b = allocb(i); - if(waserror()){ - freeb(b); - nexterror(); - } - XPRINT("out [%d]", i); - for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]); - XPRINT("\n"); - memmove(b->wp, p, i); - b->wp += i; - p += i; - n -= i; - poperror(); - qh = qxmit(e, b, tok); - tok = TokOUT; - e->data01 ^= 1; - if(e->ntd >= e->nbuf) { -XPRINT("qh %s: q=%p first=%p last=%p entries=%.8lux\n", - "writeusb sleep", qh, qh->first, qh->last, qh->entries); - XPRINT("writeusb: sleep %lux\n", &e->wr); - sleep(&e->wr, qisempty, qh); - XPRINT("writeusb: awake\n"); + case Qstatus: + p = seprint(s, se, "%s %#6.6lux\n", devstates[d->state], d->csp); + for(i=0; iep); i++) { + e = d->ep[i]; + if(e == nil) + continue; + /* TO DO: freeze e */ + p = epstatus(p, se, e, i); } - } while(n > 0); + } + n = readstr(offset, a, n, s); poperror(); - qunlock(&e->wlock); - return p-(uchar*)a; + free(s); + return n; } long @@ -2085,44 +667,42 @@ usbwrite(Chan *c, void *a, long n, vlong offset) { Udev *d; Endpt *e; - Cmdbuf *cb; Cmdtab *ct; + Cmdbuf *cb; + Usbhost *uh; int id, nw, t, i; char cmd[50]; if(c->qid.type == QTDIR) error(Egreg); - t = QID(c->qid); - if(t == Qbusctl){ + d = usbdevice(c); + uh = d->uh; + t = TYPE(c->qid); + switch(t){ + case Qport: cb = parsecmd(a, n); if(waserror()){ free(cb); nexterror(); } - ct = lookupcmd(cb, usbbusctlmsg, nelem(usbbusctlmsg)); + ct = lookupcmd(cb, usbportmsg, nelem(usbportmsg)); id = strtol(cb->f[1], nil, 0); - if(id != 1 && id != 2) - cmderror(cb, "usb port number not 1 or 2 in"); switch(ct->index){ - case BCMdisable: - portenable(id, 0); + case PMdisable: + uh->portenable(uh, id, 0); break; - case BCMenable: - portenable(id, 1); + case PMenable: + uh->portenable(uh, id, 1); break; - case BCMreset: - portreset(id); + case PMreset: + uh->portreset(uh, id); break; } poperror(); free(cb); return n; - } - d = usbdevice(c); - t = QID(c->qid); - switch(t){ case Qctl: cb = parsecmd(a, n); if(waserror()){ @@ -2217,31 +797,17 @@ usbwrite(Chan *c, void *a, long n, vlong offset) XPRINT("calling devendpt in usbwrite (CMep)\n"); e = devendpt(d, i, 1); } - qlock(&usbstate); + qlock(uh); if(waserror()){ freept(e); - qunlock(&usbstate); + qunlock(uh); nexterror(); } - if (e->active) + if(e->active) error(Eperm); if(strcmp(cb->f[2], "bulk") == 0){ - Ctlr *ub; - - e->iso = 0; /* ep n `bulk' mode maxpkt nbuf */ - ub = &ubus; - /* Each bulk device gets a queue head hanging off the - * bulk queue head - */ - if (e->epq == nil) { - e->epq = allocqh(ub); - if(e->epq == nil) - panic("usbwrite: allocqh"); - } - queueqh(e->epq); - e->mode = strcmp(cb->f[3],"r") == 0? OREAD : - strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR; + e->iso = 0; i = strtoul(cb->f[4], nil, 0); if(i < 8 || i > 1023) i = 8; @@ -2250,7 +816,7 @@ usbwrite(Chan *c, void *a, long n, vlong offset) if(i >= 1 && i <= 32) e->nbuf = i; } else { - /* ep n period mode samplesize KHz */ + /* ep n period mode samplesize Hz */ i = strtoul(cb->f[2], nil, 0); if(i > 0 && i <= 1000){ e->pollms = i; @@ -2258,8 +824,6 @@ usbwrite(Chan *c, void *a, long n, vlong offset) XPRINT("field 4: 0 <= %d <= 1000\n", i); error(Ebadarg); } - e->mode = strcmp(cb->f[3],"r") == 0? OREAD : - strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR; i = strtoul(cb->f[4], nil, 0); if(i >= 1 && i <= 8){ e->samplesz = i; @@ -2279,26 +843,30 @@ usbwrite(Chan *c, void *a, long n, vlong offset) e->maxpkt = (e->hz * e->pollms + 999)/1000 * e->samplesz; e->iso = 1; } + e->mode = strcmp(cb->f[3],"r") == 0? OREAD : + strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR; + uh->epmode(uh, e); poperror(); - qunlock(&usbstate); + qunlock(uh); } poperror(); free(cb); return n; - case Qsetup: /* SETUP endpoint 0 */ + case Qep0: /* SETUP endpoint 0 */ /* should canqlock etc */ - if((e = d->ep[0]) == nil) - error(Eio); /* can't happen */ + e = d->ep[0]; + if(e == nil || e->iso) + error(Egreg); if(n < 8 || n > 1023) error(Eio); nw = *(uchar*)a & RD2H; e->data01 = 0; - n = writeusb(e, a, n, TokSETUP); - if(nw == 0){ /* host to device: use IN[DATA1] to ack */ + n = uh->write(uh, e, a, n, 0LL, TokSETUP); + if(nw == 0) { /* host to device: use IN[DATA1] to ack */ e->data01 = 1; - nw = readusb(e, cmd, 8); + nw = uh->read(uh, e, cmd, 0LL, 8); if(nw != 0) error(Eio); /* could provide more status */ }else @@ -2306,17 +874,13 @@ usbwrite(Chan *c, void *a, long n, vlong offset) break; default: /* sends DATA[01] */ - if((t -= Qep0) < 0 || t >= nelem(d->ep)) { - print("t = %d\n", t); - error(Eio); - } - if((e = d->ep[t]) == nil || e->mode == OREAD) { - error(Eio); /* can't happen */ - } - if (e->iso) - n = isoio(e, a, n, (ulong)offset, 1); - else - n = writeusb(e, a, n, TokOUT); + t -= Qep0; + if(t < 0 || t >= nelem(d->ep)) + error(Egreg); + e = d->ep[t]; + if(e == nil || e->mode == OREAD) + error(Egreg); + n = uh->write(uh, e, a, n, offset, TokOUT); break; } return n; diff --git a/pc/usbuhci.c b/pc/usbuhci.c index 8ff63bfa218e768920988655ca458ff59e37821c..a4f22e929009d0b7a1af466c050f7cdda2c14ea2 100644 --- a/pc/usbuhci.c +++ b/pc/usbuhci.c @@ -1198,21 +1198,24 @@ isoio(Ctlr *ctlr, Endpt *e, void *a, long n, ulong offset, int w) if(e->psize > e->maxpkt) panic("packet size > maximum"); } + if((i = n) >= e->psize) + i = e->psize; + if (w) + e->buffered += i; + else{ + e->buffered -= i; + if (e->buffered < 0) + e->buffered = 0; + } + isolock = 0; + iunlock(&ctlr->activends); td->flags &= ~IsoClean; bp = x->bp0 + (td - x->td0) * e->maxpkt / e->pollms; q = bp + e->off; - if((i = n) >= e->psize) - i = e->psize; if (w){ memmove(q, p, i); - e->buffered += i; }else{ memmove(p, q, i); - e->buffered -= i; - if (e->buffered < 0){ - XPRINT("e->buffered %d?\n", e->buffered); - e->buffered = 0; - } } p += i; n -= i; @@ -1231,11 +1234,9 @@ isoio(Ctlr *ctlr, Endpt *e, void *a, long n, ulong offset, int w) } while(n > 0); n = p-(uchar*)a; e->foffset += n; - if (isolock){ - isolock = 0; - iunlock(&ctlr->activends); - } poperror(); + if (isolock) + iunlock(&ctlr->activends); qunlock(&e->rlock); return n; } @@ -1361,24 +1362,10 @@ XPRINT("qh %s: q=%p first=%p last=%p entries=%.8lux\n", return p-(uchar*)a; } -static struct -{ - int vid; - int did; -} -supported[] = -{ - { 0x8086, 0x7112 }, /* Intel chipset PIIX 4 */ - { 0x8086, 0x2442 }, /* Intel chipset 815E */ - { 0x8086, 0x2444 }, /* Intel chipset 815E */ - { 0x1106, 0x0586 }, /* Via chipset */ - { 0x1106, 0x3038 }, /* Via chipset */ -}; - static void scanpci(void) { - int i, io; + int io; Ctlr *ctlr; Pcidev *p; static int already = 0; @@ -1386,35 +1373,39 @@ scanpci(void) if(already) return; already = 1; - for(i = 0; i < nelem(supported); i++) { - p = nil; - while(p = pcimatch(p, supported[i].vid, supported[i].did)) { - io = p->mem[4].bar & ~0x0F; - if(io == 0) { - print("usbuhci: failed to map registers\n"); - continue; - } - if(ioalloc(io, p->mem[4].size, 0, "usbuhci") < 0){ - print("usbuhci: port %d in use\n", io); - continue; - } - if(p->intl == 0xFF || p->intl == 0) { - print("usbuhci: no irq assigned for port %d\n", io); - continue; - } + p = nil; + while(p = pcimatch(p, 0, 0)) { + /* + * Find UHCI controllers. Class = 12 (serial controller), + * Sub-class = 3 (USB) and Programming Interface = 0. + */ + if(p->ccrb != 0x0C || p->ccru != 0x03 || p->ccrp != 0x00) + continue; + io = p->mem[4].bar & ~0x0F; + if(io == 0) { + print("usbuhci: failed to map registers\n"); + continue; + } + if(ioalloc(io, p->mem[4].size, 0, "usbuhci") < 0){ + print("usbuhci: port %d in use\n", io); + continue; + } + if(p->intl == 0xFF || p->intl == 0) { + print("usbuhci: no irq assigned for port %d\n", io); + continue; + } - XPRINT("usbuhci: %x/%x port 0x%ux size 0x%x irq %d\n", - p->vid, p->did, io, p->mem[4].size, p->intl); + XPRINT("usbuhci: %x/%x port 0x%ux size 0x%x irq %d\n", + p->vid, p->did, io, p->mem[4].size, p->intl); - ctlr = malloc(sizeof(Ctlr)); - ctlr->pcidev = p; - ctlr->io = io; - if(ctlrhead != nil) - ctlrtail->next = ctlr; - else - ctlrhead = ctlr; - ctlrtail = ctlr; - } + ctlr = malloc(sizeof(Ctlr)); + ctlr->pcidev = p; + ctlr->io = io; + if(ctlrhead != nil) + ctlrtail->next = ctlr; + else + ctlrhead = ctlr; + ctlrtail = ctlr; } }