## diffname pc/devusb.c 1999/1005 ## diff -e /dev/null /n/emeliedump/1999/1005/sys/src/brazil/pc/devusb.c 0a /* * 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" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" /* #ifndef Inferno #include "devtab.h" #include "brazilia.h" #endif */ #define Pprint #define Chatty 1 #define DPRINT if(Chatty)print #define XPRINT if(0)print /* * 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, }; typedef uchar byte; typedef struct Ctlr Ctlr; typedef struct Endpt Endpt; typedef struct QTree QTree; typedef struct Udev Udev; /* * UHCI hardware structures, aligned on 16-byte boundary */ typedef struct QH QH; typedef struct TD TD; struct TD { ulong link; ulong status; /* controller r/w */ ulong dev; ulong buffer; /* software */ ulong flags; Block* bp; 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 */ union { TD* first; QH* next; /* free list */ }; TD* last; }; #define QFOL(p) ((QH*)KADDR((ulong)(p) & ~0xF)) /* * UHCI interface registers and bits */ 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, FRAMESIZE= 4096, /* fixed by hardware; aligned to same */ NFRAME = FRAMESIZE/4, 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, }; /* * software structures */ struct QTree { QLock; int nel; int depth; QH* root; ulong* bw; }; #define GET2(p) ((((p)[1]&0xFF)<<8)|((p)[0]&0xFF)) #define PUT2(p,v) (((p)[0] = (v)), ((p)[1] = (v)>>8)) /* * active USB device */ struct Udev { Ref; Lock; int x; /* index in usbdev[] */ int busy; int state; int id; byte port; /* port number on connecting hub */ byte class; byte subclass; byte proto; int ls; int npt; Endpt* ep[16]; /* active end points */ Udev* ports; /* active ports, if hub */ Udev* next; /* next device on this hub */ }; /* device parameters */ enum { /* Udev.state */ Disabled = 0, Attached, Enabled, Assigned, Configured, /* Udev.class */ Noclass = 0, Hubclass = 9, }; static char *devstates[] = { [Disabled] "Disabled", [Attached] "Attached", [Enabled] "Enabled", [Assigned] "Assigned", [Configured] "Configured", }; /* * device endpoint */ struct Endpt { Ref; Lock; int x; /* index in Udev.ep */ int id; /* hardware endpoint address */ int maxpkt; /* maximum packet size (from endpoint descriptor) */ int data01; /* 0=DATA0, 1=DATA1 */ byte eof; byte class; byte subclass; byte proto; byte mode; /* OREAD, OWRITE, ORDWR */ byte nbuf; /* number of buffers allowed */ byte periodic; byte iso; byte debug; byte active; /* listed for examination by interrupts */ int sched; /* schedule index; -1 if undefined or aperiodic */ int setin; ulong bw; /* bandwidth requirement */ int pollms; /* polling interval in msec */ QH* epq; /* queue of TDs for this endpoint */ QLock rlock; Rendez rr; Queue* rq; QLock wlock; Rendez wr; Queue* wq; int ntd; char* err; Udev* dev; /* owning device */ Endpt* activef; /* active endpoint list */ ulong nbytes; ulong nblocks; }; struct Ctlr { Lock; /* protects state shared with interrupt (eg, free list) */ int io; ulong* frames; /* frame list */ int idgen; /* version number to distinguish new connections */ QLock resetl; /* lock controller during USB reset */ TD* tdpool; TD* freetd; QH* qhpool; QH* freeqh; QTree* tree; /* tree for periodic Endpt i/o */ QH* ctlq; /* queue for control i/o */ QH* bwsop; /* empty bandwidth sop (to PIIX4 errata specifications) */ QH* bulkq; /* queue for bulk i/o (points back to bandwidth sop) */ Udev* ports[2]; }; #define IN(x) ins(ub->io+(x)) #define OUT(x, v) outs(ub->io+(x), (v)) static Ctlr ubus; static char Estalled[] = "usb endpoint stalled"; static QLock usbstate; /* protects name space state */ static Udev* usbdev[32]; static struct { Lock; Endpt* f; } activends; 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; print("td %8.8lux: l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n", t, t->link, t->status, t->dev, t->buffer, t->bp?(ulong)t->bp->rp:0, t->flags); print("\ts=%s,ep=%ld,d=%ld,D=%ld\n", buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1); if(t->bp) 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 TD * alloctdring(int nbuf, Endpt *e, int pid, int maxn) { TD *t, *lt, *ft; int id, i; ulong status, dev; if(!e->iso) nbuf = (nbuf+1)&~1; id = (e->x<<7)|(e->dev->x&0x7F); dev = ((maxn-1)<<21) | ((id&0x7FF)<<8) | pid; status = 0; if(e->dev->ls) status |= LowSpeed; ft = lt = nil; for(i=0; iep = e; t->status = status; t->dev = dev; if(!e->iso) dev ^= IsDATA1; if(pid == TokIN){ t->bp = allocb(maxn); t->buffer = PADDR(t->bp->wp); } if(ft != nil){ lt->next = t; lt->link = PADDR(t); }else ft = t; lt = t; } if(lt != nil) lt->link = PADDR(ft); /* loop to form ring */ return ft; } 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->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) { int i; QH *q0; q0 = q; for(i = 0; q != nil && i < 10; i++){ pprint("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){ pprint("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) { TD *lt; for(lt = t; lt->next != nil; lt = lt->next) lt->link = PADDR(lt->next) | vf; lt->link = Terminate; ilock(ub); 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; 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) print("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); } static void cleanq(QH *q, int discard) { TD *t; Ctlr *ub; ub = &ubus; ilock(ub); for(t = q->first; t != nil;){ // XPRINT("cleanq: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); if(t->status & Active){ if(t->flags & CancelTD){ XPRINT("cancelTD: %8.8lux\n", (ulong)t); t->status = (t->status & ~Active) | IOC; /* ensure interrupt next frame */ t = t->next; continue; } break; } t->status &= ~IOC; /* TO DO: need to deal with TDs as chain if necessary */ q->first = t->next; if(q->first != nil) q->entries = PADDR(q->first); else q->entries = Terminate; t->next = nil; iunlock(ub); cleantd(t, discard); ilock(ub); t = 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); pprint("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){ 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; for(l = &activends.f; *l != e; l = &(*l)->activef) if(*l == nil){ iunlock(&activends); panic("usb eptdeactivate"); } *l = e->activef; } iunlock(&activends); } 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); 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; vf = 0; }else if((qh = e->epq) == nil || e->mode != OREAD){ qh = ub->bulkq; vf = Vf; } queuetd(ub, qh, t, vf); 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) { int i; for(i=0; (1<nel = n; qt->depth = depth; qt->bw = mallocz(n*sizeof(qt->bw), 1); if(qt->bw == nil){ free(qt); return nil; } tree = xspanalloc(n*sizeof(QH), 16, 0); if(tree == nil){ free(qt); return nil; } qt->root = tree; tree->head = Terminate; /* root */ tree->entries = Terminate; for(i=1; ihead = PADDR(&tree[(i-1)/2]) | IsQH; qh->entries = Terminate; } /* distribute leaves evenly round the frame list */ leaf0 = n/2; for(i=0; i= n){ pprint("leaf0=%d o=%d i=%d n=%d\n", leaf0, o, i, n); break; } frame[i] = PADDR(&tree[leaf0+o]) | IsQH; } return qt; } static void dumpframe(int f, int t) { QH *q, *tree; ulong p, *frame; int i, n; n = ubus.tree->nel; tree = ubus.tree->root; frame = ubus.frames; if(f < 0) f = 0; if(t < 0) t = 32; for(i=f; ihead); for(p=frame[i]; (p & IsQH) && (p &Terminate) == 0; p = q->head){ q = QFOL(p); if(!(q >= tree && q < &tree[n])){ pprint("Q: p=%8.8lux out of range\n", p); break; } pprint(" -> %8.8lux h=%8.8lux e=%8.8lux\n", p, q->head, q->entries); } } } static int pickschedq(QTree *qt, int pollms, ulong bw, ulong limit) { int i, j, d, ub, q; ulong best, worst, total; d = flog2(pollms); if(d > qt->depth) d = qt->depth; q = -1; worst = 0; best = ~0; ub = (1<<(d+1))-1; for(i=(1<bw[0]; for(j=i; j > 0; j=(j-1)/2) total += qt->bw[j]; if(total < best){ best = total; q = i; } if(total > worst) worst = total; } if(worst+bw >= limit) return -1; return q; } static int schedendpt(Endpt *e) { Ctlr *ub; QH *qh; int q; if(!e->periodic || e->sched >= 0) return 0; ub = &ubus; if(e->epq == nil){ e->epq = allocqh(ub); if(e->epq == nil) return -1; } qlock(ub->tree); q = pickschedq(ub->tree, e->pollms, e->bw, ~0); /* TO DO: bus bandwidth limit */ if(q < 0) return -1; ub->tree->bw[q] += e->bw; qh = &ub->tree->root[q]; e->sched = q; e->epq->head = qh->entries; e->epq->entries = Terminate; qh->entries = PADDR(e->epq) | IsQH; qunlock(ub->tree); return 0; } static void unschedendpt(Endpt *e) { Ctlr *ub; ulong p; QH *qh; int q; ub = &ubus; if(e->epq == nil || (q = e->sched) < 0) return; p = PADDR(e->epq) | IsQH; qlock(ub->tree); ub->tree->bw[q] -= e->bw; qh = &ub->tree->root[q]; for(; qh->entries != p; qh = QFOL(qh->entries)) if(qh->entries & Terminate || (qh->entries & IsQH) == 0){ qunlock(ub->tree); panic("usb: unschedendpt"); } qh->entries = e->epq->head; qunlock(ub->tree); e->epq->head = Terminate; } static Endpt * devendpt(Udev *d, int id, int add) { Endpt *e, **p; p = &d->ep[id&0xF]; lock(d); if((e = *p) != nil){ incref(e); unlock(d); return e; } unlock(d); if(!add) return nil; e = mallocz(sizeof(*e), 1); e->ref = 1; e->x = id&0xF; e->id = id; e->periodic = 0; e->sched = -1; e->maxpkt = 8; e->pollms = 0; e->bw = 0; e->nbuf = 1; e->dev = d; e->active = 0; lock(d); if(*p != nil){ incref(*p); unlock(d); 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 void freept(Endpt *e) { if(e != nil && decref(e) == 0){ XPRINT("freept(%d,%d)\n", e->dev->x, e->x); unschedendpt(e); e->dev->ep[e->x] = nil; eptdeactivate(e); if(e->epq != nil) freeqh(&ubus, e->epq); free(e); } } static void usbdevreset(Udev *d) { d->state = Disabled; if(d->class == Hubclass) for(d = d->ports; d != nil; d = d->next) usbdevreset(d); } static void freedev(Udev *d) { int i; if(d != nil && decref(d) == 0){ for(i=0; iep); i++) freept(d->ep[i]); if(d->x >= 0) usbdev[d->x] = nil; free(d); } } 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 interrupt(Ureg*, void *a) { Ctlr *ub; Endpt *e; int s; ub = a; s = IN(Status); if (s & 0x1a) { print("usbint: #%x f%d\n", s, IN(Frnum)); print("cmd #%x sofmod #%x\n", IN(Cmd), inb(ub->io+SOFMod)); print("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1)); } OUT(Status, s); // XPRINT("cleanq(ub->ctlq, 0)\n"); cleanq(ub->ctlq, 0); // XPRINT("cleanq(ub->bulkq, 0)\n"); cleanq(ub->bulkq, 0); ilock(&activends); for(e = activends.f; e != nil; e = e->activef) if(e->epq != nil) { // XPRINT("cleanq(e->epq, 0)\n"); cleanq(e->epq, 0); } iunlock(&activends); } static void resetusbctlr(void) { Ctlr *ub; Pcidev *cfg; int i; ulong port; QTree *qt; TD *t; ub = &ubus; memset(&cfg, 0, sizeof(cfg)); cfg = pcimatch(0, 0x8086, 0x7112); /* Intel chipset PIIX 4*/ if(cfg == nil) { // cfg = pcimatch(0, 0x8086, 0x7112); /* Intel chipset PIIX 3*/ // if(cfg == nil) { cfg = pcimatch(0, 0x1106, 0x0586); /* Via chipset */ if(cfg == nil) { DPRINT("No USB device found\n"); return; } // } } port = cfg->mem[4].bar & ~0x0F; if (port == 0) { print("usb: failed to map registers\n"); return; } print("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 intrenable(cfg->intl, interrupt, ub, cfg->tbdf, "usb"); ub->io = port; ub->tdpool = xspanalloc(128*sizeof(TD), 16, 0); for(i=128; --i>=0;){ ub->tdpool[i].next = ub->freetd; ub->freetd = &ub->tdpool[i]; } ub->qhpool = xspanalloc(32*sizeof(QH), 16, 0); for(i=32; --i>=0;){ ub->qhpool[i].next = ub->freeqh; ub->freeqh = &ub->qhpool[i]; } /* * the root of the periodic (interrupt & isochronous) scheduling tree * points 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->bulkq = allocqh(ub); t = alloctd(ub); /* inactive TD, looped */ t->link = PADDR(t); ub->bwsop = allocqh(ub); ub->bwsop->head = PADDR(ub->bulkq) | IsQH; ub->bwsop->entries = PADDR(t); ub->ctlq = allocqh(ub); ub->ctlq->head = PADDR(ub->bwsop) | IsQH; // ub->bulkq->head = PADDR(ub->bwsop) | IsQH; /* loop back */ print("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n", IN(Cmd), IN(Status), IN(Usbintr), inb(port+Frnum)); print("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 = xspanalloc(FRAMESIZE, FRAMESIZE, 0); qt = mkqhtree(ub->frames, NFRAME, 32); if(qt == nil){ print("usb: can't allocate scheduling tree\n"); ub->io = 0; return; } qt->root->head = PADDR(ub->ctlq) | IsQH; ub->tree = qt; print("usb tree: nel=%d depth=%d\n", qt->nel, qt->depth); outl(port+Flbaseadd, PADDR(ub->frames)); OUT(Frnum, 0); OUT(Usbintr, 0xF); /* enable all interrupts */ print("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(port+SOFMod)); print("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1)); } 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}, 1, 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]; } static Udev * usbdevice(Chan *c) { Udev *d; d = usbdeviceofpath(c->qid.path); if(d == nil || d->id != c->qid.vers || d->state == Disabled) error(Ehungup); return d; } static Udev * usbnewdevice(void) { Udev *d; Endpt *e; int i; d = nil; qlock(&usbstate); if(waserror()){ qunlock(&usbstate); nexterror(); } for(i=0; iref = 1; d->x = i; d->id = (ubus.idgen << 8) | i; d->state = Enabled; e = devendpt(d, 0, 1); /* always provide control endpoint 0 */ e->mode = ORDWR; e->periodic = 0; e->sched = -1; usbdev[i] = d; break; } poperror(); qunlock(&usbstate); return d; } static void usbreset(void) { resetusbctlr(); } 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; ub = &ubus; if(ub->io == 0) 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); } Chan * usbclone(Chan *c, Chan *nc) { return devclone(c, nc); } static int usbgen(Chan *c, Dirtab*, int, int s, Dir *dp) { int t; Qid q; ulong path; Udev *d; Dirtab *tab; Endpt *e; char buf[NAMELEN]; /* * Top level directory contains the name of the device. */ if(c->qid.path == CHDIR){ if(s == 0){ devdir(c, (Qid){CHDIR|Q2nd, 0}, "usb", 0, eve, 0555, dp); return 1; } return -1; } /* * Second level contains "new" plus all the clients. */ t = QID(c->qid); if(t < Q3rd){ if(s < nelem(usbdir2)){ tab = &usbdir2[s]; devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); return 1; } if((s -= nelem(usbdir2)) >= 0 && s < nelem(usbdev)){ d = usbdev[s]; if(d == nil) return 0; sprint(buf, "%d", s); q = (Qid){CHDIR|((s+1)<id}; devdir(c, q, buf, 0, eve, 0555, dp); return 1; } return -1; } /* * Third level. */ path = c->qid.path&~(CHDIR|QMASK); /* slot component */ q.vers = c->qid.vers; if(s < nelem(usbdir3)){ Dirtab *tab = &usbdir3[s]; q.path = path | tab->qid.path; devdir(c, q, tab->name, tab->length, eve, tab->perm, dp); return 1; } /* active endpoints */ d = usbdeviceofpath(path); if(d == nil) return -1; s -= nelem(usbdir3); if(s < 0 || s >= nelem(d->ep)) return -1; if(s == 0 || (e = d->ep[s]) == nil) /* ep0data is called "setup" */ return 0; sprint(buf, "ep%ddata", s); q.path = path | (Qep0+s); devdir(c, q, buf, 0, eve, e->mode==OREAD? 0444: e->mode==OWRITE? 0222: 0666, dp); return 1; } int usbwalk(Chan *c, char *name) { if(strcmp(name, "..") == 0){ switch(QID(c->qid)){ case Qtopdir: return 1; case Q2nd: c->qid = (Qid){CHDIR|Qtopdir, 0}; break; case Q3rd: c->qid = (Qid){CHDIR|Q2nd, 0}; break; default: panic("usbwalk %lux", c->qid.path); } return 1; } return devwalk(c, name, nil, 0, usbgen); } void usbstat(Chan *c, char *db) { devstat(c, db, nil, 0, usbgen); } Chan * usbopen(Chan *c, int omode) { Udev *d; int f, s; if(c->qid.path & CHDIR) return devopen(c, omode, nil, 0, usbgen); f = 0; if(QID(c->qid) == Qnew){ d = usbnewdevice(); if(d == nil) error(Enodev); c->qid.path = Qctl|((d->x+1)<qid.vers = d->id; f = 1; } if(c->qid.path < Q3rd) return devopen(c, omode, nil, 0, usbgen); qlock(&usbstate); if(waserror()){ qunlock(&usbstate); nexterror(); } switch(QID(c->qid)){ case Qctl: d = usbdevice(c); if(0&&d->busy) error(Einuse); d->busy = 1; if(!f) incref(d); break; default: d = usbdevice(c); s = QID(c->qid) - Qep0; if(s >= 0 && s < nelem(d->ep)){ Endpt *e; if((e = d->ep[s]) == nil) error(Enodev); if(schedendpt(e) < 0) error("can't schedule USB endpoint"); eptactivate(e); } incref(d); break; } poperror(); qunlock(&usbstate); 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 usbwstat(Chan *c, char *dp) { USED(c, dp); error(Eperm); } void usbclose(Chan *c) { Udev *d; if(c->qid.path & CHDIR || c->qid.path < Q3rd) return; qlock(&usbstate); if(waserror()){ qunlock(&usbstate); nexterror(); } d = usbdevice(c); if(QID(c->qid) == Qctl) d->busy = 0; if(c->flag & COPEN) freedev(d); poperror(); qunlock(&usbstate); } static int eptinput(void *arg) { Endpt *e; e = arg; return e->eof || e->err || qcanread(e->rq); } static long readusb(Endpt *e, void *a, long n) { Block *b; uchar *p; long l, i; qlock(&e->rlock); if(waserror()){ qunlock(&e->rlock); eptcancel(e); nexterror(); } p = a; do { if(e->eof) { pprint("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) { pprint("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; } while (l == e->maxpkt && n > 0); poperror(); qunlock(&e->rlock); return p-(uchar*)a; } long usbread(Chan *c, void *a, long n, vlong offset) { Endpt *e; Udev *d; char buf[48], *s; int t, w0, w1, ps, l, i; if(c->qid.path & CHDIR) 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); 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; e->data01 = 1; writeusb(e, "", 0, TokOUT); } break; case Qdebug: n=0; break; case Qstatus: d = usbdevice(c); s = smalloc(READSTR); if(waserror()){ free(s); nexterror(); } l = snprint(s, READSTR, "%s %d %d %d\n", devstates[d->state], d->class, d->subclass, d->proto); for(i=0; iep); i++) if((e = d->ep[i]) != nil) /* TO DO: freeze e */ l += snprint(s+l, READSTR-l, "%d bytes %lud blocks %lud\n", i, e->nbytes, e->nblocks); n = readstr(offset, a, n, s); poperror(); free(s); 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 */ n=readusb(e, a, n); break; } return n; } static int qisempty(void *arg) { return ((QH*)arg)->entries & Terminate; } static long writeusb(Endpt *e, void *a, long n, int tok) { long i; Block *b; uchar *p; QH *qh; p = a; qlock(&e->wlock); if(waserror()){ qunlock(&e->wlock); eptcancel(e); nexterror(); } 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 [%ld]", 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; if(!e->iso) e->data01 ^= 1; if(e->ntd >= e->nbuf) { XPRINT("writeusb: sleep %lux\n", &e->wr); sleep(&e->wr, qisempty, qh); XPRINT("writeusb: awake\n"); } } while(n > 0); poperror(); qunlock(&e->wlock); return p-(uchar*)a; } int getfields(char *ss, char **sp, int nptrs) { uchar *s = (uchar*)ss; uchar **p = (uchar**)sp; uint c; c = 0; for(;;){ if(--nptrs < 0) break; *p++ = s; do { c = *s++; if (c == ' ') break; if (c == '\t') break; if (c == '\n') break; } while(c); if(c == 0) break; s[-1] = 0; } if(nptrs > 0) *p = 0; else if(--s >= (uchar*)ss) *s = c; return p - (uchar**)sp; } long usbwrite(Chan *c, void *a, long n, vlong) { Udev *d; Endpt *e; int id, nw, nf, t, i; char cmd[50], *fields[10]; if(c->qid.path & CHDIR) error(Egreg); t = QID(c->qid); if(t == Qbusctl){ if(n >= sizeof(cmd)-1) n = sizeof(cmd)-1; memmove(cmd, a, n); cmd[n] = 0; nf = getfields(cmd, fields, nelem(fields)); if(nf==1 && strcmp(fields[0], "dump")==0){ dumpframe(-1, -1); return n; } if(nf < 2) error(Ebadarg); id = strtol(fields[1], nil, 0); if(id != 1 && id != 2) error(Ebadarg); /* there are two ports on the root hub */ if(strcmp(fields[0], "reset") == 0) portreset(id); else if(strcmp(fields[0], "enable") == 0) portenable(id, 1); else if(strcmp(fields[0], "disable") == 0) portenable(id, 0); else error(Ebadarg); return n; } d = usbdevice(c); t = QID(c->qid); switch(t){ case Qctl: if(n >= sizeof(cmd)-1) n = sizeof(cmd)-1; memmove(cmd, a, n); cmd[n] = 0; nf = getfields(cmd, fields, nelem(fields)); if(nf > 1 && strcmp(fields[0], "speed") == 0){ d->ls = strtoul(fields[1], nil, 0) == 0; } else if(nf > 4 && strcmp(fields[0], "class") == 0){ /* class class subclass proto */ d->class = strtoul(fields[4], nil, 0); d->subclass = strtoul(fields[5], nil, 0); d->proto = strtoul(fields[6], nil, 0); }else if(nf > 2 && strcmp(fields[0], "data") == 0){ i = strtoul(fields[1], nil, 0); if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) error(Ebadarg); e = d->ep[i]; e->data01 = strtoul(fields[2], nil, 0) != 0; }else if(nf > 2 && strcmp(fields[0], "maxpkt") == 0){ i = strtoul(fields[1], nil, 0); if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) error(Ebadarg); e = d->ep[i]; e->maxpkt = strtoul(fields[2], nil, 0); if(e->maxpkt > 1500) e->maxpkt = 1500; }else if(nf > 2 && strcmp(fields[0], "debug") == 0){ i = strtoul(fields[1], nil, 0); if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) error(Ebadarg); e = d->ep[i]; e->debug = strtoul(fields[2], nil, 0); }else if(nf > 1 && strcmp(fields[0], "unstall") == 0){ i = strtoul(fields[1], nil, 0); if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) error(Ebadarg); e = d->ep[i]; e->err = nil; }else if(nf == 6 && strcmp(fields[0], "ep") == 0){ /* ep n maxpkt mode poll nbuf */ i = strtoul(fields[1], nil, 0); if(i < 0 || i >= nelem(d->ep)) error(Ebadarg); if(d->ep[i] != nil) error(Einuse); e = devendpt(d, i, 1); if(waserror()){ freept(e); nexterror(); } i = strtoul(fields[2], nil, 0); if(i < 8 || i > 1023) i = 8; e->maxpkt = i; e->mode = strcmp(fields[3],"r")==0? OREAD: strcmp(fields[3],"w") == 0? OWRITE: ORDWR; e->periodic = 0; e->sched = -1; if(strcmp(fields[4], "bulk") != 0){ e->periodic = 1; i = strtoul(fields[4], nil, 0); if(i > 0 && i <= 1000) e->pollms = i; else error(Ebadarg); } i = strtoul(fields[5], nil, 0); if(i >= 1 && i <= 32) e->nbuf = i; poperror(); }else error(Ebadarg); return n; case Qsetup: /* SETUP endpoint 0 */ /* should canqlock etc */ if((e = d->ep[0]) == nil) error(Eio); /* can't happen */ 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 */ e->data01 = 1; nw = readusb(e, cmd, 8); if(nw != 0) error(Eio); /* could provide more status */ }else e->setin = 1; /* two-phase */ break; default: /* sends DATA[01] */ if((t -= Qep0) < 0 || t >= nelem(d->ep)) error(Eio); if((e = d->ep[t]) == nil || e->mode == OREAD) error(Eio); /* can't happen */ n = writeusb(e, a, n, TokOUT); break; } return n; } Dev usbdevtab = { 'U', "usb", usbreset, usbinit, usbattach, usbclone, usbwalk, usbstat, usbopen, devcreate, usbclose, usbread, devbread, usbwrite, devbwrite, devremove, devwstat, }; . ## diffname pc/devusb.c 1999/1006 ## diff -e /n/emeliedump/1999/1005/sys/src/brazil/pc/devusb.c /n/emeliedump/1999/1006/sys/src/brazil/pc/devusb.c 1883a } . 1882c }else { pprint("command %s, fields %d\n", fields[0], nf); . 1876a } . 1875c else { pprint("field 4: 0 <= %d <= 1000\n", i); . 1855a } . 1854c if(i < 0 || i >= nelem(d->ep)) { pprint("field 1: 0 <= %d < %d\n", i, nelem(d->ep)); . 1320c Ctlr *ub; Pcidev *cfg; int i; ulong port; QTree *qt; TD *t; ub = &ubus; memset(&cfg, 0, sizeof(cfg)); cfg = pcimatch(0, 0x8086, 0x7112); /* Intel chipset PIIX 4*/ if(cfg == nil) { // cfg = pcimatch(0, 0x8086, 0x7112); /* Intel chipset PIIX 3*/ // if(cfg == nil) { cfg = pcimatch(0, 0x1106, 0x0586); /* Via chipset */ if(cfg == nil) { DPRINT("No USB device found\n"); return; } // } } port = cfg->mem[4].bar & ~0x0F; if (port == 0) { print("usb: failed to map registers\n"); return; } print("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 intrenable(cfg->intl, interrupt, ub, cfg->tbdf, "usb"); ub->io = port; ub->tdpool = xspanalloc(128*sizeof(TD), 16, 0); for(i=128; --i>=0;){ ub->tdpool[i].next = ub->freetd; ub->freetd = &ub->tdpool[i]; } ub->qhpool = xspanalloc(32*sizeof(QH), 16, 0); for(i=32; --i>=0;){ ub->qhpool[i].next = ub->freeqh; ub->freeqh = &ub->qhpool[i]; } /* * the root of the periodic (interrupt & isochronous) scheduling tree * points 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->bulkq = allocqh(ub); t = alloctd(ub); /* inactive TD, looped */ t->link = PADDR(t); ub->bwsop = allocqh(ub); ub->bwsop->head = PADDR(ub->bulkq) | IsQH; ub->bwsop->entries = PADDR(t); ub->ctlq = allocqh(ub); ub->ctlq->head = PADDR(ub->bwsop) | IsQH; // ub->bulkq->head = PADDR(ub->bwsop) | IsQH; /* loop back */ print("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n", IN(Cmd), IN(Status), IN(Usbintr), inb(port+Frnum)); print("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 = xspanalloc(FRAMESIZE, FRAMESIZE, 0); qt = mkqhtree(ub->frames, NFRAME, 32); if(qt == nil){ print("usb: can't allocate scheduling tree\n"); ub->io = 0; return; } qt->root->head = PADDR(ub->ctlq) | IsQH; ub->tree = qt; print("usb tree: nel=%d depth=%d\n", qt->nel, qt->depth); outl(port+Flbaseadd, PADDR(ub->frames)); OUT(Frnum, 0); OUT(Usbintr, 0xF); /* enable all interrupts */ print("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(port+SOFMod)); print("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1)); . 1126,1220d ## diffname pc/devusb.c 1999/1007 ## diff -e /n/emeliedump/1999/1006/sys/src/brazil/pc/devusb.c /n/emeliedump/1999/1007/sys/src/brazil/pc/devusb.c 1881c XPRINT("command %s, fields %d\n", fields[0], nf); . 1872c XPRINT("field 4: 0 <= %d <= 1000\n", i); . 1849c XPRINT("field 1: 0 <= %d < %d\n", i, nelem(d->ep)); . 1586c XPRINT("b == nil\n"); . 1573c XPRINT("e->eof\n"); . 1107,1109c XPRINT("usbint: #%x f%d\n", s, IN(Frnum)); XPRINT("cmd #%x sofmod #%x\n", IN(Cmd), inb(ub->io+SOFMod)); XPRINT("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1)); . 838c XPRINT(" -> %8.8lux h=%8.8lux e=%8.8lux\n", p, q->head, q->entries); . 835c XPRINT("Q: p=%8.8lux out of range\n", p); . 831c XPRINT("F%.2d %8.8lux %8.8lux\n", i, frame[i], QFOL(frame[i])->head); . 808c XPRINT("leaf0=%d o=%d i=%d n=%d\n", leaf0, o, i, n); . 627c XPRINT("cancel:\n"); . 528c XPRINT("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); . 484c XPRINT("head:"); . 478c XPRINT("qh %8.8lux: %8.8lux %8.8lux\n", q, q->head, q->entries); . 404,441d 374c XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n", buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1); . 372c XPRINT("td %8.8lux: l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n", . ## diffname pc/devusb.c 1999/1116 ## diff -e /n/emeliedump/1999/1007/sys/src/brazil/pc/devusb.c /n/emeliedump/1999/1116/sys/src/9/pc/devusb.c 1799,1800c if (i == -1) debug = 0; else { debug = 1; e = d->ep[i]; e->debug = strtoul(fields[2], nil, 0); } . 1797c if(i < -1 || i >= nelem(d->ep) || d->ep[i] == nil) . 1526a XPRINT("got qlock(%p)\n", &e->rlock); . 1525a XPRINT("qlock(%p)\n", &e->rlock); . 1255c ub->bulkq->head = PADDR(ub->bwsop) | IsQH; /* loop back */ . 1082,1083c XPRINT("cleanq(e->epq, 0, 0)\n"); cleanq(e->epq, 0, 0); . 1075,1078c XPRINT("cleanq(ub->ctlq, 0, 0)\n"); cleanq(ub->ctlq, 0, 0); XPRINT("cleanq(ub->bulkq, 0, Vf)\n"); cleanq(ub->bulkq, 0, Vf); . 1069d 1067a XPRINT("usbint: #%x f%d\n", s, IN(Frnum)); . 914a e->epq = allocqh(ub); if(e->epq == nil) panic("devendpt"); . 688a XPRINT("bulkenq\n"); . 687d 685a XPRINT("enq\n"); . 678a XPRINT("qrcv\n"); . 573c if (tp) t = tp->next; else t = q->first; XPRINT("t = %8.8lux\n", t); dumpqh(q); . 563,569c 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; . 559a tp = t; t = t->next; continue; . 556a tp = t; . 553a if(t->status & NAKed){ t->status = (t->status & ~NAKed) | IOC; /* ensure interrupt next frame */ tp = t; t = t->next; continue; } . 552c 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); . 550a tp = nil; . 546c TD *t, *tp; . 544c cleanq(QH *q, int discard, int vf) . 472a dumpqh(q); . 241c int ntd; . 231,232c int pollms; /* polling interval in msec */ QH* epq; /* queue of TDs for this endpoint */ . 228,229c int sched; /* schedule index; -1 if undefined or aperiodic */ int setin; . 214,217c int x; /* index in Udev.ep */ int id; /* hardware endpoint address */ int maxpkt; /* maximum packet size (from endpoint descriptor) */ int data01; /* 0=DATA0, 1=DATA1 */ . 34a static int debug = 1; . 33c #define XPRINT if(debug)print . ## diffname pc/devusb.c 1999/1117 ## diff -e /n/emeliedump/1999/1116/sys/src/9/pc/devusb.c /n/emeliedump/1999/1117/sys/src/9/pc/devusb.c 1868c if(strcmp(fields[4], "bulk") == 0){ Ctlr *ub; 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); } else { . 1288c ub->bwsop->head = PADDR(ub->bulkq) | IsQH; ub->bulkq->head = PADDR(ub->recvq) | IsQH; ub->recvq->head = PADDR(ub->bwsop) | IsQH; /* loop back */ . 1286d 1283,1284d 1280a ub->recvq = allocqh(ub); XPRINT("bulkq: 0x%8.8lux\n", ub->bulkq); XPRINT("recvq: 0x%8.8lux\n", ub->recvq); XPRINT("bwsop: 0x%8.8lux\n", ub->bwsop); XPRINT("ctlq: 0x%8.8lux\n", ub->ctlq); . 1279a ub->ctlq = allocqh(ub); ub->bwsop = allocqh(ub); . 1225a Ctlr *ub; . 1220d 1111a XPRINT("clean recvq\n"); for (q = ub->recvq->next; q; q = q->hlink) { XPRINT("cleanq(q, 0, Vf)\n"); cleanq(q, 0, Vf); } . 1097a QH *q; . 921a ub = &ubus; . 920a Ctlr *ub; . 901c if(!e->periodic || (q = e->sched) < 0) . 689a XPRINT("bulkenq\n"); . 686a XPRINT("enq\n"); . 672a XPRINT("qxmit\n"); . 664a 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; } . 419a qh->hlink = nil; . 377,378c // if(t->bp && (t->flags & CancelTD) == 0 && (t->status & Active) == 0) // dumpdata(t->bp, n); . 374,375c 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); . 269a QH* recvq; /* receive queues for bulk i/o */ . 89,93c QH* hlink; TD* first; QH* next; /* free list */ TD* last; ulong _d1; /* fillers */ ulong _d2; . 35c static int debug = 0; . ## diffname pc/devusb.c 1999/1118 ## diff -e /n/emeliedump/1999/1117/sys/src/9/pc/devusb.c /n/emeliedump/1999/1118/sys/src/9/pc/devusb.c 1347,1348c 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)); . 1342c XPRINT("usb tree: nel=%d depth=%d\n", qt->nel, qt->depth); . 1330c XPRINT("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n", . 1328c XPRINT("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n", . 1317,1320d 1279c DPRINT("USB: %x/%x port 0x%lux size 0x%x irq %d\n", . 1188c "ctl", {Qctl}, 0, 0666, . 1184c "port", {Qport}, 0, 0444, . 1182c "new", {Qnew}, 0, 0666, . 742d 739d 731d 714d 710d 707c if(e->debug) pprint("QTD: %8.8lux n=%ld\n", t, b?BLEN(b): 0); . 695d 380,381c if(debug && t->bp && (t->flags & CancelTD) == 0) dumpdata(t->bp, n); . 30d ## diffname pc/devusb.c 1999/1119 ## diff -e /n/emeliedump/1999/1118/sys/src/9/pc/devusb.c /n/emeliedump/1999/1119/sys/src/9/pc/devusb.c 1841,1843c d->class = strtoul(fields[1], nil, 0); d->subclass = strtoul(fields[2], nil, 0); d->proto = strtoul(fields[3], nil, 0); . 1839c } else if(nf > 3 && strcmp(fields[0], "class") == 0){ . ## diffname pc/devusb.c 1999/1120 ## diff -e /n/emeliedump/1999/1119/sys/src/9/pc/devusb.c /n/emeliedump/1999/1120/sys/src/9/pc/devusb.c 1882,1884c if((e = d->ep[i]) == nil) e = devendpt(d, i, 1); . 1839,1843c } else if(nf > 5 && strcmp(fields[0], "class") == 0){ i = strtoul(fields[2], nil, 0); d->npt = strtoul(fields[1], nil, 0); /* class config# class subclass proto */ if (i < 0 || i >= nelem(d->ep) || d->npt > nelem(d->ep) || i >= d->npt) error(Ebadarg); if (i == 0) { d->class = strtoul(fields[3], nil, 0); d->subclass = strtoul(fields[4], nil, 0); d->proto = strtoul(fields[5], nil, 0); } if(d->ep[i] == nil) d->ep[i] = devendpt(d, i, 1); d->ep[i]->class = strtoul(fields[3], nil, 0); d->ep[i]->subclass = strtoul(fields[4], nil, 0); d->ep[i]->proto = strtoul(fields[5], nil, 0); . 1688c l += snprint(s+l, READSTR-l, "%2d class 0x%2.2ux subclass 0x%2.2ux proto 0x%2.2ux bytes %10lud blocks %10lud\n", i, e->class, e->subclass, e->proto, e->nbytes, e->nblocks); . 203,207c [Disabled] "Disabled", [Attached] "Attached", [Enabled] "Enabled", [Assigned] "Assigned", [Configured] "Configured", . 184c Udev* ports; /* active ports, if hub */ . 181,182c int ls; int npt; . 173,176c int x; /* index in usbdev[] */ int busy; int state; int id; . ## diffname pc/devusb.c 1999/1209 ## diff -e /n/emeliedump/1999/1120/sys/src/9/pc/devusb.c /n/emeliedump/1999/1209/sys/src/9/pc/devusb.c 1283c /* * Interrupt handler. * Bail out if no IRQ assigned by the BIOS. */ if(cfg->intl == 0xFF) return; . ## diffname pc/devusb.c 1999/1230 ## diff -e /n/emeliedump/1999/1209/sys/src/9/pc/devusb.c /n/emeliedump/1999/1230/sys/src/9/pc/devusb.c 1425a if(s == DEVDOTDOT){ devdir(c, (Qid){CHDIR|Q2nd, 0}, "usb", 0, eve, 0555, dp); return 1; } . 1403a if(s == DEVDOTDOT){ devdir(c, (Qid){CHDIR, 0}, "#U", 0, eve, 0555, dp); return 1; } . 1391a if(s == DEVDOTDOT){ devdir(c, (Qid){CHDIR, 0}, "#U", 0, eve, 0555, dp); return 1; } . ## diffname pc/devusb.c 2000/0308 ## diff -e /n/emeliedump/1999/1230/sys/src/9/pc/devusb.c /n/emeliedump/2000/0308/sys/src/9/pc/devusb.c 1853c nf = getfields(cmd, fields, nelem(fields), 0, " \t\n"); . 1825c nf = getfields(cmd, fields, nelem(fields), 0, " \t\n"); . 1779,1808d ## diffname pc/devusb.c 2000/0517 ## diff -e /n/emeliedump/2000/0308/sys/src/9/pc/devusb.c /n/emeliedump/2000/0517/sys/src/9/pc/devusb.c 1287c if(cfg->intl == 0xFF || cfg->intl == 0) . ## diffname pc/devusb.c 2000/0719 ## diff -e /n/emeliedump/2000/0517/sys/src/9/pc/devusb.c /n/emeliedump/2000/0719/sys/src/9/pc/devusb.c 23,29d ## diffname pc/devusb.c 2000/0720 ## diff -e /n/emeliedump/2000/0719/sys/src/9/pc/devusb.c /n/emeliedump/2000/0720/sys/src/9/pc/devusb.c 1250,1257c cfg = pcimatch(0, 0x1106, 0x0586); /* Via chipset */ if(cfg == nil) { DPRINT("No USB device found\n"); return; } . ## diffname pc/devusb.c 2000/0722 ## diff -e /n/emeliedump/2000/0720/sys/src/9/pc/devusb.c /n/emeliedump/2000/0722/sys/src/9/pc/devusb.c 1695c l += snprint(s+l, READSTR-l, "%2d %ud.%ud.%ud bytes %10lud blocks %10lud\n", i, e->class, e->subclass, e->proto, e->nbytes, e->nblocks); . 1692c l = snprint(s, READSTR, "%s %d.%d.%d\n", devstates[d->state], d->class, d->subclass, d->proto); . ## diffname pc/devusb.c 2000/0724 ## diff -e /n/emeliedump/2000/0722/sys/src/9/pc/devusb.c /n/emeliedump/2000/0724/sys/src/9/pc/devusb.c 1830,1832c d->ep[i]->csp = strtoul(fields[3], nil, 0); . 1824,1826c d->csp = strtoul(fields[3], nil, 0); . 1819c /* class config# csp ( == class subclass proto) */ . 1816c } else if(nf > 3 && strcmp(fields[0], "class") == 0){ . 1695c l += snprint(s+l, READSTR-l, "%2d %#6.6x %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks); . 1692c l = snprint(s, READSTR, "%s %#6.6x\n", devstates[d->state], d->csp); . 993c if(Class(d->csp) == Hubclass) . 214,216c ulong csp; . 171,173c ulong csp; . 61a #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)) . ## diffname pc/devusb.c 2000/0725 ## diff -e /n/emeliedump/2000/0724/sys/src/9/pc/devusb.c /n/emeliedump/2000/0725/sys/src/9/pc/devusb.c 1936a } . 1935c } if((e = d->ep[t]) == nil || e->mode == OREAD) { . 1933c if((t -= Qep0) < 0 || t >= nelem(d->ep)) { print("t = %d\n", t); . 1878c e->mode = strcmp(fields[3],"r") == 0? OREAD : strcmp(fields[3],"w") == 0? OWRITE : ORDWR; . 1696c l += snprint(s+l, READSTR-l, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks); . 1693c l = snprint(s, READSTR, "%s %#6.6lux\n", devstates[d->state], d->csp); . ## diffname pc/devusb.c 2001/0126 ## diff -e /n/emeliedump/2000/0725/sys/src/9/pc/devusb.c /n/emeliedump/2001/0126/sys/src/9/pc/devusb.c 1263c port = cfg->mem[4].bar & ~0x0F; DPRINT("USB: %x/%x port 0x%ux size 0x%x irq %d\n", . 1261d 1257,1259c if(cfg == nil) . 1255a if((cfg->mem[4].bar & ~0x0F) != 0) break; . 1248,1254c for(cfg = pcimatch(nil, 0, 0); cfg != nil; 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; switch((cfg->did<<16)|cfg->vid){ default: continue; case (0x7112<<16)|0x8086: /* 82371[AE]B (PIIX4[E]) */ case (0x719A<<16)|0x8086: /* 82443MX */ case (0x1106<<16)|0x0586: /* VIA 82C586 */ break; . 1241,1242c int i, port; . ## diffname pc/devusb.c 2001/0131 ## diff -e /n/emeliedump/2001/0126/sys/src/9/pc/devusb.c /n/emeliedump/2001/0131/sys/src/9/pc/devusb.c 1319a } . 1312a if(0){ /* DISABLE ERRATA FOR NOW */ . 841c pprint(" -> %8.8lux h=%8.8lux e=%8.8lux\n", p, q->head, q->entries); . 838c pprint("Q: p=%8.8lux out of range\n", p); . 834c pprint("F%.2d %8.8lux %8.8lux\n", i, frame[i], QFOL(frame[i])->head); . 800a print("after loop tree root %8.8lux head %8.8lux entries %8.8lux\n", tree, tree->head, tree->entries); . 793a print("tree root %8.8lux\n", tree); . ## diffname pc/devusb.c 2001/0201 ## diff -e /n/emeliedump/2001/0131/sys/src/9/pc/devusb.c /n/emeliedump/2001/0201/sys/src/9/pc/devusb.c 1315c if(0){ . ## diffname pc/devusb.c 2001/0216 ## diff -e /n/emeliedump/2001/0201/sys/src/9/pc/devusb.c /n/emeliedump/2001/0216/sys/src/9/pc/devusb.c 802d 794d ## diffname pc/devusb.c 2001/0503 ## diff -e /n/emeliedump/2001/0216/sys/src/9/pc/devusb.c /n/emeliedump/2001/0503/sys/src/9/pc/devusb.c 1815a if (d == nil) error(Ehungup); . 1712a if (d == nil) error(Ehungup); . 1696a if (d == nil) error(Ehungup); . 1679a if (d == nil) error(Ehungup); . 1674a if (d == nil) error(Ehungup); . 1579,1584d 1575,1577c d = usbdeviceofpath(c->qid.path); if (d && d->id == c->qid.vers) { if(QID(c->qid) == Qctl) d->busy = 0; if(c->flag & COPEN) freedev(d); . 1526a if (d == nil) error(Ehungup); . 1517a if (d == nil) error(Ehungup); . 1200c return nil; . ## diffname pc/devusb.c 2001/0504 ## diff -e /n/emeliedump/2001/0503/sys/src/9/pc/devusb.c /n/emeliedump/2001/0504/sys/src/9/pc/devusb.c 735,749d 722a t = alloctde(e, TokIN, e->maxpkt); . 721d ## diffname pc/devusb.c 2001/0527 ## diff -e /n/emeliedump/2001/0504/sys/src/9/pc/devusb.c /n/emeliedump/2001/0527/sys/src/9/pc/devusb.c 1810,1811d 1705,1706d 1687,1688d 1668,1669d 1661,1662d 1570a d = usbdevice(c); if(QID(c->qid) == Qctl) d->busy = 0; if(c->flag & COPEN) freedev(d); poperror(); . 1564,1569c if(waserror()){ qunlock(&usbstate); nexterror(); . 1514,1515d 1503,1504d 1306d 1298d 1253,1255c DPRINT("USB: %x/%x port 0x%lux size 0x%x irq %d\n", . 1251a } . 1250c port = cfg->mem[4].bar & ~0x0F; if (port == 0) { print("usb: failed to map registers\n"); . 1247,1248d 1232,1245c memset(&cfg, 0, sizeof(cfg)); cfg = pcimatch(0, 0x8086, 0x7112); /* Intel chipset PIIX 4*/ if(cfg == nil) { cfg = pcimatch(0, 0x1106, 0x0586); /* Via chipset */ if(cfg == nil) { DPRINT("No USB device found\n"); return; . 1226c int i; ulong port; . 1185c error(Ehungup); . 826c XPRINT(" -> %8.8lux h=%8.8lux e=%8.8lux\n", p, q->head, q->entries); . 823c XPRINT("Q: p=%8.8lux out of range\n", p); . 819c XPRINT("F%.2d %8.8lux %8.8lux\n", i, frame[i], QFOL(frame[i])->head); . 736a 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; } . 722a b = allocb(e->maxpkt); . 721d ## diffname pc/devusb.c 2001/0624 ## diff -e /n/emeliedump/2001/0527/sys/src/9/pc/devusb.c /n/emeliedump/2001/0624/sys/src/9/pc/devusb.c 882,1966c if(!e->periodic || e->sched . ## diffname pc/devusb.c 2001/0626 ## diff -e /n/emeliedump/2001/0624/sys/src/9/pc/devusb.c /n/emeliedump/2001/0626/sys/src/9/pc/devusb.c 882c if(!e->periodic || e->sched >= 0) return 0; ub = &ubus; if(e->epq == nil){ e->epq = allocqh(ub); if(e->epq == nil) return -1; } qlock(ub->tree); q = pickschedq(ub->tree, e->pollms, e->bw, ~0); /* TO DO: bus bandwidth limit */ if(q < 0) return -1; ub->tree->bw[q] += e->bw; qh = &ub->tree->root[q]; e->sched = q; e->epq->head = qh->entries; e->epq->entries = Terminate; qh->entries = PADDR(e->epq) | IsQH; qunlock(ub->tree); return 0; } static void unschedendpt(Endpt *e) { Ctlr *ub; ulong p; QH *qh; int q; ub = &ubus; if(!e->periodic || (q = e->sched) < 0) return; p = PADDR(e->epq) | IsQH; qlock(ub->tree); ub->tree->bw[q] -= e->bw; qh = &ub->tree->root[q]; for(; qh->entries != p; qh = QFOL(qh->entries)) if(qh->entries & Terminate || (qh->entries & IsQH) == 0){ qunlock(ub->tree); panic("usb: unschedendpt"); } qh->entries = e->epq->head; qunlock(ub->tree); e->epq->head = Terminate; } static Endpt * devendpt(Udev *d, int id, int add) { Endpt *e, **p; Ctlr *ub; ub = &ubus; p = &d->ep[id&0xF]; lock(d); if((e = *p) != nil){ incref(e); unlock(d); return e; } unlock(d); if(!add) return nil; e = mallocz(sizeof(*e), 1); e->ref = 1; e->x = id&0xF; e->id = id; e->periodic = 0; e->sched = -1; e->maxpkt = 8; e->pollms = 0; e->bw = 0; e->nbuf = 1; e->dev = d; e->active = 0; e->epq = allocqh(ub); if(e->epq == nil) panic("devendpt"); lock(d); if(*p != nil){ incref(*p); unlock(d); 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 void freept(Endpt *e) { if(e != nil && decref(e) == 0){ XPRINT("freept(%d,%d)\n", e->dev->x, e->x); unschedendpt(e); e->dev->ep[e->x] = nil; eptdeactivate(e); if(e->epq != nil) freeqh(&ubus, e->epq); free(e); } } 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 i; if(d != nil && decref(d) == 0){ for(i=0; iep); i++) freept(d->ep[i]); if(d->x >= 0) usbdev[d->x] = nil; free(d); } } 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 interrupt(Ureg*, void *a) { Ctlr *ub; Endpt *e; int s; QH *q; ub = a; s = IN(Status); OUT(Status, s); if ((s & 0x1f) == 0) return; XPRINT("usbint: #%x f%d\n", s, IN(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)); } 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); } ilock(&activends); for(e = activends.f; e != nil; e = e->activef) if(e->epq != nil) { XPRINT("cleanq(e->epq, 0, 0)\n"); cleanq(e->epq, 0, 0); } iunlock(&activends); } 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]; } static Udev * usbdevice(Chan *c) { Udev *d; d = usbdeviceofpath(c->qid.path); if(d == nil || d->id != c->qid.vers || d->state == Disabled) error(Ehungup); return d; } static Udev * usbnewdevice(void) { Udev *d; Endpt *e; int i; d = nil; qlock(&usbstate); if(waserror()){ qunlock(&usbstate); nexterror(); } for(i=0; iref = 1; d->x = i; d->id = (ubus.idgen << 8) | i; d->state = Enabled; e = devendpt(d, 0, 1); /* always provide control endpoint 0 */ e->mode = ORDWR; e->periodic = 0; e->sched = -1; usbdev[i] = d; break; } poperror(); qunlock(&usbstate); return d; } static void usbreset(void) { Pcidev *cfg; int i; ulong port; QTree *qt; TD *t; Ctlr *ub; ISAConf isa; if(isaconfig("usb", 0, &isa) == 0) { XPRINT("usb not in plan9.ini\n"); // return; } ub = &ubus; memset(&cfg, 0, sizeof(cfg)); cfg = pcimatch(0, 0x8086, 0x7112); /* Intel chipset PIIX 4*/ if(cfg == nil) { cfg = pcimatch(0, 0x1106, 0x0586); /* Via chipset */ if(cfg == nil) { DPRINT("No USB device found\n"); return; } } port = cfg->mem[4].bar & ~0x0F; if (port == 0) { print("usb: failed to map registers\n"); return; } 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(1){ 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, interrupt, ub, cfg->tbdf, "usb"); ub->io = port; ub->tdpool = xspanalloc(128*sizeof(TD), 16, 0); for(i=128; --i>=0;){ ub->tdpool[i].next = ub->freetd; ub->freetd = &ub->tdpool[i]; } ub->qhpool = xspanalloc(32*sizeof(QH), 16, 0); for(i=32; --i>=0;){ ub->qhpool[i].next = ub->freeqh; ub->freeqh = &ub->qhpool[i]; } /* * the root of the periodic (interrupt & isochronous) scheduling tree * points 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->bwsop->entries = PADDR(t); ub->ctlq->head = PADDR(ub->bwsop) | IsQH; ub->bwsop->head = PADDR(ub->bulkq) | IsQH; ub->bulkq->head = PADDR(ub->recvq) | IsQH; ub->recvq->head = PADDR(ub->bwsop) | IsQH; /* loop back */ 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 = xspanalloc(FRAMESIZE, FRAMESIZE, 0); qt = mkqhtree(ub->frames, NFRAME, 32); if(qt == nil){ print("usb: can't allocate scheduling tree\n"); ub->io = 0; return; } qt->root->head = PADDR(ub->ctlq) | IsQH; ub->tree = qt; XPRINT("usb tree: nel=%d depth=%d\n", qt->nel, qt->depth); 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; 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); } static int usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) { int t; Qid q; ulong path; Udev *d; Dirtab *tab; Endpt *e; /* * Top level directory contains the name of the device. */ 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; } /* * Second level contains "new" plus all the clients. */ t = QID(c->qid); if(t < Q3rd){ if(s == DEVDOTDOT){ mkqid(&q, Qtopdir, 0, QTDIR); devdir(c, q, "#U", 0, eve, 0555, dp); return 1; } if(s < nelem(usbdir2)){ tab = &usbdir2[s]; devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); return 1; } s -= nelem(usbdir2); if(s >= 0 && s < nelem(usbdev)){ d = usbdev[s]; if(d == nil) return -1; sprint(up->genbuf, "%d", s); mkqid(&q, ((s+1)<id, QTDIR); devdir(c, q, up->genbuf, 0, eve, 0555, dp); return 1; } return -1; } /* * Third level. */ 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); return 1; } if(s < nelem(usbdir3)){ Dirtab *tab = &usbdir3[s]; mkqid(&q, path | tab->qid.path, c->qid.vers, QTFILE); devdir(c, q, tab->name, tab->length, eve, tab->perm, dp); return 1; } /* active endpoints */ d = usbdeviceofpath(path); if(d == nil) return -1; s -= nelem(usbdir3); if(s < 0 || 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); devdir(c, q, up->genbuf, 0, eve, e->mode==OREAD? 0444: e->mode==OWRITE? 0222: 0666, dp); return 1; } static Walkqid* usbwalk(Chan *c, Chan *nc, char **name, int nname) { return devwalk(c, nc, name, nname, nil, 0, usbgen); } static int usbstat(Chan *c, uchar *db, int n) { return devstat(c, db, n, nil, 0, usbgen); } Chan * usbopen(Chan *c, int omode) { Udev *d; int f, s; if(c->qid.type == QTDIR) return devopen(c, omode, nil, 0, usbgen); f = 0; if(QID(c->qid) == Qnew){ d = usbnewdevice(); if(d == nil) { XPRINT("usbopen failed (usbnewdevice)\n"); error(Enodev); } c->qid.path = Qctl|((d->x+1)<qid.vers = d->id; f = 1; } if(c->qid.path < Q3rd) return devopen(c, omode, nil, 0, usbgen); qlock(&usbstate); if(waserror()){ qunlock(&usbstate); nexterror(); } switch(QID(c->qid)){ case Qctl: d = usbdevice(c); if(0&&d->busy) error(Einuse); d->busy = 1; if(!f) incref(d); break; default: d = usbdevice(c); s = QID(c->qid) - Qep0; if(s >= 0 && s < nelem(d->ep)){ Endpt *e; if((e = d->ep[s]) == nil) { XPRINT("usbopen failed (endpoint)\n"); error(Enodev); } if(schedendpt(e) < 0) error("can't schedule USB endpoint"); eptactivate(e); } incref(d); break; } poperror(); qunlock(&usbstate); 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 usbwstat(Chan *c, char *dp) { USED(c, dp); error(Eperm); } void usbclose(Chan *c) { Udev *d; if(c->qid.type == QTDIR || c->qid.path < Q3rd) return; qlock(&usbstate); if(waserror()){ qunlock(&usbstate); nexterror(); } d = usbdevice(c); if(QID(c->qid) == Qctl) d->busy = 0; if(c->flag & COPEN) freedev(d); poperror(); qunlock(&usbstate); } static int eptinput(void *arg) { Endpt *e; e = arg; return e->eof || e->err || qcanread(e->rq); } static long readusb(Endpt *e, void *a, long n) { Block *b; uchar *p; long 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; } while (l == e->maxpkt && n > 0); poperror(); qunlock(&e->rlock); return p-(uchar*)a; } long usbread(Chan *c, void *a, long n, vlong offset) { Endpt *e; Udev *d; char buf[48], *s; int t, w0, w1, ps, l, i; 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); 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; e->data01 = 1; writeusb(e, "", 0, TokOUT); } break; case Qdebug: n=0; break; 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 += snprint(s+l, READSTR-l, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks); n = readstr(offset, a, n, s); poperror(); free(s); 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 */ n=readusb(e, a, n); break; } return n; } static int qisempty(void *arg) { return ((QH*)arg)->entries & Terminate; } static long writeusb(Endpt *e, void *a, long n, int tok) { long i; Block *b; uchar *p; QH *qh; p = a; qlock(&e->wlock); if(waserror()){ qunlock(&e->wlock); eptcancel(e); nexterror(); } 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 [%ld]", 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; if(!e->iso) e->data01 ^= 1; if(e->ntd >= e->nbuf) { XPRINT("writeusb: sleep %lux\n", &e->wr); sleep(&e->wr, qisempty, qh); XPRINT("writeusb: awake\n"); } } while(n > 0); poperror(); qunlock(&e->wlock); return p-(uchar*)a; } long usbwrite(Chan *c, void *a, long n, vlong) { Udev *d; Endpt *e; int id, nw, nf, t, i; char cmd[50], *fields[10]; if(c->qid.type == QTDIR) error(Egreg); t = QID(c->qid); if(t == Qbusctl){ if(n >= sizeof(cmd)-1) n = sizeof(cmd)-1; memmove(cmd, a, n); cmd[n] = 0; nf = getfields(cmd, fields, nelem(fields), 0, " \t\n"); if(nf==1 && strcmp(fields[0], "dump")==0){ dumpframe(-1, -1); return n; } if(nf < 2) error(Ebadarg); id = strtol(fields[1], nil, 0); if(id != 1 && id != 2) error(Ebadarg); /* there are two ports on the root hub */ if(strcmp(fields[0], "reset") == 0) portreset(id); else if(strcmp(fields[0], "enable") == 0) portenable(id, 1); else if(strcmp(fields[0], "disable") == 0) portenable(id, 0); else error(Ebadarg); return n; } d = usbdevice(c); t = QID(c->qid); switch(t){ case Qctl: if(n >= sizeof(cmd)-1) n = sizeof(cmd)-1; memmove(cmd, a, n); cmd[n] = 0; nf = getfields(cmd, fields, nelem(fields), 0, " \t\n"); if(nf > 1 && strcmp(fields[0], "speed") == 0){ d->ls = strtoul(fields[1], nil, 0) == 0; } else if(nf > 3 && strcmp(fields[0], "class") == 0){ i = strtoul(fields[2], nil, 0); d->npt = strtoul(fields[1], nil, 0); /* class config# csp ( == class subclass proto) */ if (i < 0 || i >= nelem(d->ep) || d->npt > nelem(d->ep) || i >= d->npt) error(Ebadarg); if (i == 0) { d->csp = strtoul(fields[3], nil, 0); } if(d->ep[i] == nil) d->ep[i] = devendpt(d, i, 1); d->ep[i]->csp = strtoul(fields[3], nil, 0); }else if(nf > 2 && strcmp(fields[0], "data") == 0){ i = strtoul(fields[1], nil, 0); if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) error(Ebadarg); e = d->ep[i]; e->data01 = strtoul(fields[2], nil, 0) != 0; }else if(nf > 2 && strcmp(fields[0], "maxpkt") == 0){ i = strtoul(fields[1], nil, 0); if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) error(Ebadarg); e = d->ep[i]; e->maxpkt = strtoul(fields[2], nil, 0); if(e->maxpkt > 1500) e->maxpkt = 1500; }else if(nf > 2 && strcmp(fields[0], "debug") == 0){ i = strtoul(fields[1], nil, 0); if(i < -1 || i >= nelem(d->ep) || d->ep[i] == nil) error(Ebadarg); if (i == -1) debug = 0; else { debug = 1; e = d->ep[i]; e->debug = strtoul(fields[2], nil, 0); } }else if(nf > 1 && strcmp(fields[0], "unstall") == 0){ i = strtoul(fields[1], nil, 0); if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) error(Ebadarg); e = d->ep[i]; e->err = nil; }else if(nf == 6 && strcmp(fields[0], "ep") == 0){ /* ep n maxpkt mode poll nbuf */ i = strtoul(fields[1], nil, 0); if(i < 0 || i >= nelem(d->ep)) { XPRINT("field 1: 0 <= %d < %d\n", i, nelem(d->ep)); error(Ebadarg); } if((e = d->ep[i]) == nil) e = devendpt(d, i, 1); if(waserror()){ freept(e); nexterror(); } i = strtoul(fields[2], nil, 0); if(i < 8 || i > 1023) i = 8; e->maxpkt = i; e->mode = strcmp(fields[3],"r") == 0? OREAD : strcmp(fields[3],"w") == 0? OWRITE : ORDWR; e->periodic = 0; e->sched = -1; if(strcmp(fields[4], "bulk") == 0){ Ctlr *ub; 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); } else { e->periodic = 1; i = strtoul(fields[4], nil, 0); if(i > 0 && i <= 1000) e->pollms = i; else { XPRINT("field 4: 0 <= %d <= 1000\n", i); error(Ebadarg); } } i = strtoul(fields[5], nil, 0); if(i >= 1 && i <= 32) e->nbuf = i; poperror(); }else { XPRINT("command %s, fields %d\n", fields[0], nf); error(Ebadarg); } return n; case Qsetup: /* SETUP endpoint 0 */ /* should canqlock etc */ if((e = d->ep[0]) == nil) error(Eio); /* can't happen */ 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 */ e->data01 = 1; nw = readusb(e, cmd, 8); if(nw != 0) error(Eio); /* could provide more status */ }else e->setin = 1; /* two-phase */ 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 */ } n = writeusb(e, a, n, TokOUT); break; } return n; } Dev usbdevtab = { 'U', "usb", usbreset, usbinit, usbattach, usbwalk, usbstat, usbopen, devcreate, usbclose, usbread, devbread, usbwrite, devbwrite, devremove, devwstat, }; . 568,570c tp = t; t = t->next; continue; . 553c 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); . 490c XPRINT("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); . 482c XPRINT("cleanTD: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); . 372c XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n", buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1); . 218,223c byte mode; /* OREAD, OWRITE, ORDWR */ byte nbuf; /* number of buffers allowed */ byte periodic; byte iso; byte debug; byte active; /* listed for examination by interrupts */ . 216c byte eof; . 202c [Assigned] "Assigned", . 200c [Attached] "Attached", . ## diffname pc/devusb.c 2001/0726 ## diff -e /n/emeliedump/2001/0626/sys/src/9/pc/devusb.c /n/emeliedump/2001/0726/sys/src/9/pc/devusb.c 1256c return; . ## diffname pc/devusb.c 2001/0916 ## diff -e /n/emeliedump/2001/0726/sys/src/9/pc/devusb.c /n/emeliedump/2001/0916/sys/src/9/pc/devusb.c 1904,1906d 1902a e->mode = strcmp(fields[3],"r") == 0? OREAD : strcmp(fields[3],"w") == 0? OWRITE : ORDWR; i = strtoul(fields[4], nil, 0); if(i >= 1 && i <= 8){ e->samplesz = i; }else { XPRINT("field 4: 0 < %d <= 8\n", i); error(Ebadarg); } i = strtoul(fields[5], nil, 0); if(i >= 1 && i <= 100000){ /* Hz */ e->hz = i; e->remain = 999/e->pollms; }else { XPRINT("field 5: 1 < %d <= 100000 Hz\n", i); error(Ebadarg); } e->maxpkt = (e->hz * e->pollms + 999)/1000 * e->samplesz; e->iso = 1; . 1899c }else { . 1897c if(i < 8 || i > 1023) i = 8; e->maxpkt = i; i = strtoul(fields[5], nil, 0); if(i >= 1 && i <= 32) e->nbuf = i; } else { /* ep n period mode samplesize KHz */ i = strtoul(fields[2], nil, 0); if(i > 0 && i <= 1000){ . 1894,1895c e->mode = strcmp(fields[3],"r") == 0? OREAD : strcmp(fields[3],"w") == 0? OWRITE : ORDWR; . 1883a /* ep n `bulk' mode maxpkt nbuf */ . 1873,1881c if (e->iso && e->sched >= 0){ unschedendpt(e); } e->iso = 0; if(strcmp(fields[2], "bulk") == 0){ . 1861c /* ep n `bulk' mode maxpkt nbuf OR * ep n period mode samplesize KHz */ . 1786,1789d 1746,1762d 1739,1744c 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; } if (e->psize > e->maxpkt) panic("packet size > maximum"); b = e->curblock; if (b == nil) panic("writeusb: block"); 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; p += i; n -= i; e->curbytes += i; 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->curbytes = 0; e->curblock->wp = e->curblock->rp; e->nbytes += e->psize; e->nblocks++; }else{ int j; if((i = n) >= e->maxpkt) i = e->maxpkt; b = allocb(i); if(waserror()){ freeb(b); nexterror(); } XPRINT("out [%ld]", 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; if(!e->iso) e->data01 ^= 1; if(e->ntd >= e->nbuf) { XPRINT("writeusb: sleep %lux\n", &e->wr); sleep(&e->wr, qisempty, qh); XPRINT("writeusb: awake\n"); } . 1735d 1336,1338d 1330,1334c 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; . 1306,1307c * the last entries of the periodic (interrupt & isochronous) scheduling TD entries * point to the control queue and the bandwidth sop for bulk traffic. . 1294,1295c ub->tdpool = xspanalloc((NFRAME*NISOTD+128)*sizeof(TD), 16, 0); for(i=NFRAME*NISOTD+128; --i>=NFRAME*NISOTD;){ . 1249d 1247c int i, j; . 1233c e->iso = 0; . 1143a if(e->iso && e->etd != nil) { wakeup(&e->wr); } } . 1139,1140c for(e = activends.f; e != nil; e = e->activef){ if(!e->iso && e->epq != nil) { . 958d 954c e->iso = 0; . 919,930c ilock(ub); for (td = e->etd; q < NFRAME; q += e->pollms){ 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); e->sched = -1; iunlock(ub); . 917c if(!e->iso || (q = e->sched) < 0) . 912,913c TD *td, *next; . 898,904c /* 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; 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->ep = e; if (td->bp) panic("usb: schedendpt: block not empty"); td->bp = allocb(e->maxpkt); if (td->bp == nil) panic("schedept: allocb"); if (i == e->sched){ first = td; e->etd = td; }else{ *prev = td; } prev = &td->next; ub->frameld[i] += e->maxpkt; } *prev = first; /* complete circular link */ iunlock(ub); . 889,896c ilock(ub); e->sched = usbsched(ub, e->pollms, e->maxpkt); if(e->sched < 0) . 886c e->curblock = allocb(e->maxpkt); if (e->curblock == nil) panic("schedept: allocb"); e->curbytes = 0; if(!e->iso || e->sched >= 0) . 883,884c TD *td, **prev, *first; int i, j; . 874,875d 871,872d 847,869c if (worst < best){ best = worst; q = d; . 845c if (ub->frameld[i] + load > worst) worst = ub->frameld[i] + load; . 780,842c best = 1000000; q = -1; 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 */ . 776,778c int i, j, d, q; ulong best, worst; . 769,774c static int usbsched( Ctlr *ub, int pollms, ulong load) . 683a static int tdisready(void *arg) { return (((TD*)arg)->status & Active) == 0; } static Block * isoxmit(Endpt *e, Block *b, int pid) { TD *td; int n, id; Block *ob; static int ioc; 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; } . 260,264c QH* ctlq; /* queue for control i/o */ QH* bwsop; /* empty bandwidth sop (to PIIX4 errata specifications) */ QH* bulkq; /* queue for bulk i/o (points back to bandwidth sop) */ QH* recvq; /* receive queues for bulk i/o */ . 255,258c TD* tdpool; /* first NFRAMES*NISOTD entries are preallocated */ TD* freetd; QH* qhpool; QH* freeqh; . 252c ulong* frameld; /* real time load on each of the frame list entries */ int idgen; /* version number to distinguish new connections */ . 250c int io; . 228,229c 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 */ . 226d 223a int hz; int remain; int samplesz; . 220d 212c int x; /* index in Udev.ep */ . 179,180c Endpt* ep[16]; /* active end points */ Udev* ports; /* active ports, if hub */ . 175c byte port; /* port number on connecting hub */ . 171c int x; /* index in usbdev[] */ . 151,161d 120a NISOTD = 4, /* number of TDs for isochronous io per frame */ . 53d 25c #define XPRINT if(debug)iprint . ## diffname pc/devusb.c 2001/0917 ## diff -e /n/emeliedump/2001/0916/sys/src/9/pc/devusb.c /n/emeliedump/2001/0917/sys/src/9/pc/devusb.c 1730d 1620,1630c } while (n > 0); . 1618c n -= i; if (l != e->maxpkt) break; . 1605,1616c if (e->iso){ if (e->curbytes == 0){ /* calculate size of next receive buffer */ e->psize = (e->hz + e->remain)*e->pollms/1000; e->remain = (e->hz + e->remain)*e->pollms%1000; e->psize *= e->samplesz; if (e->psize > e->maxpkt) panic("packet size > maximum"); e->curblock->wp = e->curblock->rp + e->psize; /* 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 (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{ 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(); . 845a e->curblock = allocb(e->maxpkt); if (e->curblock == nil) panic("schedept: allocb"); e->curbytes = 0; . 838,841d 716a 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: isoxmit"); 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; 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; } . ## diffname pc/devusb.c 2001/0918 ## diff -e /n/emeliedump/2001/0917/sys/src/9/pc/devusb.c /n/emeliedump/2001/0918/sys/src/9/pc/devusb.c 2029a qunlock(&usbstate); . 2021c // e->remain = 999/e->pollms; e->remain = 0; . 1978a e->iso = 0; . 1972,1975c if (e->isopen) error(Eperm); . 1969a qunlock(&usbstate); . 1967a qlock(&usbstate); . 1646,1651d 1557a e->isopen = 1; . 1001a e->isopen = 0; . 947,948d 934,935c . 920d 910c if (e->mode == OREAD){ int id; /* 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; } if(i == e->sched){ . 908c if(td->bp == nil) . 905c if(td->bp) . 887a } . 886c if(e->sched < 0){ qunlock(&usbstate); . 884d 879c if (e->isopen){ return -1; } . 746,750c td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | pid; td->status = ErrLimit3 | Active | IsoSelect | IOC; . 744d 727c panic("usb: isorecv"); . 206a byte isopen; /* ep operations forbidde on open endpoints */ . ## diffname pc/devusb.c 2001/0922 ## diff -e /n/emeliedump/2001/0918/sys/src/9/pc/devusb.c /n/emeliedump/2001/0922/sys/src/9/pc/devusb.c 1610a s = QID(c->qid) - Qep0; if(s >= 0 && s < nelem(d->ep)){ Endpt *e; if((e = d->ep[s]) == nil) { XPRINT("usbopen failed (endpoint)\n"); error(Enodev); } e->isopen--; } . 1601a int s; . 1565d 1563a e->isopen++; . 1008d 981d 978d 882a e->curblock = allocb(e->maxpkt); if (e->curblock == nil) . 880,881d 207c byte isopen; /* ep operations forbidden on open endpoints */ . ## diffname pc/devusb.c 2001/0928 ## diff -e /n/emeliedump/2001/0922/sys/src/9/pc/devusb.c /n/emeliedump/2001/0928/sys/src/9/pc/devusb.c 1968c * ep n period mode samplesize hz . ## diffname pc/devusb.c 2001/1003 ## diff -e /n/emeliedump/2001/0928/sys/src/9/pc/devusb.c /n/emeliedump/2001/1003/sys/src/9/pc/devusb.c 710,713c td->status = ErrLimit1 | Active | IsoSelect | IOC; . 695c while(td->status & Active && (td->dev & 0x7ff<21) != (0x7ff<21)){ . ## diffname pc/devusb.c 2001/1004 ## diff -e /n/emeliedump/2001/1003/sys/src/9/pc/devusb.c /n/emeliedump/2001/1004/sys/src/9/pc/devusb.c 710c if ((ioc++ & 0xff) == 0) td->status = ErrLimit3 | Active | IsoSelect | IOC; else td->status = ErrLimit3 | Active | IsoSelect; . 695c while(td->status & Active){ . 690a TD *td; . 687,688d ## diffname pc/devusb.c 2001/1005 ## diff -e /n/emeliedump/2001/1004/sys/src/9/pc/devusb.c /n/emeliedump/2001/1005/sys/src/9/pc/devusb.c 688c static int ioc, n, id; . ## diffname pc/devusb.c 2001/1006 ## diff -e /n/emeliedump/2001/1005/sys/src/9/pc/devusb.c /n/emeliedump/2001/1006/sys/src/9/pc/devusb.c 1967c * ep n period mode samplesize KHz . 1871,1872c } while(n > 0); } . 1854c XPRINT("out [%d]", i); . 1846a if(e->err) error(e->err); . 1844c } while(n > 0); poperror(); }else{ do { . 1841c e->psize = ((e->etd->dev >> 21) & 0x7ff) + 1; if (e->psize > e->maxpkt) panic("packet size > maximum"); . 1837,1839c e->etd = td->next; . 1832d 1830a td->flags &= ~IsoLock; . 1824,1827c memmove(q, p, i); . 1817,1821c td = e->etd; e->isolock = 0; td->flags |= IsoLock; td->flags &= ~IsoClean; q = (uchar*)td->bp + e->curbytes; . 1808,1815c 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"); . 1799a TD *td; . 1798c uchar *p, *q; . 1796c int i; . 1792a 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; } . 1708,1709c } while (n > 0); } . 1670,1680c 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); . 1650,1668c 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"); } . 1638,1639c uchar *p, *q; long l, i, id; TD *td; . 1633a 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; } . 1615a if (e->isopen == 0){ eptdeactivate(e); unschedendpt(e); } . 1355,1374c for (i = 0; i < NFRAME; i++) ub->frames[i] = PADDR(ub->ctlq) | IsQH; . 1317,1318c ub->tdpool = xspanalloc(128*sizeof(TD), 16, 0); for(i=128; --i>=0;){ . 1271c int i; . 1164,1165c if(e->iso) { XPRINT("cleaniso(e)\n"); cleaniso(e, frnum); . 1143c frnum = IN(Frnum) & 0x3ff; XPRINT("usbint: #%x f%d\n", s, frnum); . 1135c int s, frnum; . 1130a 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 . 1007d 1004a eptdeactivate(e); . 952c free(e->tdalloc); free(e->bpalloc); e->tdalloc = nil; e->bpalloc = nil; e->etd = nil; e->td0 = nil; . 945,950d 943c 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; . 940c if(!e->iso || e->sched < 0) . 936c TD *td; ulong *addr; . 928c e->isolock = 0; . 919,926c td->status = ErrLimit3 | Active | IsoSelect | IOC; td->link = ub->frames[ix]; ub->frames[ix] = PADDR(td); . 917c 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; . 914,915d 906,912c 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) . 898,904c td->bp = (Block *)(bp + e->maxpkt*i/e->pollms); td->buffer = PADDR(td->bp); . 890,896c 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; . 879,882d 869,870c TD *td; uchar *bp; int i, id, ix, size, frnum; . 847,853d 839c int i, d, q; . 678,750d 227a int isolock; . 223c 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 */ . 220d 212a byte * tdalloc; byte * bpalloc; . 210c volatile byte iso; . 148a IsoLock= 1<<1, IsoClean= 1<<2, . 119c NFRAME = (FRAMESIZE/4), . ## diffname pc/devusb.c 2001/1007 ## diff -e /n/emeliedump/2001/1006/sys/src/9/pc/devusb.c /n/emeliedump/2001/1007/sys/src/9/pc/devusb.c 1885a e->isolock = 0; . 1878,1880c e->off = 0; e->psize = ((e->etd->dev >> 21) + 1) & 0x7ff; . 1861,1876c if ((td = e->etd) == nil){ iprint("0"); ub = &ubus; frnum = (IN(Frnum) + 8) & 0x3ff; e->etd = e->td0 +frnum; }else{ td->flags &= ~IsoClean; q = (uchar*)td->bp + e->off; if((i = n) >= e->psize) i = e->psize; memmove(q, p, i); p += i; n -= i; e->off += i; e->psize -= i; if (e->psize){ if (n != 0) panic("usb iso: can't happen"); break; } td->status = ErrLimit3 | Active | IsoSelect | IOC; e->etd = td->next; . 1856,1859c while (isoready(e) == 0){ iprint("i"); sleep(&e->wr, isoready, e); . 1854a e->isolock = 1; . 1851,1852d 1837a Ctlr *ub; . 1834c int i, frnum; . 1804,1830d 1674,1683c e->off = 0; e->psize = (e->etd->status + 1) & 0x7ff; if (e->psize > e->maxpkt) panic("packet size > maximum"); e->nbytes += e->psize; e->nblocks++; } while(n > 0); e->isolock = 0; poperror(); . 1672a td->status = ErrLimit3 | Active | IsoSelect | IOC; e->etd = td->next; . 1667,1668c if (e->psize){ . 1665c e->off += i; . 1655,1659c if ((td = e->etd) == nil){ ub = &ubus; frnum = (IN(Frnum) + 8) & 0x3ff; e->etd = e->td0 +frnum; }else{ q = (uchar*)td->bp + e->off; . 1648,1653c while (isoready(e) == 0){ XPRINT("readusb: sleep %lux\n", &e->wr); sleep(&e->wr, isoready, e); XPRINT("readusb: awake\n"); . 1646a if(waserror()){ e->isolock = 0; nexterror(); } e->isolock = 1; . 1635a Ctlr *ub; . 1634c int l, i, frnum; . 1607,1626c return e->etd == nil || (e->etd->status & Active) == 0; . 1602,1603d 1600c isoready(void *arg) . 1103,1104d 1100d 1093,1096c if (e->mode == OREAD){ td->dev = ((e->maxpkt -1)<<21) | ((id&0x7FF)<<8) | TokIN; }else{ 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 = td->next; if (e->xtd == td){ iprint("@"); break; } } while ((td->status & Active) == 0); e->xtd = td; if (e->isolock){ wakeup(&e->wr); return; } for (n = 2; n < 6; n++){ td = e->td0 + ((frnum + n)&0x3ff); if (td->status & Active) continue; if (td == e->etd) { memset((byte*)td->bp+e->off, 0, e->maxpkt-e->off); if (e->off == 0) td->flags |= IsoClean; e->etd = nil; }else if ((td->flags & IsoClean) == 0){ . 1081,1090c do{ . 1072,1079c td = e->xtd; if (td->status & Active) return; . 1070c int id, n; . 854d 821,823c e->xtd = &e->td0[(frnum+8)&0x3ff]; /* Next td to finish */ e->etd = nil; . 806c e->off = 0; . 233d 226,228d 223,224c int psize; /* (remaining) size of this packet */ int off; /* offset into packet */ int isolock; /* reader/writer interlock with interrupt */ TD* td0; /* first td in array */ TD* etd; /* pointer into circular list of TDs for isochronous ept */ TD* xtd; /* next td to be cleaned */ /* end ISO stuff */ . 221d 218c int remain; /* for packet size calculations */ . 214a int setin; /* ISO related: */ . 149d ## diffname pc/devusb.c 2001/1008 ## diff -e /n/emeliedump/2001/1007/sys/src/9/pc/devusb.c /n/emeliedump/2001/1008/sys/src/9/pc/devusb.c 1857a p = a; . 1856a qunlock(&e->wlock); return n; . 1815,1855c n = isoio(e, a, n, 1); . 1813d 1806d 1803,1804d 1801c uchar *p; . 1799c int i; . 1676a p = a; . 1675a qunlock(&e->rlock); return n; . 1673,1674c } td->flags &= ~IsoClean; q = (uchar*)td->bp + e->off; if((i = n) >= e->psize) i = e->psize; 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; } td->status = ErrLimit3 | Active | IsoSelect | IOC; e->etd = td->next; e->off = 0; } while(n > 0); e->isolock = 0; poperror(); return p-(uchar*)a; } static long readusb(Endpt *e, void *a, long n) { 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(); } if (e->iso){ n = isoio(e, a, n, 0); . 1667,1668c if (w) e->psize = ((td->dev >> 21) + 1) & 0x7ff; else e->psize = (e->etd->status + 1) & 0x7ff; . 1646,1665c e->isolock = 1; if (e->etd == nil){ XPRINT("!"); continue; . 1644d 1642d 1637,1640d 1633,1635c e->isolock = 1; do { td = e->etd; if (td == nil || e->off == 0){ if (td == nil){ XPRINT("0"); ub = &ubus; if (w) frnum = (IN(Frnum) + 8) & 0x3ff; else frnum = (IN(Frnum) - 8) & 0x3ff; td = e->etd = e->td0 +frnum; e->off = 0; } /* New td, make sure it's ready */ . 1629,1630c e->isolock = 0; . 1625,1627c p = a; . 1623a TD *td; . 1621,1622d 1619c int i, frnum; . 1617c isoio(Endpt *e, void *a, long n, int w) . 1613c return e->etd == nil || (e->etd != e->xtd && (e->etd->status & Active) == 0); . 1112a wakeup(&e->wr); . 1111c td->status = ErrLimit1 | Active | IsoSelect | IOC; . 1107a XPRINT("-"); . 1102a XPRINT("*"); . 1096d 1093,1094c if (e->isolock) . 1088c XPRINT("@"); . 851a td->flags |= IsoClean; . 850c td->status = ErrLimit1 | Active | IsoSelect | IOC; . 847d 836c ix = (frnum+i) & 0x3ff; . ## diffname pc/devusb.c 2001/1009 ## diff -e /n/emeliedump/2001/1008/sys/src/9/pc/devusb.c /n/emeliedump/2001/1009/sys/src/9/pc/devusb.c 1805,1806c if((e = d->ep[i]) != nil){ /* TO DO: freeze e */ l += snprint(s+l, READSTR-l, "%2d %#6.6lux %10lud bytes %10lud blocks", i, e->csp, e->nbytes, e->nblocks); if (e->iso){ l += snprint(s+l, READSTR-l, "iso "); } l += snprint(s+l, READSTR-l, "\n"); } . ## diffname pc/devusb.c 2001/1010 ## diff -e /n/emeliedump/2001/1009/sys/src/9/pc/devusb.c /n/emeliedump/2001/1010/sys/src/9/pc/devusb.c 2087c if (e->iso) n = isoio(e, a, n, offset, 1); else n = writeusb(e, a, n, TokOUT); . 1893c usbwrite(Chan *c, void *a, long n, vlong offset) . 1859,1886c 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("writeusb: sleep %lux\n", &e->wr); sleep(&e->wr, qisempty, qh); XPRINT("writeusb: awake\n"); } } while(n > 0); . 1849,1857c p = a; do { int j; . 1823c if (e->iso) n=isoio(e, a, n, offset, 0); else n=readusb(e, a, n); . 1742,1746c 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); . 1704,1740c 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()){ . 1685a qunlock(&e->rlock); . 1664c bp = e->bp0 + (td - e->td0) * e->maxpkt / e->pollms; q = bp + e->off; . 1660,1661d 1629a p = a; . 1627a qunlock(&e->rlock); eptcancel(e); . 1625c qlock(&e->rlock); . 1621c uchar *p, *q, *bp; . 1618c isoio(Endpt *e, void *a, long n, vlong offset, int w) . 1528,1529c if(schedendpt(e) < 0){ if (e->isopen) error("can't schedule USB endpoint, isopen"); else error("can't schedule USB endpoint"); } . 1334c 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); . 1330,1332c ub->ctlq->head = PADDR(ub->bulkq) | IsQH; . 1289c if(0){ . 1283a port = cfg->mem[4].bar & ~0x0F; . 1279,1281c if(cfg == nil) { DPRINT("No USB device found\n"); . 1277a if((cfg->mem[4].bar & ~0x0F) != 0) break; . 1270,1276c 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; 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; . 1156a 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); } . 1136,1144d 1130c // iprint("usbint: #%x f%d\n", s, frnum); . 1128a sapecount2++; . 1125a sapestatus = s; sapeintenb = IN(Usbintr); sapeframe = IN(Frnum); sapeport1 = IN(Portsc0); sapeport2 = IN(Portsc1); sapecmd = IN(Cmd); . 1123a sapecount1++; . 1115a static int sapecount1; static int sapecount2; static int sapecmd; static int sapestatus; static int sapeintenb; static int sapeframe; static int sapeport1; static int sapeport2; . 1108c memset(bp, 0, e->maxpkt); . 1102c memset(bp+e->off, 0, e->maxpkt-e->off); . 1096c i = ((frnum + n)&0x3ff); td = e->td0 + i; bp = e->bp0 + e->maxpkt*i/e->pollms; . 1091a e->time = todget(nil); . 1077a e->nbytes += (td->status + 1) & 0x3ff; if ((td->flags & IsoClean) == 0) e->nblocks++; . 1075c do { . 1069c int id, n, i; uchar *bp; . 827,828c bp = e->bp0 + e->maxpkt*i/e->pollms; td->buffer = PADDR(bp); . 824a e->nbytes = 0; . 819c e->bp0 = (uchar *)(((ulong)e->bpalloc + 0xf) & ~0xf); . 246a vlong time; . 230c QH * epq; /* queue of TDs for this endpoint */ . 226,228c uchar* bp0; /* first block in array */ TD * td0; /* first td in array */ TD * etd; /* pointer into circular list of TDs for isochronous ept */ TD * xtd; /* next td to be cleaned */ . 216,217c uchar* tdalloc; uchar* bpalloc; . 208,213c uchar isopen; /* ep operations forbidden on open endpoints */ uchar mode; /* OREAD, OWRITE, ORDWR */ uchar nbuf; /* number of buffers allowed */ uchar iso; uchar debug; uchar active; /* listed for examination by interrupts */ . 206c uchar eof; . 165c uchar port; /* port number on connecting hub */ . 74c union{ Block* bp; /* non-iso */ ulong offset; /* iso */ }; . 49,50d ## diffname pc/devusb.c 2001/1011 ## diff -e /n/emeliedump/2001/1010/sys/src/9/pc/devusb.c /n/emeliedump/2001/1011/sys/src/9/pc/devusb.c 2125c n = isoio(e, a, n, (ulong)offset, 1); . 1866c n=isoio(e, a, n, (ulong)offset, 0); . 1849,1850c if (e->iso && e->toffset){ l += snprint(s+l, READSTR-l, " %10lud offset %19lld time", e->toffset, e->time); . 1735c return n; . 1733a if (isolock) iunlock(&activends); . 1732c n = p-(uchar*)a; e->foffset += n; . 1709a isolock = 0; iunlock(&activends); . 1698c ilock(&activends); isolock = 1; . 1694c isolock = 0; iunlock(&activends); . 1685d 1680a if (isolock == 0){ ilock(&activends); isolock = 1; } . 1679c ub = &ubus; if (offset != 0 && 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; } . 1673c if (isolock){ isolock = 0; iunlock(&activends); } . 1671a isolock = 0; . 1666a volatile int isolock; . 1664c isoio(Endpt *e, void *a, long n, ulong offset, int w) . 1157d 1147,1152d 1144d 1127,1135d 1102,1103d 1092a td->offset = e->poffset; e->poffset += n; . 1088a e->toffset = td->offset; . 1087a e->toffset = td->offset; . 1086a e->poffset += (td->status + 1) & 0x3ff; td->offset = e->poffset; . 828a e->foffset = 0; e->toffset = 0; e->poffset = 0; . 249d 230a /* Real-time iso stuff */ ulong foffset; /* file offset (to detect seeks) */ ulong poffset; /* offset of next packet to be queued */ ulong toffset; /* offset associated with time */ vlong time; /* timeassociated with offset */ . 226d ## diffname pc/devusb.c 2001/1012 ## diff -e /n/emeliedump/2001/1011/sys/src/9/pc/devusb.c /n/emeliedump/2001/1012/sys/src/9/pc/devusb.c 1880,1884c l += epstatus(s+l, READSTR-l, e, i); . 1825a 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", i, e->csp, e->nbytes, e->nblocks); if (e->iso && e->toffset){ l += snprint(s+l, n-l, " %6d buffered %10lud offset %19lld time", e->buffered, e->toffset, e->time); } l += snprint(s+l, n-l, "\n"); return l; } . 1747a e->buffered -= i; if (e->buffered < 0){ iprint("e->buffered %d?\n", e->buffered); e->buffered = 0; } } . 1746c e->buffered += i; }else{ . 1744c if (w){ . 1718,1729d 1716a }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; } . 1711,1715c 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; . 1126,1130c } } else { /* Unread bytes are now lost */ e->buffered -= (td->status + 1) & 0x3ff; . 1121,1124c 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); . 1114c for (n = 2; n < 4; n++){ . 1097a if ((td->flags & IsoClean) == 0){ e->buffered -= n; if (e->buffered < 0){ iprint("e->buffered %d?\n", e->buffered); e->buffered = 0; } } . 1092a e->buffered += n; . 1089c n = (td->status + 1) & 0x3ff; e->nbytes += n; . 834a e->buffered = 0; . 234a int buffered; /* bytes captured but unread, or written but unsent */ . ## diffname pc/devusb.c 2001/1013 ## diff -e /n/emeliedump/2001/1012/sys/src/9/pc/devusb.c /n/emeliedump/2001/1013/sys/src/9/pc/devusb.c 1516c len = e->buffered; devdir(c, q, up->genbuf, len, eve, e->mode==OREAD? 0444: e->mode==OWRITE? 0222: 0666, dp); . 1491a len = 0; . 1442a vlong len; . ## diffname pc/devusb.c 2001/1015 ## diff -e /n/emeliedump/2001/1013/sys/src/9/pc/devusb.c /n/emeliedump/2001/1015/sys/src/9/pc/devusb.c 1655d 1645,1649d 1641,1643c if(e = d->ep[s]) { if (e->isopen == 1){ e->isopen = 0; eptdeactivate(e); unschedendpt(e); } . 1633,1636d ## diffname pc/devusb.c 2001/1016 ## diff -e /n/emeliedump/2001/1015/sys/src/9/pc/devusb.c /n/emeliedump/2001/1016/sys/src/9/pc/devusb.c 2106c if (e->active) . 1867d 1864,1865c l += snprint(s+l, n-l, "bufsize %6d buffered %6d offset %10lud time %19lld\n", e->maxpkt, e->buffered, e->toffset, e->time); . 1861c l += snprint(s+l, n-l, "%2d %#6.6lux %10lud bytes %10lud blocks\n", . 1648a } . 1647c if(c->flag & COPEN){ XPRINT("usbclose: freedev 0x%p\n", d); . 1634,1644d 1630c if(c->qid.type == QTDIR || QID(c->qid) < Q3rd) . 1628d 1590d 1585,1586c if (e->active) error("can't schedule USB endpoint, active"); . 1583a XPRINT("usbopen: dev 0x%p, ept 0x%p\n", d, e); . 1577a XPRINT("usbopen, default 0x%p, %d\n", d, s); . 1572a XPRINT("usbopen, Qctl 0x%p\n", d); . 1557a } . 1556c if(QID(c->qid) < Q3rd){ XPRINT("usbopen, devopen < Q3rd\n"); . 1546a XPRINT("usbopen, new dev 0x%p\n", d); . 1448c if(QID(c->qid) == Qtopdir){ . 811c if (e->active){ . 663a XPRINT("deactivate 0x%p\n", e); . 647a XPRINT("activate 0x%p\n", e); . 209c /* uchar isopen; ep operations forbidden on open endpoints */ . ## diffname pc/devusb.c 2001/1017 ## diff -e /n/emeliedump/2001/1016/sys/src/9/pc/devusb.c /n/emeliedump/2001/1017/sys/src/9/pc/devusb.c 1860,1862c if (e->iso){ l += snprint(s+l, n-l, "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'; . 1689a iprint("offset %lud, foffset %lud\n", offset, e->foffset); . 1597a e->foffset = 0; e->toffset = 0; e->poffset = 0; e->buffered = 0; . 835,838d ## diffname pc/devusb.c 2001/1019 ## diff -e /n/emeliedump/2001/1017/sys/src/9/pc/devusb.c /n/emeliedump/2001/1019/sys/src/9/pc/devusb.c 1783a if(w) td->offset = offset + (p-(uchar*)a) - (((td->dev >> 21) + 1) & 0x7ff); . 1391,1392c ub->frames = (ulong*)(((ulong)xalloc(2*FRAMESIZE) + FRAMEMASK) & ~FRAMEMASK); ub->frameld = xallocz(FRAMESIZE, 1); . 1358c ub->qhpool = (QH*)(((ulong)xalloc(32*sizeof(QH) + 0x10) + 0xf) & ~0xf); . 1353c ub->tdpool = (TD*)(((ulong)xalloc(128*sizeof(TD) + 0x10) + 0xf) & ~0xf); . 217,218c void* tdalloc; void* bpalloc; . 119,120c NFRAME = 1024, FRAMESIZE= NFRAME*sizeof(ulong), /* fixed by hardware; aligned to same */ FRAMEMASK= FRAMESIZE-1, /* fixed by hardware; aligned to same */ . ## diffname pc/devusb.c 2001/1106 ## diff -e /n/emeliedump/2001/1019/sys/src/9/pc/devusb.c /n/emeliedump/2001/1106/sys/src/9/pc/devusb.c 1626,1632d ## diffname pc/devusb.c 2001/1117 ## diff -e /n/emeliedump/2001/1106/sys/src/9/pc/devusb.c /n/emeliedump/2001/1117/sys/src/9/pc/devusb.c 2041c nf = tokenize(cmd, fields, nelem(fields)); . 2017c nf = tokenize(cmd, fields, nelem(fields)); . ## diffname pc/devusb.c 2001/1120 ## diff -e /n/emeliedump/2001/1117/sys/src/9/pc/devusb.c /n/emeliedump/2001/1120/sys/src/9/pc/devusb.c 2167a poperror(); free(cb); . 2164,2166d 2149c i = strtoul(cb->f[5], nil, 0); . 2140,2142c e->mode = strcmp(cb->f[3],"r") == 0? OREAD : strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR; i = strtoul(cb->f[4], nil, 0); . 2133c i = strtoul(cb->f[2], nil, 0); . 2128c i = strtoul(cb->f[5], nil, 0); . 2122,2124c e->mode = strcmp(cb->f[3],"r") == 0? OREAD : strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR; i = strtoul(cb->f[4], nil, 0); . 2107c if(strcmp(cb->f[2], "bulk") == 0){ . 2092c i = strtoul(cb->f[1], nil, 0); . 2088c break; case CMep: . 2085c error(Ebadusbmsg); . 2082,2083c break; case CMunstall: i = strtoul(cb->f[1], nil, 0); . 2080c e->debug = strtoul(cb->f[2], nil, 0); . 2074c error(Ebadusbmsg); . 2071,2072c break; case CMdebug: i = strtoul(cb->f[1], nil, 0); . 2068c e->maxpkt = strtoul(cb->f[2], nil, 0); . 2066c error(Ebadusbmsg); . 2062,2064c e->data01 = strtoul(cb->f[2], nil, 0) != 0; break; case CMmaxpkt: i = strtoul(cb->f[1], nil, 0); . 2060c error(Ebadusbmsg); . 2056,2058c d->ep[i]->csp = strtoul(cb->f[3], nil, 0); break; case CMdata: i = strtoul(cb->f[1], nil, 0); . 2050,2053c cmderror(cb, Ebadusbmsg); if (i == 0) d->csp = strtoul(cb->f[3], nil, 0); . 2037,2046c cb = parsecmd(a, n); if(waserror()){ free(cb); nexterror(); } ct = lookupcmd(cb, usbctlmsg, nelem(usbctlmsg)); switch(ct->index){ case CMspeed: d->ls = strtoul(cb->f[1], nil, 0) == 0; break; case CMclass: i = strtoul(cb->f[2], nil, 0); d->npt = strtoul(cb->f[1], nil, 0); . 2029,2030c break; case BCMenable: portenable(id, 1); break; case BCMreset: portreset(id); break; } poperror(); free(cb); . 2022,2027c cmderror(cb, "usb port number not 1 or 2 in"); switch(ct->index){ case BCMdisable: . 2013,2019c cb = parsecmd(a, n); if(waserror()){ free(cb); nexterror(); } ct = lookupcmd(cb, usbbusctlmsg, nelem(usbbusctlmsg)); . 2006c Cmdbuf *cb; Cmdtab *ct; int id, nw, t, i; . 289a enum { BCMdisable, BCMenable, BCMreset, }; enum { CMclass, CMdata, CMdebug, CMep, CMmaxpkt, CMspeed, CMunstall, }; static Cmdtab usbbusctlmsg[] = { BCMdisable, "disable", 2, BCMenable, "enable", 2, BCMreset, "reset", 2, }; static Cmdtab usbctlmsg[] = { CMclass, "class", 4, CMdata, "data", 3, CMdebug, "debug", 3, CMep, "ep", 6, CMmaxpkt, "maxpkt", 3, CMspeed, "speed", 2, CMunstall, "unstall", 2, }; . 281a static char Ebadusbmsg[] = "invalid parameters to USB ctl message"; . ## diffname pc/devusb.c 2001/1122 ## diff -e /n/emeliedump/2001/1120/sys/src/9/pc/devusb.c /n/emeliedump/2001/1122/sys/src/9/pc/devusb.c 1354,1361c // 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; // } . ## diffname pc/devusb.c 2001/1124 ## diff -e /n/emeliedump/2001/1122/sys/src/9/pc/devusb.c /n/emeliedump/2001/1124/sys/src/9/pc/devusb.c 2059c id = strtol(cb->f[1], nil, 0); . 2046c char cmd[50]; . 2027a XPRINT("qh %s: q=%p first=%p last=%p entries=%.8lux\n", "writeusb sleep", qh, qh->first, qh->last, qh->entries); . 1388c intrenable(cfg->intl, usbinterrupt, ub, cfg->tbdf, "usb"); . 1353a if(cfg->did == 0x2482 || cfg->did == 0x2487) continue; . 1206d 1204a usbints++; . 1201a frameptr = inl(ub->io+Flbaseadd); framenumber = IN(Frnum) & 0x3ff; . 1192c usbinterrupt(Ureg*, void *a) . 859d 856,857c if(e->sched < 0) . 785c queuetd(ub, qh, t, vf, "qrcv"); . 760c queuetd(ub, qh, t, vf, "qxmit"); . 648a if(q->first && q->entries != PADDR(q->first)){ usbbogus++; q->entries = PADDR(q->first); } . 518a XPRINT(" t=%p q=%p first=%p last=%p entries=%.8lux\n", t, q, q->first, q->last, q->entries); . 510a 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); . 503c queuetd(Ctlr *ub, QH *q, TD *t, int vf, char *why) . 279a static long usbints; static long framenumber; static long frameptr; static long usbbogus; . ## diffname pc/devusb.c 2001/1127 ## diff -e /n/emeliedump/2001/1124/sys/src/9/pc/devusb.c /n/emeliedump/2001/1127/sys/src/9/pc/devusb.c 328c CMspeed, "speed", 2, . 323,325c CMclass, "class", 4, CMdata, "data", 3, CMdebug, "debug", 3, . 143c BitstuffErr = 1<<17, . 129,141c 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, . 124,126c Vf = 1<<2, /* TD only */ IsQH = 1<<1, Terminate = 1<<0, . 121c FRAMEMASK= FRAMESIZE-1, . 110,117c 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, . ## diffname pc/devusb.c 2001/1130 ## diff -e /n/emeliedump/2001/1127/sys/src/9/pc/devusb.c /n/emeliedump/2001/1130/sys/src/9/pc/devusb.c 1691a poperror(); . 1684a if(waserror()){ qunlock(&usbstate); return; } . 1152c print("e->buffered %d?\n", e->buffered); . ## diffname pc/devusb.c 2001/1201 ## diff -e /n/emeliedump/2001/1130/sys/src/9/pc/devusb.c /n/emeliedump/2001/1201/sys/src/9/pc/devusb.c 2173a } . 2172c if((e = d->ep[i]) == nil){ XPRINT("calling devendpt in usbwrite (CMep)\n"); . 2125a } . 2124c if(d->ep[i] == nil){ XPRINT("calling devendpt in usbwrite (CMclass)\n"); . 1822c XPRINT("e->buffered %d?\n", e->buffered); . 1694c freedev(d, ept); . 1692a ept = (QID(c->qid) != Qctl) ? QID(c->qid) - Qep0 : -1; . 1691a XPRINT("usbclose: dev 0x%p\n", d); . 1680a int ept; . 1331a XPRINT("calling devendpt in usbnewdevice\n"); . 1028c } 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); } } } . 1022a XPRINT("freedev 0x%p, 0\n", d); . 1020a Endpt *e; . 1018c freedev(Udev *d, int ept) . 1005a if (e) XPRINT("decref(0x%p) in freept, new value %ld\n", e, e->ref); return 0; . 1004a return 1; . 994c static int . 982a XPRINT("incref(0x%p) in devendpt, new value %ld\n", *p, (*p)->ref); . 960a XPRINT("incref(0x%p) in devendpt, new value %ld\n", e, e->ref); . ## diffname pc/devusb.c 2001/1204 ## diff -e /n/emeliedump/2001/1201/sys/src/9/pc/devusb.c /n/emeliedump/2001/1204/sys/src/9/pc/devusb.c 1705c nexterror(); . 1703c if(waserror()){ /* usbdevice can error */ . ## diffname pc/devusb.c 2002/0109 ## diff -e /n/emeliedump/2001/1204/sys/src/9/pc/devusb.c /n/emeliedump/2002/0109/sys/src/9/pc/devusb.c 2309a devshutdown, . ## diffname pc/devusb.c 2002/0212 ## diff -e /n/emeliedump/2002/0109/sys/src/9/pc/devusb.c /n/emeliedump/2002/0212/sys/src/9/pc/devusb.c 2252d 2187c * ep n period mode samplesize Hz . 2165a case CMadjust: i = strtoul(cb->f[1], nil, 0); if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) error(Ebadusbmsg); e = d->ep[i]; if (e->iso == 0) error(Eperm); i = strtoul(cb->f[2], nil, 0); /* speed may not result in change of maxpkt */ if (i < (e->maxpkt-1)/e->samplesz * 1000/e->pollms || i > e->maxpkt/e->samplesz * 1000/e->pollms){ snprint(cmd, sizeof(cmd), "%d < %d < %d?", (e->maxpkt-1)/e->samplesz * 1000/e->pollms, i, e->maxpkt/e->samplesz * 1000/e->pollms); error(cmd); } e->hz = i; break; . 1865a } poperror(); . 1863,1864c if (isolock){ isolock = 0; . 1828,1829d 327a CMadjust, "adjust", 3, . 309a CMadjust, . ## diffname pc/devusb.c 2002/0216 ## diff -e /n/emeliedump/2002/0212/sys/src/9/pc/devusb.c /n/emeliedump/2002/0216/sys/src/9/pc/devusb.c 1867a if (isolock) iunlock(&activends); . 1863,1866d 1840,1844d 1837d 1833,1834d 1829a 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); . ## diffname pc/devusb.c 2002/0219 ## diff -e /n/emeliedump/2002/0216/sys/src/9/pc/devusb.c /n/emeliedump/2002/0219/sys/src/9/pc/devusb.c 2309,2319c 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); . 2301c nw = uh->read(uh, e, cmd, 0LL, 8); . 2298,2299c n = uh->write(uh, e, a, n, 0LL, TokSETUP); if(nw == 0) { /* host to device: use IN[DATA1] to ack */ . 2292,2293c e = d->ep[0]; if(e == nil || e->iso) error(Egreg); . 2290c case Qep0: /* SETUP endpoint 0 */ . 2283c qunlock(uh); . 2281a e->mode = strcmp(cb->f[3],"r") == 0? OREAD : strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR; uh->epmode(uh, e); . 2261,2262d 2253c /* ep n period mode samplesize Hz */ . 2233,2244c e->iso = 0; . 2229,2231d 2226c if(e->active) . 2223c qunlock(uh); . 2220c qlock(uh); . 2122,2125d 2114,2115c case PMreset: uh->portreset(uh, id); . 2111,2112c case PMenable: uh->portenable(uh, id, 1); . 2108,2109c case PMdisable: uh->portenable(uh, id, 0); . 2105,2106d 2103c ct = lookupcmd(cb, usbportmsg, nelem(usbportmsg)); . 2095,2096c d = usbdevice(c); uh = d->uh; t = TYPE(c->qid); switch(t){ case Qport: . 2089a Cmdbuf *cb; Usbhost *uh; . 2088d 2026,2082d 2022a n = readstr(offset, a, n, s); poperror(); free(s); . 2001,2021d 1995,1999c 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); . 1990,1993d 1973,1987c seprint(s, se, "%11d %11d\n", d->x, d->id); . 1968,1970c uh->portinfo(uh, s, se); break; . 1963,1966d 1888,1961d 1884,1885c free(s); . 1873,1882c s = smalloc(READSTR); se = s+READSTR; . 1830,1871c return uh->read(uh, e, a, n, offset); } . 1823,1828c return n; . 1749,1821c 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; n = uh->read(uh, e, a, n, 0LL); if(e->setin){ e->setin = 0; e->data01 = 1; uh->write(uh, e, "", 0, 0LL, TokOUT); . 1740,1747c d = usbdevice(c); uh = d->uh; t = TYPE(c->qid); . 1736,1738c if(c->qid.type == QTDIR) return devdirread(c, a, n, nil, 0, usbgen); . 1734a Usbhost *uh; char *s, *se, *p; . 1733a int t, i; Udev *d; . 1731,1732c long usbread(Chan *c, void *a, long n, vlong offset) . 1727,1728c 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) p = seprint(p, se, " offset %10lud time %19lld\n", e->toffset, e->time); p = seprint(p, se, "\n"); } return p; . 1725c char *p; . 1722,1723c static char * epstatus(char *s, char *se, Endpt *e, int i) . 1719c qunlock(uh); . 1714c ept = (type != Qctl) ? type - Qep0 : -1; . 1709,1710c if(type == Qctl) . 1704,1706c d = usbdevice(c); uh = d->uh; qlock(uh); if(waserror()){ qunlock(uh); . 1702c type = TYPE(c->qid); if(c->qid.type == QTDIR || type < Q3rd) . 1700c int ept, type; Usbhost *uh; . 1684,1696d 1676c qunlock(uh); . 1670d 1660,1665c uh->epopen(uh, e); . 1654d 1650,1651c s = type - Qep0; . 1640d 1638c switch(type){ . 1634c qunlock(uh); . 1632c d = usbdevice(c); uh = d->uh; qlock(uh); . 1627c if(type < Q3rd){ . 1622,1623c type = Qctl; mkqid(&c->qid, PATH(type, d->x, CTLR(c->qid)), d->id, QTFILE); . 1615,1616c type = TYPE(c->qid); if(type == Qnew){ d = usbdevice(c); d = usbnewdevice(d->uh); . 1609c Endpt *e; int f, s, type; Usbhost *uh; . 1605c Chan* . 1592a 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); } . 1587,1589c 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); . 1581,1583d 1578,1579c d = usbdeviceofslot(uh, slot); if(d == nil || s >= nelem(d->ep)) . 1575a s -= nelem(usbdir3); . 1570,1572c if(s < nelem(usbdir3)) { tab = &usbdir3[s]; mkqid(&q, PATH(tab->qid.path, slot, bus), c->qid.vers, QTFILE); . 1563,1567c 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); . 1553c mkqid(&q, PATH(Q3rd, s, bus), d->id, QTDIR); . 1551c return 0; . 1548,1549c if(s >= 0 && s < nelem(uh->dev)) { d = uh->dev[s]; . 1544c mkqid(&q, PATH(tab->qid.path, 0, bus), d->id, QTFILE); devdir(c, q, tab->name, tab->length, eve, tab->perm, dp); . 1542a d = uh->dev[0]; if(d == nil) return -1; . 1535c t = TYPE(c->qid); . 1533c * Second level contains "new", "port", and a numbered * directory for each enumerated device on the bus. . 1530a bus = CTLR(c->qid); if(bus >= nelem(usbhost) || (uh = usbhost[bus]) == nil) return -1; . 1524,1529c 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; . 1518c if(c->qid.path == Qtopdir){ . 1516c * Top level directory contains the controller names. . 1513c Dirtab *tab; Usbhost *uh; int t, bus, slot, perm; . 1511d 1509d 1507d 1476,1503d 1373,1473c 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); } } . 1368,1371c Endpt *e; Usbhost *uh; . 1366d 1364c freedev(Udev *d, int ept) . 1359c qunlock(uh); . 1355c uh->dev[i] = d; . 1348c d->id = (uh->idgen << 8) | i; . 1345a d->uh = uh; . 1342,1344c for(i=0; idev); i++) if(uh->dev[i] == nil){ uh->idgen++; . 1339c qunlock(uh); . 1337c qlock(uh); . 1334d 1322,1332d 1028,1320d 1018,1026d 1015,1016c static Udev* usbnewdevice(Usbhost *uh) . 1011,1012d 1009d 1006,1007c uh->epfree(uh, e); . 1003,1004c uh = e->dev->uh; uh->epclose(uh, e); . 1000a Usbhost *uh; . 998c static void . 987a uh->epfree(uh, e); . 982a uh = d->uh; uh->epalloc(uh, e); . 979,981d 969a . 961c e = *p; if(e != nil){ . 958d 956d 954a Usbhost *uh; . 437,951d 434a d = usbdeviceofslot(uh, SLOT(c->qid)); if(d == nil || d->id != c->qid.vers || d->state == Disabled) error(Ehungup); return d; . 395,433c bus = CTLR(c->qid); if(bus > nelem(usbhost) || (uh = usbhost[bus]) == nil) { error(Egreg); return nil; /* for compiler */ . 391,393c int bus; Udev *d; Usbhost *uh; . 388,389c static Udev* usbdevice(Chan *c) . 378,385c if(s < 0 || s > nelem(uh->dev)) return nil; return uh->dev[s]; . 375,376c static Udev* usbdeviceofslot(Usbhost *uh, int s) . 363,372c if(ntype == MaxUsb) panic("too many USB host interface types"); usbtypes[ntype].type = t; usbtypes[ntype].reset = r; ntype++; . 361c static int ntype; . 342,359c void addusbtype(char* t, int (*r)(Usbhost*)) . 340c char* type; int (*reset)(Usbhost*); } usbtypes[MaxUsb+1]; . 334,338c static struct . 317,319c PMdisable, "disable", 2, PMenable, "enable", 2, PMreset, "reset", 2, . 315c static Cmdtab usbportmsg[] = . 298,300c PMdisable, PMenable, PMreset, . 280,295d 277,278d 257,275c static Dirtab usbdir3[]={ "ctl", {Qctl}, 0, 0666, "status", {Qstatus}, 0, 0444, "setup", {Qep0}, 0, 0666, /* epNdata names are generated on demand */ . 253,254c static Dirtab usbdir2[] = { "new", {Qnew}, 0, 0666, "port", {Qport}, 0, 0666, . 251c #define TYPE(q) (((ulong)(q).path)&TYPEMASK) #define SLOT(q) ((((ulong)(q).path)>>SLOTSHIFT)&SLOTMASK) #define CTLR(q) ((((ulong)(q).path)>>CTLRSHIFT)&CTLRMASK) #define PATH(t, s, c) ((t)|((s)<= 1 && i*e->samplesz <= 12*1000*1000){ . ## diffname pc/devusb.c 2002/0403 ## diff -e /n/emeliedump/2002/0221/sys/src/9/pc/devusb.c /n/emeliedump/2002/0403/sys/src/9/pc/devusb.c 434,435d 432a if((uh = usbprobe(cardno, ctlrno)) == nil){ cardno++; continue; } usbhost[ctlrno] = uh; ctlrno++; . 429,431c ebuf = buf + sizeof buf; p = seprint(buf, ebuf, "#U/usb%d: %s: port 0x%luX irq %lud", ctlrno, usbtypes[cardno].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); return uh; } static void usbreset(void) { int cardno, ctlrno; Usbhost *uh; for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){ if((uh = usbprobe(-1, ctlrno)) == nil) continue; usbhost[ctlrno] = uh; } cardno = ctlrno = 0; while(usbtypes[cardno].type != nil && ctlrno < MaxUsb){ if(usbhost[ctlrno] != nil){ ctlrno++; continue; . 421,427c /* * 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); . 411,419c if(cardno >= MaxUsb || usbtypes[cardno].type == nil){ free(uh); return nil; } if(usbtypes[cardno].reset(uh) < 0){ free(uh); return nil; } . 408,409c break; } } . 406c if(cistrcmp(usbtypes[cardno].type, type)) . 404c if(type==nil || *type==0) . 394,402c uh = malloc(sizeof(Usbhost)); memset(uh, 0, sizeof(Usbhost)); uh->tbdf = BUSUNKNOWN; if(cardno < 0){ if(isaconfig("usb", ctlrno, uh) == 0){ free(uh); return nil; } for(cardno = 0; usbtypes[cardno].type; cardno++){ . 392c char buf[128], *ebuf, name[64], *p, *type; . 390d 387,388c static Usbhost* usbprobe(int cardno, int ctlrno) . ## diffname pc/devusb.c 2002/0502 ## diff -e /n/emeliedump/2002/0403/sys/src/9/pc/devusb.c /n/emeliedump/2002/0502/sys/src/9/pc/devusb.c 453a if(getconf("*nousbprobe")) return; . ## diffname pc/devusb.c 2002/0512 ## diff -e /n/emeliedump/2002/0502/sys/src/9/pc/devusb.c /n/emeliedump/2002/0512/sys/src/9/pc/devusb.c 896c if(n < 8) . ## diffname pc/devusb.c 2003/0301 ## diff -e /n/emeliedump/2002/0512/sys/src/9/pc/devusb.c /n/emeliedump/2003/0301/sys/src/9/pc/devusb.c 432c p = seprint(buf, ebuf, "#U/usb%d: %s: port 0x%luX irq %d", ctlrno, usbtypes[cardno].type, uh->port, uh->irq); .