📄 usbuhci.c
字号:
#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "../port/error.h"#include "usb.h"#define XPRINT if(debug)print#define XXPRINT if(0)printstatic int Chatty = 0;static int debug = 0;static char Estalled[] = "usb endpoint stalled";/* * 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, NFRAME = 1024, FRAMESIZE= NFRAME*sizeof(ulong), /* fixed by hardware; aligned to same */ Vf = 1<<2, /* TD only */ IsQH = 1<<1, Terminate = 1<<0, /* TD.status */ SPD = 1<<29, ErrLimit0 = 0<<27, ErrLimit1 = 1<<27, ErrLimit2 = 2<<27, ErrLimit3 = 3<<27, LowSpeed = 1<<26, IsoSelect = 1<<25, IOC = 1<<24, Active = 1<<23, Stalled = 1<<22, DataBufferErr = 1<<21, Babbling = 1<<20, NAKed = 1<<19, CRCorTimeout = 1<<18, BitstuffErr = 1<<17, AnyError = (Stalled | DataBufferErr | Babbling | NAKed | CRCorTimeout | BitstuffErr), /* TD.dev */ IsDATA1 = 1<<19, /* TD.flags (software) */ CancelTD= 1<<0, IsoClean= 1<<2,};static struct{ int bit; char *name;}portstatus[] ={ { Suspend, "suspend", }, { PortReset, "reset", }, { SlowDevice, "lowspeed", }, { ResumeDetect, "resume", }, { PortChange, "portchange", }, { PortEnable, "enable", }, { StatusChange, "statuschange", }, { DevicePresent, "present", },};typedef struct Ctlr Ctlr;typedef struct Endptx Endptx;typedef struct QH QH;typedef struct TD TD;/* * software structures */struct Ctlr{ Lock; /* protects state shared with interrupt (eg, free list) */ Ctlr* next; Pcidev* pcidev; int active; int io; ulong* frames; /* frame list */ ulong* frameld; /* real time load on each of the frame list entries */ QLock resetl; /* lock controller during USB reset */ TD* tdpool; TD* freetd; QH* qhpool; QH* freeqh; 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 */ Udev* ports[2]; struct { Lock; Endpt* f; } activends; long usbints; /* debugging */ long framenumber; long frameptr; long usbbogus;};#define IN(x) ins(ctlr->io+(x))#define OUT(x, v) outs(ctlr->io+(x), (v))static Ctlr* ctlrhead;static Ctlr* ctlrtail;struct Endptx{ QH* epq; /* queue of TDs for this endpoint */ /* ISO related: */ void* tdalloc; void* bpalloc; 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 */};/* * UHCI hardware structures, aligned on 16-byte boundary */struct TD{ ulong link; ulong status; /* controller r/w */ ulong dev; ulong buffer; /* software */ ulong flags; union{ Block* bp; /* non-iso */ ulong offset; /* iso */ }; Endpt* ep; TD* next;};#define TFOL(p) ((TD*)KADDR((ulong)(p) & ~(0xF|PCIWINDOW)))struct QH{ ulong head; ulong entries; /* address of next TD or QH to process (updated by controller) */ /* software */ QH* hlink; TD* first; QH* next; /* free list */ TD* last; ulong _d1; /* fillers */ ulong _d2;};#define QFOL(p) ((QH*)KADDR((ulong)(p) & ~(0xF|PCIWINDOW)))static TD *alloctd(Ctlr *ctlr){ TD *t; ilock(ctlr); t = ctlr->freetd; if(t == nil) panic("alloctd"); /* TO DO */ ctlr->freetd = t->next; t->next = nil; iunlock(ctlr); t->ep = nil; t->bp = nil; t->status = 0; t->link = Terminate; t->buffer = 0; t->flags = 0; return t;}static voidfreetd(Ctlr *ctlr, TD *t){ t->ep = nil; if(t->bp) freeb(t->bp); t->bp = nil; ilock(ctlr); t->buffer = 0xdeadbeef; t->next = ctlr->freetd; ctlr->freetd = t; iunlock(ctlr);}static voiddumpdata(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 voiddumptd(TD *t, int follow){ int i, n; char buf[20], *s; TD *t0; t0 = t; while(t){ i = t->dev & 0xFF; if(i == TokOUT || i == TokSETUP) n = ((t->dev>>21) + 1) & 0x7FF; else if((t->status & Active) == 0) n = (t->status + 1) & 0x7FF; else n = 0; s = buf; if(t->status & Active) *s++ = 'A'; if(t->status & Stalled) *s++ = 'S'; if(t->status & DataBufferErr) *s++ = 'D'; if(t->status & Babbling) *s++ = 'B'; if(t->status & NAKed) *s++ = 'N'; if(t->status & CRCorTimeout) *s++ = 'T'; if(t->status & BitstuffErr) *s++ = 'b'; if(t->status & LowSpeed) *s++ = 'L'; *s = 0; XPRINT("td %8.8lux: ", t); XPRINT("l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n", t->link, t->status, t->dev, t->buffer, t->bp?(ulong)t->bp->rp:0, t->flags); XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n", buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1); if(debug && t->bp && (t->flags & CancelTD) == 0) dumpdata(t->bp, n); if(!follow || t->link & Terminate || t->link & IsQH) break; t = TFOL(t->link); if(t == t0) break; /* looped */ }}static TD *alloctde(Ctlr *ctlr, Endpt *e, int pid, int n){ TD *t; int tog, id; t = alloctd(ctlr); id = (e->x<<7)|(e->dev->x&0x7F); tog = 0; if((pid == TokOUT && e->wdata01) || (pid == TokIN && e->rdata01)) 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 *ctlr){ QH *qh; ilock(ctlr); qh = ctlr->freeqh; if(qh == nil) panic("allocqh"); /* TO DO */ ctlr->freeqh = qh->next; qh->next = nil; iunlock(ctlr); qh->head = Terminate; qh->entries = Terminate; qh->hlink = nil; qh->first = nil; qh->last = nil; return qh;}static voidfreeqh(Ctlr *ctlr, QH *qh){ ilock(ctlr); qh->next = ctlr->freeqh; ctlr->freeqh = qh; iunlock(ctlr);}static voiddumpqh(QH *q){ int i; QH *q0; q0 = q; for(i = 0; q != nil && i < 10; i++){ XPRINT("qh %8.8lux: %8.8lux %8.8lux\n", q, q->head, q->entries); if((q->entries & (IsQH|Terminate)) == 0) dumptd(TFOL(q->entries), 1); if(q->head & Terminate) break; if((q->head & IsQH) == 0){ XPRINT("head:"); dumptd(TFOL(q->head), 1); break; } q = QFOL(q->head); if(q == q0) break; /* looped */ }}static voidqueuetd(Ctlr *ctlr, QH *q, TD *t, int vf, char *why){ TD *lt; for(lt = t; lt->next != nil; lt = lt->next) lt->link = PCIWADDR(lt->next) | vf; lt->link = Terminate; ilock(ctlr); XPRINT("queuetd %s: t=%p lt=%p q=%p first=%p last=%p entries=%.8lux\n", why, t, lt, q, q->first, q->last, q->entries); if(q->first != nil){ q->last->link = PCIWADDR(t) | vf; q->last->next = t; }else{ q->first = t; q->entries = PCIWADDR(t); } q->last = lt; XPRINT(" t=%p q=%p first=%p last=%p entries=%.8lux\n", t, q, q->first, q->last, q->entries); dumpqh(q); iunlock(ctlr);}static voidcleantd(Ctlr *ctlr, TD *t, int discard){ Block *b; int n, err; XPRINT("cleanTD: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); if(t->ep != nil && t->ep->debug) dumptd(t, 0); if(t->status & Active) panic("cleantd Active"); err = t->status & (AnyError&~NAKed); /* TO DO: on t->status&AnyError, q->entries will not have advanced */ if (err) { XPRINT("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);// 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(ctlr, t);}static voidcleanq(Ctlr *ctlr, QH *q, int discard, int vf){ TD *t, *tp; ilock(ctlr); tp = nil; for(t = q->first; t != nil;){ XPRINT("cleanq: %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer, t->flags, t->next); if(t->status & Active){ if(t->status & NAKed){ t->status = (t->status & ~NAKed) | IOC; /* ensure interrupt next frame */ tp = t; t = t->next; continue; } if(t->flags & CancelTD){ XPRINT("cancelTD: %8.8lux\n", (ulong)t); t->status = (t->status & ~Active) | IOC; /* ensure interrupt next frame */ tp = t; t = t->next; continue; } tp = t; t = t->next; continue; } t->status &= ~IOC; if (tp == nil) { q->first = t->next; if(q->first != nil) q->entries = PCIWADDR(q->first); else q->entries = Terminate; } else { tp->next = t->next; if (t->next != nil) tp->link = PCIWADDR(t->next) | vf; else tp->link = Terminate; } if (q->last == t) q->last = tp; iunlock(ctlr); cleantd(ctlr, t, discard); ilock(ctlr); if (tp) t = tp->next; else t = q->first; XPRINT("t = %8.8lux\n", t); dumpqh(q); } if(q->first && q->entries != PCIWADDR(q->first)){ ctlr->usbbogus++; q->entries = PCIWADDR(q->first); } iunlock(ctlr);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -