| plan 9 kernel history: overview | file list | diff list |
2001/1124/pc/devusb.c (diff list | history)
| pc/devusb.c on 1999/1005 | ||
| 1999/1005 | /* * 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" #define Chatty 1 #define DPRINT if(Chatty)print | |
| 2001/0916 | #define XPRINT if(debug)iprint | |
| 1999/1005 | ||
| 1999/1117 | static int debug = 0; | |
| 1999/1116 | ||
| 1999/1005 | /* * 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 struct Ctlr Ctlr; typedef struct Endpt Endpt; typedef struct Udev Udev; /* * UHCI hardware structures, aligned on 16-byte boundary */ typedef struct QH QH; typedef struct TD TD; | |
| 2000/0724 | #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)) | |
| 1999/1005 | struct TD { ulong link; ulong status; /* controller r/w */ ulong dev; ulong buffer; /* software */ ulong flags; | |
| 2001/1010 | union{ Block* bp; /* non-iso */ ulong offset; /* iso */ }; | |
| 1999/1005 | 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 */ | |
| 1999/1117 | QH* hlink; TD* first; QH* next; /* free list */ TD* last; ulong _d1; /* fillers */ ulong _d2; | |
| 1999/1005 | }; #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, | |
| 2001/1019 | NFRAME = 1024, FRAMESIZE= NFRAME*sizeof(ulong), /* fixed by hardware; aligned to same */ FRAMEMASK= FRAMESIZE-1, /* fixed by hardware; aligned to same */ | |
| 2001/0916 | NISOTD = 4, /* number of TDs for isochronous io per frame */ | |
| 1999/1005 | 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, | |
| 2001/1006 | IsoClean= 1<<2, | |
| 1999/1005 | }; #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; | |
| 2001/0916 | int x; /* index in usbdev[] */ | |
| 1999/1120 | int busy; int state; int id; | |
| 2001/1010 | uchar port; /* port number on connecting hub */ | |
| 2000/0724 | ulong csp; | |
| 1999/1120 | int ls; int npt; | |
| 2001/0916 | Endpt* ep[16]; /* active end points */ Udev* ports; /* active ports, if hub */ | |
| 1999/1005 | 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[] = { | |
| 1999/1120 | [Disabled] "Disabled", | |
| 2001/0626 | [Attached] "Attached", | |
| 1999/1120 | [Enabled] "Enabled", | |
| 2001/0626 | [Assigned] "Assigned", | |
| 1999/1120 | [Configured] "Configured", | |
| 1999/1005 | }; /* * device endpoint */ struct Endpt { Ref; Lock; | |
| 2001/0916 | int x; /* index in Udev.ep */ | |
| 1999/1116 | int id; /* hardware endpoint address */ int maxpkt; /* maximum packet size (from endpoint descriptor) */ int data01; /* 0=DATA0, 1=DATA1 */ | |
| 2001/1010 | uchar eof; | |
| 2000/0724 | ulong csp; | |
| 2001/1016 | /* uchar isopen; ep operations forbidden on open endpoints */ | |
| 2001/1010 | uchar mode; /* OREAD, OWRITE, ORDWR */ uchar nbuf; /* number of buffers allowed */ uchar iso; uchar debug; uchar active; /* listed for examination by interrupts */ | |
| 2001/1007 | int setin; /* ISO related: */ | |
| 2001/1019 | void* tdalloc; void* bpalloc; | |
| 2001/0916 | int hz; | |
| 2001/1007 | int remain; /* for packet size calculations */ | |
| 2001/0916 | int samplesz; | |
| 1999/1116 | int sched; /* schedule index; -1 if undefined or aperiodic */ int pollms; /* polling interval in msec */ | |
| 2001/1007 | int psize; /* (remaining) size of this packet */ int off; /* offset into packet */ | |
| 2001/1010 | 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 */ | |
| 2001/1011 | /* 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 */ | |
| 2001/1012 | int buffered; /* bytes captured but unread, or written but unsent */ | |
| 2001/1007 | /* end ISO stuff */ | |
| 2001/1010 | QH * epq; /* queue of TDs for this endpoint */ | |
| 1999/1005 | QLock rlock; Rendez rr; Queue* rq; QLock wlock; Rendez wr; Queue* wq; | |
| 1999/1116 | int ntd; | |
| 1999/1005 | 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) */ | |
| 2001/0916 | int io; | |
| 1999/1005 | ulong* frames; /* frame list */ | |
| 2001/0916 | ulong* frameld; /* real time load on each of the frame list entries */ int idgen; /* version number to distinguish new connections */ | |
| 1999/1005 | QLock resetl; /* lock controller during USB reset */ | |
| 2001/0916 | TD* tdpool; /* first NFRAMES*NISOTD entries are preallocated */ TD* freetd; QH* qhpool; QH* freeqh; | |
| 1999/1005 | ||
| 2001/0916 | 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 */ | |
| 1999/1005 | Udev* ports[2]; }; #define IN(x) ins(ub->io+(x)) #define OUT(x, v) outs(ub->io+(x), (v)) | |
| 2001/1124 | static long usbints; static long framenumber; static long frameptr; static long usbbogus; | |
| 1999/1005 | static Ctlr ubus; static char Estalled[] = "usb endpoint stalled"; | |
| 2001/1120 | static char Ebadusbmsg[] = "invalid parameters to USB ctl message"; | |
| 1999/1005 | static QLock usbstate; /* protects name space state */ static Udev* usbdev[32]; static struct { Lock; Endpt* f; } activends; | |
| 2001/1120 | 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, }; | |
| 1999/1005 | 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; i<n; i++) XPRINT(" %2.2ux", b->rp[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; | |
| 1999/1117 | 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); | |
| 2001/0626 | XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n", buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1); | |
| 1999/1118 | if(debug && t->bp && (t->flags & CancelTD) == 0) dumpdata(t->bp, n); | |
| 1999/1005 | if(!follow || t->link & Terminate || t->link & IsQH) break; t = TFOL(t->link); if(t == t0) break; /* looped */ } } static TD * alloctde(Endpt *e, int pid, int n) { TD *t; int tog, id; t = alloctd(&ubus); id = (e->x<<7)|(e->dev->x&0x7F); tog = 0; if(e->data01 && pid != TokSETUP) tog = IsDATA1; t->ep = e; t->status = ErrLimit3 | Active | IOC; /* or put IOC only on last? */ if(e->dev->ls) t->status |= LowSpeed; t->dev = ((n-1)<<21) | ((id&0x7FF)<<8) | pid | tog; return t; } static QH * allocqh(Ctlr *ub) { QH *qh; ilock(ub); qh = ub->freeqh; if(qh == nil) panic("allocqh"); /* TO DO */ ub->freeqh = qh->next; qh->next = nil; iunlock(ub); qh->head = Terminate; qh->entries = Terminate; | |
| 1999/1117 | qh->hlink = nil; | |
| 1999/1005 | 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++){ | |
| 1999/1007 | XPRINT("qh %8.8lux: %8.8lux %8.8lux\n", q, q->head, q->entries); | |
| 1999/1005 | if((q->entries & Terminate) == 0) dumptd(TFOL(q->entries), 1); if(q->head & Terminate) break; if((q->head & IsQH) == 0){ | |
| 1999/1007 | XPRINT("head:"); | |
| 1999/1005 | dumptd(TFOL(q->head), 1); break; } q = QFOL(q->head); if(q == q0) break; /* looped */ } } static void | |
| 2001/1124 | queuetd(Ctlr *ub, QH *q, TD *t, int vf, char *why) | |
| 1999/1005 | { TD *lt; for(lt = t; lt->next != nil; lt = lt->next) lt->link = PADDR(lt->next) | vf; lt->link = Terminate; ilock(ub); | |
| 2001/1124 | 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); | |
| 1999/1005 | 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; | |
| 2001/1124 | XPRINT(" t=%p q=%p first=%p last=%p entries=%.8lux\n", t, q, q->first, q->last, q->entries); | |
| 1999/1116 | dumpqh(q); | |
| 1999/1005 | iunlock(ub); } static void cleantd(TD *t, int discard) { Block *b; int n, err; | |
| 2001/0626 | XPRINT("cleanTD: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); | |
| 1999/1005 | 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) | |
| 2001/0626 | XPRINT("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); | |
| 1999/1005 | 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 | |
| 1999/1116 | cleanq(QH *q, int discard, int vf) | |
| 1999/1005 | { | |
| 1999/1116 | TD *t, *tp; | |
| 1999/1005 | Ctlr *ub; ub = &ubus; ilock(ub); | |
| 1999/1116 | tp = nil; | |
| 1999/1005 | for(t = q->first; t != nil;){ | |
| 2001/0626 | 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); | |
| 1999/1005 | if(t->status & Active){ | |
| 1999/1116 | if(t->status & NAKed){ t->status = (t->status & ~NAKed) | IOC; /* ensure interrupt next frame */ tp = t; t = t->next; continue; } | |
| 1999/1005 | if(t->flags & CancelTD){ XPRINT("cancelTD: %8.8lux\n", (ulong)t); t->status = (t->status & ~Active) | IOC; /* ensure interrupt next frame */ | |
| 1999/1116 | tp = t; | |
| 1999/1005 | t = t->next; continue; } | |
| 2001/0626 | tp = t; t = t->next; continue; | |
| 1999/1005 | break; } t->status &= ~IOC; | |
| 1999/1116 | 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; | |
| 1999/1005 | iunlock(ub); cleantd(t, discard); ilock(ub); | |
| 1999/1116 | if (tp) t = tp->next; else t = q->first; XPRINT("t = %8.8lux\n", t); dumpqh(q); | |
| 1999/1005 | } | |
| 2001/1124 | if(q->first && q->entries != PADDR(q->first)){ usbbogus++; q->entries = PADDR(q->first); } | |
| 1999/1005 | 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); | |
| 1999/1007 | XPRINT("cancel:\n"); | |
| 1999/1005 | 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){ | |
| 2001/1016 | XPRINT("activate 0x%p\n", e); | |
| 1999/1005 | 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; | |
| 2001/1016 | XPRINT("deactivate 0x%p\n", e); | |
| 1999/1005 | for(l = &activends.f; *l != e; l = &(*l)->activef) if(*l == nil){ iunlock(&activends); panic("usb eptdeactivate"); } *l = e->activef; } iunlock(&activends); } | |
| 1999/1117 | 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; } | |
| 1999/1005 | 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); | |
| 1999/1118 | if(e->debug) pprint("QTD: %8.8lux n=%ld\n", t, b?BLEN(b): 0); | |
| 1999/1005 | 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; } | |
| 2001/1124 | queuetd(ub, qh, t, vf, "qxmit"); | |
| 1999/1005 | return qh; } static QH* qrcv(Endpt *e) { TD *t; Block *b; Ctlr *ub; QH *qh; int vf; | |
| 2001/0504 | t = alloctde(e, TokIN, e->maxpkt); | |
| 2001/0527 | b = allocb(e->maxpkt); | |
| 1999/1005 | t->bp = b; t->buffer = PADDR(b->wp); ub = &ubus; vf = 0; if(e->x == 0){ qh = ub->ctlq; }else if((qh = e->epq) == nil || e->mode != OREAD){ qh = ub->bulkq; vf = Vf; } | |
| 2001/1124 | queuetd(ub, qh, t, vf, "qrcv"); | |
| 1999/1005 | return qh; } | |
| 2001/0527 | 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; } | |
| 1999/1005 | /* * return smallest power of 2 >= n */ static int flog2(int n) { int i; for(i=0; (1<<i)<n; i++) ; return i; } | |
| 2001/0916 | static int usbsched( Ctlr *ub, int pollms, ulong load) | |
| 1999/1005 | { | |
| 2001/1006 | int i, d, q; | |
| 2001/0916 | ulong best, worst; | |
| 1999/1005 | ||
| 2001/0916 | best = 1000000; q = -1; for (d = 0; d < pollms; d++){ worst = 0; for (i = d; i < NFRAME; i++){ if (ub->frameld[i] + load > worst) worst = ub->frameld[i] + load; | |
| 1999/1005 | } | |
| 2001/0916 | if (worst < best){ best = worst; q = d; | |
| 1999/1005 | } } return q; } static int schedendpt(Endpt *e) { Ctlr *ub; | |
| 2001/1006 | TD *td; uchar *bp; int i, id, ix, size, frnum; | |
| 1999/1005 | ||
| 2001/0916 | if(!e->iso || e->sched >= 0) | |
| 2001/0626 | return 0; ub = &ubus; | |
| 2001/0918 | ||
| 2001/1016 | if (e->active){ | |
| 2001/0918 | return -1; } | |
| 2001/1007 | e->off = 0; | |
| 2001/0916 | e->sched = usbsched(ub, e->pollms, e->maxpkt); | |
| 2001/1124 | if(e->sched < 0) | |
| 2001/0626 | return -1; | |
| 2001/0916 | ||
| 2001/1006 | 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); | |
| 2001/1010 | e->bp0 = (uchar *)(((ulong)e->bpalloc + 0xf) & ~0xf); | |
| 2001/1006 | frnum = (IN(Frnum) + 1) & 0x3ff; frnum = (frnum & ~(e->pollms - 1)) + e->sched; | |
| 2001/1007 | e->xtd = &e->td0[(frnum+8)&0x3ff]; /* Next td to finish */ e->etd = nil; | |
| 2001/1006 | e->remain = 0; | |
| 2001/1010 | e->nbytes = 0; | |
| 2001/1006 | td = e->td0; | |
| 2001/0916 | for(i = e->sched; i < NFRAME; i += e->pollms){ | |
| 2001/1010 | bp = e->bp0 + e->maxpkt*i/e->pollms; td->buffer = PADDR(bp); | |
| 2001/0916 | td->ep = e; | |
| 2001/1006 | td->next = &td[1]; ub->frameld[i] += e->maxpkt; td++; } td[-1].next = e->td0; for(i = e->sched; i < NFRAME; i += e->pollms){ | |
| 2001/1008 | ix = (frnum+i) & 0x3ff; | |
| 2001/1006 | td = &e->td0[ix]; id = (e->x<<7)|(e->dev->x&0x7F); if (e->mode == OREAD) | |
| 2001/0918 | /* enable receive on this entry */ td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | TokIN; | |
| 2001/1006 | else{ size = (e->hz + e->remain)*e->pollms/1000; e->remain = (e->hz + e->remain)*e->pollms%1000; size *= e->samplesz; td->dev = ((size-1)<<21) | ((id&0x7FF)<<8) | TokOUT; | |
| 2001/0918 | } | |
| 2001/1008 | td->status = ErrLimit1 | Active | IsoSelect | IOC; | |
| 2001/1006 | td->link = ub->frames[ix]; | |
| 2001/1008 | td->flags |= IsoClean; | |
| 2001/1006 | ub->frames[ix] = PADDR(td); | |
| 2001/0916 | } | |
| 2001/0626 | return 0; } static void unschedendpt(Endpt *e) { Ctlr *ub; | |
| 2001/1006 | TD *td; ulong *addr; | |
| 2001/0626 | int q; ub = &ubus; | |
| 2001/1006 | if(!e->iso || e->sched < 0) | |
| 2001/0626 | return; | |
| 2001/0918 | ||
| 2001/1006 | 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; | |
| 2001/0916 | ub->frameld[q] -= e->maxpkt; } | |
| 2001/1006 | free(e->tdalloc); free(e->bpalloc); e->tdalloc = nil; e->bpalloc = nil; e->etd = nil; e->td0 = nil; | |
| 2001/0916 | e->sched = -1; | |
| 2001/0626 | } 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->sched = -1; e->maxpkt = 8; 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); | |
| 2001/1006 | eptdeactivate(e); | |
| 2001/0626 | unschedendpt(e); e->dev->ep[e->x] = nil; 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; i<nelem(d->ep); 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 | |
| 2001/1006 | cleaniso(Endpt *e, int frnum) { TD *td; | |
| 2001/1010 | int id, n, i; uchar *bp; | |
| 2001/1006 | ||
| 2001/1007 | td = e->xtd; if (td->status & Active) return; | |
| 2001/1006 | id = (e->x<<7)|(e->dev->x&0x7F); | |
| 2001/1010 | do { | |
| 2001/1006 | if (td->status & AnyError) iprint("usbisoerror 0x%lux\n", td->status); | |
| 2001/1012 | n = (td->status + 1) & 0x3ff; e->nbytes += n; | |
| 2001/1010 | if ((td->flags & IsoClean) == 0) e->nblocks++; | |
| 2001/1007 | if (e->mode == OREAD){ | |
| 2001/1012 | e->buffered += n; | |
| 2001/1011 | e->poffset += (td->status + 1) & 0x3ff; td->offset = e->poffset; | |
| 2001/1007 | td->dev = ((e->maxpkt -1)<<21) | ((id&0x7FF)<<8) | TokIN; | |
| 2001/1011 | e->toffset = td->offset; | |
| 2001/1007 | }else{ | |
| 2001/1012 | if ((td->flags & IsoClean) == 0){ e->buffered -= n; if (e->buffered < 0){ iprint("e->buffered %d?\n", e->buffered); e->buffered = 0; } } | |
| 2001/1011 | e->toffset = td->offset; | |
| 2001/1007 | 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; | |
| 2001/1011 | td->offset = e->poffset; e->poffset += n; | |
| 2001/1007 | } td = td->next; if (e->xtd == td){ | |
| 2001/1008 | XPRINT("@"); | |
| 2001/1007 | break; } } while ((td->status & Active) == 0); | |
| 2001/1010 | e->time = todget(nil); | |
| 2001/1007 | e->xtd = td; | |
| 2001/1012 | for (n = 2; n < 4; n++){ | |
| 2001/1010 | i = ((frnum + n)&0x3ff); td = e->td0 + i; bp = e->bp0 + e->maxpkt*i/e->pollms; | |
| 2001/1007 | if (td->status & Active) continue; | |
| 2001/1012 | 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); | |
| 2001/1007 | td->flags |= IsoClean; | |
| 2001/1012 | } } else { /* Unread bytes are now lost */ e->buffered -= (td->status + 1) & 0x3ff; | |
| 2001/1006 | } | |
| 2001/1008 | td->status = ErrLimit1 | Active | IsoSelect | IOC; | |
| 2001/1006 | } | |
| 2001/1008 | wakeup(&e->wr); | |
| 2001/1006 | } static void | |
| 2001/1124 | usbinterrupt(Ureg*, void *a) | |
| 2001/0626 | { Ctlr *ub; Endpt *e; | |
| 2001/1006 | int s, frnum; | |
| 2001/0626 | QH *q; ub = a; s = IN(Status); | |
| 2001/1010 | ||
| 2001/1124 | frameptr = inl(ub->io+Flbaseadd); framenumber = IN(Frnum) & 0x3ff; | |
| 2001/0626 | OUT(Status, s); if ((s & 0x1f) == 0) return; | |
| 2001/1124 | usbints++; | |
| 2001/1006 | frnum = IN(Frnum) & 0x3ff; | |
| 2001/0626 | if (s & 0x1a) { XPRINT("cmd #%x sofmod #%x\n", IN(Cmd), inb(ub->io+SOFMod)); XPRINT("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1)); } ilock(&activends); | |
| 2001/0916 | for(e = activends.f; e != nil; e = e->activef){ if(!e->iso && e->epq != nil) { | |
| 2001/0626 | XPRINT("cleanq(e->epq, 0, 0)\n"); cleanq(e->epq, 0, 0); } | |
| 2001/1006 | if(e->iso) { XPRINT("cleaniso(e)\n"); cleaniso(e, frnum); | |
| 2001/0916 | } } | |
| 2001/0626 | iunlock(&activends); | |
| 2001/1010 | 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); } | |
| 2001/0626 | } 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)-1) #define QID(q) ((ulong)(q).path&QMASK) #define DEVPATH(p) ((p)>>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; i<nelem(usbdev); i++) if(usbdev[i] == nil){ ubus.idgen++; d = mallocz(sizeof(*d), 1); d->ref = 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; | |
| 2001/0916 | e->iso = 0; | |
| 2001/0626 | e->sched = -1; usbdev[i] = d; break; } poperror(); qunlock(&usbstate); return d; } static void usbreset(void) { Pcidev *cfg; | |
| 2001/1006 | int i; | |
| 2001/0626 | ulong port; TD *t; Ctlr *ub; ISAConf isa; if(isaconfig("usb", 0, &isa) == 0) { XPRINT("usb not in plan9.ini\n"); | |
| 2001/0726 | return; | |
| 2001/0626 | } ub = &ubus; | |
| 2001/1010 | 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; | |
| 2001/1124 | if(cfg->did == 0x2482 || cfg->did == 0x2487) continue; | |
| 2001/1122 | // 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; // } | |
| 2001/1010 | if((cfg->mem[4].bar & ~0x0F) != 0) break; | |
| 2001/0626 | } | |
| 2001/1010 | if(cfg == nil) { DPRINT("No USB device found\n"); | |
| 2001/0626 | return; } | |
| 2001/1010 | port = cfg->mem[4].bar & ~0x0F; | |
| 2001/0626 | 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); | |
| 2001/1010 | if(0){ | |
| 2001/0626 | 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; | |
| 2001/1124 | intrenable(cfg->intl, usbinterrupt, ub, cfg->tbdf, "usb"); | |
| 2001/0626 | ub->io = port; | |
| 2001/1019 | ub->tdpool = (TD*)(((ulong)xalloc(128*sizeof(TD) + 0x10) + 0xf) & ~0xf); | |
| 2001/1006 | for(i=128; --i>=0;){ | |
| 2001/0626 | ub->tdpool[i].next = ub->freetd; ub->freetd = &ub->tdpool[i]; } | |
| 2001/1019 | ub->qhpool = (QH*)(((ulong)xalloc(32*sizeof(QH) + 0x10) + 0xf) & ~0xf); | |
| 2001/0626 | for(i=32; --i>=0;){ ub->qhpool[i].next = ub->freeqh; ub->freeqh = &ub->qhpool[i]; } /* | |
| 2001/0916 | * the last entries of the periodic (interrupt & isochronous) scheduling TD entries * point to the control queue and the bandwidth sop for bulk traffic. | |
| 2001/0626 | * 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); | |
| 2001/1010 | ub->ctlq->head = PADDR(ub->bulkq) | IsQH; | |
| 2001/0626 | ub->bulkq->head = PADDR(ub->recvq) | IsQH; | |
| 2001/1010 | 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); | |
| 2001/0626 | 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 */ | |
| 2001/1019 | ub->frames = (ulong*)(((ulong)xalloc(2*FRAMESIZE) + FRAMEMASK) & ~FRAMEMASK); ub->frameld = xallocz(FRAMESIZE, 1); | |
| 2001/0916 | ||
| 2001/1006 | for (i = 0; i < NFRAME; i++) ub->frames[i] = PADDR(ub->ctlq) | IsQH; | |
| 2001/0626 | 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; | |
| 2001/1013 | vlong len; | |
| 2001/0626 | /* * Top level directory contains the name of the device. */ | |
| 2001/1016 | if(QID(c->qid) == Qtopdir){ | |
| 2001/0626 | 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)<<QSHIFT)|Q3rd, d->id, QTDIR); devdir(c, q, up->genbuf, 0, eve, 0555, dp); return 1; } return -1; } /* * Third level. */ | |
| 2001/1013 | len = 0; | |
| 2001/0626 | 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); | |
| 2001/1013 | len = e->buffered; devdir(c, q, up->genbuf, len, eve, e->mode==OREAD? 0444: e->mode==OWRITE? 0222: 0666, dp); | |
| 2001/0626 | 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(); | |
| 2001/1016 | XPRINT("usbopen, new dev 0x%p\n", d); | |
| 2001/0626 | if(d == nil) { XPRINT("usbopen failed (usbnewdevice)\n"); error(Enodev); } c->qid.path = Qctl|((d->x+1)<<QSHIFT); c->qid.vers = d->id; f = 1; } | |
| 2001/1016 | if(QID(c->qid) < Q3rd){ XPRINT("usbopen, devopen < Q3rd\n"); | |
| 2001/0626 | return devopen(c, omode, nil, 0, usbgen); | |
| 2001/1016 | } | |
| 2001/0626 | 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); | |
| 2001/1016 | XPRINT("usbopen, Qctl 0x%p\n", d); | |
| 2001/0626 | break; default: d = usbdevice(c); s = QID(c->qid) - Qep0; | |
| 2001/1016 | XPRINT("usbopen, default 0x%p, %d\n", d, s); | |
| 2001/0626 | if(s >= 0 && s < nelem(d->ep)){ Endpt *e; if((e = d->ep[s]) == nil) { XPRINT("usbopen failed (endpoint)\n"); error(Enodev); } | |
| 2001/1016 | XPRINT("usbopen: dev 0x%p, ept 0x%p\n", d, e); | |
| 2001/1010 | if(schedendpt(e) < 0){ | |
| 2001/1016 | if (e->active) error("can't schedule USB endpoint, active"); | |
| 2001/1010 | else error("can't schedule USB endpoint"); } | |
| 2001/1017 | e->foffset = 0; e->toffset = 0; e->poffset = 0; e->buffered = 0; | |
| 2001/0626 | 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 usbclose(Chan *c) { Udev *d; | |
| 2001/1016 | if(c->qid.type == QTDIR || QID(c->qid) < Q3rd) | |
| 2001/0626 | return; qlock(&usbstate); d = usbdevice(c); if(QID(c->qid) == Qctl) d->busy = 0; | |
| 2001/1016 | if(c->flag & COPEN){ XPRINT("usbclose: freedev 0x%p\n", d); | |
| 2001/0626 | freedev(d); | |
| 2001/1016 | } | |
| 2001/0626 | qunlock(&usbstate); } static int eptinput(void *arg) { Endpt *e; e = arg; return e->eof || e->err || qcanread(e->rq); } | |
| 2001/1006 | static int | |
| 2001/1007 | isoready(void *arg) | |
| 2001/1006 | { Endpt *e; e = arg; | |
| 2001/1008 | return e->etd == nil || (e->etd != e->xtd && (e->etd->status & Active) == 0); | |
| 2001/1006 | } | |
| 2001/0626 | static long | |
| 2001/1011 | isoio(Endpt *e, void *a, long n, ulong offset, int w) | |
| 2001/0626 | { | |
| 2001/1008 | int i, frnum; | |
| 2001/1011 | volatile int isolock; | |
| 2001/1010 | uchar *p, *q, *bp; | |
| 2001/1007 | Ctlr *ub; | |
| 2001/1008 | TD *td; | |
| 2001/0626 | ||
| 2001/1010 | qlock(&e->rlock); | |
| 2001/1011 | isolock = 0; | |
| 2001/0626 | if(waserror()){ | |
| 2001/1011 | if (isolock){ isolock = 0; iunlock(&activends); } | |
| 2001/1010 | qunlock(&e->rlock); eptcancel(e); | |
| 2001/0626 | nexterror(); } | |
| 2001/1010 | p = a; | |
| 2001/1011 | ub = &ubus; if (offset != 0 && offset != e->foffset){ | |
| 2001/1017 | iprint("offset %lud, foffset %lud\n", offset, e->foffset); | |
| 2001/1011 | /* 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; } | |
| 2001/1008 | do { | |
| 2001/1011 | if (isolock == 0){ ilock(&activends); isolock = 1; } | |
| 2001/1008 | td = e->etd; if (td == nil || e->off == 0){ if (td == nil){ XPRINT("0"); | |
| 2001/1012 | 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; | |
| 2001/1008 | e->off = 0; | |
| 2001/1012 | }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; } | |
| 2001/1008 | } if (w) e->psize = ((td->dev >> 21) + 1) & 0x7ff; else e->psize = (e->etd->status + 1) & 0x7ff; | |
| 2001/1007 | if (e->psize > e->maxpkt) panic("packet size > maximum"); | |
| 2001/1008 | } | |
| 2001/1011 | isolock = 0; iunlock(&activends); | |
| 2001/1008 | td->flags &= ~IsoClean; | |
| 2001/1010 | bp = e->bp0 + (td - e->td0) * e->maxpkt / e->pollms; q = bp + e->off; | |
| 2001/1008 | if((i = n) >= e->psize) i = e->psize; | |
| 2001/1012 | if (w){ | |
| 2001/1008 | memmove(q, p, i); | |
| 2001/1012 | e->buffered += i; }else{ | |
| 2001/1008 | memmove(p, q, i); | |
| 2001/1012 | e->buffered -= i; if (e->buffered < 0){ iprint("e->buffered %d?\n", e->buffered); e->buffered = 0; } } | |
| 2001/1008 | p += i; n -= i; e->off += i; e->psize -= i; if (e->psize){ if (n != 0) panic("usb iso: can't happen"); break; } | |
| 2001/1019 | if(w) td->offset = offset + (p-(uchar*)a) - (((td->dev >> 21) + 1) & 0x7ff); | |
| 2001/1008 | td->status = ErrLimit3 | Active | IsoSelect | IOC; e->etd = td->next; e->off = 0; } while(n > 0); | |
| 2001/1011 | n = p-(uchar*)a; e->foffset += n; | |
| 2001/1008 | poperror(); | |
| 2001/1011 | if (isolock) iunlock(&activends); | |
| 2001/1010 | qunlock(&e->rlock); | |
| 2001/1011 | return n; | |
| 2001/1008 | } 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(); } | |
| 2001/1010 | 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()){ | |
| 2001/0626 | freeb(b); | |
| 2001/1010 | 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); | |
| 2001/0626 | poperror(); qunlock(&e->rlock); return p-(uchar*)a; } | |
| 2001/1012 | int epstatus(char *s, int n, Endpt *e, int i) { int l; l = 0; | |
| 2001/1016 | l += snprint(s+l, n-l, "%2d %#6.6lux %10lud bytes %10lud blocks\n", | |
| 2001/1012 | i, e->csp, e->nbytes, e->nblocks); | |
| 2001/1017 | 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'; | |
| 2001/1012 | } return l; } | |
| 2001/0626 | 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; i<nelem(d->ep); i++) | |
| 2001/1009 | if((e = d->ep[i]) != nil){ /* TO DO: freeze e */ | |
| 2001/1012 | l += epstatus(s+l, READSTR-l, e, i); | |
| 2001/1009 | } | |
| 2001/0626 | 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 */ | |
| 2001/1010 | if (e->iso) | |
| 2001/1011 | n=isoio(e, a, n, (ulong)offset, 0); | |
| 2001/1010 | else n=readusb(e, a, n); | |
| 2001/0626 | break; } return n; } static int qisempty(void *arg) { return ((QH*)arg)->entries & Terminate; } static long writeusb(Endpt *e, void *a, long n, int tok) { | |
| 2001/1008 | int i; | |
| 2001/0626 | Block *b; | |
| 2001/1008 | uchar *p; | |
| 2001/0626 | QH *qh; qlock(&e->wlock); if(waserror()){ qunlock(&e->wlock); eptcancel(e); nexterror(); } | |
| 2001/1010 | p = a; do { int j; | |
| 2001/0916 | ||
| 2001/1010 | 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) { | |
| 2001/1124 | XPRINT("qh %s: q=%p first=%p last=%p entries=%.8lux\n", "writeusb sleep", qh, qh->first, qh->last, qh->entries); | |
| 2001/1010 | XPRINT("writeusb: sleep %lux\n", &e->wr); sleep(&e->wr, qisempty, qh); XPRINT("writeusb: awake\n"); } } while(n > 0); | |
| 2001/0626 | poperror(); qunlock(&e->wlock); return p-(uchar*)a; } long | |
| 2001/1010 | usbwrite(Chan *c, void *a, long n, vlong offset) | |
| 2001/0626 | { Udev *d; Endpt *e; | |
| 2001/1120 | Cmdbuf *cb; Cmdtab *ct; int id, nw, t, i; | |
| 2001/1124 | char cmd[50]; | |
| 2001/0626 | if(c->qid.type == QTDIR) error(Egreg); t = QID(c->qid); if(t == Qbusctl){ | |
| 2001/1120 | cb = parsecmd(a, n); if(waserror()){ free(cb); nexterror(); } ct = lookupcmd(cb, usbbusctlmsg, nelem(usbbusctlmsg)); | |
| 2001/1124 | id = strtol(cb->f[1], nil, 0); | |
| 2001/0626 | if(id != 1 && id != 2) | |
| 2001/1120 | cmderror(cb, "usb port number not 1 or 2 in"); switch(ct->index){ case BCMdisable: | |
| 2001/0626 | portenable(id, 0); | |
| 2001/1120 | break; case BCMenable: portenable(id, 1); break; case BCMreset: portreset(id); break; } poperror(); free(cb); | |
| 2001/0626 | return n; } d = usbdevice(c); t = QID(c->qid); switch(t){ case Qctl: | |
| 2001/1120 | 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); | |
| 2001/0626 | /* class config# csp ( == class subclass proto) */ if (i < 0 || i >= nelem(d->ep) || d->npt > nelem(d->ep) || i >= d->npt) | |
| 2001/1120 | cmderror(cb, Ebadusbmsg); if (i == 0) d->csp = strtoul(cb->f[3], nil, 0); | |
| 2001/0626 | if(d->ep[i] == nil) d->ep[i] = devendpt(d, i, 1); | |
| 2001/1120 | d->ep[i]->csp = strtoul(cb->f[3], nil, 0); break; case CMdata: i = strtoul(cb->f[1], nil, 0); | |
| 2001/0626 | if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) | |
| 2001/1120 | error(Ebadusbmsg); | |
| 2001/0626 | e = d->ep[i]; | |
| 2001/1120 | e->data01 = strtoul(cb->f[2], nil, 0) != 0; break; case CMmaxpkt: i = strtoul(cb->f[1], nil, 0); | |
| 2001/0626 | if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) | |
| 2001/1120 | error(Ebadusbmsg); | |
| 2001/0626 | e = d->ep[i]; | |
| 2001/1120 | e->maxpkt = strtoul(cb->f[2], nil, 0); | |
| 2001/0626 | if(e->maxpkt > 1500) e->maxpkt = 1500; | |
| 2001/1120 | break; case CMdebug: i = strtoul(cb->f[1], nil, 0); | |
| 2001/0626 | if(i < -1 || i >= nelem(d->ep) || d->ep[i] == nil) | |
| 2001/1120 | error(Ebadusbmsg); | |
| 2001/0626 | if (i == -1) debug = 0; else { debug = 1; e = d->ep[i]; | |
| 2001/1120 | e->debug = strtoul(cb->f[2], nil, 0); | |
| 2001/0626 | } | |
| 2001/1120 | break; case CMunstall: i = strtoul(cb->f[1], nil, 0); | |
| 2001/0626 | if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) | |
| 2001/1120 | error(Ebadusbmsg); | |
| 2001/0626 | e = d->ep[i]; e->err = nil; | |
| 2001/1120 | break; case CMep: | |
| 2001/0916 | /* ep n `bulk' mode maxpkt nbuf OR | |
| 2001/1006 | * ep n period mode samplesize KHz | |
| 2001/0916 | */ | |
| 2001/1120 | i = strtoul(cb->f[1], nil, 0); | |
| 2001/0626 | 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); | |
| 2001/0918 | qlock(&usbstate); | |
| 2001/0626 | if(waserror()){ freept(e); | |
| 2001/0918 | qunlock(&usbstate); | |
| 2001/0626 | nexterror(); } | |
| 2001/1016 | if (e->active) | |
| 2001/0918 | error(Eperm); | |
| 2001/1120 | if(strcmp(cb->f[2], "bulk") == 0){ | |
| 2001/0626 | Ctlr *ub; | |
| 2001/0918 | e->iso = 0; | |
| 2001/0916 | /* ep n `bulk' mode maxpkt nbuf */ | |
| 2001/0626 | 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); | |
| 2001/1120 | e->mode = strcmp(cb->f[3],"r") == 0? OREAD : strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR; i = strtoul(cb->f[4], nil, 0); | |
| 2001/0916 | if(i < 8 || i > 1023) i = 8; e->maxpkt = i; | |
| 2001/1120 | i = strtoul(cb->f[5], nil, 0); | |
| 2001/0916 | if(i >= 1 && i <= 32) e->nbuf = i; } else { /* ep n period mode samplesize KHz */ | |
| 2001/1120 | i = strtoul(cb->f[2], nil, 0); | |
| 2001/0916 | if(i > 0 && i <= 1000){ | |
| 2001/0626 | e->pollms = i; | |
| 2001/0916 | }else { | |
| 2001/0626 | XPRINT("field 4: 0 <= %d <= 1000\n", i); error(Ebadarg); } | |
| 2001/1120 | e->mode = strcmp(cb->f[3],"r") == 0? OREAD : strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR; i = strtoul(cb->f[4], nil, 0); | |
| 2001/0916 | if(i >= 1 && i <= 8){ e->samplesz = i; }else { XPRINT("field 4: 0 < %d <= 8\n", i); error(Ebadarg); } | |
| 2001/1120 | i = strtoul(cb->f[5], nil, 0); | |
| 2001/0916 | if(i >= 1 && i <= 100000){ /* Hz */ e->hz = i; | |
| 2001/0918 | // e->remain = 999/e->pollms; e->remain = 0; | |
| 2001/0916 | }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; | |
| 2001/0626 | } poperror(); | |
| 2001/0918 | qunlock(&usbstate); | |
| 2001/0626 | } | |
| 2001/1120 | poperror(); free(cb); | |
| 2001/0626 | 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 */ } | |
| 2001/1010 | if (e->iso) | |
| 2001/1011 | n = isoio(e, a, n, (ulong)offset, 1); | |
| 2001/1010 | else n = writeusb(e, a, n, TokOUT); | |
| 2001/0626 | break; } return n; } Dev usbdevtab = { 'U', "usb", usbreset, usbinit, usbattach, usbwalk, usbstat, usbopen, devcreate, usbclose, usbread, devbread, usbwrite, devbwrite, devremove, devwstat, }; | |