1ade43d10SDavid du Colombier /* 2906943f9SDavid du Colombier * USB Universal Host Controller Interface (sic) driver. 3906943f9SDavid du Colombier * 4906943f9SDavid du Colombier * BUGS: 5906943f9SDavid du Colombier * - Too many delays and ilocks. 6906943f9SDavid du Colombier * - bandwidth admission control must be done per-frame. 7906943f9SDavid du Colombier * - interrupt endpoints should go on a tree like [oe]hci. 8906943f9SDavid du Colombier * - must warn of power overruns. 9ade43d10SDavid du Colombier */ 10906943f9SDavid du Colombier 119a747e4fSDavid du Colombier #include "u.h" 129a747e4fSDavid du Colombier #include "../port/lib.h" 139a747e4fSDavid du Colombier #include "mem.h" 149a747e4fSDavid du Colombier #include "dat.h" 159a747e4fSDavid du Colombier #include "fns.h" 169a747e4fSDavid du Colombier #include "io.h" 179a747e4fSDavid du Colombier #include "../port/error.h" 189a747e4fSDavid du Colombier #include "usb.h" 199a747e4fSDavid du Colombier 20906943f9SDavid du Colombier typedef struct Ctlio Ctlio; 21906943f9SDavid du Colombier typedef struct Ctlr Ctlr; 22906943f9SDavid du Colombier typedef struct Isoio Isoio; 23906943f9SDavid du Colombier typedef struct Qh Qh; 24906943f9SDavid du Colombier typedef struct Qhpool Qhpool; 25906943f9SDavid du Colombier typedef struct Qio Qio; 26906943f9SDavid du Colombier typedef struct Td Td; 27906943f9SDavid du Colombier typedef struct Tdpool Tdpool; 289a747e4fSDavid du Colombier 299a747e4fSDavid du Colombier enum 309a747e4fSDavid du Colombier { 31906943f9SDavid du Colombier Resetdelay = 100, /* delay after a controller reset (ms) */ 32906943f9SDavid du Colombier Enabledelay = 100, /* waiting for a port to enable */ 33906943f9SDavid du Colombier Abortdelay = 5, /* delay after cancelling Tds (ms) */ 34906943f9SDavid du Colombier Incr = 64, /* for Td and Qh pools */ 35906943f9SDavid du Colombier 36906943f9SDavid du Colombier Tdatomic = 8, /* max nb. of Tds per bulk I/O op. */ 37906943f9SDavid du Colombier 38906943f9SDavid du Colombier /* Queue states (software) */ 39906943f9SDavid du Colombier Qidle = 0, 40906943f9SDavid du Colombier Qinstall, 41906943f9SDavid du Colombier Qrun, 42906943f9SDavid du Colombier Qdone, 43906943f9SDavid du Colombier Qclose, 44906943f9SDavid du Colombier Qfree, 45906943f9SDavid du Colombier 46ade43d10SDavid du Colombier /* 47906943f9SDavid du Colombier * HW constants 48ade43d10SDavid du Colombier */ 496a5dc222SDavid du Colombier 50906943f9SDavid du Colombier Nframes = 1024, /* 2ⁿ for xspanalloc; max 1024 */ 51906943f9SDavid du Colombier Align = 16, /* for data structures */ 52ade43d10SDavid du Colombier 53906943f9SDavid du Colombier /* Size of small buffer kept within Tds. (software) */ 54906943f9SDavid du Colombier /* Keep as a multiple of Align to maintain alignment of Tds in pool */ 55906943f9SDavid du Colombier Tdndata = 1*Align, 56906943f9SDavid du Colombier 57906943f9SDavid du Colombier /* i/o space 58906943f9SDavid du Colombier * Some ports are short, some are long, some are byte. 59906943f9SDavid du Colombier * We use ins[bsl] and not vmap. 60906943f9SDavid du Colombier */ 619a747e4fSDavid du Colombier Cmd = 0, 62906943f9SDavid du Colombier Crun = 0x01, 63906943f9SDavid du Colombier Chcreset = 0x02, /* host controller reset */ 64906943f9SDavid du Colombier Cgreset = 0x04, /* global reset */ 65906943f9SDavid du Colombier Cegsm = 0x08, /* enter global suspend */ 66906943f9SDavid du Colombier Cfgr = 0x10, /* forge global resume */ 67906943f9SDavid du Colombier Cdbg = 0x20, /* single step, debug */ 68906943f9SDavid du Colombier Cmaxp = 0x80, /* max packet */ 69906943f9SDavid du Colombier 709a747e4fSDavid du Colombier Status = 2, 71906943f9SDavid du Colombier Susbintr = 0x01, /* interrupt */ 72906943f9SDavid du Colombier Seintr = 0x02, /* error interrupt */ 73906943f9SDavid du Colombier Sresume = 0x04, /* resume detect */ 74906943f9SDavid du Colombier Shserr = 0x08, /* host system error */ 75906943f9SDavid du Colombier Shcerr = 0x10, /* host controller error */ 76906943f9SDavid du Colombier Shalted = 0x20, /* controller halted */ 77906943f9SDavid du Colombier Sall = 0x3F, 78906943f9SDavid du Colombier 799a747e4fSDavid du Colombier Usbintr = 4, 80906943f9SDavid du Colombier Itmout = 0x01, /* timeout or crc */ 81906943f9SDavid du Colombier Iresume = 0x02, /* resume interrupt enable */ 82906943f9SDavid du Colombier Ioc = 0x04, /* interrupt on complete */ 83906943f9SDavid du Colombier Ishort = 0x08, /* short packet interrupt */ 84906943f9SDavid du Colombier Iall = 0x0F, 859a747e4fSDavid du Colombier Frnum = 6, 869a747e4fSDavid du Colombier Flbaseadd = 8, 87906943f9SDavid du Colombier SOFmod = 0xC, /* start of frame modifier register */ 88906943f9SDavid du Colombier 899a747e4fSDavid du Colombier Portsc0 = 0x10, 90906943f9SDavid du Colombier PSpresent = 0x0001, /* device present */ 91906943f9SDavid du Colombier PSstatuschg = 0x0002, /* PSpresent changed */ 92906943f9SDavid du Colombier PSenable = 0x0004, /* device enabled */ 93906943f9SDavid du Colombier PSchange = 0x0008, /* PSenable changed */ 94906943f9SDavid du Colombier PSresume = 0x0040, /* resume detected */ 95906943f9SDavid du Colombier PSreserved1 = 0x0080, /* always read as 1; reserved */ 96906943f9SDavid du Colombier PSslow = 0x0100, /* device has low speed */ 97906943f9SDavid du Colombier PSreset = 0x0200, /* port reset */ 98906943f9SDavid du Colombier PSsuspend = 0x1000, /* port suspended */ 999a747e4fSDavid du Colombier 100906943f9SDavid du Colombier /* Transfer descriptor link */ 101906943f9SDavid du Colombier Tdterm = 0x1, /* nil (terminate) */ 102906943f9SDavid du Colombier Tdlinkqh = 0x2, /* link refers to a QH */ 103906943f9SDavid du Colombier Tdvf = 0x4, /* run linked Tds first (depth-first)*/ 1049a747e4fSDavid du Colombier 105906943f9SDavid du Colombier /* Transfer status bits */ 106906943f9SDavid du Colombier Tdbitstuff = 0x00020000, /* bit stuffing error */ 107906943f9SDavid du Colombier Tdcrcto = 0x00040000, /* crc or timeout error */ 108906943f9SDavid du Colombier Tdnak = 0x00080000, /* nak packet received */ 109906943f9SDavid du Colombier Tdbabble = 0x00100000, /* babble detected */ 110906943f9SDavid du Colombier Tddberr = 0x00200000, /* data buf. error */ 111906943f9SDavid du Colombier Tdstalled = 0x00400000, /* serious error to ep. */ 112906943f9SDavid du Colombier Tdactive = 0x00800000, /* enabled/in use by hw */ 113906943f9SDavid du Colombier /* Transfer control bits */ 114906943f9SDavid du Colombier Tdioc = 0x01000000, /* interrupt on complete */ 115906943f9SDavid du Colombier Tdiso = 0x02000000, /* isochronous select */ 116906943f9SDavid du Colombier Tdlow = 0x04000000, /* low speed device */ 117906943f9SDavid du Colombier Tderr1 = 0x08000000, /* bit 0 of error counter */ 118906943f9SDavid du Colombier Tderr2 = 0x10000000, /* bit 1 of error counter */ 119906943f9SDavid du Colombier Tdspd = 0x20000000, /* short packet detect */ 1209a747e4fSDavid du Colombier 121906943f9SDavid du Colombier Tdlen = 0x000003FF, /* actual length field */ 1229a747e4fSDavid du Colombier 123906943f9SDavid du Colombier Tdfatalerr = Tdnak|Tdbabble|Tdstalled, /* hw retries others */ 124906943f9SDavid du Colombier Tderrors = Tdfatalerr|Tdbitstuff|Tdcrcto|Tddberr, 1259a747e4fSDavid du Colombier 126906943f9SDavid du Colombier /* Transfer descriptor token bits */ 127906943f9SDavid du Colombier Tddata0 = 0, 128906943f9SDavid du Colombier Tddata1 = 0x80000, /* data toggle (1==DATA1) */ 129906943f9SDavid du Colombier Tdtokin = 0x69, 130906943f9SDavid du Colombier Tdtokout = 0xE1, 131906943f9SDavid du Colombier Tdtoksetup = 0x2D, 1329a747e4fSDavid du Colombier 133906943f9SDavid du Colombier Tdmaxpkt = 0x800, /* max packet size */ 134906943f9SDavid du Colombier 135906943f9SDavid du Colombier /* Queue head bits */ 136906943f9SDavid du Colombier QHterm = 1<<0, /* nil (terminate) */ 137906943f9SDavid du Colombier QHlinkqh = 1<<1, /* link refers to a QH */ 138906943f9SDavid du Colombier QHvf = 1<<2, /* vertical first (depth first) */ 1399a747e4fSDavid du Colombier }; 1409a747e4fSDavid du Colombier 1419a747e4fSDavid du Colombier struct Ctlr 1429a747e4fSDavid du Colombier { 143906943f9SDavid du Colombier Lock; /* for ilock. qh lists and basic ctlr I/O */ 144906943f9SDavid du Colombier QLock portlck; /* for port resets/enable... */ 1459a747e4fSDavid du Colombier Pcidev* pcidev; 1469a747e4fSDavid du Colombier int active; 147906943f9SDavid du Colombier int port; /* I/O address */ 148906943f9SDavid du Colombier Qh* qhs; /* list of Qhs for this controller */ 149906943f9SDavid du Colombier Qh* qh[Tmax]; /* Dummy Qhs to insert Qhs after */ 150906943f9SDavid du Colombier Isoio* iso; /* list of active iso I/O */ 151906943f9SDavid du Colombier ulong* frames; /* frame list (used by hw) */ 152906943f9SDavid du Colombier ulong load; /* max load for a single frame */ 153906943f9SDavid du Colombier ulong isoload; /* max iso load for a single frame */ 154906943f9SDavid du Colombier int nintr; /* number of interrupts attended */ 155906943f9SDavid du Colombier int ntdintr; /* number of intrs. with something to do */ 156906943f9SDavid du Colombier int nqhintr; /* number of intrs. for Qhs */ 157906943f9SDavid du Colombier int nisointr; /* number of intrs. for iso transfers */ 158906943f9SDavid du Colombier }; 1599a747e4fSDavid du Colombier 160906943f9SDavid du Colombier struct Qio 161906943f9SDavid du Colombier { 162906943f9SDavid du Colombier QLock; /* for the entire I/O process */ 163906943f9SDavid du Colombier Rendez; /* wait for completion */ 164906943f9SDavid du Colombier Qh* qh; /* Td list (field const after init) */ 165906943f9SDavid du Colombier int usbid; /* usb address for endpoint/device */ 166906943f9SDavid du Colombier int toggle; /* Tddata0/Tddata1 */ 167906943f9SDavid du Colombier int tok; /* Tdtoksetup, Tdtokin, Tdtokout */ 168906943f9SDavid du Colombier ulong iotime; /* time of last I/O */ 169906943f9SDavid du Colombier int debug; /* debug flag from the endpoint */ 170906943f9SDavid du Colombier char* err; /* error string */ 171906943f9SDavid du Colombier }; 1729a747e4fSDavid du Colombier 173906943f9SDavid du Colombier struct Ctlio 174906943f9SDavid du Colombier { 175906943f9SDavid du Colombier Qio; /* a single Qio for each RPC */ 176906943f9SDavid du Colombier uchar* data; /* read from last ctl req. */ 177906943f9SDavid du Colombier int ndata; /* number of bytes read */ 178906943f9SDavid du Colombier }; 1799a747e4fSDavid du Colombier 180906943f9SDavid du Colombier struct Isoio 181906943f9SDavid du Colombier { 182906943f9SDavid du Colombier QLock; 183906943f9SDavid du Colombier Rendez; /* wait for space/completion/errors */ 184906943f9SDavid du Colombier int usbid; /* address used for device/endpoint */ 185906943f9SDavid du Colombier int tok; /* Tdtokin or Tdtokout */ 186906943f9SDavid du Colombier int state; /* Qrun -> Qdone -> Qrun... -> Qclose */ 187906943f9SDavid du Colombier int nframes; /* Nframes/ep->pollival */ 188906943f9SDavid du Colombier uchar* data; /* iso data buffers if not embedded */ 189906943f9SDavid du Colombier int td0frno; /* frame number for first Td */ 190906943f9SDavid du Colombier Td* tdu; /* next td for user I/O in tdps */ 191906943f9SDavid du Colombier Td* tdi; /* next td processed by interrupt */ 192906943f9SDavid du Colombier char* err; /* error string */ 193906943f9SDavid du Colombier int nerrs; /* nb of consecutive I/O errors */ 194906943f9SDavid du Colombier long nleft; /* number of bytes left from last write */ 195906943f9SDavid du Colombier int debug; /* debug flag from the endpoint */ 196906943f9SDavid du Colombier Isoio* next; /* in list of active Isoios */ 197906943f9SDavid du Colombier Td* tdps[Nframes]; /* pointer to Td used for i-th frame or nil */ 198906943f9SDavid du Colombier }; 1999a747e4fSDavid du Colombier 200906943f9SDavid du Colombier struct Tdpool 201906943f9SDavid du Colombier { 2029a747e4fSDavid du Colombier Lock; 203906943f9SDavid du Colombier Td* free; 204906943f9SDavid du Colombier int nalloc; 205906943f9SDavid du Colombier int ninuse; 206906943f9SDavid du Colombier int nfree; 2079a747e4fSDavid du Colombier }; 2089a747e4fSDavid du Colombier 209906943f9SDavid du Colombier struct Qhpool 2109a747e4fSDavid du Colombier { 211906943f9SDavid du Colombier Lock; 212906943f9SDavid du Colombier Qh* free; 213906943f9SDavid du Colombier int nalloc; 214906943f9SDavid du Colombier int ninuse; 215906943f9SDavid du Colombier int nfree; 2169a747e4fSDavid du Colombier }; 2179a747e4fSDavid du Colombier 2189a747e4fSDavid du Colombier /* 219906943f9SDavid du Colombier * HW data structures 2209a747e4fSDavid du Colombier */ 2219a747e4fSDavid du Colombier 222ade43d10SDavid du Colombier /* 223906943f9SDavid du Colombier * Queue header (known by hw). 224906943f9SDavid du Colombier * 16-byte aligned. first two words used by hw. 225906943f9SDavid du Colombier * They are taken from the pool upon endpoint opening and 226906943f9SDavid du Colombier * queued after the dummy queue header for the endpoint type 227906943f9SDavid du Colombier * in the controller. Actual I/O happens as Tds are linked into it. 228906943f9SDavid du Colombier * The driver does I/O in lock-step. 229906943f9SDavid du Colombier * The user builds a list of Tds and links it into the Qh, 230906943f9SDavid du Colombier * then the Qh goes from Qidle to Qrun and nobody touches it until 231906943f9SDavid du Colombier * it becomes Qdone at interrupt time. 232906943f9SDavid du Colombier * At that point the user collects the Tds and it goes Qidle. 233906943f9SDavid du Colombier * A premature cancel may set the state to Qclose and abort I/O. 234906943f9SDavid du Colombier * The Ctlr lock protects change of state for Qhs in use. 235ade43d10SDavid du Colombier */ 236906943f9SDavid du Colombier struct Qh 2379a747e4fSDavid du Colombier { 238906943f9SDavid du Colombier ulong link; /* link to next horiz. item (eg. Qh) */ 239906943f9SDavid du Colombier ulong elink; /* link to element (eg. Td; updated by hw) */ 2409a747e4fSDavid du Colombier 241906943f9SDavid du Colombier ulong state; /* Qidle -> Qinstall -> Qrun -> Qdone | Qclose */ 242906943f9SDavid du Colombier Qio* io; /* for this queue */ 2439a747e4fSDavid du Colombier 244906943f9SDavid du Colombier Qh* next; /* in active or free list */ 245906943f9SDavid du Colombier Td* tds; /* Td list in this Qh (initially, elink) */ 246906943f9SDavid du Colombier char* tag; /* debug and align, mostly */ 247906943f9SDavid du Colombier ulong align; 248906943f9SDavid du Colombier }; 2499a747e4fSDavid du Colombier 250906943f9SDavid du Colombier /* 251906943f9SDavid du Colombier * Transfer descriptor. 252906943f9SDavid du Colombier * 16-byte aligned. first two words used by hw. Next 4 by sw. 253906943f9SDavid du Colombier * We keep an embedded buffer for small I/O transfers. 254906943f9SDavid du Colombier * They are taken from the pool when buffers are needed for I/O 255906943f9SDavid du Colombier * and linked at the Qh/Isoio for the endpoint and direction requiring it. 256906943f9SDavid du Colombier * The block keeps actual data. They are protected from races by 257906943f9SDavid du Colombier * the queue or the pool keeping it. The owner of the link to the Td 258906943f9SDavid du Colombier * is free to use it and can be the only one using it. 2599a747e4fSDavid du Colombier */ 260906943f9SDavid du Colombier struct Td 261906943f9SDavid du Colombier { 262906943f9SDavid du Colombier ulong link; /* Link to next Td or Qh */ 263906943f9SDavid du Colombier ulong csw; /* control and status word (updated by hw) */ 264906943f9SDavid du Colombier ulong token; /* endpt, device, pid */ 265906943f9SDavid du Colombier ulong buffer; /* buffer pointer */ 2669a747e4fSDavid du Colombier 267906943f9SDavid du Colombier Td* next; /* in qh or Isoio or free list */ 268906943f9SDavid du Colombier ulong ndata; /* bytes available/used at data */ 269906943f9SDavid du Colombier uchar* data; /* pointer to actual data */ 270906943f9SDavid du Colombier void* buff; /* allocated data, for large transfers */ 271906943f9SDavid du Colombier 272906943f9SDavid du Colombier uchar sbuff[Tdndata]; /* embedded buffer, for small transfers */ 273906943f9SDavid du Colombier }; 274906943f9SDavid du Colombier 275906943f9SDavid du Colombier #define INB(x) inb(ctlr->port+(x)) 276906943f9SDavid du Colombier #define INS(x) ins(ctlr->port+(x)) 277906943f9SDavid du Colombier #define INL(x) inl(ctlr->port+(x)) 278906943f9SDavid du Colombier #define OUTB(x, v) outb(ctlr->port+(x), (v)) 279906943f9SDavid du Colombier #define OUTS(x, v) outs(ctlr->port+(x), (v)) 280906943f9SDavid du Colombier #define OUTL(x, v) outl(ctlr->port+(x), (v)) 281906943f9SDavid du Colombier #define TRUNC(x, sz) ((x) & ((sz)-1)) 282906943f9SDavid du Colombier #define PTR(q) ((void*)KADDR((ulong)(q) & ~ (0xF|PCIWINDOW))) 283906943f9SDavid du Colombier #define QPTR(q) ((Qh*)PTR(q)) 284906943f9SDavid du Colombier #define TPTR(q) ((Td*)PTR(q)) 285906943f9SDavid du Colombier #define PORT(p) (Portsc0 + 2*(p)) 286906943f9SDavid du Colombier #define diprint if(debug || iso->debug)print 287906943f9SDavid du Colombier #define ddiprint if(debug>1 || iso->debug>1)print 288906943f9SDavid du Colombier #define dqprint if(debug || (qh->io && qh->io->debug))print 289906943f9SDavid du Colombier #define ddqprint if(debug>1 || (qh->io && qh->io->debug>1))print 290906943f9SDavid du Colombier 291906943f9SDavid du Colombier static Ctlr* ctlrs[Nhcis]; 292906943f9SDavid du Colombier 293906943f9SDavid du Colombier static Tdpool tdpool; 294906943f9SDavid du Colombier static Qhpool qhpool; 295906943f9SDavid du Colombier static int debug; 296906943f9SDavid du Colombier 297906943f9SDavid du Colombier static char* qhsname[] = { "idle", "install", "run", "done", "close", "FREE" }; 2989a747e4fSDavid du Colombier 2999a747e4fSDavid du Colombier static void 300906943f9SDavid du Colombier uhcicmd(Ctlr *ctlr, int c) 3019a747e4fSDavid du Colombier { 302906943f9SDavid du Colombier OUTS(Cmd, c); 3039a747e4fSDavid du Colombier } 3049a747e4fSDavid du Colombier 3059a747e4fSDavid du Colombier static void 306906943f9SDavid du Colombier uhcirun(Ctlr *ctlr, int on) 3079a747e4fSDavid du Colombier { 308906943f9SDavid du Colombier int i; 3099a747e4fSDavid du Colombier 310906943f9SDavid du Colombier ddprint("uhci %#ux setting run to %d\n", ctlr->port, on); 3119a747e4fSDavid du Colombier 3129a747e4fSDavid du Colombier if(on) 313906943f9SDavid du Colombier uhcicmd(ctlr, INS(Cmd)|Crun); 3149a747e4fSDavid du Colombier else 315906943f9SDavid du Colombier uhcicmd(ctlr, INS(Cmd) & ~Crun); 316906943f9SDavid du Colombier for(i = 0; i < 100; i++) 317906943f9SDavid du Colombier if(on == 0 && (INS(Status) & Shalted) != 0) 318906943f9SDavid du Colombier break; 319906943f9SDavid du Colombier else if(on != 0 && (INS(Status) & Shalted) == 0) 320906943f9SDavid du Colombier break; 321906943f9SDavid du Colombier else 322906943f9SDavid du Colombier delay(1); 323906943f9SDavid du Colombier if(i == 100) 324906943f9SDavid du Colombier dprint("uhci %#x run cmd timed out\n", ctlr->port); 325906943f9SDavid du Colombier ddprint("uhci %#ux cmd %#ux sts %#ux\n", 326906943f9SDavid du Colombier ctlr->port, INS(Cmd), INS(Status)); 327906943f9SDavid du Colombier } 328906943f9SDavid du Colombier 329906943f9SDavid du Colombier static int 330906943f9SDavid du Colombier tdlen(Td *td) 331906943f9SDavid du Colombier { 332906943f9SDavid du Colombier return (td->csw+1) & Tdlen; 333906943f9SDavid du Colombier } 334906943f9SDavid du Colombier 335906943f9SDavid du Colombier static int 336906943f9SDavid du Colombier maxtdlen(Td *td) 337906943f9SDavid du Colombier { 338906943f9SDavid du Colombier return ((td->token>>21)+1) & (Tdmaxpkt-1); 339906943f9SDavid du Colombier } 340906943f9SDavid du Colombier 341906943f9SDavid du Colombier static int 342906943f9SDavid du Colombier tdtok(Td *td) 343906943f9SDavid du Colombier { 344906943f9SDavid du Colombier return td->token & 0xFF; 345906943f9SDavid du Colombier } 346906943f9SDavid du Colombier 347906943f9SDavid du Colombier static char* 348906943f9SDavid du Colombier seprinttd(char *s, char *se, Td *td) 349906943f9SDavid du Colombier { 350906943f9SDavid du Colombier s = seprint(s, se, "%#p link %#ulx", td, td->link); 351906943f9SDavid du Colombier if((td->link & Tdvf) != 0) 352906943f9SDavid du Colombier s = seprint(s, se, "V"); 353906943f9SDavid du Colombier if((td->link & Tdterm) != 0) 354906943f9SDavid du Colombier s = seprint(s, se, "T"); 355906943f9SDavid du Colombier if((td->link & Tdlinkqh) != 0) 356906943f9SDavid du Colombier s = seprint(s, se, "Q"); 357906943f9SDavid du Colombier s = seprint(s, se, " csw %#ulx ", td->csw); 358906943f9SDavid du Colombier if(td->csw & Tdactive) 359906943f9SDavid du Colombier s = seprint(s, se, "a"); 360906943f9SDavid du Colombier if(td->csw & Tdiso) 361906943f9SDavid du Colombier s = seprint(s, se, "I"); 362906943f9SDavid du Colombier if(td->csw & Tdioc) 363906943f9SDavid du Colombier s = seprint(s, se, "i"); 364906943f9SDavid du Colombier if(td->csw & Tdlow) 365906943f9SDavid du Colombier s = seprint(s, se, "l"); 366906943f9SDavid du Colombier if((td->csw & (Tderr1|Tderr2)) == 0) 367906943f9SDavid du Colombier s = seprint(s, se, "z"); 368906943f9SDavid du Colombier if(td->csw & Tderrors) 369906943f9SDavid du Colombier s = seprint(s, se, " err %#ulx", td->csw & Tderrors); 370906943f9SDavid du Colombier if(td->csw & Tdstalled) 371906943f9SDavid du Colombier s = seprint(s, se, "s"); 372906943f9SDavid du Colombier if(td->csw & Tddberr) 373906943f9SDavid du Colombier s = seprint(s, se, "d"); 374906943f9SDavid du Colombier if(td->csw & Tdbabble) 375906943f9SDavid du Colombier s = seprint(s, se, "b"); 376906943f9SDavid du Colombier if(td->csw & Tdnak) 377906943f9SDavid du Colombier s = seprint(s, se, "n"); 378906943f9SDavid du Colombier if(td->csw & Tdcrcto) 379906943f9SDavid du Colombier s = seprint(s, se, "c"); 380906943f9SDavid du Colombier if(td->csw & Tdbitstuff) 381906943f9SDavid du Colombier s = seprint(s, se, "B"); 382906943f9SDavid du Colombier s = seprint(s, se, " stslen %d", tdlen(td)); 383906943f9SDavid du Colombier 384906943f9SDavid du Colombier s = seprint(s, se, " token %#ulx", td->token); 385906943f9SDavid du Colombier if(td->token == 0) /* the BWS loopback Td, ignore rest */ 386906943f9SDavid du Colombier return s; 387906943f9SDavid du Colombier s = seprint(s, se, " maxlen %d", maxtdlen(td)); 388906943f9SDavid du Colombier if(td->token & Tddata1) 389906943f9SDavid du Colombier s = seprint(s, se, " d1"); 390906943f9SDavid du Colombier else 391906943f9SDavid du Colombier s = seprint(s, se, " d0"); 392906943f9SDavid du Colombier s = seprint(s, se, " id %#ulx:", (td->token>>15) & Epmax); 393906943f9SDavid du Colombier s = seprint(s, se, "%#ulx", (td->token>>8) & Devmax); 394906943f9SDavid du Colombier switch(tdtok(td)){ 395906943f9SDavid du Colombier case Tdtokin: 396906943f9SDavid du Colombier s = seprint(s, se, " in"); 397906943f9SDavid du Colombier break; 398906943f9SDavid du Colombier case Tdtokout: 399906943f9SDavid du Colombier s = seprint(s, se, " out"); 400906943f9SDavid du Colombier break; 401906943f9SDavid du Colombier case Tdtoksetup: 402906943f9SDavid du Colombier s = seprint(s, se, " setup"); 403906943f9SDavid du Colombier break; 404906943f9SDavid du Colombier default: 405906943f9SDavid du Colombier s = seprint(s, se, " BADPID"); 406906943f9SDavid du Colombier } 407906943f9SDavid du Colombier s = seprint(s, se, "\n\t buffer %#ulx data %#p", td->buffer, td->data); 408906943f9SDavid du Colombier s = seprint(s, se, " ndata %uld sbuff %#p buff %#p", 409906943f9SDavid du Colombier td->ndata, td->sbuff, td->buff); 410906943f9SDavid du Colombier if(td->ndata > 0) 411906943f9SDavid du Colombier s = seprintdata(s, se, td->data, td->ndata); 412906943f9SDavid du Colombier return s; 4139a747e4fSDavid du Colombier } 4149a747e4fSDavid du Colombier 4159a747e4fSDavid du Colombier static void 416906943f9SDavid du Colombier isodump(Isoio *iso, int all) 4179a747e4fSDavid du Colombier { 418906943f9SDavid du Colombier char buf[256]; 419906943f9SDavid du Colombier Td *td; 420906943f9SDavid du Colombier int i; 4219a747e4fSDavid du Colombier 422906943f9SDavid du Colombier print("iso %#p %s state %d nframes %d" 423906943f9SDavid du Colombier " td0 %#p tdu %#p tdi %#p data %#p\n", 424906943f9SDavid du Colombier iso, iso->tok == Tdtokin ? "in" : "out", 425906943f9SDavid du Colombier iso->state, iso->nframes, iso->tdps[iso->td0frno], 426906943f9SDavid du Colombier iso->tdu, iso->tdi, iso->data); 427906943f9SDavid du Colombier if(iso->err != nil) 428906943f9SDavid du Colombier print("\terr='%s'\n", iso->err); 429906943f9SDavid du Colombier if(all == 0){ 430906943f9SDavid du Colombier seprinttd(buf, buf+sizeof(buf), iso->tdu); 431906943f9SDavid du Colombier print("\ttdu %s\n", buf); 432906943f9SDavid du Colombier seprinttd(buf, buf+sizeof(buf), iso->tdi); 433906943f9SDavid du Colombier print("\ttdi %s\n", buf); 4349a747e4fSDavid du Colombier }else{ 435906943f9SDavid du Colombier td = iso->tdps[iso->td0frno]; 436906943f9SDavid du Colombier for(i = 0; i < iso->nframes; i++){ 437906943f9SDavid du Colombier seprinttd(buf, buf+sizeof(buf), td); 438906943f9SDavid du Colombier if(td == iso->tdi) 439906943f9SDavid du Colombier print("i->"); 440906943f9SDavid du Colombier if(td == iso->tdu) 441906943f9SDavid du Colombier print("u->"); 442906943f9SDavid du Colombier print("\t%s\n", buf); 4439a747e4fSDavid du Colombier td = td->next; 444906943f9SDavid du Colombier } 445906943f9SDavid du Colombier } 446906943f9SDavid du Colombier } 447906943f9SDavid du Colombier 448906943f9SDavid du Colombier static int 449906943f9SDavid du Colombier sameptr(void *p, ulong l) 450906943f9SDavid du Colombier { 451906943f9SDavid du Colombier if(l & QHterm) 452906943f9SDavid du Colombier return p == nil; 453906943f9SDavid du Colombier return PTR(l) == p; 454906943f9SDavid du Colombier } 455906943f9SDavid du Colombier 456906943f9SDavid du Colombier static void 457906943f9SDavid du Colombier dumptd(Td *td, char *pref) 458906943f9SDavid du Colombier { 459906943f9SDavid du Colombier char buf[256]; 460906943f9SDavid du Colombier char *s; 461906943f9SDavid du Colombier char *se; 462906943f9SDavid du Colombier int i; 463906943f9SDavid du Colombier 464906943f9SDavid du Colombier i = 0; 465906943f9SDavid du Colombier se = buf+sizeof(buf); 466906943f9SDavid du Colombier for(; td != nil; td = td->next){ 467906943f9SDavid du Colombier s = seprinttd(buf, se, td); 468906943f9SDavid du Colombier if(!sameptr(td->next, td->link)) 469906943f9SDavid du Colombier seprint(s, se, " next %#p != link %#ulx %#p", 470906943f9SDavid du Colombier td->next, td->link, TPTR(td->link)); 471906943f9SDavid du Colombier print("%std %s\n", pref, buf); 472906943f9SDavid du Colombier if(i++ > 20){ 473906943f9SDavid du Colombier print("...more tds...\n"); 4749a747e4fSDavid du Colombier break; 4759a747e4fSDavid du Colombier } 476906943f9SDavid du Colombier } 477906943f9SDavid du Colombier } 4789a747e4fSDavid du Colombier 479906943f9SDavid du Colombier static void 480906943f9SDavid du Colombier qhdump(Qh *qh, char *pref) 481906943f9SDavid du Colombier { 482906943f9SDavid du Colombier char buf[256]; 483906943f9SDavid du Colombier char *s; 484906943f9SDavid du Colombier char *se; 485906943f9SDavid du Colombier ulong td; 486906943f9SDavid du Colombier int i; 487906943f9SDavid du Colombier 488906943f9SDavid du Colombier s = buf; 489906943f9SDavid du Colombier se = buf+sizeof(buf); 490906943f9SDavid du Colombier s = seprint(s, se, "%sqh %s %#p state %s link %#ulx", pref, 491906943f9SDavid du Colombier qh->tag, qh, qhsname[qh->state], qh->link); 492906943f9SDavid du Colombier if(!sameptr(qh->tds, qh->elink)) 493906943f9SDavid du Colombier s = seprint(s, se, " [tds %#p != elink %#ulx %#p]", 494906943f9SDavid du Colombier qh->tds, qh->elink, TPTR(qh->elink)); 495906943f9SDavid du Colombier if(!sameptr(qh->next, qh->link)) 496906943f9SDavid du Colombier s = seprint(s, se, " [next %#p != link %#ulx %#p]", 497906943f9SDavid du Colombier qh->next, qh->link, QPTR(qh->link)); 498906943f9SDavid du Colombier if((qh->link & Tdterm) != 0) 499906943f9SDavid du Colombier s = seprint(s, se, "T"); 500906943f9SDavid du Colombier if((qh->link & Tdlinkqh) != 0) 501906943f9SDavid du Colombier s = seprint(s, se, "Q"); 502906943f9SDavid du Colombier s = seprint(s, se, " elink %#ulx", qh->elink); 503906943f9SDavid du Colombier if((qh->elink & Tdterm) != 0) 504906943f9SDavid du Colombier s = seprint(s, se, "T"); 505906943f9SDavid du Colombier if((qh->elink & Tdlinkqh) != 0) 506906943f9SDavid du Colombier s = seprint(s, se, "Q"); 507906943f9SDavid du Colombier s = seprint(s, se, " io %#p", qh->io); 508906943f9SDavid du Colombier if(qh->io != nil && qh->io->err != nil) 509906943f9SDavid du Colombier seprint(s, se, " err='%s'", qh->io->err); 510906943f9SDavid du Colombier print("%s\n", buf); 511906943f9SDavid du Colombier dumptd(qh->tds, "\t"); 512906943f9SDavid du Colombier if((qh->elink & QHterm) == 0){ 513906943f9SDavid du Colombier print("\thw tds:"); 514906943f9SDavid du Colombier i = 0; 515906943f9SDavid du Colombier for(td = qh->elink; (td & Tdterm) == 0; td = TPTR(td)->link){ 516906943f9SDavid du Colombier print(" %#ulx", td); 517906943f9SDavid du Colombier if(td == TPTR(td)->link) /* BWS Td */ 518906943f9SDavid du Colombier break; 519906943f9SDavid du Colombier if(i++ > 40){ 520906943f9SDavid du Colombier print("..."); 521906943f9SDavid du Colombier break; 522906943f9SDavid du Colombier } 523906943f9SDavid du Colombier } 524906943f9SDavid du Colombier print("\n"); 525906943f9SDavid du Colombier } 526906943f9SDavid du Colombier } 527906943f9SDavid du Colombier 528906943f9SDavid du Colombier static void 529906943f9SDavid du Colombier xdump(Ctlr *ctlr, int doilock) 530906943f9SDavid du Colombier { 531906943f9SDavid du Colombier Isoio *iso; 532906943f9SDavid du Colombier Qh *qh; 533906943f9SDavid du Colombier int i; 534906943f9SDavid du Colombier 535906943f9SDavid du Colombier if(doilock){ 536906943f9SDavid du Colombier if(ctlr == ctlrs[0]){ 537906943f9SDavid du Colombier lock(&tdpool); 538906943f9SDavid du Colombier print("tds: alloc %d = inuse %d + free %d\n", 539906943f9SDavid du Colombier tdpool.nalloc, tdpool.ninuse, tdpool.nfree); 540906943f9SDavid du Colombier unlock(&tdpool); 541906943f9SDavid du Colombier lock(&qhpool); 542906943f9SDavid du Colombier print("qhs: alloc %d = inuse %d + free %d\n", 543906943f9SDavid du Colombier qhpool.nalloc, qhpool.ninuse, qhpool.nfree); 544906943f9SDavid du Colombier unlock(&qhpool); 545906943f9SDavid du Colombier } 546906943f9SDavid du Colombier ilock(ctlr); 547906943f9SDavid du Colombier } 548906943f9SDavid du Colombier print("uhci port %#x frames %#p nintr %d ntdintr %d", 549906943f9SDavid du Colombier ctlr->port, ctlr->frames, ctlr->nintr, ctlr->ntdintr); 550906943f9SDavid du Colombier print(" nqhintr %d nisointr %d\n", ctlr->nqhintr, ctlr->nisointr); 551906943f9SDavid du Colombier print("cmd %#ux sts %#ux fl %#ulx ps1 %#ux ps2 %#ux frames[0] %#ulx\n", 552906943f9SDavid du Colombier INS(Cmd), INS(Status), 553906943f9SDavid du Colombier INL(Flbaseadd), INS(PORT(0)), INS(PORT(1)), 554906943f9SDavid du Colombier ctlr->frames[0]); 555906943f9SDavid du Colombier for(iso = ctlr->iso; iso != nil; iso = iso->next) 556906943f9SDavid du Colombier isodump(iso, 1); 557906943f9SDavid du Colombier i = 0; 558906943f9SDavid du Colombier for(qh = ctlr->qhs; qh != nil; qh = qh->next){ 559906943f9SDavid du Colombier qhdump(qh, ""); 560906943f9SDavid du Colombier if(i++ > 20){ 561906943f9SDavid du Colombier print("qhloop\n"); 562906943f9SDavid du Colombier break; 563906943f9SDavid du Colombier } 564906943f9SDavid du Colombier } 565906943f9SDavid du Colombier print("\n"); 566906943f9SDavid du Colombier if(doilock) 567906943f9SDavid du Colombier iunlock(ctlr); 568906943f9SDavid du Colombier } 569906943f9SDavid du Colombier 570906943f9SDavid du Colombier static void 571906943f9SDavid du Colombier dump(Hci *hp) 572906943f9SDavid du Colombier { 573906943f9SDavid du Colombier xdump(hp->aux, 1); 574906943f9SDavid du Colombier } 575906943f9SDavid du Colombier 576906943f9SDavid du Colombier static Td* 577906943f9SDavid du Colombier tdalloc(void) 578906943f9SDavid du Colombier { 579906943f9SDavid du Colombier int i; 580906943f9SDavid du Colombier Td *td; 581906943f9SDavid du Colombier Td *pool; 582906943f9SDavid du Colombier 583906943f9SDavid du Colombier lock(&tdpool); 584906943f9SDavid du Colombier if(tdpool.free == nil){ 585906943f9SDavid du Colombier ddprint("uhci: tdalloc %d Tds\n", Incr); 586906943f9SDavid du Colombier pool = xspanalloc(Incr*sizeof(Td), Align, 0); 587906943f9SDavid du Colombier if(pool == nil) 588906943f9SDavid du Colombier panic("tdalloc"); 589906943f9SDavid du Colombier for(i=Incr; --i>=0;){ 590906943f9SDavid du Colombier pool[i].next = tdpool.free; 591906943f9SDavid du Colombier tdpool.free = &pool[i]; 592906943f9SDavid du Colombier } 593906943f9SDavid du Colombier tdpool.nalloc += Incr; 594906943f9SDavid du Colombier tdpool.nfree += Incr; 595906943f9SDavid du Colombier } 596906943f9SDavid du Colombier td = tdpool.free; 597906943f9SDavid du Colombier tdpool.free = td->next; 598906943f9SDavid du Colombier tdpool.ninuse++; 599906943f9SDavid du Colombier tdpool.nfree--; 600906943f9SDavid du Colombier unlock(&tdpool); 601906943f9SDavid du Colombier 602906943f9SDavid du Colombier memset(td, 0, sizeof(Td)); 603906943f9SDavid du Colombier td->link = Tdterm; 604906943f9SDavid du Colombier assert(((ulong)td & 0xF) == 0); 605906943f9SDavid du Colombier return td; 606906943f9SDavid du Colombier } 607906943f9SDavid du Colombier 608906943f9SDavid du Colombier static void 609906943f9SDavid du Colombier tdfree(Td *td) 610906943f9SDavid du Colombier { 611906943f9SDavid du Colombier if(td == nil) 612906943f9SDavid du Colombier return; 613906943f9SDavid du Colombier free(td->buff); 614906943f9SDavid du Colombier td->buff = nil; 615906943f9SDavid du Colombier lock(&tdpool); 616906943f9SDavid du Colombier td->next = tdpool.free; 617906943f9SDavid du Colombier tdpool.free = td; 618906943f9SDavid du Colombier tdpool.ninuse--; 619906943f9SDavid du Colombier tdpool.nfree++; 620906943f9SDavid du Colombier unlock(&tdpool); 621906943f9SDavid du Colombier } 622906943f9SDavid du Colombier 623906943f9SDavid du Colombier static void 624906943f9SDavid du Colombier qhlinkqh(Qh* qh, Qh* next) 625906943f9SDavid du Colombier { 626906943f9SDavid du Colombier if(next == nil) 627906943f9SDavid du Colombier qh->link = QHterm; 628906943f9SDavid du Colombier else{ 629906943f9SDavid du Colombier next->link = qh->link; 630906943f9SDavid du Colombier next->next = qh->next; 631906943f9SDavid du Colombier qh->link = PCIWADDR(next)|QHlinkqh; 632906943f9SDavid du Colombier } 633906943f9SDavid du Colombier qh->next = next; 634906943f9SDavid du Colombier } 635906943f9SDavid du Colombier 636906943f9SDavid du Colombier static void 637906943f9SDavid du Colombier qhlinktd(Qh *qh, Td *td) 638906943f9SDavid du Colombier { 639906943f9SDavid du Colombier qh->tds = td; 640906943f9SDavid du Colombier if(td == nil) 641906943f9SDavid du Colombier qh->elink = QHvf|QHterm; 6429a747e4fSDavid du Colombier else 643906943f9SDavid du Colombier qh->elink = PCIWADDR(td); 6449a747e4fSDavid du Colombier } 645906943f9SDavid du Colombier 646906943f9SDavid du Colombier static void 647906943f9SDavid du Colombier tdlinktd(Td *td, Td *next) 648906943f9SDavid du Colombier { 649906943f9SDavid du Colombier td->next = next; 650906943f9SDavid du Colombier if(next == nil) 651906943f9SDavid du Colombier td->link = Tdterm; 652906943f9SDavid du Colombier else 653906943f9SDavid du Colombier td->link = PCIWADDR(next)|Tdvf; 654906943f9SDavid du Colombier } 655906943f9SDavid du Colombier 656906943f9SDavid du Colombier static Qh* 657906943f9SDavid du Colombier qhalloc(Ctlr *ctlr, Qh *prev, Qio *io, char *tag) 658906943f9SDavid du Colombier { 659906943f9SDavid du Colombier int i; 660906943f9SDavid du Colombier Qh *qh; 661906943f9SDavid du Colombier Qh *pool; 662906943f9SDavid du Colombier 663906943f9SDavid du Colombier lock(&qhpool); 664906943f9SDavid du Colombier if(qhpool.free == nil){ 665906943f9SDavid du Colombier ddprint("uhci: qhalloc %d Qhs\n", Incr); 666906943f9SDavid du Colombier pool = xspanalloc(Incr*sizeof(Qh), Align, 0); 667906943f9SDavid du Colombier if(pool == nil) 668906943f9SDavid du Colombier panic("qhalloc"); 669906943f9SDavid du Colombier for(i=Incr; --i>=0;){ 670906943f9SDavid du Colombier pool[i].next = qhpool.free; 671906943f9SDavid du Colombier qhpool.free = &pool[i]; 672906943f9SDavid du Colombier } 673906943f9SDavid du Colombier qhpool.nalloc += Incr; 674906943f9SDavid du Colombier qhpool.nfree += Incr; 675906943f9SDavid du Colombier } 676906943f9SDavid du Colombier qh = qhpool.free; 677906943f9SDavid du Colombier qhpool.free = qh->next; 678906943f9SDavid du Colombier qh->next = nil; 679906943f9SDavid du Colombier qh->link = QHterm; 680906943f9SDavid du Colombier qhpool.ninuse++; 681906943f9SDavid du Colombier qhpool.nfree--; 682906943f9SDavid du Colombier unlock(&qhpool); 683906943f9SDavid du Colombier 684906943f9SDavid du Colombier qh->tds = nil; 685906943f9SDavid du Colombier qh->elink = QHterm; 686906943f9SDavid du Colombier qh->state = Qidle; 687906943f9SDavid du Colombier qh->io = io; 688906943f9SDavid du Colombier qh->tag = nil; 689906943f9SDavid du Colombier kstrdup(&qh->tag, tag); 690906943f9SDavid du Colombier 691906943f9SDavid du Colombier if(prev != nil){ 692906943f9SDavid du Colombier coherence(); 693906943f9SDavid du Colombier ilock(ctlr); 694906943f9SDavid du Colombier qhlinkqh(prev, qh); 695906943f9SDavid du Colombier iunlock(ctlr); 696906943f9SDavid du Colombier } 697906943f9SDavid du Colombier 698906943f9SDavid du Colombier assert(((ulong)qh & 0xF) == 0); 699906943f9SDavid du Colombier return qh; 700906943f9SDavid du Colombier } 701906943f9SDavid du Colombier 702906943f9SDavid du Colombier static void 703906943f9SDavid du Colombier qhfree(Ctlr *ctlr, Qh *qh) 704906943f9SDavid du Colombier { 705906943f9SDavid du Colombier Td *td; 706906943f9SDavid du Colombier Td *ltd; 707906943f9SDavid du Colombier Qh *q; 708906943f9SDavid du Colombier 709906943f9SDavid du Colombier if(qh == nil) 710906943f9SDavid du Colombier return; 711906943f9SDavid du Colombier 712906943f9SDavid du Colombier ilock(ctlr); 713906943f9SDavid du Colombier for(q = ctlr->qhs; q != nil; q = q->next) 714906943f9SDavid du Colombier if(q->next == qh) 715906943f9SDavid du Colombier break; 716906943f9SDavid du Colombier if(q == nil) 717906943f9SDavid du Colombier panic("qhfree: nil q"); 718906943f9SDavid du Colombier q->next = qh->next; 719906943f9SDavid du Colombier q->link = qh->link; 720906943f9SDavid du Colombier iunlock(ctlr); 721906943f9SDavid du Colombier 722906943f9SDavid du Colombier for(td = qh->tds; td != nil; td = ltd){ 723906943f9SDavid du Colombier ltd = td->next; 724906943f9SDavid du Colombier tdfree(td); 725906943f9SDavid du Colombier } 726906943f9SDavid du Colombier lock(&qhpool); 727906943f9SDavid du Colombier qh->state = Qfree; /* paranoia */ 728906943f9SDavid du Colombier qh->next = qhpool.free; 729906943f9SDavid du Colombier qh->tag = nil; 730906943f9SDavid du Colombier qh->io = nil; 731906943f9SDavid du Colombier qhpool.free = qh; 732906943f9SDavid du Colombier qhpool.ninuse--; 733906943f9SDavid du Colombier qhpool.nfree++; 734906943f9SDavid du Colombier unlock(&qhpool); 735906943f9SDavid du Colombier ddprint("qhfree: qh %#p\n", qh); 736906943f9SDavid du Colombier } 737906943f9SDavid du Colombier 738906943f9SDavid du Colombier static char* 739906943f9SDavid du Colombier errmsg(int err) 740906943f9SDavid du Colombier { 741906943f9SDavid du Colombier if(err == 0) 742906943f9SDavid du Colombier return "ok"; 743906943f9SDavid du Colombier if(err & Tdcrcto) 744906943f9SDavid du Colombier return "crc/timeout error"; 745906943f9SDavid du Colombier if(err & Tdbabble) 746906943f9SDavid du Colombier return "babble detected"; 747906943f9SDavid du Colombier if(err & Tddberr) 748906943f9SDavid du Colombier return "db error"; 749906943f9SDavid du Colombier if(err & Tdbitstuff) 750906943f9SDavid du Colombier return "bit stuffing error"; 751906943f9SDavid du Colombier if(err & Tdstalled) 752906943f9SDavid du Colombier return Estalled; 753906943f9SDavid du Colombier return Eio; 754906943f9SDavid du Colombier } 755906943f9SDavid du Colombier 756906943f9SDavid du Colombier static int 757906943f9SDavid du Colombier isocanread(void *a) 758906943f9SDavid du Colombier { 759906943f9SDavid du Colombier Isoio *iso; 760906943f9SDavid du Colombier 761906943f9SDavid du Colombier iso = a; 762906943f9SDavid du Colombier return iso->state == Qclose || 763906943f9SDavid du Colombier (iso->state == Qrun && 764906943f9SDavid du Colombier iso->tok == Tdtokin && iso->tdi != iso->tdu); 765906943f9SDavid du Colombier } 766906943f9SDavid du Colombier 767906943f9SDavid du Colombier static int 768906943f9SDavid du Colombier isocanwrite(void *a) 769906943f9SDavid du Colombier { 770906943f9SDavid du Colombier Isoio *iso; 771906943f9SDavid du Colombier 772906943f9SDavid du Colombier iso = a; 773906943f9SDavid du Colombier return iso->state == Qclose || 774906943f9SDavid du Colombier (iso->state == Qrun && 775906943f9SDavid du Colombier iso->tok == Tdtokout && iso->tdu->next != iso->tdi); 776906943f9SDavid du Colombier } 777906943f9SDavid du Colombier 778906943f9SDavid du Colombier static void 779906943f9SDavid du Colombier tdisoinit(Isoio *iso, Td *td, long count) 780906943f9SDavid du Colombier { 781906943f9SDavid du Colombier td->ndata = count; 782906943f9SDavid du Colombier td->token = ((count-1)<<21)| ((iso->usbid & 0x7FF)<<8) | iso->tok; 783906943f9SDavid du Colombier td->csw = Tderr1|Tdiso|Tdactive|Tdioc; 784906943f9SDavid du Colombier } 785906943f9SDavid du Colombier 786906943f9SDavid du Colombier /* 787906943f9SDavid du Colombier * Process Iso i/o on interrupt. For writes update just error status. 788906943f9SDavid du Colombier * For reads update tds to reflect data and also error status. 789906943f9SDavid du Colombier * When tdi aproaches tdu, advance tdu; data may be lost. 790906943f9SDavid du Colombier * (If nframes is << Nframes tdu might be far away but this avoids 791906943f9SDavid du Colombier * races regarding frno.) 792906943f9SDavid du Colombier * If we suffer errors for more than half the frames we stall. 793906943f9SDavid du Colombier */ 794906943f9SDavid du Colombier static void 795906943f9SDavid du Colombier isointerrupt(Ctlr *ctlr, Isoio* iso) 796906943f9SDavid du Colombier { 797906943f9SDavid du Colombier Td *tdi; 798906943f9SDavid du Colombier int err; 799906943f9SDavid du Colombier int i; 800906943f9SDavid du Colombier int nframes; 801906943f9SDavid du Colombier 802906943f9SDavid du Colombier tdi = iso->tdi; 803906943f9SDavid du Colombier if((tdi->csw & Tdactive) != 0) /* nothing new done */ 804906943f9SDavid du Colombier return; 805906943f9SDavid du Colombier ctlr->nisointr++; 806906943f9SDavid du Colombier ddiprint("isointr: iso %#p: tdi %#p tdu %#p\n", iso, tdi, iso->tdu); 807906943f9SDavid du Colombier if(iso->state != Qrun && iso->state != Qdone) 808906943f9SDavid du Colombier panic("isointr: iso state"); 809906943f9SDavid du Colombier if(debug > 1 || iso->debug > 1) 810906943f9SDavid du Colombier isodump(iso, 0); 811906943f9SDavid du Colombier 812906943f9SDavid du Colombier nframes = iso->nframes / 2; /* limit how many we look */ 813906943f9SDavid du Colombier if(nframes > 64) 814906943f9SDavid du Colombier nframes = 64; 815906943f9SDavid du Colombier 816906943f9SDavid du Colombier for(i = 0; i < nframes && (tdi->csw & Tdactive) == 0; i++){ 817906943f9SDavid du Colombier tdi->csw &= ~Tdioc; 818906943f9SDavid du Colombier err = tdi->csw & Tderrors; 819906943f9SDavid du Colombier if(err == 0) 820906943f9SDavid du Colombier iso->nerrs = 0; 821906943f9SDavid du Colombier else if(iso->nerrs++ > iso->nframes/2) 822906943f9SDavid du Colombier tdi->csw |= Tdstalled; 823906943f9SDavid du Colombier if((tdi->csw & Tdstalled) != 0){ 824906943f9SDavid du Colombier if(iso->err == nil){ 825906943f9SDavid du Colombier iso->err = errmsg(err); 826906943f9SDavid du Colombier diprint("isointerrupt: tdi %#p error %#ux %s\n", 827906943f9SDavid du Colombier tdi, err, iso->err); 828906943f9SDavid du Colombier diprint("ctlr load %uld\n", ctlr->load); 829906943f9SDavid du Colombier } 830906943f9SDavid du Colombier tdi->ndata = 0; 831ade43d10SDavid du Colombier }else 832906943f9SDavid du Colombier tdi->ndata = tdlen(tdi); 833906943f9SDavid du Colombier 834906943f9SDavid du Colombier if(tdi->next == iso->tdu || tdi->next->next == iso->tdu){ 835906943f9SDavid du Colombier memset(iso->tdu->data, 0, maxtdlen(iso->tdu)); 836906943f9SDavid du Colombier tdisoinit(iso, iso->tdu, maxtdlen(iso->tdu)); 837906943f9SDavid du Colombier iso->tdu = iso->tdu->next; 838906943f9SDavid du Colombier iso->nleft = 0; 8399a747e4fSDavid du Colombier } 840906943f9SDavid du Colombier tdi = tdi->next; 841906943f9SDavid du Colombier } 842906943f9SDavid du Colombier ddiprint("isointr: %d frames processed\n", nframes); 843906943f9SDavid du Colombier if(i == nframes) 844906943f9SDavid du Colombier tdi->csw |= Tdioc; 845906943f9SDavid du Colombier iso->tdi = tdi; 846906943f9SDavid du Colombier if(isocanwrite(iso) || isocanread(iso)){ 847906943f9SDavid du Colombier diprint("wakeup iso %#p tdi %#p tdu %#p\n", iso, 848906943f9SDavid du Colombier iso->tdi, iso->tdu); 849906943f9SDavid du Colombier wakeup(iso); 850906943f9SDavid du Colombier } 851906943f9SDavid du Colombier 852906943f9SDavid du Colombier } 853906943f9SDavid du Colombier 854906943f9SDavid du Colombier /* 855906943f9SDavid du Colombier * Process a Qh upon interrupt. There's one per ongoing user I/O. 856906943f9SDavid du Colombier * User process releases resources later, that is not done here. 857906943f9SDavid du Colombier * We may find in this order one or more Tds: 858906943f9SDavid du Colombier * - none/many non active and completed Tds 859906943f9SDavid du Colombier * - none/one (usually(!) not active) and failed Td 860906943f9SDavid du Colombier * - none/many active Tds. 861906943f9SDavid du Colombier * Upon errors the entire transfer is aborted and error reported. 862906943f9SDavid du Colombier * Otherwise, the transfer is complete only when all Tds are done or 863906943f9SDavid du Colombier * when a read with less than maxpkt is found. 864906943f9SDavid du Colombier * Use the software list and not qh->elink to avoid races. 865906943f9SDavid du Colombier * We could use qh->elink to see if there's something new or not. 866906943f9SDavid du Colombier */ 867906943f9SDavid du Colombier static void 868906943f9SDavid du Colombier qhinterrupt(Ctlr *ctlr, Qh *qh) 869906943f9SDavid du Colombier { 870906943f9SDavid du Colombier Td *td; 871906943f9SDavid du Colombier int err; 872906943f9SDavid du Colombier 873906943f9SDavid du Colombier ctlr->nqhintr++; 874906943f9SDavid du Colombier if(qh->state != Qrun) 875906943f9SDavid du Colombier panic("qhinterrupt: qh state"); 876906943f9SDavid du Colombier if(qh->tds == nil) 877906943f9SDavid du Colombier panic("qhinterrupt: no tds"); 878906943f9SDavid du Colombier if((qh->tds->csw & Tdactive) == 0) 879906943f9SDavid du Colombier ddqprint("qhinterrupt port %#ux qh %#p p0 %#x p1 %#x\n", 880906943f9SDavid du Colombier ctlr->port, qh, INS(PORT(0)), INS(PORT(1))); 881906943f9SDavid du Colombier for(td = qh->tds; td != nil; td = td->next){ 882906943f9SDavid du Colombier if(td->csw & Tdactive) 883906943f9SDavid du Colombier return; 884906943f9SDavid du Colombier td->csw &= ~Tdioc; 885906943f9SDavid du Colombier if((td->csw & Tdstalled) != 0){ 886906943f9SDavid du Colombier err = td->csw & Tderrors; 887906943f9SDavid du Colombier /* just stalled is end of xfer but not an error */ 888906943f9SDavid du Colombier if(err != Tdstalled && qh->io->err == nil){ 889906943f9SDavid du Colombier qh->io->err = errmsg(td->csw & Tderrors); 890906943f9SDavid du Colombier dqprint("qhinterrupt: td %#p error %#ux %s\n", 891906943f9SDavid du Colombier td, err, qh->io->err); 892906943f9SDavid du Colombier dqprint("ctlr load %uld\n", ctlr->load); 893906943f9SDavid du Colombier } 894906943f9SDavid du Colombier break; 895906943f9SDavid du Colombier } 896906943f9SDavid du Colombier if((td->csw & Tdnak) != 0){ /* retransmit; not serious */ 897906943f9SDavid du Colombier td->csw &= ~Tdnak; 898906943f9SDavid du Colombier if(td->next == nil) 899906943f9SDavid du Colombier td->csw |= Tdioc; 900906943f9SDavid du Colombier } 901906943f9SDavid du Colombier td->ndata = tdlen(td); 902906943f9SDavid du Colombier if(td->ndata < maxtdlen(td)){ /* EOT */ 903906943f9SDavid du Colombier td = td->next; 904906943f9SDavid du Colombier break; 905906943f9SDavid du Colombier } 906906943f9SDavid du Colombier } 907906943f9SDavid du Colombier 908906943f9SDavid du Colombier /* 909906943f9SDavid du Colombier * Done. Make void the Tds not used (errors or EOT) and wakeup epio. 910906943f9SDavid du Colombier */ 911906943f9SDavid du Colombier qh->elink = QHterm; 912906943f9SDavid du Colombier for(; td != nil; td = td->next) 913906943f9SDavid du Colombier td->ndata = 0; 914906943f9SDavid du Colombier qh->state = Qdone; 915906943f9SDavid du Colombier wakeup(qh->io); 9169a747e4fSDavid du Colombier } 9179a747e4fSDavid du Colombier 9189a747e4fSDavid du Colombier static void 9199a747e4fSDavid du Colombier interrupt(Ureg*, void *a) 9209a747e4fSDavid du Colombier { 921906943f9SDavid du Colombier Hci *hp; 9229a747e4fSDavid du Colombier Ctlr *ctlr; 923906943f9SDavid du Colombier int frptr; 924906943f9SDavid du Colombier int frno; 925906943f9SDavid du Colombier Qh *qh; 926906943f9SDavid du Colombier Isoio *iso; 927906943f9SDavid du Colombier int sts; 928906943f9SDavid du Colombier int cmd; 9299a747e4fSDavid du Colombier 930906943f9SDavid du Colombier hp = a; 931906943f9SDavid du Colombier ctlr = hp->aux; 932906943f9SDavid du Colombier ilock(ctlr); 933906943f9SDavid du Colombier ctlr->nintr++; 934906943f9SDavid du Colombier sts = INS(Status); 935906943f9SDavid du Colombier if((sts & Sall) == 0){ /* not for us; sharing irq */ 936906943f9SDavid du Colombier iunlock(ctlr); 9379a747e4fSDavid du Colombier return; 938906943f9SDavid du Colombier } 939906943f9SDavid du Colombier OUTS(Status, sts & Sall); 940906943f9SDavid du Colombier cmd = INS(Cmd); 941906943f9SDavid du Colombier if(cmd & Crun == 0){ 942906943f9SDavid du Colombier print("uhci %#ux: not running: uhci bug?\n", ctlr->port); 943906943f9SDavid du Colombier /* BUG: should abort everything in this case */ 944906943f9SDavid du Colombier } 945906943f9SDavid du Colombier if(debug > 1){ 946906943f9SDavid du Colombier frptr = INL(Flbaseadd); 947906943f9SDavid du Colombier frno = INL(Frnum); 948906943f9SDavid du Colombier frno = TRUNC(frno, Nframes); 949906943f9SDavid du Colombier print("cmd %#ux sts %#ux frptr %#ux frno %d\n", 950906943f9SDavid du Colombier cmd, sts, frptr, frno); 951906943f9SDavid du Colombier } 952906943f9SDavid du Colombier ctlr->ntdintr++; 953906943f9SDavid du Colombier /* 954906943f9SDavid du Colombier * Will we know in USB 3.0 who the interrupt was for?. 955906943f9SDavid du Colombier * Do they still teach indexing in CS? 956906943f9SDavid du Colombier * This is Intel's doing. 957906943f9SDavid du Colombier */ 958906943f9SDavid du Colombier for(iso = ctlr->iso; iso != nil; iso = iso->next) 959906943f9SDavid du Colombier if(iso->state == Qrun || iso->state == Qdone) 960906943f9SDavid du Colombier isointerrupt(ctlr, iso); 961906943f9SDavid du Colombier for(qh = ctlr->qhs; qh != nil; qh = qh->next) 962906943f9SDavid du Colombier if(qh->state == Qrun) 963906943f9SDavid du Colombier qhinterrupt(ctlr, qh); 964906943f9SDavid du Colombier else if(qh->state == Qclose) 965906943f9SDavid du Colombier qhlinktd(qh, nil); 966906943f9SDavid du Colombier iunlock(ctlr); 9679a747e4fSDavid du Colombier } 9689a747e4fSDavid du Colombier 969906943f9SDavid du Colombier /* 970906943f9SDavid du Colombier * iso->tdu is the next place to put data. When it gets full 971906943f9SDavid du Colombier * it is activated and tdu advanced. 972906943f9SDavid du Colombier */ 9739a747e4fSDavid du Colombier static long 974906943f9SDavid du Colombier putsamples(Isoio *iso, uchar *b, long count) 9759a747e4fSDavid du Colombier { 976906943f9SDavid du Colombier long tot; 977906943f9SDavid du Colombier long n; 9789a747e4fSDavid du Colombier 979906943f9SDavid du Colombier for(tot = 0; isocanwrite(iso) && tot < count; tot += n){ 980906943f9SDavid du Colombier n = count-tot; 981906943f9SDavid du Colombier if(n > maxtdlen(iso->tdu) - iso->nleft) 982906943f9SDavid du Colombier n = maxtdlen(iso->tdu) - iso->nleft; 983906943f9SDavid du Colombier memmove(iso->tdu->data+iso->nleft, b+tot, n); 984906943f9SDavid du Colombier iso->nleft += n; 985906943f9SDavid du Colombier if(iso->nleft == maxtdlen(iso->tdu)){ 986906943f9SDavid du Colombier tdisoinit(iso, iso->tdu, iso->nleft); 987906943f9SDavid du Colombier iso->nleft = 0; 988906943f9SDavid du Colombier iso->tdu = iso->tdu->next; 9899a747e4fSDavid du Colombier } 990906943f9SDavid du Colombier } 991906943f9SDavid du Colombier return tot; 992906943f9SDavid du Colombier } 993906943f9SDavid du Colombier 994906943f9SDavid du Colombier /* 995906943f9SDavid du Colombier * Queue data for writing and return error status from 996906943f9SDavid du Colombier * last writes done, to maintain buffered data. 997906943f9SDavid du Colombier */ 998906943f9SDavid du Colombier static long 999906943f9SDavid du Colombier episowrite(Ep *ep, Isoio *iso, void *a, long count) 1000906943f9SDavid du Colombier { 1001906943f9SDavid du Colombier Ctlr *ctlr; 1002906943f9SDavid du Colombier uchar *b; 1003906943f9SDavid du Colombier int tot; 1004906943f9SDavid du Colombier int nw; 1005906943f9SDavid du Colombier char *err; 1006906943f9SDavid du Colombier 1007906943f9SDavid du Colombier iso->debug = ep->debug; 1008906943f9SDavid du Colombier diprint("uhci: episowrite: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb); 1009906943f9SDavid du Colombier 1010906943f9SDavid du Colombier ctlr = ep->hp->aux; 1011906943f9SDavid du Colombier qlock(iso); 1012906943f9SDavid du Colombier if(waserror()){ 1013906943f9SDavid du Colombier qunlock(iso); 10149a747e4fSDavid du Colombier nexterror(); 10159a747e4fSDavid du Colombier } 1016906943f9SDavid du Colombier ilock(ctlr); 1017906943f9SDavid du Colombier if(iso->state == Qclose){ 1018906943f9SDavid du Colombier iunlock(ctlr); 1019906943f9SDavid du Colombier error(iso->err ? iso->err : Eio); 10209a747e4fSDavid du Colombier } 1021906943f9SDavid du Colombier iso->state = Qrun; 1022906943f9SDavid du Colombier b = a; 1023906943f9SDavid du Colombier for(tot = 0; tot < count; tot += nw){ 1024906943f9SDavid du Colombier while(isocanwrite(iso) == 0){ 1025906943f9SDavid du Colombier iunlock(ctlr); 1026906943f9SDavid du Colombier diprint("uhci: episowrite: %#p sleep\n", iso); 1027906943f9SDavid du Colombier if(waserror()){ 1028906943f9SDavid du Colombier if(iso->err == nil) 1029906943f9SDavid du Colombier iso->err = "I/O timed out"; 1030906943f9SDavid du Colombier ilock(ctlr); 1031906943f9SDavid du Colombier break; 10329a747e4fSDavid du Colombier } 1033d37e33ffSDavid du Colombier tsleep(iso, isocanwrite, iso, ep->tmout); 1034906943f9SDavid du Colombier poperror(); 1035906943f9SDavid du Colombier ilock(ctlr); 10369a747e4fSDavid du Colombier } 1037906943f9SDavid du Colombier err = iso->err; 1038906943f9SDavid du Colombier iso->err = nil; 1039906943f9SDavid du Colombier if(iso->state == Qclose || err != nil){ 1040906943f9SDavid du Colombier iunlock(ctlr); 1041906943f9SDavid du Colombier error(err ? err : Eio); 10429a747e4fSDavid du Colombier } 1043906943f9SDavid du Colombier if(iso->state != Qrun) 1044906943f9SDavid du Colombier panic("episowrite: iso not running"); 1045906943f9SDavid du Colombier iunlock(ctlr); /* We could page fault here */ 1046906943f9SDavid du Colombier nw = putsamples(iso, b+tot, count-tot); 1047906943f9SDavid du Colombier ilock(ctlr); 10489a747e4fSDavid du Colombier } 1049906943f9SDavid du Colombier if(iso->state != Qclose) 1050906943f9SDavid du Colombier iso->state = Qdone; 1051906943f9SDavid du Colombier iunlock(ctlr); 1052906943f9SDavid du Colombier err = iso->err; /* in case it failed early */ 1053906943f9SDavid du Colombier iso->err = nil; 1054906943f9SDavid du Colombier qunlock(iso); 1055906943f9SDavid du Colombier poperror(); 1056906943f9SDavid du Colombier if(err != nil) 1057906943f9SDavid du Colombier error(err); 1058906943f9SDavid du Colombier diprint("uhci: episowrite: %#p %d bytes\n", iso, tot); 1059906943f9SDavid du Colombier return tot; 10603ff48bf5SDavid du Colombier } 1061906943f9SDavid du Colombier 1062906943f9SDavid du Colombier /* 1063906943f9SDavid du Colombier * Available data is kept at tdu and following tds, up to tdi (excluded). 1064906943f9SDavid du Colombier */ 1065906943f9SDavid du Colombier static long 1066906943f9SDavid du Colombier episoread(Ep *ep, Isoio *iso, void *a, int count) 1067906943f9SDavid du Colombier { 1068906943f9SDavid du Colombier Ctlr *ctlr; 1069906943f9SDavid du Colombier uchar *b; 1070906943f9SDavid du Colombier int nr; 1071906943f9SDavid du Colombier int tot; 1072906943f9SDavid du Colombier Td *tdu; 1073906943f9SDavid du Colombier 1074906943f9SDavid du Colombier iso->debug = ep->debug; 1075906943f9SDavid du Colombier diprint("uhci: episoread: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb); 1076906943f9SDavid du Colombier 1077906943f9SDavid du Colombier b = a; 1078906943f9SDavid du Colombier ctlr = ep->hp->aux; 1079906943f9SDavid du Colombier qlock(iso); 1080906943f9SDavid du Colombier if(waserror()){ 1081906943f9SDavid du Colombier qunlock(iso); 1082906943f9SDavid du Colombier nexterror(); 10839a747e4fSDavid du Colombier } 1084906943f9SDavid du Colombier iso->err = nil; 1085906943f9SDavid du Colombier iso->nerrs = 0; 1086906943f9SDavid du Colombier ilock(ctlr); 1087906943f9SDavid du Colombier if(iso->state == Qclose){ 1088906943f9SDavid du Colombier iunlock(ctlr); 1089906943f9SDavid du Colombier error(iso->err ? iso->err : Eio); 10909a747e4fSDavid du Colombier } 1091906943f9SDavid du Colombier iso->state = Qrun; 1092906943f9SDavid du Colombier while(isocanread(iso) == 0){ 1093906943f9SDavid du Colombier iunlock(ctlr); 1094906943f9SDavid du Colombier diprint("uhci: episoread: %#p sleep\n", iso); 1095906943f9SDavid du Colombier if(waserror()){ 1096906943f9SDavid du Colombier if(iso->err == nil) 1097906943f9SDavid du Colombier iso->err = "I/O timed out"; 1098906943f9SDavid du Colombier ilock(ctlr); 1099906943f9SDavid du Colombier break; 11009a747e4fSDavid du Colombier } 1101d37e33ffSDavid du Colombier tsleep(iso, isocanread, iso, ep->tmout); 1102906943f9SDavid du Colombier poperror(); 1103906943f9SDavid du Colombier ilock(ctlr); 1104906943f9SDavid du Colombier } 1105906943f9SDavid du Colombier if(iso->state == Qclose){ 1106906943f9SDavid du Colombier iunlock(ctlr); 1107906943f9SDavid du Colombier error(iso->err ? iso->err : Eio); 1108906943f9SDavid du Colombier } 1109906943f9SDavid du Colombier iso->state = Qdone; 1110906943f9SDavid du Colombier assert(iso->tdu != iso->tdi); 1111906943f9SDavid du Colombier 1112906943f9SDavid du Colombier for(tot = 0; iso->tdi != iso->tdu && tot < count; tot += nr){ 1113906943f9SDavid du Colombier tdu = iso->tdu; 1114906943f9SDavid du Colombier if(tdu->csw & Tdactive){ 1115906943f9SDavid du Colombier diprint("uhci: episoread: %#p tdu active\n", iso); 1116906943f9SDavid du Colombier break; 1117906943f9SDavid du Colombier } 1118906943f9SDavid du Colombier nr = tdu->ndata; 1119906943f9SDavid du Colombier if(tot + nr > count) 1120906943f9SDavid du Colombier nr = count - tot; 1121906943f9SDavid du Colombier if(nr == 0) 1122906943f9SDavid du Colombier print("uhci: ep%d.%d: too many polls\n", 1123906943f9SDavid du Colombier ep->dev->nb, ep->nb); 11249a747e4fSDavid du Colombier else{ 1125906943f9SDavid du Colombier iunlock(ctlr); /* We could page fault here */ 1126906943f9SDavid du Colombier memmove(b+tot, tdu->data, nr); 1127906943f9SDavid du Colombier ilock(ctlr); 1128906943f9SDavid du Colombier if(nr < tdu->ndata) 1129906943f9SDavid du Colombier memmove(tdu->data, tdu->data+nr, tdu->ndata - nr); 1130906943f9SDavid du Colombier tdu->ndata -= nr; 11319a747e4fSDavid du Colombier } 1132906943f9SDavid du Colombier if(tdu->ndata == 0){ 1133906943f9SDavid du Colombier tdisoinit(iso, tdu, ep->maxpkt); 1134906943f9SDavid du Colombier iso->tdu = tdu->next; 11359a747e4fSDavid du Colombier } 1136906943f9SDavid du Colombier } 1137906943f9SDavid du Colombier iunlock(ctlr); 1138906943f9SDavid du Colombier qunlock(iso); 11399a747e4fSDavid du Colombier poperror(); 1140906943f9SDavid du Colombier diprint("uhci: episoread: %#p %d bytes err '%s'\n", iso, tot, iso->err); 1141906943f9SDavid du Colombier if(iso->err != nil) 1142906943f9SDavid du Colombier error(iso->err); 1143906943f9SDavid du Colombier return tot; 11449a747e4fSDavid du Colombier } 11459a747e4fSDavid du Colombier 11469a747e4fSDavid du Colombier static int 1147906943f9SDavid du Colombier nexttoggle(int tog) 11489a747e4fSDavid du Colombier { 1149906943f9SDavid du Colombier if(tog == Tddata0) 1150906943f9SDavid du Colombier return Tddata1; 1151906943f9SDavid du Colombier else 1152906943f9SDavid du Colombier return Tddata0; 11539a747e4fSDavid du Colombier } 11549a747e4fSDavid du Colombier 1155906943f9SDavid du Colombier static Td* 1156906943f9SDavid du Colombier epgettd(Ep *ep, Qio *io, int flags, void *a, int count) 11579a747e4fSDavid du Colombier { 1158906943f9SDavid du Colombier Td *td; 1159906943f9SDavid du Colombier int tok; 11609a747e4fSDavid du Colombier 1161906943f9SDavid du Colombier if(ep->maxpkt < count) 1162906943f9SDavid du Colombier error("maxpkt too short"); 1163906943f9SDavid du Colombier td = tdalloc(); 1164906943f9SDavid du Colombier if(count <= Tdndata) 1165906943f9SDavid du Colombier td->data = td->sbuff; 1166906943f9SDavid du Colombier else 1167906943f9SDavid du Colombier td->data = td->buff = smalloc(ep->maxpkt); 1168906943f9SDavid du Colombier td->buffer = PCIWADDR(td->data); 1169906943f9SDavid du Colombier td->ndata = count; 1170906943f9SDavid du Colombier if(a != nil && count > 0) 1171906943f9SDavid du Colombier memmove(td->data, a, count); 1172906943f9SDavid du Colombier td->csw = Tderr2|Tderr1|flags; 1173906943f9SDavid du Colombier if(ep->dev->speed == Lowspeed) 1174906943f9SDavid du Colombier td->csw |= Tdlow; 1175906943f9SDavid du Colombier tok = io->tok | io->toggle; 1176906943f9SDavid du Colombier io->toggle = nexttoggle(io->toggle); 1177906943f9SDavid du Colombier td->token = ((count-1)<<21) | ((io->usbid&0x7FF)<<8) | tok; 11789a747e4fSDavid du Colombier 1179906943f9SDavid du Colombier return td; 11809a747e4fSDavid du Colombier } 1181906943f9SDavid du Colombier 1182906943f9SDavid du Colombier /* 1183906943f9SDavid du Colombier * Try to get them idle 1184906943f9SDavid du Colombier */ 1185906943f9SDavid du Colombier static void 1186906943f9SDavid du Colombier aborttds(Qh *qh) 1187906943f9SDavid du Colombier { 1188906943f9SDavid du Colombier Td *td; 1189906943f9SDavid du Colombier 1190906943f9SDavid du Colombier qh->state = Qdone; 1191906943f9SDavid du Colombier qh->elink = QHterm; 1192906943f9SDavid du Colombier for(td = qh->tds; td != nil; td = td->next){ 1193906943f9SDavid du Colombier if(td->csw & Tdactive) 1194906943f9SDavid du Colombier td->ndata = 0; 1195906943f9SDavid du Colombier td->csw &= ~(Tdactive|Tdioc); 11969a747e4fSDavid du Colombier } 11979a747e4fSDavid du Colombier } 1198906943f9SDavid du Colombier 1199906943f9SDavid du Colombier static int 1200906943f9SDavid du Colombier epiodone(void *a) 1201906943f9SDavid du Colombier { 1202906943f9SDavid du Colombier Qh *qh; 1203906943f9SDavid du Colombier 1204906943f9SDavid du Colombier qh = a; 1205906943f9SDavid du Colombier return qh->state != Qrun; 12069a747e4fSDavid du Colombier } 12079a747e4fSDavid du Colombier 12089a747e4fSDavid du Colombier static void 1209906943f9SDavid du Colombier epiowait(Ctlr *ctlr, Qio *io, int tmout, ulong load) 1210906943f9SDavid du Colombier { 1211906943f9SDavid du Colombier Qh *qh; 1212906943f9SDavid du Colombier int timedout; 1213906943f9SDavid du Colombier 1214906943f9SDavid du Colombier qh = io->qh; 1215906943f9SDavid du Colombier ddqprint("uhci io %#p sleep on qh %#p state %uld\n", io, qh, qh->state); 1216906943f9SDavid du Colombier timedout = 0; 1217906943f9SDavid du Colombier if(waserror()){ 1218906943f9SDavid du Colombier dqprint("uhci io %#p qh %#p timed out\n", io, qh); 1219906943f9SDavid du Colombier timedout++; 1220906943f9SDavid du Colombier }else{ 1221906943f9SDavid du Colombier if(tmout == 0) 1222906943f9SDavid du Colombier sleep(io, epiodone, qh); 1223906943f9SDavid du Colombier else 1224906943f9SDavid du Colombier tsleep(io, epiodone, qh, tmout); 1225906943f9SDavid du Colombier poperror(); 1226906943f9SDavid du Colombier } 1227906943f9SDavid du Colombier ilock(ctlr); 1228906943f9SDavid du Colombier if(qh->state == Qrun) 1229906943f9SDavid du Colombier timedout = 1; 1230906943f9SDavid du Colombier else if(qh->state != Qdone && qh->state != Qclose) 1231906943f9SDavid du Colombier panic("epio: queue not done and not closed"); 1232906943f9SDavid du Colombier if(timedout){ 1233906943f9SDavid du Colombier aborttds(io->qh); 1234906943f9SDavid du Colombier io->err = "request timed out"; 1235906943f9SDavid du Colombier iunlock(ctlr); 1236906943f9SDavid du Colombier if(!waserror()){ 1237906943f9SDavid du Colombier tsleep(&up->sleep, return0, 0, Abortdelay); 1238906943f9SDavid du Colombier poperror(); 1239906943f9SDavid du Colombier } 1240906943f9SDavid du Colombier ilock(ctlr); 1241906943f9SDavid du Colombier } 1242906943f9SDavid du Colombier if(qh->state != Qclose) 1243906943f9SDavid du Colombier qh->state = Qidle; 1244906943f9SDavid du Colombier qhlinktd(qh, nil); 1245906943f9SDavid du Colombier ctlr->load -= load; 1246906943f9SDavid du Colombier iunlock(ctlr); 1247906943f9SDavid du Colombier } 1248906943f9SDavid du Colombier 1249906943f9SDavid du Colombier /* 1250906943f9SDavid du Colombier * Non iso I/O. 1251906943f9SDavid du Colombier * To make it work for control transfers, the caller may 1252906943f9SDavid du Colombier * lock the Qio for the entire control transfer. 1253906943f9SDavid du Colombier */ 1254906943f9SDavid du Colombier static long 1255d37e33ffSDavid du Colombier epio(Ep *ep, Qio *io, void *a, long count, int mustlock) 1256906943f9SDavid du Colombier { 1257d37e33ffSDavid du Colombier Td *td, *ltd, *td0, *ntd; 1258906943f9SDavid du Colombier Ctlr *ctlr; 1259906943f9SDavid du Colombier Qh* qh; 1260d37e33ffSDavid du Colombier long n, tot; 1261906943f9SDavid du Colombier char buf[128]; 1262906943f9SDavid du Colombier uchar *c; 1263d37e33ffSDavid du Colombier int saved, ntds, tmout; 1264906943f9SDavid du Colombier ulong load; 1265906943f9SDavid du Colombier char *err; 1266906943f9SDavid du Colombier 1267906943f9SDavid du Colombier qh = io->qh; 1268906943f9SDavid du Colombier ctlr = ep->hp->aux; 1269906943f9SDavid du Colombier io->debug = ep->debug; 1270d37e33ffSDavid du Colombier tmout = ep->tmout; 1271906943f9SDavid du Colombier ddeprint("epio: %s ep%d.%d io %#p count %ld load %uld\n", 1272906943f9SDavid du Colombier io->tok == Tdtokin ? "in" : "out", 1273906943f9SDavid du Colombier ep->dev->nb, ep->nb, io, count, ctlr->load); 1274906943f9SDavid du Colombier if((debug > 1 || ep->debug > 1) && io->tok != Tdtokin){ 1275906943f9SDavid du Colombier seprintdata(buf, buf+sizeof(buf), a, count); 1276906943f9SDavid du Colombier print("uchi epio: user data: %s\n", buf); 1277906943f9SDavid du Colombier } 1278906943f9SDavid du Colombier if(mustlock){ 1279906943f9SDavid du Colombier qlock(io); 1280906943f9SDavid du Colombier if(waserror()){ 1281906943f9SDavid du Colombier qunlock(io); 1282906943f9SDavid du Colombier nexterror(); 1283906943f9SDavid du Colombier } 1284906943f9SDavid du Colombier } 1285906943f9SDavid du Colombier io->err = nil; 1286906943f9SDavid du Colombier ilock(ctlr); 1287906943f9SDavid du Colombier if(qh->state == Qclose){ /* Tds released by cancelio */ 1288906943f9SDavid du Colombier iunlock(ctlr); 1289906943f9SDavid du Colombier error(io->err ? io->err : Eio); 1290906943f9SDavid du Colombier } 1291906943f9SDavid du Colombier if(qh->state != Qidle) 1292906943f9SDavid du Colombier panic("epio: qh not idle"); 1293906943f9SDavid du Colombier qh->state = Qinstall; 1294906943f9SDavid du Colombier iunlock(ctlr); 1295906943f9SDavid du Colombier 1296906943f9SDavid du Colombier c = a; 1297906943f9SDavid du Colombier td0 = ltd = nil; 1298906943f9SDavid du Colombier load = tot = 0; 1299906943f9SDavid du Colombier do{ 1300906943f9SDavid du Colombier n = ep->maxpkt; 1301906943f9SDavid du Colombier if(count-tot < n) 1302906943f9SDavid du Colombier n = count-tot; 1303906943f9SDavid du Colombier if(io->tok != Tdtokin) 1304906943f9SDavid du Colombier td = epgettd(ep, io, Tdactive, c+tot, n); 1305906943f9SDavid du Colombier else 1306906943f9SDavid du Colombier td = epgettd(ep, io, Tdactive|Tdspd, nil, n); 1307906943f9SDavid du Colombier if(td0 == nil) 1308906943f9SDavid du Colombier td0 = td; 1309906943f9SDavid du Colombier else 1310906943f9SDavid du Colombier tdlinktd(ltd, td); 1311906943f9SDavid du Colombier ltd = td; 1312906943f9SDavid du Colombier tot += n; 1313906943f9SDavid du Colombier load += ep->load; 1314906943f9SDavid du Colombier }while(tot < count); 1315906943f9SDavid du Colombier if(td0 == nil || ltd == nil) 1316906943f9SDavid du Colombier panic("epio: no td"); 1317906943f9SDavid du Colombier 1318906943f9SDavid du Colombier ltd->csw |= Tdioc; /* the last one interrupts */ 1319906943f9SDavid du Colombier ddeprint("uhci: load %uld ctlr load %uld\n", load, ctlr->load); 1320906943f9SDavid du Colombier ilock(ctlr); 1321906943f9SDavid du Colombier if(qh->state != Qclose){ 1322906943f9SDavid du Colombier io->iotime = TK2MS(MACHP(0)->ticks); 1323906943f9SDavid du Colombier qh->state = Qrun; 1324906943f9SDavid du Colombier coherence(); 1325906943f9SDavid du Colombier qhlinktd(qh, td0); 1326906943f9SDavid du Colombier ctlr->load += load; 1327906943f9SDavid du Colombier } 1328906943f9SDavid du Colombier iunlock(ctlr); 1329906943f9SDavid du Colombier 1330906943f9SDavid du Colombier epiowait(ctlr, io, tmout, load); 1331d37e33ffSDavid du Colombier 1332906943f9SDavid du Colombier if(debug > 1 || ep->debug > 1) 1333906943f9SDavid du Colombier dumptd(td0, "epio: got tds: "); 1334906943f9SDavid du Colombier 1335906943f9SDavid du Colombier tot = 0; 1336906943f9SDavid du Colombier c = a; 1337906943f9SDavid du Colombier saved = 0; 1338906943f9SDavid du Colombier ntds = 0; 1339906943f9SDavid du Colombier for(td = td0; td != nil; td = ntd){ 1340906943f9SDavid du Colombier ntds++; 1341906943f9SDavid du Colombier /* 1342906943f9SDavid du Colombier * Use td tok, not io tok, because of setup packets. 1343906943f9SDavid du Colombier * Also, if the Td was stalled or active (previous Td 1344906943f9SDavid du Colombier * was a short packet), we must save the toggle as it is. 1345906943f9SDavid du Colombier */ 1346906943f9SDavid du Colombier if(td->csw & (Tdstalled|Tdactive)){ 1347906943f9SDavid du Colombier if(saved++ == 0) 1348906943f9SDavid du Colombier io->toggle = td->token & Tddata1; 1349906943f9SDavid du Colombier }else{ 1350906943f9SDavid du Colombier tot += td->ndata; 1351906943f9SDavid du Colombier if(tdtok(td) == Tdtokin && td->ndata > 0){ 1352906943f9SDavid du Colombier memmove(c, td->data, td->ndata); 1353906943f9SDavid du Colombier c += td->ndata; 1354906943f9SDavid du Colombier } 1355906943f9SDavid du Colombier } 1356906943f9SDavid du Colombier ntd = td->next; 1357906943f9SDavid du Colombier tdfree(td); 1358906943f9SDavid du Colombier } 1359906943f9SDavid du Colombier err = io->err; 1360906943f9SDavid du Colombier if(mustlock){ 1361906943f9SDavid du Colombier qunlock(io); 1362906943f9SDavid du Colombier poperror(); 1363906943f9SDavid du Colombier } 1364906943f9SDavid du Colombier ddeprint("epio: io %#p: %d tds: return %ld err '%s'\n", 1365906943f9SDavid du Colombier io, ntds, tot, err); 1366906943f9SDavid du Colombier if(err != nil) 1367906943f9SDavid du Colombier error(err); 1368906943f9SDavid du Colombier if(tot < 0) 1369906943f9SDavid du Colombier error(Eio); 1370906943f9SDavid du Colombier return tot; 1371906943f9SDavid du Colombier } 1372906943f9SDavid du Colombier 1373906943f9SDavid du Colombier /* 1374906943f9SDavid du Colombier * halt condition was cleared on the endpoint. update our toggles. 1375906943f9SDavid du Colombier */ 1376906943f9SDavid du Colombier static void 1377906943f9SDavid du Colombier clrhalt(Ep *ep) 1378906943f9SDavid du Colombier { 1379906943f9SDavid du Colombier Qio *io; 1380906943f9SDavid du Colombier 1381906943f9SDavid du Colombier ep->clrhalt = 0; 1382906943f9SDavid du Colombier switch(ep->ttype){ 1383906943f9SDavid du Colombier case Tbulk: 1384906943f9SDavid du Colombier case Tintr: 1385906943f9SDavid du Colombier io = ep->aux; 1386906943f9SDavid du Colombier if(ep->mode != OREAD){ 1387906943f9SDavid du Colombier qlock(&io[OWRITE]); 1388906943f9SDavid du Colombier io[OWRITE].toggle = Tddata0; 1389906943f9SDavid du Colombier deprint("ep clrhalt for io %#p\n", io+OWRITE); 1390906943f9SDavid du Colombier qunlock(&io[OWRITE]); 1391906943f9SDavid du Colombier } 1392906943f9SDavid du Colombier if(ep->mode != OWRITE){ 1393906943f9SDavid du Colombier qlock(&io[OREAD]); 1394906943f9SDavid du Colombier io[OREAD].toggle = Tddata0; 1395906943f9SDavid du Colombier deprint("ep clrhalt for io %#p\n", io+OREAD); 1396906943f9SDavid du Colombier qunlock(&io[OREAD]); 1397906943f9SDavid du Colombier } 1398906943f9SDavid du Colombier break; 1399906943f9SDavid du Colombier } 1400906943f9SDavid du Colombier } 1401906943f9SDavid du Colombier 1402906943f9SDavid du Colombier static long 1403906943f9SDavid du Colombier epread(Ep *ep, void *a, long count) 1404906943f9SDavid du Colombier { 1405906943f9SDavid du Colombier Ctlio *cio; 1406906943f9SDavid du Colombier Qio *io; 1407906943f9SDavid du Colombier Isoio *iso; 1408906943f9SDavid du Colombier char buf[160]; 1409906943f9SDavid du Colombier ulong delta; 1410906943f9SDavid du Colombier 1411906943f9SDavid du Colombier ddeprint("uhci: epread\n"); 1412906943f9SDavid du Colombier if(ep->aux == nil) 1413906943f9SDavid du Colombier panic("epread: not open"); 1414906943f9SDavid du Colombier 1415906943f9SDavid du Colombier switch(ep->ttype){ 1416906943f9SDavid du Colombier case Tctl: 1417906943f9SDavid du Colombier cio = ep->aux; 1418906943f9SDavid du Colombier qlock(cio); 1419906943f9SDavid du Colombier if(waserror()){ 1420906943f9SDavid du Colombier qunlock(cio); 1421906943f9SDavid du Colombier nexterror(); 1422906943f9SDavid du Colombier } 1423906943f9SDavid du Colombier ddeprint("epread ctl ndata %d\n", cio->ndata); 1424906943f9SDavid du Colombier if(cio->ndata < 0) 1425906943f9SDavid du Colombier error("request expected"); 1426906943f9SDavid du Colombier else if(cio->ndata == 0){ 1427906943f9SDavid du Colombier cio->ndata = -1; 1428906943f9SDavid du Colombier count = 0; 1429906943f9SDavid du Colombier }else{ 1430906943f9SDavid du Colombier if(count > cio->ndata) 1431906943f9SDavid du Colombier count = cio->ndata; 1432906943f9SDavid du Colombier if(count > 0) 1433906943f9SDavid du Colombier memmove(a, cio->data, count); 1434906943f9SDavid du Colombier /* BUG for big transfers */ 1435906943f9SDavid du Colombier free(cio->data); 1436906943f9SDavid du Colombier cio->data = nil; 1437906943f9SDavid du Colombier cio->ndata = 0; /* signal EOF next time */ 1438906943f9SDavid du Colombier } 1439906943f9SDavid du Colombier qunlock(cio); 1440906943f9SDavid du Colombier poperror(); 1441906943f9SDavid du Colombier if(debug>1 || ep->debug){ 1442906943f9SDavid du Colombier seprintdata(buf, buf+sizeof(buf), a, count); 1443906943f9SDavid du Colombier print("epread: %s\n", buf); 1444906943f9SDavid du Colombier } 1445906943f9SDavid du Colombier return count; 1446906943f9SDavid du Colombier case Tbulk: 1447906943f9SDavid du Colombier io = ep->aux; 1448906943f9SDavid du Colombier if(ep->clrhalt) 1449906943f9SDavid du Colombier clrhalt(ep); 1450d37e33ffSDavid du Colombier return epio(ep, &io[OREAD], a, count, 1); 1451906943f9SDavid du Colombier case Tintr: 1452906943f9SDavid du Colombier io = ep->aux; 1453906943f9SDavid du Colombier delta = TK2MS(MACHP(0)->ticks) - io[OREAD].iotime + 1; 1454906943f9SDavid du Colombier if(delta < ep->pollival / 2) 1455906943f9SDavid du Colombier tsleep(&up->sleep, return0, 0, ep->pollival/2 - delta); 1456906943f9SDavid du Colombier if(ep->clrhalt) 1457906943f9SDavid du Colombier clrhalt(ep); 1458d37e33ffSDavid du Colombier return epio(ep, &io[OREAD], a, count, 1); 1459906943f9SDavid du Colombier case Tiso: 1460906943f9SDavid du Colombier iso = ep->aux; 1461906943f9SDavid du Colombier return episoread(ep, iso, a, count); 1462906943f9SDavid du Colombier default: 1463906943f9SDavid du Colombier panic("epread: bad ep ttype %d", ep->ttype); 1464906943f9SDavid du Colombier } 1465906943f9SDavid du Colombier return -1; 1466906943f9SDavid du Colombier } 1467906943f9SDavid du Colombier 1468906943f9SDavid du Colombier /* 1469906943f9SDavid du Colombier * Control transfers are one setup write (data0) 1470906943f9SDavid du Colombier * plus zero or more reads/writes (data1, data0, ...) 1471906943f9SDavid du Colombier * plus a final write/read with data1 to ack. 1472906943f9SDavid du Colombier * For both host to device and device to host we perform 1473906943f9SDavid du Colombier * the entire transfer when the user writes the request, 1474906943f9SDavid du Colombier * and keep any data read from the device for a later read. 1475906943f9SDavid du Colombier * We call epio three times instead of placing all Tds at 1476906943f9SDavid du Colombier * the same time because doing so leads to crc/tmout errors 1477906943f9SDavid du Colombier * for some devices. 1478906943f9SDavid du Colombier * Upon errors on the data phase we must still run the status 1479906943f9SDavid du Colombier * phase or the device may cease responding in the future. 1480906943f9SDavid du Colombier */ 1481906943f9SDavid du Colombier static long 1482906943f9SDavid du Colombier epctlio(Ep *ep, Ctlio *cio, void *a, long count) 1483906943f9SDavid du Colombier { 1484906943f9SDavid du Colombier uchar *c; 1485906943f9SDavid du Colombier long len; 1486906943f9SDavid du Colombier 1487906943f9SDavid du Colombier ddeprint("epctlio: cio %#p ep%d.%d count %ld\n", 1488906943f9SDavid du Colombier cio, ep->dev->nb, ep->nb, count); 1489906943f9SDavid du Colombier if(count < Rsetuplen) 1490906943f9SDavid du Colombier error("short usb comand"); 1491906943f9SDavid du Colombier qlock(cio); 1492906943f9SDavid du Colombier free(cio->data); 1493906943f9SDavid du Colombier cio->data = nil; 1494906943f9SDavid du Colombier cio->ndata = 0; 1495906943f9SDavid du Colombier if(waserror()){ 1496906943f9SDavid du Colombier qunlock(cio); 1497906943f9SDavid du Colombier free(cio->data); 1498906943f9SDavid du Colombier cio->data = nil; 1499906943f9SDavid du Colombier cio->ndata = 0; 1500906943f9SDavid du Colombier nexterror(); 1501906943f9SDavid du Colombier } 1502906943f9SDavid du Colombier 1503906943f9SDavid du Colombier /* set the address if unset and out of configuration state */ 1504*a23bc242SDavid du Colombier if(ep->dev->state != Dconfig && ep->dev->state != Dreset) 1505*a23bc242SDavid du Colombier if(cio->usbid == 0) 1506906943f9SDavid du Colombier cio->usbid = ((ep->nb&Epmax)<<7)|(ep->dev->nb&Devmax); 1507906943f9SDavid du Colombier c = a; 1508906943f9SDavid du Colombier cio->tok = Tdtoksetup; 1509906943f9SDavid du Colombier cio->toggle = Tddata0; 1510d37e33ffSDavid du Colombier if(epio(ep, cio, a, Rsetuplen, 0) < Rsetuplen) 1511906943f9SDavid du Colombier error(Eio); 1512906943f9SDavid du Colombier a = c + Rsetuplen; 1513906943f9SDavid du Colombier count -= Rsetuplen; 1514906943f9SDavid du Colombier 1515906943f9SDavid du Colombier cio->toggle = Tddata1; 1516906943f9SDavid du Colombier if(c[Rtype] & Rd2h){ 1517906943f9SDavid du Colombier cio->tok = Tdtokin; 1518906943f9SDavid du Colombier len = GET2(c+Rcount); 1519906943f9SDavid du Colombier if(len <= 0) 1520906943f9SDavid du Colombier error("bad length in d2h request"); 1521906943f9SDavid du Colombier if(len > Maxctllen) 1522906943f9SDavid du Colombier error("d2h data too large to fit in uhci"); 1523906943f9SDavid du Colombier a = cio->data = smalloc(len+1); 1524906943f9SDavid du Colombier }else{ 1525906943f9SDavid du Colombier cio->tok = Tdtokout; 1526906943f9SDavid du Colombier len = count; 1527906943f9SDavid du Colombier } 1528906943f9SDavid du Colombier if(len > 0) 1529906943f9SDavid du Colombier if(waserror()) 1530906943f9SDavid du Colombier len = -1; 1531906943f9SDavid du Colombier else{ 1532d37e33ffSDavid du Colombier len = epio(ep, cio, a, len, 0); 1533906943f9SDavid du Colombier poperror(); 1534906943f9SDavid du Colombier } 1535906943f9SDavid du Colombier if(c[Rtype] & Rd2h){ 1536906943f9SDavid du Colombier count = Rsetuplen; 1537906943f9SDavid du Colombier cio->ndata = len; 1538906943f9SDavid du Colombier cio->tok = Tdtokout; 1539906943f9SDavid du Colombier }else{ 1540906943f9SDavid du Colombier if(len < 0) 1541906943f9SDavid du Colombier count = -1; 1542906943f9SDavid du Colombier else 1543906943f9SDavid du Colombier count = Rsetuplen + len; 1544906943f9SDavid du Colombier cio->tok = Tdtokin; 1545906943f9SDavid du Colombier } 1546906943f9SDavid du Colombier cio->toggle = Tddata1; 1547d37e33ffSDavid du Colombier epio(ep, cio, nil, 0, 0); 1548906943f9SDavid du Colombier qunlock(cio); 1549906943f9SDavid du Colombier poperror(); 1550906943f9SDavid du Colombier ddeprint("epctlio cio %#p return %ld\n", cio, count); 1551906943f9SDavid du Colombier return count; 1552906943f9SDavid du Colombier } 1553906943f9SDavid du Colombier 1554906943f9SDavid du Colombier static long 1555906943f9SDavid du Colombier epwrite(Ep *ep, void *a, long count) 1556906943f9SDavid du Colombier { 1557906943f9SDavid du Colombier Ctlio *cio; 1558906943f9SDavid du Colombier Isoio *iso; 1559906943f9SDavid du Colombier Qio *io; 1560906943f9SDavid du Colombier ulong delta; 1561906943f9SDavid du Colombier char *b; 1562906943f9SDavid du Colombier int tot; 1563906943f9SDavid du Colombier int nw; 1564906943f9SDavid du Colombier 1565906943f9SDavid du Colombier ddeprint("uhci: epwrite ep%d.%d\n", ep->dev->nb, ep->nb); 1566906943f9SDavid du Colombier if(ep->aux == nil) 1567906943f9SDavid du Colombier panic("uhci: epwrite: not open"); 1568906943f9SDavid du Colombier switch(ep->ttype){ 1569906943f9SDavid du Colombier case Tctl: 1570906943f9SDavid du Colombier cio = ep->aux; 1571906943f9SDavid du Colombier return epctlio(ep, cio, a, count); 1572906943f9SDavid du Colombier case Tbulk: 1573906943f9SDavid du Colombier io = ep->aux; 1574906943f9SDavid du Colombier if(ep->clrhalt) 1575906943f9SDavid du Colombier clrhalt(ep); 1576906943f9SDavid du Colombier /* 1577906943f9SDavid du Colombier * Put at most Tdatomic Tds (512 bytes) at a time. 1578906943f9SDavid du Colombier * Otherwise some devices produce babble errors. 1579906943f9SDavid du Colombier */ 1580906943f9SDavid du Colombier b = a; 1581906943f9SDavid du Colombier for(tot = 0; tot < count ; tot += nw){ 1582906943f9SDavid du Colombier nw = count - tot; 1583906943f9SDavid du Colombier if(nw > Tdatomic * ep->maxpkt) 1584906943f9SDavid du Colombier nw = Tdatomic * ep->maxpkt; 1585d37e33ffSDavid du Colombier nw = epio(ep, &io[OWRITE], b+tot, nw, 1); 1586906943f9SDavid du Colombier } 1587906943f9SDavid du Colombier return tot; 1588906943f9SDavid du Colombier case Tintr: 1589906943f9SDavid du Colombier io = ep->aux; 1590906943f9SDavid du Colombier delta = TK2MS(MACHP(0)->ticks) - io[OWRITE].iotime + 1; 1591906943f9SDavid du Colombier if(delta < ep->pollival) 1592906943f9SDavid du Colombier tsleep(&up->sleep, return0, 0, ep->pollival - delta); 1593906943f9SDavid du Colombier if(ep->clrhalt) 1594906943f9SDavid du Colombier clrhalt(ep); 1595d37e33ffSDavid du Colombier return epio(ep, &io[OWRITE], a, count, 1); 1596906943f9SDavid du Colombier case Tiso: 1597906943f9SDavid du Colombier iso = ep->aux; 1598906943f9SDavid du Colombier return episowrite(ep, iso, a, count); 1599906943f9SDavid du Colombier default: 1600906943f9SDavid du Colombier panic("uhci: epwrite: bad ep ttype %d", ep->ttype); 1601906943f9SDavid du Colombier } 1602906943f9SDavid du Colombier return -1; 1603906943f9SDavid du Colombier } 1604906943f9SDavid du Colombier 1605906943f9SDavid du Colombier static void 1606906943f9SDavid du Colombier isoopen(Ep *ep) 16079a747e4fSDavid du Colombier { 16089a747e4fSDavid du Colombier Ctlr *ctlr; 1609906943f9SDavid du Colombier Isoio *iso; 1610906943f9SDavid du Colombier int frno; 1611906943f9SDavid du Colombier int i; 1612906943f9SDavid du Colombier Td* td; 1613906943f9SDavid du Colombier Td* ltd; 1614906943f9SDavid du Colombier int size; 1615906943f9SDavid du Colombier int left; 16169a747e4fSDavid du Colombier 1617906943f9SDavid du Colombier if(ep->mode == ORDWR) 1618906943f9SDavid du Colombier error("iso i/o is half-duplex"); 1619906943f9SDavid du Colombier ctlr = ep->hp->aux; 1620906943f9SDavid du Colombier iso = ep->aux; 1621906943f9SDavid du Colombier iso->debug = ep->debug; 1622906943f9SDavid du Colombier iso->next = nil; /* paranoia */ 1623906943f9SDavid du Colombier if(ep->mode == OREAD) 1624906943f9SDavid du Colombier iso->tok = Tdtokin; 1625906943f9SDavid du Colombier else 1626906943f9SDavid du Colombier iso->tok = Tdtokout; 1627906943f9SDavid du Colombier iso->usbid = ((ep->nb & Epmax)<<7)|(ep->dev->nb & Devmax); 1628906943f9SDavid du Colombier iso->state = Qidle; 1629906943f9SDavid du Colombier iso->nframes = Nframes/ep->pollival; 1630906943f9SDavid du Colombier if(iso->nframes < 3) 1631906943f9SDavid du Colombier error("uhci isoopen bug"); /* we need at least 3 tds */ 1632906943f9SDavid du Colombier 16339a747e4fSDavid du Colombier ilock(ctlr); 1634d37e33ffSDavid du Colombier if(ctlr->load + ep->load > 800) 1635d37e33ffSDavid du Colombier print("usb: uhci: bandwidth may be exceeded\n"); 1636906943f9SDavid du Colombier ctlr->load += ep->load; 1637906943f9SDavid du Colombier ctlr->isoload += ep->load; 1638906943f9SDavid du Colombier dprint("uhci: load %uld isoload %uld\n", ctlr->load, ctlr->isoload); 1639906943f9SDavid du Colombier iunlock(ctlr); 1640906943f9SDavid du Colombier 1641906943f9SDavid du Colombier /* 1642906943f9SDavid du Colombier * From here on this cannot raise errors 1643906943f9SDavid du Colombier * unless we catch them and release here all memory allocated. 1644906943f9SDavid du Colombier */ 1645906943f9SDavid du Colombier if(ep->maxpkt > Tdndata) 1646906943f9SDavid du Colombier iso->data = smalloc(iso->nframes*ep->maxpkt); 1647906943f9SDavid du Colombier ilock(ctlr); 1648906943f9SDavid du Colombier frno = INS(Frnum) + 10; /* start 10ms ahead */ 1649906943f9SDavid du Colombier frno = TRUNC(frno, Nframes); 1650906943f9SDavid du Colombier iunlock(ctlr); 1651906943f9SDavid du Colombier iso->td0frno = frno; 1652906943f9SDavid du Colombier ltd = nil; 1653906943f9SDavid du Colombier left = 0; 1654906943f9SDavid du Colombier for(i = 0; i < iso->nframes; i++){ 1655906943f9SDavid du Colombier td = iso->tdps[frno] = tdalloc(); 1656906943f9SDavid du Colombier if(ep->mode == OREAD) 1657906943f9SDavid du Colombier size = ep->maxpkt; 1658906943f9SDavid du Colombier else{ 1659906943f9SDavid du Colombier size = (ep->hz+left) * ep->pollival / 1000; 1660906943f9SDavid du Colombier size *= ep->samplesz; 1661906943f9SDavid du Colombier left = (ep->hz+left) * ep->pollival % 1000; 1662906943f9SDavid du Colombier if(size > ep->maxpkt){ 1663906943f9SDavid du Colombier print("uhci: ep%d.%d: size > maxpkt\n", 1664906943f9SDavid du Colombier ep->dev->nb, ep->nb); 1665906943f9SDavid du Colombier print("size = %d max = %ld\n", size, ep->maxpkt); 1666906943f9SDavid du Colombier size = ep->maxpkt; 1667906943f9SDavid du Colombier } 1668906943f9SDavid du Colombier } 1669906943f9SDavid du Colombier if(size > Tdndata) 1670906943f9SDavid du Colombier td->data = iso->data + i * ep->maxpkt; 1671906943f9SDavid du Colombier else 1672906943f9SDavid du Colombier td->data = td->sbuff; 1673906943f9SDavid du Colombier td->buffer = PCIWADDR(td->data); 1674906943f9SDavid du Colombier tdisoinit(iso, td, size); 1675906943f9SDavid du Colombier if(ltd != nil) 1676906943f9SDavid du Colombier ltd->next = td; 1677906943f9SDavid du Colombier ltd = td; 1678906943f9SDavid du Colombier frno = TRUNC(frno+ep->pollival, Nframes); 1679906943f9SDavid du Colombier } 1680906943f9SDavid du Colombier ltd->next = iso->tdps[iso->td0frno]; 1681906943f9SDavid du Colombier iso->tdi = iso->tdps[iso->td0frno]; 1682906943f9SDavid du Colombier iso->tdu = iso->tdi; /* read: right now; write: 1s ahead */ 1683906943f9SDavid du Colombier ilock(ctlr); 1684906943f9SDavid du Colombier frno = iso->td0frno; 1685906943f9SDavid du Colombier for(i = 0; i < iso->nframes; i++){ 1686906943f9SDavid du Colombier iso->tdps[frno]->link = ctlr->frames[frno]; 1687906943f9SDavid du Colombier frno = TRUNC(frno+ep->pollival, Nframes); 1688906943f9SDavid du Colombier } 1689906943f9SDavid du Colombier coherence(); 1690906943f9SDavid du Colombier frno = iso->td0frno; 1691906943f9SDavid du Colombier for(i = 0; i < iso->nframes; i++){ 1692906943f9SDavid du Colombier ctlr->frames[frno] = PCIWADDR(iso->tdps[frno]); 1693906943f9SDavid du Colombier frno = TRUNC(frno+ep->pollival, Nframes); 1694906943f9SDavid du Colombier } 1695906943f9SDavid du Colombier iso->next = ctlr->iso; 1696906943f9SDavid du Colombier ctlr->iso = iso; 1697906943f9SDavid du Colombier iso->state = Qdone; 1698906943f9SDavid du Colombier iunlock(ctlr); 1699906943f9SDavid du Colombier if(debug > 1 || iso->debug >1) 1700906943f9SDavid du Colombier isodump(iso, 0); 1701906943f9SDavid du Colombier } 1702906943f9SDavid du Colombier 1703906943f9SDavid du Colombier /* 1704906943f9SDavid du Colombier * Allocate the endpoint and set it up for I/O 1705906943f9SDavid du Colombier * in the controller. This must follow what's said 1706906943f9SDavid du Colombier * in Ep regarding configuration, including perhaps 1707906943f9SDavid du Colombier * the saved toggles (saved on a previous close of 1708906943f9SDavid du Colombier * the endpoint data file by epclose). 1709906943f9SDavid du Colombier */ 1710906943f9SDavid du Colombier static void 1711906943f9SDavid du Colombier epopen(Ep *ep) 1712906943f9SDavid du Colombier { 1713906943f9SDavid du Colombier Ctlr *ctlr; 1714906943f9SDavid du Colombier Qh *cqh; 1715906943f9SDavid du Colombier Qio *io; 1716906943f9SDavid du Colombier Ctlio *cio; 1717906943f9SDavid du Colombier int usbid; 1718906943f9SDavid du Colombier 1719906943f9SDavid du Colombier ctlr = ep->hp->aux; 1720906943f9SDavid du Colombier deprint("uhci: epopen ep%d.%d\n", ep->dev->nb, ep->nb); 1721906943f9SDavid du Colombier if(ep->aux != nil) 1722906943f9SDavid du Colombier panic("uhci: epopen called with open ep"); 1723906943f9SDavid du Colombier if(waserror()){ 1724906943f9SDavid du Colombier free(ep->aux); 1725906943f9SDavid du Colombier ep->aux = nil; 1726906943f9SDavid du Colombier nexterror(); 1727906943f9SDavid du Colombier } 1728906943f9SDavid du Colombier if(ep->maxpkt > Tdmaxpkt){ 1729906943f9SDavid du Colombier print("uhci: maxkpkt too large: using %d\n", Tdmaxpkt); 1730906943f9SDavid du Colombier ep->maxpkt = Tdmaxpkt; 1731906943f9SDavid du Colombier } 1732906943f9SDavid du Colombier cqh = ctlr->qh[ep->ttype]; 1733906943f9SDavid du Colombier switch(ep->ttype){ 1734906943f9SDavid du Colombier case Tnone: 1735906943f9SDavid du Colombier error("endpoint not configured"); 1736906943f9SDavid du Colombier case Tiso: 1737906943f9SDavid du Colombier ep->aux = smalloc(sizeof(Isoio)); 1738906943f9SDavid du Colombier isoopen(ep); 1739906943f9SDavid du Colombier break; 1740906943f9SDavid du Colombier case Tctl: 1741906943f9SDavid du Colombier cio = ep->aux = smalloc(sizeof(Ctlio)); 1742906943f9SDavid du Colombier cio->debug = ep->debug; 1743906943f9SDavid du Colombier cio->ndata = -1; 1744906943f9SDavid du Colombier cio->data = nil; 1745906943f9SDavid du Colombier if(ep->dev->isroot != 0 && ep->nb == 0) /* root hub */ 1746906943f9SDavid du Colombier break; 1747906943f9SDavid du Colombier cio->qh = qhalloc(ctlr, cqh, cio, "epc"); 1748906943f9SDavid du Colombier break; 1749906943f9SDavid du Colombier case Tbulk: 1750906943f9SDavid du Colombier case Tintr: 1751906943f9SDavid du Colombier io = ep->aux = smalloc(sizeof(Qio)*2); 1752906943f9SDavid du Colombier io[OREAD].debug = io[OWRITE].debug = ep->debug; 1753906943f9SDavid du Colombier usbid = ((ep->nb&Epmax)<<7)|(ep->dev->nb &Devmax); 1754906943f9SDavid du Colombier if(ep->mode != OREAD){ 1755906943f9SDavid du Colombier if(ep->toggle[OWRITE] != 0) 1756906943f9SDavid du Colombier io[OWRITE].toggle = Tddata1; 1757906943f9SDavid du Colombier else 1758906943f9SDavid du Colombier io[OWRITE].toggle = Tddata0; 1759906943f9SDavid du Colombier io[OWRITE].tok = Tdtokout; 1760906943f9SDavid du Colombier io[OWRITE].qh = qhalloc(ctlr, cqh, io+OWRITE, "epw"); 1761906943f9SDavid du Colombier io[OWRITE].usbid = usbid; 1762906943f9SDavid du Colombier } 1763906943f9SDavid du Colombier if(ep->mode != OWRITE){ 1764906943f9SDavid du Colombier if(ep->toggle[OREAD] != 0) 1765906943f9SDavid du Colombier io[OREAD].toggle = Tddata1; 1766906943f9SDavid du Colombier else 1767906943f9SDavid du Colombier io[OREAD].toggle = Tddata0; 1768906943f9SDavid du Colombier io[OREAD].tok = Tdtokin; 1769906943f9SDavid du Colombier io[OREAD].qh = qhalloc(ctlr, cqh, io+OREAD, "epr"); 1770906943f9SDavid du Colombier io[OREAD].usbid = usbid; 1771906943f9SDavid du Colombier } 1772906943f9SDavid du Colombier break; 1773906943f9SDavid du Colombier } 1774906943f9SDavid du Colombier if(debug>1 || ep->debug) 1775906943f9SDavid du Colombier dump(ep->hp); 1776906943f9SDavid du Colombier deprint("uhci: epopen done\n"); 1777906943f9SDavid du Colombier poperror(); 1778906943f9SDavid du Colombier } 1779906943f9SDavid du Colombier 1780906943f9SDavid du Colombier static void 1781906943f9SDavid du Colombier cancelio(Ctlr *ctlr, Qio *io) 1782906943f9SDavid du Colombier { 1783906943f9SDavid du Colombier Qh *qh; 1784906943f9SDavid du Colombier 1785906943f9SDavid du Colombier ilock(ctlr); 1786906943f9SDavid du Colombier qh = io->qh; 1787906943f9SDavid du Colombier if(io == nil || io->qh == nil || io->qh->state == Qclose){ 1788906943f9SDavid du Colombier iunlock(ctlr); 1789906943f9SDavid du Colombier return; 1790906943f9SDavid du Colombier } 1791906943f9SDavid du Colombier dqprint("uhci: cancelio for qh %#p state %s\n", 1792906943f9SDavid du Colombier qh, qhsname[qh->state]); 1793906943f9SDavid du Colombier aborttds(qh); 1794906943f9SDavid du Colombier qh->state = Qclose; 1795906943f9SDavid du Colombier iunlock(ctlr); 1796906943f9SDavid du Colombier if(!waserror()){ 1797906943f9SDavid du Colombier tsleep(&up->sleep, return0, 0, Abortdelay); 1798906943f9SDavid du Colombier poperror(); 1799906943f9SDavid du Colombier } 1800906943f9SDavid du Colombier 1801906943f9SDavid du Colombier wakeup(io); 1802906943f9SDavid du Colombier qlock(io); 1803906943f9SDavid du Colombier /* wait for epio if running */ 1804906943f9SDavid du Colombier qunlock(io); 1805906943f9SDavid du Colombier 1806906943f9SDavid du Colombier qhfree(ctlr, qh); 1807906943f9SDavid du Colombier io->qh = nil; 1808906943f9SDavid du Colombier } 1809906943f9SDavid du Colombier 1810906943f9SDavid du Colombier static void 1811906943f9SDavid du Colombier cancelisoio(Ctlr *ctlr, Isoio *iso, int pollival, ulong load) 1812906943f9SDavid du Colombier { 1813906943f9SDavid du Colombier Isoio **il; 1814906943f9SDavid du Colombier ulong *lp; 1815906943f9SDavid du Colombier int i; 1816906943f9SDavid du Colombier int frno; 1817906943f9SDavid du Colombier Td *td; 1818906943f9SDavid du Colombier 1819906943f9SDavid du Colombier ilock(ctlr); 1820906943f9SDavid du Colombier if(iso->state == Qclose){ 1821906943f9SDavid du Colombier iunlock(ctlr); 1822906943f9SDavid du Colombier return; 1823906943f9SDavid du Colombier } 1824906943f9SDavid du Colombier if(iso->state != Qrun && iso->state != Qdone) 1825906943f9SDavid du Colombier panic("bad iso state"); 1826906943f9SDavid du Colombier iso->state = Qclose; 1827906943f9SDavid du Colombier if(ctlr->isoload < load) 1828906943f9SDavid du Colombier panic("uhci: low isoload"); 1829906943f9SDavid du Colombier ctlr->isoload -= load; 1830906943f9SDavid du Colombier ctlr->load -= load; 1831906943f9SDavid du Colombier for(il = &ctlr->iso; *il != nil; il = &(*il)->next) 1832906943f9SDavid du Colombier if(*il == iso) 1833906943f9SDavid du Colombier break; 1834906943f9SDavid du Colombier if(*il == nil) 1835906943f9SDavid du Colombier panic("isocancel: not found"); 1836906943f9SDavid du Colombier *il = iso->next; 1837906943f9SDavid du Colombier frno = iso->td0frno; 1838906943f9SDavid du Colombier for(i = 0; i < iso->nframes; i++){ 1839906943f9SDavid du Colombier td = iso->tdps[frno]; 1840906943f9SDavid du Colombier td->csw &= ~(Tdioc|Tdactive); 1841906943f9SDavid du Colombier for(lp=&ctlr->frames[frno]; !(*lp & Tdterm); 1842906943f9SDavid du Colombier lp = &TPTR(*lp)->link) 1843906943f9SDavid du Colombier if(TPTR(*lp) == td) 1844906943f9SDavid du Colombier break; 1845906943f9SDavid du Colombier if(*lp & Tdterm) 1846906943f9SDavid du Colombier panic("cancelisoio: td not found"); 1847906943f9SDavid du Colombier *lp = td->link; 1848906943f9SDavid du Colombier frno = TRUNC(frno+pollival, Nframes); 1849906943f9SDavid du Colombier } 1850906943f9SDavid du Colombier iunlock(ctlr); 1851906943f9SDavid du Colombier 1852906943f9SDavid du Colombier /* 1853906943f9SDavid du Colombier * wakeup anyone waiting for I/O and 1854906943f9SDavid du Colombier * wait to be sure no I/O is in progress in the controller. 1855906943f9SDavid du Colombier * and then wait to be sure episo-io is no longer running. 1856906943f9SDavid du Colombier */ 1857906943f9SDavid du Colombier wakeup(iso); 1858906943f9SDavid du Colombier diprint("cancelisoio iso %#p waiting for I/O to cease\n", iso); 1859906943f9SDavid du Colombier tsleep(&up->sleep, return0, 0, 5); 1860906943f9SDavid du Colombier qlock(iso); 1861906943f9SDavid du Colombier qunlock(iso); 1862906943f9SDavid du Colombier diprint("cancelisoio iso %#p releasing iso\n", iso); 1863906943f9SDavid du Colombier 1864906943f9SDavid du Colombier frno = iso->td0frno; 1865906943f9SDavid du Colombier for(i = 0; i < iso->nframes; i++){ 1866906943f9SDavid du Colombier tdfree(iso->tdps[frno]); 1867906943f9SDavid du Colombier iso->tdps[frno] = nil; 1868906943f9SDavid du Colombier frno = TRUNC(frno+pollival, Nframes); 1869906943f9SDavid du Colombier } 1870906943f9SDavid du Colombier free(iso->data); 1871906943f9SDavid du Colombier iso->data = nil; 1872906943f9SDavid du Colombier } 1873906943f9SDavid du Colombier 1874906943f9SDavid du Colombier static void 1875906943f9SDavid du Colombier epclose(Ep *ep) 1876906943f9SDavid du Colombier { 1877906943f9SDavid du Colombier Ctlr *ctlr; 1878906943f9SDavid du Colombier Ctlio *cio; 1879906943f9SDavid du Colombier Isoio *iso; 1880906943f9SDavid du Colombier Qio *io; 1881906943f9SDavid du Colombier 1882906943f9SDavid du Colombier ctlr = ep->hp->aux; 1883906943f9SDavid du Colombier deprint("uhci: epclose ep%d.%d\n", ep->dev->nb, ep->nb); 1884906943f9SDavid du Colombier 1885906943f9SDavid du Colombier if(ep->aux == nil) 1886906943f9SDavid du Colombier panic("uhci: epclose called with closed ep"); 1887906943f9SDavid du Colombier switch(ep->ttype){ 1888906943f9SDavid du Colombier case Tctl: 1889906943f9SDavid du Colombier cio = ep->aux; 1890906943f9SDavid du Colombier cancelio(ctlr, cio); 1891906943f9SDavid du Colombier free(cio->data); 1892906943f9SDavid du Colombier cio->data = nil; 1893906943f9SDavid du Colombier break; 1894906943f9SDavid du Colombier case Tbulk: 1895906943f9SDavid du Colombier case Tintr: 1896906943f9SDavid du Colombier io = ep->aux; 1897906943f9SDavid du Colombier ep->toggle[OREAD] = ep->toggle[OWRITE] = 0; 1898906943f9SDavid du Colombier if(ep->mode != OWRITE){ 1899906943f9SDavid du Colombier cancelio(ctlr, &io[OREAD]); 1900906943f9SDavid du Colombier if(io[OREAD].toggle == Tddata1) 1901906943f9SDavid du Colombier ep->toggle[OREAD] = 1; 1902906943f9SDavid du Colombier } 1903906943f9SDavid du Colombier if(ep->mode != OREAD){ 1904906943f9SDavid du Colombier cancelio(ctlr, &io[OWRITE]); 1905906943f9SDavid du Colombier if(io[OWRITE].toggle == Tddata1) 1906906943f9SDavid du Colombier ep->toggle[OWRITE] = 1; 1907906943f9SDavid du Colombier } 1908906943f9SDavid du Colombier break; 1909906943f9SDavid du Colombier case Tiso: 1910906943f9SDavid du Colombier iso = ep->aux; 1911906943f9SDavid du Colombier cancelisoio(ctlr, iso, ep->pollival, ep->load); 1912906943f9SDavid du Colombier break; 1913906943f9SDavid du Colombier default: 1914906943f9SDavid du Colombier panic("epclose: bad ttype %d", ep->ttype); 1915906943f9SDavid du Colombier } 1916906943f9SDavid du Colombier 1917906943f9SDavid du Colombier free(ep->aux); 1918906943f9SDavid du Colombier ep->aux = nil; 1919906943f9SDavid du Colombier 1920906943f9SDavid du Colombier } 1921906943f9SDavid du Colombier 1922906943f9SDavid du Colombier static char* 1923906943f9SDavid du Colombier seprintep(char *s, char *e, Ep *ep) 1924906943f9SDavid du Colombier { 1925906943f9SDavid du Colombier Ctlio *cio; 1926906943f9SDavid du Colombier Qio *io; 1927906943f9SDavid du Colombier Isoio *iso; 1928906943f9SDavid du Colombier Ctlr *ctlr; 1929906943f9SDavid du Colombier 1930906943f9SDavid du Colombier ctlr = ep->hp->aux; 1931906943f9SDavid du Colombier ilock(ctlr); 1932906943f9SDavid du Colombier if(ep->aux == nil){ 1933906943f9SDavid du Colombier *s = 0; 1934906943f9SDavid du Colombier iunlock(ctlr); 1935906943f9SDavid du Colombier return s; 1936906943f9SDavid du Colombier } 1937906943f9SDavid du Colombier switch(ep->ttype){ 1938906943f9SDavid du Colombier case Tctl: 1939906943f9SDavid du Colombier cio = ep->aux; 1940906943f9SDavid du Colombier s = seprint(s,e,"cio %#p qh %#p" 1941906943f9SDavid du Colombier " id %#x tog %#x tok %#x err %s\n", 1942906943f9SDavid du Colombier cio, cio->qh, cio->usbid, cio->toggle, 1943906943f9SDavid du Colombier cio->tok, cio->err); 1944906943f9SDavid du Colombier break; 1945906943f9SDavid du Colombier case Tbulk: 1946906943f9SDavid du Colombier case Tintr: 1947906943f9SDavid du Colombier io = ep->aux; 1948906943f9SDavid du Colombier if(ep->mode != OWRITE) 1949906943f9SDavid du Colombier s = seprint(s,e,"r: qh %#p id %#x tog %#x tok %#x err %s\n", 1950906943f9SDavid du Colombier io[OREAD].qh, io[OREAD].usbid, io[OREAD].toggle, 1951906943f9SDavid du Colombier io[OREAD].tok, io[OREAD].err); 1952906943f9SDavid du Colombier if(ep->mode != OREAD) 1953906943f9SDavid du Colombier s = seprint(s,e,"w: qh %#p id %#x tog %#x tok %#x err %s\n", 1954906943f9SDavid du Colombier io[OWRITE].qh, io[OWRITE].usbid, io[OWRITE].toggle, 1955906943f9SDavid du Colombier io[OWRITE].tok, io[OWRITE].err); 1956906943f9SDavid du Colombier break; 1957906943f9SDavid du Colombier case Tiso: 1958906943f9SDavid du Colombier iso = ep->aux; 1959906943f9SDavid du Colombier s = seprint(s,e,"iso %#p id %#x tok %#x tdu %#p tdi %#p err %s\n", 1960906943f9SDavid du Colombier iso, iso->usbid, iso->tok, iso->tdu, iso->tdi, iso->err); 1961906943f9SDavid du Colombier break; 1962906943f9SDavid du Colombier } 1963906943f9SDavid du Colombier iunlock(ctlr); 1964906943f9SDavid du Colombier return s; 1965906943f9SDavid du Colombier } 1966906943f9SDavid du Colombier 1967906943f9SDavid du Colombier static int 1968906943f9SDavid du Colombier portenable(Hci *hp, int port, int on) 1969906943f9SDavid du Colombier { 1970906943f9SDavid du Colombier int s; 1971906943f9SDavid du Colombier int ioport; 1972906943f9SDavid du Colombier Ctlr *ctlr; 1973906943f9SDavid du Colombier 1974906943f9SDavid du Colombier ctlr = hp->aux; 1975906943f9SDavid du Colombier dprint("uhci: %#x port %d enable=%d\n", ctlr->port, port, on); 1976906943f9SDavid du Colombier ioport = PORT(port-1); 1977906943f9SDavid du Colombier qlock(&ctlr->portlck); 1978906943f9SDavid du Colombier if(waserror()){ 1979906943f9SDavid du Colombier qunlock(&ctlr->portlck); 1980906943f9SDavid du Colombier nexterror(); 1981906943f9SDavid du Colombier } 1982906943f9SDavid du Colombier ilock(ctlr); 1983906943f9SDavid du Colombier s = INS(ioport); 1984906943f9SDavid du Colombier if(on) 1985906943f9SDavid du Colombier OUTS(ioport, s | PSenable); 1986906943f9SDavid du Colombier else 1987906943f9SDavid du Colombier OUTS(ioport, s & ~PSenable); 1988906943f9SDavid du Colombier microdelay(64); 1989906943f9SDavid du Colombier iunlock(ctlr); 1990906943f9SDavid du Colombier tsleep(&up->sleep, return0, 0, Enabledelay); 1991906943f9SDavid du Colombier dprint("uhci %#ux port %d enable=%d: sts %#x\n", 1992906943f9SDavid du Colombier ctlr->port, port, on, INS(ioport)); 1993906943f9SDavid du Colombier qunlock(&ctlr->portlck); 1994906943f9SDavid du Colombier poperror(); 1995906943f9SDavid du Colombier return 0; 1996906943f9SDavid du Colombier } 1997906943f9SDavid du Colombier 1998906943f9SDavid du Colombier static int 1999906943f9SDavid du Colombier portreset(Hci *hp, int port, int on) 2000906943f9SDavid du Colombier { 2001906943f9SDavid du Colombier int i, p; 2002906943f9SDavid du Colombier Ctlr *ctlr; 2003906943f9SDavid du Colombier 2004906943f9SDavid du Colombier if(on == 0) 2005906943f9SDavid du Colombier return 0; 2006906943f9SDavid du Colombier ctlr = hp->aux; 2007906943f9SDavid du Colombier dprint("uhci: %#ux port %d reset\n", ctlr->port, port); 2008906943f9SDavid du Colombier p = PORT(port-1); 2009906943f9SDavid du Colombier ilock(ctlr); 2010906943f9SDavid du Colombier OUTS(p, PSreset); 2011906943f9SDavid du Colombier delay(50); 2012906943f9SDavid du Colombier OUTS(p, INS(p) & ~PSreset); 2013906943f9SDavid du Colombier OUTS(p, INS(p) | PSenable); 2014906943f9SDavid du Colombier microdelay(64); 2015906943f9SDavid du Colombier for(i=0; i<1000 && (INS(p) & PSenable) == 0; i++) 2016906943f9SDavid du Colombier ; 2017906943f9SDavid du Colombier OUTS(p, (INS(p) & ~PSreset)|PSenable); 2018906943f9SDavid du Colombier iunlock(ctlr); 2019906943f9SDavid du Colombier dprint("uhci %#ux after port %d reset: sts %#x\n", 2020906943f9SDavid du Colombier ctlr->port, port, INS(p)); 2021906943f9SDavid du Colombier return 0; 2022906943f9SDavid du Colombier } 2023906943f9SDavid du Colombier 2024906943f9SDavid du Colombier static int 2025906943f9SDavid du Colombier portstatus(Hci *hp, int port) 2026906943f9SDavid du Colombier { 2027906943f9SDavid du Colombier int s; 2028906943f9SDavid du Colombier int r; 2029906943f9SDavid du Colombier int ioport; 2030906943f9SDavid du Colombier Ctlr *ctlr; 2031906943f9SDavid du Colombier 2032906943f9SDavid du Colombier ctlr = hp->aux; 2033906943f9SDavid du Colombier ioport = PORT(port-1); 2034906943f9SDavid du Colombier qlock(&ctlr->portlck); 2035906943f9SDavid du Colombier if(waserror()){ 2036906943f9SDavid du Colombier iunlock(ctlr); 2037906943f9SDavid du Colombier qunlock(&ctlr->portlck); 2038906943f9SDavid du Colombier nexterror(); 2039906943f9SDavid du Colombier } 2040906943f9SDavid du Colombier ilock(ctlr); 2041906943f9SDavid du Colombier s = INS(ioport); 2042906943f9SDavid du Colombier if(s & (PSstatuschg | PSchange)){ 2043906943f9SDavid du Colombier OUTS(ioport, s); 2044906943f9SDavid du Colombier ddprint("uhci %#ux port %d status %#x\n", ctlr->port, port, s); 2045906943f9SDavid du Colombier } 2046906943f9SDavid du Colombier iunlock(ctlr); 2047906943f9SDavid du Colombier qunlock(&ctlr->portlck); 2048906943f9SDavid du Colombier poperror(); 2049906943f9SDavid du Colombier 2050906943f9SDavid du Colombier /* 2051906943f9SDavid du Colombier * We must return status bits as a 2052906943f9SDavid du Colombier * get port status hub request would do. 2053906943f9SDavid du Colombier */ 2054906943f9SDavid du Colombier r = 0; 2055906943f9SDavid du Colombier if(s & PSpresent) 2056906943f9SDavid du Colombier r |= HPpresent; 2057906943f9SDavid du Colombier if(s & PSenable) 2058906943f9SDavid du Colombier r |= HPenable; 2059906943f9SDavid du Colombier if(s & PSsuspend) 2060906943f9SDavid du Colombier r |= HPsuspend; 2061906943f9SDavid du Colombier if(s & PSreset) 2062906943f9SDavid du Colombier r |= HPreset; 2063906943f9SDavid du Colombier if(s & PSslow) 2064906943f9SDavid du Colombier r |= HPslow; 2065906943f9SDavid du Colombier if(s & PSstatuschg) 2066906943f9SDavid du Colombier r |= HPstatuschg; 2067906943f9SDavid du Colombier if(s & PSchange) 2068906943f9SDavid du Colombier r |= HPchange; 2069906943f9SDavid du Colombier return r; 20709a747e4fSDavid du Colombier } 20719a747e4fSDavid du Colombier 20729a747e4fSDavid du Colombier static void 20739a747e4fSDavid du Colombier scanpci(void) 20749a747e4fSDavid du Colombier { 2075906943f9SDavid du Colombier static int already = 0; 20769a747e4fSDavid du Colombier int io; 2077906943f9SDavid du Colombier int i; 20789a747e4fSDavid du Colombier Ctlr *ctlr; 20799a747e4fSDavid du Colombier Pcidev *p; 20809a747e4fSDavid du Colombier 20819a747e4fSDavid du Colombier if(already) 20829a747e4fSDavid du Colombier return; 20839a747e4fSDavid du Colombier already = 1; 20849a747e4fSDavid du Colombier p = nil; 20859a747e4fSDavid du Colombier while(p = pcimatch(p, 0, 0)){ 20869a747e4fSDavid du Colombier /* 2087ade43d10SDavid du Colombier * Find UHCI controllers (Programming Interface = 0). 20889a747e4fSDavid du Colombier */ 2089ade43d10SDavid du Colombier if(p->ccrb != Pcibcserial || p->ccru != Pciscusb) 20909a747e4fSDavid du Colombier continue; 209115174232SDavid du Colombier switch(p->ccrp){ 2092ade43d10SDavid du Colombier case 0: 20939a747e4fSDavid du Colombier io = p->mem[4].bar & ~0x0F; 209415174232SDavid du Colombier break; 209515174232SDavid du Colombier default: 209615174232SDavid du Colombier continue; 209715174232SDavid du Colombier } 20989a747e4fSDavid du Colombier if(io == 0){ 2099906943f9SDavid du Colombier print("usbuhci: %#x %#x: failed to map registers\n", 210041dd6b47SDavid du Colombier p->vid, p->did); 21019a747e4fSDavid du Colombier continue; 21029a747e4fSDavid du Colombier } 21039a747e4fSDavid du Colombier if(ioalloc(io, p->mem[4].size, 0, "usbuhci") < 0){ 210441dd6b47SDavid du Colombier print("usbuhci: port %#ux in use\n", io); 21059a747e4fSDavid du Colombier continue; 21069a747e4fSDavid du Colombier } 21079a747e4fSDavid du Colombier if(p->intl == 0xFF || p->intl == 0){ 210841dd6b47SDavid du Colombier print("usbuhci: no irq assigned for port %#ux\n", io); 21099a747e4fSDavid du Colombier continue; 21109a747e4fSDavid du Colombier } 21119a747e4fSDavid du Colombier 2112906943f9SDavid du Colombier dprint("uhci: %#x %#x: port %#ux size %#x irq %d\n", 21139a747e4fSDavid du Colombier p->vid, p->did, io, p->mem[4].size, p->intl); 21149a747e4fSDavid du Colombier 2115906943f9SDavid du Colombier ctlr = mallocz(sizeof(Ctlr), 1); 21169a747e4fSDavid du Colombier ctlr->pcidev = p; 2117906943f9SDavid du Colombier ctlr->port = io; 2118906943f9SDavid du Colombier for(i = 0; i < Nhcis; i++) 2119906943f9SDavid du Colombier if(ctlrs[i] == nil){ 2120906943f9SDavid du Colombier ctlrs[i] = ctlr; 2121906943f9SDavid du Colombier break; 21229a747e4fSDavid du Colombier } 2123906943f9SDavid du Colombier if(i == Nhcis) 2124906943f9SDavid du Colombier print("uhci: bug: no more controllers\n"); 2125906943f9SDavid du Colombier } 2126906943f9SDavid du Colombier } 2127906943f9SDavid du Colombier 2128906943f9SDavid du Colombier static void 2129906943f9SDavid du Colombier uhcimeminit(Ctlr *ctlr) 2130906943f9SDavid du Colombier { 2131906943f9SDavid du Colombier Td* td; 2132906943f9SDavid du Colombier Qh *qh; 2133906943f9SDavid du Colombier int frsize; 2134906943f9SDavid du Colombier int i; 2135906943f9SDavid du Colombier 2136906943f9SDavid du Colombier ctlr->qhs = ctlr->qh[Tctl] = qhalloc(ctlr, nil, nil, "CTL"); 2137906943f9SDavid du Colombier ctlr->qh[Tintr] = qhalloc(ctlr, ctlr->qh[Tctl], nil, "INT"); 2138906943f9SDavid du Colombier ctlr->qh[Tbulk] = qhalloc(ctlr, ctlr->qh[Tintr], nil, "BLK"); 2139906943f9SDavid du Colombier 2140906943f9SDavid du Colombier /* idle Td from dummy Qh at the end. looped back to itself */ 2141906943f9SDavid du Colombier /* This is a workaround for PIIX4 errata 29773804.pdf */ 2142906943f9SDavid du Colombier qh = qhalloc(ctlr, ctlr->qh[Tbulk], nil, "BWS"); 2143906943f9SDavid du Colombier td = tdalloc(); 2144906943f9SDavid du Colombier td->link = PCIWADDR(td); 2145906943f9SDavid du Colombier qhlinktd(qh, td); 2146906943f9SDavid du Colombier 2147906943f9SDavid du Colombier /* loop (hw only) from the last qh back to control xfers. 2148906943f9SDavid du Colombier * this may be done only for some of them. Disable until ehci comes. 2149906943f9SDavid du Colombier */ 2150906943f9SDavid du Colombier if(0) 2151906943f9SDavid du Colombier qh->link = PCIWADDR(ctlr->qhs); 2152906943f9SDavid du Colombier 2153906943f9SDavid du Colombier frsize = Nframes*sizeof(ulong); 2154906943f9SDavid du Colombier ctlr->frames = xspanalloc(frsize, frsize, 0); 2155906943f9SDavid du Colombier if(ctlr->frames == nil) 2156c9b6d007SDavid du Colombier panic("uhci reset: no memory"); 2157906943f9SDavid du Colombier 2158906943f9SDavid du Colombier ctlr->iso = nil; 2159906943f9SDavid du Colombier for(i = 0; i < Nframes; i++) 2160906943f9SDavid du Colombier ctlr->frames[i] = PCIWADDR(ctlr->qhs)|QHlinkqh; 2161906943f9SDavid du Colombier OUTL(Flbaseadd, PCIWADDR(ctlr->frames)); 2162906943f9SDavid du Colombier OUTS(Frnum, 0); 2163906943f9SDavid du Colombier dprint("uhci %#ux flb %#ulx frno %#ux\n", ctlr->port, 2164906943f9SDavid du Colombier INL(Flbaseadd), INS(Frnum)); 2165906943f9SDavid du Colombier } 2166906943f9SDavid du Colombier 2167906943f9SDavid du Colombier static void 2168906943f9SDavid du Colombier init(Hci *hp) 2169906943f9SDavid du Colombier { 2170906943f9SDavid du Colombier Ctlr *ctlr; 2171906943f9SDavid du Colombier int sts; 2172906943f9SDavid du Colombier int i; 2173906943f9SDavid du Colombier 2174906943f9SDavid du Colombier ctlr = hp->aux; 2175906943f9SDavid du Colombier dprint("uhci %#ux init\n", ctlr->port); 2176906943f9SDavid du Colombier coherence(); 2177906943f9SDavid du Colombier ilock(ctlr); 2178906943f9SDavid du Colombier OUTS(Usbintr, Itmout|Iresume|Ioc|Ishort); 2179906943f9SDavid du Colombier uhcirun(ctlr, 1); 2180906943f9SDavid du Colombier dprint("uhci: init: cmd %#ux sts %#ux sof %#ux", 2181906943f9SDavid du Colombier INS(Cmd), INS(Status), INS(SOFmod)); 2182906943f9SDavid du Colombier dprint(" flb %#ulx frno %#ux psc0 %#ux psc1 %#ux", 2183906943f9SDavid du Colombier INL(Flbaseadd), INS(Frnum), INS(PORT(0)), INS(PORT(1))); 2184906943f9SDavid du Colombier /* guess other ports */ 2185906943f9SDavid du Colombier for(i = 2; i < 6; i++){ 2186906943f9SDavid du Colombier sts = INS(PORT(i)); 2187906943f9SDavid du Colombier if(sts != 0xFFFF && (sts & PSreserved1) == 1){ 2188906943f9SDavid du Colombier dprint(" psc%d %#ux", i, sts); 2189906943f9SDavid du Colombier hp->nports++; 2190906943f9SDavid du Colombier }else 2191906943f9SDavid du Colombier break; 2192906943f9SDavid du Colombier } 2193906943f9SDavid du Colombier for(i = 0; i < hp->nports; i++) 2194906943f9SDavid du Colombier OUTS(PORT(i), 0); 2195906943f9SDavid du Colombier iunlock(ctlr); 2196906943f9SDavid du Colombier } 2197906943f9SDavid du Colombier 2198906943f9SDavid du Colombier static void 2199906943f9SDavid du Colombier uhcireset(Ctlr *ctlr) 2200906943f9SDavid du Colombier { 2201906943f9SDavid du Colombier int i; 2202906943f9SDavid du Colombier int sof; 2203906943f9SDavid du Colombier 2204906943f9SDavid du Colombier ilock(ctlr); 2205906943f9SDavid du Colombier dprint("uhci %#ux reset\n", ctlr->port); 2206906943f9SDavid du Colombier 2207906943f9SDavid du Colombier /* 2208906943f9SDavid du Colombier * Turn off legacy mode. Some controllers won't 2209906943f9SDavid du Colombier * interrupt us as expected otherwise. 2210906943f9SDavid du Colombier */ 2211906943f9SDavid du Colombier uhcirun(ctlr, 0); 2212906943f9SDavid du Colombier pcicfgw16(ctlr->pcidev, 0xc0, 0x2000); 2213906943f9SDavid du Colombier 2214906943f9SDavid du Colombier OUTS(Usbintr, 0); 2215906943f9SDavid du Colombier sof = INB(SOFmod); 2216906943f9SDavid du Colombier uhcicmd(ctlr, Cgreset); /* global reset */ 2217906943f9SDavid du Colombier delay(Resetdelay); 2218906943f9SDavid du Colombier uhcicmd(ctlr, 0); /* all halt */ 2219906943f9SDavid du Colombier uhcicmd(ctlr, Chcreset); /* controller reset */ 2220906943f9SDavid du Colombier for(i = 0; i < 100; i++){ 2221906943f9SDavid du Colombier if((INS(Cmd) & Chcreset) == 0) 2222906943f9SDavid du Colombier break; 2223906943f9SDavid du Colombier delay(1); 2224906943f9SDavid du Colombier } 2225906943f9SDavid du Colombier if(i == 100) 2226906943f9SDavid du Colombier print("uhci %#x controller reset timed out\n", ctlr->port); 2227906943f9SDavid du Colombier OUTB(SOFmod, sof); 2228906943f9SDavid du Colombier iunlock(ctlr); 2229906943f9SDavid du Colombier } 2230906943f9SDavid du Colombier 2231906943f9SDavid du Colombier static void 2232906943f9SDavid du Colombier setdebug(Hci*, int d) 2233906943f9SDavid du Colombier { 2234906943f9SDavid du Colombier debug = d; 22359a747e4fSDavid du Colombier } 22369a747e4fSDavid du Colombier 22379a747e4fSDavid du Colombier static int 2238906943f9SDavid du Colombier reset(Hci *hp) 22399a747e4fSDavid du Colombier { 2240906943f9SDavid du Colombier static Lock resetlck; 22419a747e4fSDavid du Colombier int i; 22429a747e4fSDavid du Colombier Ctlr *ctlr; 22439a747e4fSDavid du Colombier Pcidev *p; 22449a747e4fSDavid du Colombier 2245906943f9SDavid du Colombier if(getconf("*nousbuhci")) 2246906943f9SDavid du Colombier return -1; 2247906943f9SDavid du Colombier 2248906943f9SDavid du Colombier ilock(&resetlck); 22499a747e4fSDavid du Colombier scanpci(); 22509a747e4fSDavid du Colombier 22519a747e4fSDavid du Colombier /* 2252906943f9SDavid du Colombier * Any adapter matches if no hp->port is supplied, 22539a747e4fSDavid du Colombier * otherwise the ports must match. 22549a747e4fSDavid du Colombier */ 2255906943f9SDavid du Colombier ctlr = nil; 2256906943f9SDavid du Colombier for(i = 0; i < Nhcis && ctlrs[i] != nil; i++){ 2257906943f9SDavid du Colombier ctlr = ctlrs[i]; 2258906943f9SDavid du Colombier if(ctlr->active == 0) 2259906943f9SDavid du Colombier if(hp->port == 0 || hp->port == ctlr->port){ 22609a747e4fSDavid du Colombier ctlr->active = 1; 22619a747e4fSDavid du Colombier break; 22629a747e4fSDavid du Colombier } 22639a747e4fSDavid du Colombier } 2264906943f9SDavid du Colombier iunlock(&resetlck); 2265906943f9SDavid du Colombier if(ctlrs[i] == nil || i == Nhcis) 22669a747e4fSDavid du Colombier return -1; 22679a747e4fSDavid du Colombier 22689a747e4fSDavid du Colombier p = ctlr->pcidev; 2269906943f9SDavid du Colombier hp->aux = ctlr; 2270906943f9SDavid du Colombier hp->port = ctlr->port; 2271906943f9SDavid du Colombier hp->irq = p->intl; 2272906943f9SDavid du Colombier hp->tbdf = p->tbdf; 2273906943f9SDavid du Colombier hp->nports = 2; /* default */ 22749a747e4fSDavid du Colombier 2275906943f9SDavid du Colombier uhcireset(ctlr); 2276906943f9SDavid du Colombier uhcimeminit(ctlr); 22779a747e4fSDavid du Colombier 22789a747e4fSDavid du Colombier /* 2279906943f9SDavid du Colombier * Linkage to the generic HCI driver. 22809a747e4fSDavid du Colombier */ 2281906943f9SDavid du Colombier hp->init = init; 2282906943f9SDavid du Colombier hp->dump = dump; 2283906943f9SDavid du Colombier hp->interrupt = interrupt; 2284906943f9SDavid du Colombier hp->epopen = epopen; 2285906943f9SDavid du Colombier hp->epclose = epclose; 2286906943f9SDavid du Colombier hp->epread = epread; 2287906943f9SDavid du Colombier hp->epwrite = epwrite; 2288906943f9SDavid du Colombier hp->seprintep = seprintep; 2289906943f9SDavid du Colombier hp->portenable = portenable; 2290906943f9SDavid du Colombier hp->portreset = portreset; 2291906943f9SDavid du Colombier hp->portstatus = portstatus; 2292906943f9SDavid du Colombier hp->debug = setdebug; 2293906943f9SDavid du Colombier hp->type = "uhci"; 22949a747e4fSDavid du Colombier return 0; 22959a747e4fSDavid du Colombier } 22969a747e4fSDavid du Colombier 22979a747e4fSDavid du Colombier void 22989a747e4fSDavid du Colombier usbuhcilink(void) 22999a747e4fSDavid du Colombier { 2300906943f9SDavid du Colombier addhcitype("uhci", reset); 23019a747e4fSDavid du Colombier } 2302