16a5dc222SDavid du Colombier /* 2*906943f9SDavid du Colombier * USB Open Host Controller Interface (Ohci) driver 3*906943f9SDavid du Colombier * 4*906943f9SDavid du Colombier * BUGS: 5*906943f9SDavid du Colombier * - Missing isochronous input streams. 6*906943f9SDavid du Colombier * - Too many delays and ilocks. 7*906943f9SDavid du Colombier * - bandwidth admission control must be done per-frame. 8*906943f9SDavid du Colombier * - Buffering could be handled like in uhci, to avoid 9*906943f9SDavid du Colombier * needed block allocation and avoid allocs for small Tds. 10*906943f9SDavid du Colombier * - must warn of power overruns. 116a5dc222SDavid du Colombier */ 12*906943f9SDavid du Colombier 136a5dc222SDavid du Colombier #include "u.h" 146a5dc222SDavid du Colombier #include "../port/lib.h" 156a5dc222SDavid du Colombier #include "mem.h" 166a5dc222SDavid du Colombier #include "dat.h" 176a5dc222SDavid du Colombier #include "fns.h" 186a5dc222SDavid du Colombier #include "io.h" 196a5dc222SDavid du Colombier #include "../port/error.h" 206a5dc222SDavid du Colombier 216a5dc222SDavid du Colombier #include "usb.h" 226a5dc222SDavid du Colombier 236a5dc222SDavid du Colombier typedef struct Ctlr Ctlr; 24*906943f9SDavid du Colombier typedef struct Qtree Qtree; 25*906943f9SDavid du Colombier typedef struct Epx Epx; 26*906943f9SDavid du Colombier typedef struct Td Td; 27*906943f9SDavid du Colombier typedef struct Hcca Hcca; 28*906943f9SDavid du Colombier typedef struct Ohci Ohci; 29*906943f9SDavid du Colombier typedef struct Qio Qio; 30*906943f9SDavid du Colombier typedef struct Ed Ed; 31*906943f9SDavid du Colombier typedef struct Ctlio Ctlio; 32*906943f9SDavid du Colombier typedef struct Isoio Isoio; 33*906943f9SDavid du Colombier typedef struct Tdpool Tdpool; 34*906943f9SDavid du Colombier typedef struct Edpool Edpool; 356a5dc222SDavid du Colombier 36*906943f9SDavid du Colombier enum 376a5dc222SDavid du Colombier { 38*906943f9SDavid du Colombier Incr = 64, /* for Td and Ed pools */ 396a5dc222SDavid du Colombier 40*906943f9SDavid du Colombier Align = 0x20, /* OHCI only requires 0x10 */ 41*906943f9SDavid du Colombier /* use always a power of 2 */ 426a5dc222SDavid du Colombier 43*906943f9SDavid du Colombier Ctltmout = 2000, /* timeout for a ctl. request (ms) */ 44*906943f9SDavid du Colombier Bulktmout = 2000, /* timeout for a bulk xfer. (ms) */ 45*906943f9SDavid du Colombier Isotmout = 2000, /* timeout for an iso. request (ms) */ 46*906943f9SDavid du Colombier Abortdelay = 1, /* delay after cancelling Tds (ms) */ 47*906943f9SDavid du Colombier Tdatomic = 8, /* max nb. of Tds per bulk I/O op. */ 48*906943f9SDavid du Colombier Enabledelay = 100, /* waiting for a port to enable */ 496a5dc222SDavid du Colombier 506a5dc222SDavid du Colombier 51*906943f9SDavid du Colombier /* Queue states (software) */ 52*906943f9SDavid du Colombier Qidle = 0, 53*906943f9SDavid du Colombier Qinstall, 54*906943f9SDavid du Colombier Qrun, 55*906943f9SDavid du Colombier Qdone, 56*906943f9SDavid du Colombier Qclose, 57*906943f9SDavid du Colombier Qfree, 586a5dc222SDavid du Colombier 59*906943f9SDavid du Colombier /* Ed control bits */ 60*906943f9SDavid du Colombier Edmpsmask = 0x7ff, /* max packet size */ 61*906943f9SDavid du Colombier Edmpsshift = 16, 62*906943f9SDavid du Colombier Edlow = 1 << 13, /* low speed */ 63*906943f9SDavid du Colombier Edskip = 1 << 14, /* skip this ed */ 64*906943f9SDavid du Colombier Ediso = 1 << 15, /* iso Tds used */ 65*906943f9SDavid du Colombier Edtddir = 0, /* get dir from td */ 66*906943f9SDavid du Colombier Edin = 2 << 11, /* direction in */ 67*906943f9SDavid du Colombier Edout = 1 << 11, /* direction out */ 68*906943f9SDavid du Colombier Eddirmask = 3 << 11, /* direction bits */ 69*906943f9SDavid du Colombier Edhalt = 1, /* halted (in head ptr) */ 70*906943f9SDavid du Colombier Edtoggle = 2, /* toggle (in head ptr) 1 == data1 */ 716a5dc222SDavid du Colombier 72*906943f9SDavid du Colombier /* Td control bits */ 73*906943f9SDavid du Colombier Tdround = 1<<18, /* (rounding) short packets ok */ 74*906943f9SDavid du Colombier Tdtoksetup = 0<<19, /* setup packet */ 75*906943f9SDavid du Colombier Tdtokin = 2<<19, /* in packet */ 76*906943f9SDavid du Colombier Tdtokout = 1<<19, /* out packet */ 77*906943f9SDavid du Colombier Tdtokmask = 3<<19, /* in/out/setup bits */ 78*906943f9SDavid du Colombier Tdnoioc = 7<<21, /* intr. cnt. value for no interrupt */ 79*906943f9SDavid du Colombier Tdusetog = 1<<25, /* use toggle from Td (1) or Ed (0) */ 80*906943f9SDavid du Colombier Tddata1 = 1<<24, /* data toggle (1 == data1) */ 81*906943f9SDavid du Colombier Tddata0 = 0<<24, 82*906943f9SDavid du Colombier Tdfcmask = 7, /* frame count (iso) */ 83*906943f9SDavid du Colombier Tdfcshift = 24, 84*906943f9SDavid du Colombier Tdsfmask = 0xFFFF, /* starting frame (iso) */ 85*906943f9SDavid du Colombier Tderrmask = 3, /* error counter */ 86*906943f9SDavid du Colombier Tderrshift = 26, 87*906943f9SDavid du Colombier Tdccmask = 0xf, /* condition code (status) */ 88*906943f9SDavid du Colombier Tdccshift = 28, 89*906943f9SDavid du Colombier Tdiccmask = 0xf, /* condition code (iso, offsets) */ 90*906943f9SDavid du Colombier Tdiccshift = 12, 916a5dc222SDavid du Colombier 92*906943f9SDavid du Colombier Ntdframes = 0x10000, /* # of different iso frame numbers */ 936a5dc222SDavid du Colombier 94*906943f9SDavid du Colombier /* Td errors (condition code) */ 95*906943f9SDavid du Colombier Tdok = 0, 96*906943f9SDavid du Colombier Tdcrc = 1, 97*906943f9SDavid du Colombier Tdbitstuff = 2, 98*906943f9SDavid du Colombier Tdbadtog = 3, 99*906943f9SDavid du Colombier Tdstalled = 4, 100*906943f9SDavid du Colombier Tdtmout = 5, 101*906943f9SDavid du Colombier Tdpidchk = 6, 102*906943f9SDavid du Colombier Tdbadpid = 7, 103*906943f9SDavid du Colombier Tddataovr = 8, 104*906943f9SDavid du Colombier Tddataund = 9, 105*906943f9SDavid du Colombier Tdbufovr = 0xC, 106*906943f9SDavid du Colombier Tdbufund = 0xD, 107*906943f9SDavid du Colombier Tdnotacc = 0xE, 1086a5dc222SDavid du Colombier 109*906943f9SDavid du Colombier /* control register */ 110*906943f9SDavid du Colombier Cple = 0x04, /* periodic list enable */ 111*906943f9SDavid du Colombier Cie = 0x08, /* iso. list enable */ 112*906943f9SDavid du Colombier Ccle = 0x10, /* ctl list enable */ 113*906943f9SDavid du Colombier Cble = 0x20, /* bulk list enable */ 114*906943f9SDavid du Colombier Cfsmask = 3 << 6, /* functional state... */ 115*906943f9SDavid du Colombier Cfsreset = 0 << 6, 116*906943f9SDavid du Colombier Cfsresume = 1 << 6, 117*906943f9SDavid du Colombier Cfsoper = 2 << 6, 118*906943f9SDavid du Colombier Cfssuspend = 3 << 6, 1196a5dc222SDavid du Colombier 120*906943f9SDavid du Colombier /* command status */ 121*906943f9SDavid du Colombier Sblf = 1 << 2, /* bulk list (load) flag */ 122*906943f9SDavid du Colombier Sclf = 1 << 1, /* control list (load) flag */ 123*906943f9SDavid du Colombier Shcr = 1 << 0, /* host controller reset */ 1246a5dc222SDavid du Colombier 125*906943f9SDavid du Colombier /* intr enable */ 1266a5dc222SDavid du Colombier Mie = 1 << 31, 1276a5dc222SDavid du Colombier Oc = 1 << 30, 1286a5dc222SDavid du Colombier Rhsc = 1 << 6, 1296a5dc222SDavid du Colombier Fno = 1 << 5, 1306a5dc222SDavid du Colombier Ue = 1 << 4, 1316a5dc222SDavid du Colombier Rd = 1 << 3, 1326a5dc222SDavid du Colombier Sf = 1 << 2, 1336a5dc222SDavid du Colombier Wdh = 1 << 1, 1346a5dc222SDavid du Colombier So = 1 << 0, 135*906943f9SDavid du Colombier 136*906943f9SDavid du Colombier Fmaxpktmask = 0x7fff, 137*906943f9SDavid du Colombier Fmaxpktshift = 16, 1386a5dc222SDavid du Colombier HcRhDescA_POTPGT_MASK = 0xff << 24, 1396a5dc222SDavid du Colombier HcRhDescA_POTPGT_SHIFT = 24, 140*906943f9SDavid du Colombier 141*906943f9SDavid du Colombier /* Rh status */ 1426a5dc222SDavid du Colombier Lps = 1 << 0, 1436a5dc222SDavid du Colombier Cgp = 1 << 0, 1446a5dc222SDavid du Colombier Oci = 1 << 1, 145*906943f9SDavid du Colombier Psm = 1 << 8, 146*906943f9SDavid du Colombier Nps = 1 << 9, 1476a5dc222SDavid du Colombier Drwe = 1 << 15, 1486a5dc222SDavid du Colombier Srwe = 1 << 15, 149*906943f9SDavid du Colombier Lpsc = 1 << 16, 1506a5dc222SDavid du Colombier Sgp = 1 << 16, 1516a5dc222SDavid du Colombier Ccic = 1 << 17, 1526a5dc222SDavid du Colombier Crwe = 1 << 31, 1536a5dc222SDavid du Colombier 154*906943f9SDavid du Colombier /* port status */ 155*906943f9SDavid du Colombier Ccs = 0x00001, /* current connect status */ 156*906943f9SDavid du Colombier Pes = 0x00002, /* port enable status */ 157*906943f9SDavid du Colombier Pss = 0x00004, /* port suspend status */ 158*906943f9SDavid du Colombier Poci = 0x00008, /* over current indicator */ 159*906943f9SDavid du Colombier Prs = 0x00010, /* port reset status */ 160*906943f9SDavid du Colombier Pps = 0x00100, /* port power status */ 161*906943f9SDavid du Colombier Lsda = 0x00200, /* low speed device attached */ 162*906943f9SDavid du Colombier Csc = 0x10000, /* connect status change */ 163*906943f9SDavid du Colombier Pesc = 0x20000, /* enable status change */ 164*906943f9SDavid du Colombier Pssc = 0x40000, /* suspend status change */ 165*906943f9SDavid du Colombier Ocic = 0x80000, /* over current ind. change */ 166*906943f9SDavid du Colombier Prsc = 0x100000, /* reset status change */ 167*906943f9SDavid du Colombier 168*906943f9SDavid du Colombier /* port status write bits */ 169*906943f9SDavid du Colombier Cpe = 0x001, /* clear port enable */ 170*906943f9SDavid du Colombier Spe = 0x002, /* set port enable */ 171*906943f9SDavid du Colombier Spr = 0x010, /* set port reset */ 172*906943f9SDavid du Colombier Spp = 0x100, /* set port power */ 173*906943f9SDavid du Colombier Cpp = 0x200, /* clear port power */ 174*906943f9SDavid du Colombier 1756a5dc222SDavid du Colombier }; 1766a5dc222SDavid du Colombier 177*906943f9SDavid du Colombier /* 178*906943f9SDavid du Colombier * Endpoint descriptor. (first 4 words used by hardware) 179*906943f9SDavid du Colombier */ 180*906943f9SDavid du Colombier struct Ed { 181*906943f9SDavid du Colombier ulong ctrl; 182*906943f9SDavid du Colombier ulong tail; /* transfer descriptor */ 183*906943f9SDavid du Colombier ulong head; 184*906943f9SDavid du Colombier ulong nexted; 185*906943f9SDavid du Colombier 186*906943f9SDavid du Colombier Ed* next; /* sw; in free list or next in list */ 187*906943f9SDavid du Colombier Td* tds; /* in use by current xfer; all for iso */ 188*906943f9SDavid du Colombier Ep* ep; /* debug/align */ 189*906943f9SDavid du Colombier Ed* inext; /* debug/align (dump interrupt eds). */ 1906a5dc222SDavid du Colombier }; 1916a5dc222SDavid du Colombier 192*906943f9SDavid du Colombier /* 193*906943f9SDavid du Colombier * Endpoint I/O state (software), per direction. 194*906943f9SDavid du Colombier */ 195*906943f9SDavid du Colombier struct Qio 196*906943f9SDavid du Colombier { 197*906943f9SDavid du Colombier QLock; /* for the entire I/O process */ 198*906943f9SDavid du Colombier Rendez; /* wait for completion */ 199*906943f9SDavid du Colombier Ed* ed; /* to place Tds on it */ 200*906943f9SDavid du Colombier int sched; /* queue number (intr/iso) */ 201*906943f9SDavid du Colombier int toggle; /* Tddata0/Tddata1 */ 202*906943f9SDavid du Colombier ulong usbid; /* device/endpoint address */ 203*906943f9SDavid du Colombier int tok; /* Tdsetup, Tdtokin, Tdtokout */ 204*906943f9SDavid du Colombier long iotime; /* last I/O time; to hold interrupt polls */ 205*906943f9SDavid du Colombier int debug; /* for the endpoint */ 206*906943f9SDavid du Colombier char* err; /* error status */ 207*906943f9SDavid du Colombier int state; /* Qidle -> Qinstall -> Qrun -> Qdone | Qclose */ 208*906943f9SDavid du Colombier long bw; /* load (intr/iso) */ 2096a5dc222SDavid du Colombier }; 2106a5dc222SDavid du Colombier 211*906943f9SDavid du Colombier struct Ctlio 212*906943f9SDavid du Colombier { 213*906943f9SDavid du Colombier Qio; /* single Ed for all transfers */ 214*906943f9SDavid du Colombier uchar* data; /* read from last ctl req. */ 215*906943f9SDavid du Colombier int ndata; /* number of bytes read */ 2166a5dc222SDavid du Colombier 217*906943f9SDavid du Colombier }; 2186a5dc222SDavid du Colombier 219*906943f9SDavid du Colombier struct Isoio 220*906943f9SDavid du Colombier { 221*906943f9SDavid du Colombier Qio; 222*906943f9SDavid du Colombier int nframes; /* number of frames for a full second */ 223*906943f9SDavid du Colombier Td* atds; /* Tds avail for further I/O */ 224*906943f9SDavid du Colombier int navail; /* number of avail Tds */ 225*906943f9SDavid du Colombier ulong frno; /* next frame number avail for I/O */ 226*906943f9SDavid du Colombier ulong left; /* remainder after rounding Hz to samples/ms */ 227*906943f9SDavid du Colombier int nerrs; /* consecutive errors on iso I/O */ 228*906943f9SDavid du Colombier }; 229*906943f9SDavid du Colombier 230*906943f9SDavid du Colombier /* 231*906943f9SDavid du Colombier * Transfer descriptor. Size must be multiple of 32 232*906943f9SDavid du Colombier * First block is used by hardware (aligned to 32). 233*906943f9SDavid du Colombier */ 234*906943f9SDavid du Colombier struct Td 235*906943f9SDavid du Colombier { 236*906943f9SDavid du Colombier ulong ctrl; 237*906943f9SDavid du Colombier ulong cbp; 238*906943f9SDavid du Colombier ulong nexttd; 239*906943f9SDavid du Colombier ulong be; 240*906943f9SDavid du Colombier ushort offsets[8]; /* used by Iso Tds only */ 241*906943f9SDavid du Colombier 242*906943f9SDavid du Colombier Td* next; /* in free or Ed tds list */ 243*906943f9SDavid du Colombier Td* anext; /* in avail td list (iso) */ 244*906943f9SDavid du Colombier Ep* ep; /* using this Td for I/O */ 245*906943f9SDavid du Colombier Qio* io; /* using this Td for I/O */ 246*906943f9SDavid du Colombier Block* bp; /* data for this Td */ 247*906943f9SDavid du Colombier ulong nbytes; /* bytes in this Td */ 248*906943f9SDavid du Colombier ulong cbp0; /* initial value for cbp */ 249*906943f9SDavid du Colombier ulong last; /* true for last Td in Qio */ 250*906943f9SDavid du Colombier }; 251*906943f9SDavid du Colombier 252*906943f9SDavid du Colombier /* 253*906943f9SDavid du Colombier * Host controller communication area (hardware) 254*906943f9SDavid du Colombier */ 255*906943f9SDavid du Colombier struct Hcca 256*906943f9SDavid du Colombier { 257*906943f9SDavid du Colombier ulong intrtable[32]; 258*906943f9SDavid du Colombier ushort framenumber; 259*906943f9SDavid du Colombier ushort pad1; 260*906943f9SDavid du Colombier ulong donehead; 261*906943f9SDavid du Colombier uchar reserved[116]; 262*906943f9SDavid du Colombier }; 263*906943f9SDavid du Colombier 264*906943f9SDavid du Colombier /* 265*906943f9SDavid du Colombier * I/O registers 266*906943f9SDavid du Colombier */ 267*906943f9SDavid du Colombier struct Ohci 268*906943f9SDavid du Colombier { 269*906943f9SDavid du Colombier /* control and status group */ 270*906943f9SDavid du Colombier ulong revision; /*00*/ 271*906943f9SDavid du Colombier ulong control; /*04*/ 272*906943f9SDavid du Colombier ulong cmdsts; /*08*/ 273*906943f9SDavid du Colombier ulong intrsts; /*0c*/ 274*906943f9SDavid du Colombier ulong intrenable; /*10*/ 275*906943f9SDavid du Colombier ulong intrdisable; /*14*/ 276*906943f9SDavid du Colombier 277*906943f9SDavid du Colombier /* memory pointer group */ 278*906943f9SDavid du Colombier ulong hcca; /*18*/ 279*906943f9SDavid du Colombier ulong periodcurred; /*1c*/ 280*906943f9SDavid du Colombier ulong ctlheaded; /*20*/ 281*906943f9SDavid du Colombier ulong ctlcurred; /*24*/ 282*906943f9SDavid du Colombier ulong bulkheaded; /*28*/ 283*906943f9SDavid du Colombier ulong bulkcurred; /*2c*/ 284*906943f9SDavid du Colombier ulong donehead; /*30*/ 285*906943f9SDavid du Colombier 286*906943f9SDavid du Colombier /* frame counter group */ 287*906943f9SDavid du Colombier ulong fminterval; /*34*/ 288*906943f9SDavid du Colombier ulong fmremaining; /*38*/ 289*906943f9SDavid du Colombier ulong fmnumber; /*3c*/ 290*906943f9SDavid du Colombier ulong periodicstart; /*40*/ 291*906943f9SDavid du Colombier ulong lsthreshold; /*44*/ 292*906943f9SDavid du Colombier 293*906943f9SDavid du Colombier /* root hub group */ 294*906943f9SDavid du Colombier ulong rhdesca; /*48*/ 295*906943f9SDavid du Colombier ulong rhdescb; /*4c*/ 296*906943f9SDavid du Colombier ulong rhsts; /*50*/ 297*906943f9SDavid du Colombier ulong rhportsts[15]; /*54*/ 298*906943f9SDavid du Colombier ulong pad25[20]; /*90*/ 299*906943f9SDavid du Colombier 300*906943f9SDavid du Colombier /* unknown */ 301*906943f9SDavid du Colombier ulong hostueaddr; /*e0*/ 302*906943f9SDavid du Colombier ulong hostuests; /*e4*/ 303*906943f9SDavid du Colombier ulong hosttimeoutctrl; /*e8*/ 304*906943f9SDavid du Colombier ulong pad59; /*ec*/ 305*906943f9SDavid du Colombier ulong pad60; /*f0*/ 306*906943f9SDavid du Colombier ulong hostrevision; /*f4*/ 307*906943f9SDavid du Colombier ulong pad62[2]; 308*906943f9SDavid du Colombier /*100*/ 309*906943f9SDavid du Colombier }; 310*906943f9SDavid du Colombier 311*906943f9SDavid du Colombier /* 312*906943f9SDavid du Colombier * Endpoint tree (software) 313*906943f9SDavid du Colombier */ 314*906943f9SDavid du Colombier struct Qtree 315*906943f9SDavid du Colombier { 316*906943f9SDavid du Colombier int nel; 317*906943f9SDavid du Colombier int depth; 318*906943f9SDavid du Colombier ulong* bw; 319*906943f9SDavid du Colombier Ed** root; 320*906943f9SDavid du Colombier }; 321*906943f9SDavid du Colombier 322*906943f9SDavid du Colombier struct Tdpool 323*906943f9SDavid du Colombier { 324*906943f9SDavid du Colombier Lock; 325*906943f9SDavid du Colombier Td* free; 326*906943f9SDavid du Colombier int nalloc; 327*906943f9SDavid du Colombier int ninuse; 328*906943f9SDavid du Colombier int nfree; 329*906943f9SDavid du Colombier }; 330*906943f9SDavid du Colombier 331*906943f9SDavid du Colombier struct Edpool 332*906943f9SDavid du Colombier { 333*906943f9SDavid du Colombier Lock; 334*906943f9SDavid du Colombier Ed* free; 335*906943f9SDavid du Colombier int nalloc; 336*906943f9SDavid du Colombier int ninuse; 337*906943f9SDavid du Colombier int nfree; 338*906943f9SDavid du Colombier }; 339*906943f9SDavid du Colombier 340*906943f9SDavid du Colombier struct Ctlr 341*906943f9SDavid du Colombier { 342*906943f9SDavid du Colombier Lock; /* for ilock; lists and basic ctlr I/O */ 343*906943f9SDavid du Colombier QLock resetl; /* lock controller during USB reset */ 344*906943f9SDavid du Colombier Pcidev* pcidev; 345*906943f9SDavid du Colombier int active; 346*906943f9SDavid du Colombier Ctlr* next; 347*906943f9SDavid du Colombier int nports; 348*906943f9SDavid du Colombier 349*906943f9SDavid du Colombier Ohci* ohci; /* base I/O address */ 350*906943f9SDavid du Colombier Hcca* hcca; /* intr/done Td lists (used by hardware) */ 351*906943f9SDavid du Colombier int overrun; /* sched. overrun */ 352*906943f9SDavid du Colombier Ed* intrhd; /* list of intr. eds in tree */ 353*906943f9SDavid du Colombier Qtree* tree; /* tree for t Ep i/o */ 354*906943f9SDavid du Colombier int ntree; /* number of dummy Eds in tree */ 355*906943f9SDavid du Colombier }; 356*906943f9SDavid du Colombier 357*906943f9SDavid du Colombier #define dqprint if(debug || io && io->debug)print 358*906943f9SDavid du Colombier #define ddqprint if(debug>1 || (io && io->debug>1))print 359*906943f9SDavid du Colombier #define diprint if(debug || iso && iso->debug)print 360*906943f9SDavid du Colombier #define ddiprint if(debug>1 || (iso && iso->debug>1))print 361*906943f9SDavid du Colombier #define TRUNC(x, sz) ((x) & ((sz)-1)) 362*906943f9SDavid du Colombier 363*906943f9SDavid du Colombier static int ohciinterrupts[Nttypes]; 364*906943f9SDavid du Colombier static char* iosname[] = { "idle", "install", "run", "done", "close", "FREE" }; 365*906943f9SDavid du Colombier 366*906943f9SDavid du Colombier static int debug; 367*906943f9SDavid du Colombier static Edpool edpool; 368*906943f9SDavid du Colombier static Tdpool tdpool; 369*906943f9SDavid du Colombier static Ctlr* ctlrs[Nhcis]; 370*906943f9SDavid du Colombier 3716a5dc222SDavid du Colombier static char EnotWritten[] = "usb write unfinished"; 3726a5dc222SDavid du Colombier static char EnotRead[] = "usb read unfinished"; 3736a5dc222SDavid du Colombier static char Eunderrun[] = "usb endpoint underrun"; 3746a5dc222SDavid du Colombier 3756a5dc222SDavid du Colombier static QLock usbhstate; /* protects name space state */ 3766a5dc222SDavid du Colombier 377*906943f9SDavid du Colombier static int schedendpt(Ctlr *ub, Ep *ep); 378*906943f9SDavid du Colombier static void unschedendpt(Ctlr *ub, Ep *ep); 379*906943f9SDavid du Colombier static long qtd(Ctlr*, Ep*, int, Block*, uchar*, uchar*, int, ulong); 3806a5dc222SDavid du Colombier 381*906943f9SDavid du Colombier static char* errmsgs[] = 3826a5dc222SDavid du Colombier { 383*906943f9SDavid du Colombier [Tdcrc] "crc error", 384*906943f9SDavid du Colombier [Tdbitstuff] "bit stuffing error", 385*906943f9SDavid du Colombier [Tdbadtog] "bad toggle", 386*906943f9SDavid du Colombier [Tdstalled] Estalled, 387*906943f9SDavid du Colombier [Tdtmout] "timeout error", 388*906943f9SDavid du Colombier [Tdpidchk] "pid check error", 389*906943f9SDavid du Colombier [Tdbadpid] "bad pid", 390*906943f9SDavid du Colombier [Tddataovr] "data overrun", 391*906943f9SDavid du Colombier [Tddataund] "data underrun", 392*906943f9SDavid du Colombier [Tdbufovr] "buffer overrun", 393*906943f9SDavid du Colombier [Tdbufund] "buffer underrun", 394*906943f9SDavid du Colombier [Tdnotacc] "not accessed" 395*906943f9SDavid du Colombier }; 3966a5dc222SDavid du Colombier 397*906943f9SDavid du Colombier static void* 398*906943f9SDavid du Colombier pa2ptr(ulong pa) 3996a5dc222SDavid du Colombier { 400*906943f9SDavid du Colombier if(pa == 0) 4016a5dc222SDavid du Colombier return nil; 4026a5dc222SDavid du Colombier else 403*906943f9SDavid du Colombier return KADDR(pa); 4046a5dc222SDavid du Colombier } 4056a5dc222SDavid du Colombier 406*906943f9SDavid du Colombier static ulong 407*906943f9SDavid du Colombier ptr2pa(void *p) 4086a5dc222SDavid du Colombier { 409*906943f9SDavid du Colombier if(p == nil) 410*906943f9SDavid du Colombier return 0; 411*906943f9SDavid du Colombier else 412*906943f9SDavid du Colombier return PADDR(p); 4136a5dc222SDavid du Colombier } 4146a5dc222SDavid du Colombier 4156a5dc222SDavid du Colombier static void 4166a5dc222SDavid du Colombier waitSOF(Ctlr *ub) 4176a5dc222SDavid du Colombier { 418*906943f9SDavid du Colombier int frame = ub->hcca->framenumber & 0x3f; 4196a5dc222SDavid du Colombier 4206a5dc222SDavid du Colombier do { 4216a5dc222SDavid du Colombier delay(2); 422*906943f9SDavid du Colombier } while(frame == (ub->hcca->framenumber & 0x3f)); 423*906943f9SDavid du Colombier } 424*906943f9SDavid du Colombier 425*906943f9SDavid du Colombier static char* 426*906943f9SDavid du Colombier errmsg(int err) 427*906943f9SDavid du Colombier { 428*906943f9SDavid du Colombier 429*906943f9SDavid du Colombier if(err < nelem(errmsgs)) 430*906943f9SDavid du Colombier return errmsgs[err]; 431*906943f9SDavid du Colombier return nil; 432*906943f9SDavid du Colombier } 433*906943f9SDavid du Colombier 434*906943f9SDavid du Colombier static Ed* 435*906943f9SDavid du Colombier ctlhd(Ctlr *ctlr) 436*906943f9SDavid du Colombier { 437*906943f9SDavid du Colombier return pa2ptr(ctlr->ohci->ctlheaded); 438*906943f9SDavid du Colombier } 439*906943f9SDavid du Colombier 440*906943f9SDavid du Colombier static Ed* 441*906943f9SDavid du Colombier bulkhd(Ctlr *ctlr) 442*906943f9SDavid du Colombier { 443*906943f9SDavid du Colombier return pa2ptr(ctlr->ohci->bulkheaded); 4446a5dc222SDavid du Colombier } 4456a5dc222SDavid du Colombier 4466a5dc222SDavid du Colombier static void 447*906943f9SDavid du Colombier edlinked(Ed *ed, Ed *next) 4486a5dc222SDavid du Colombier { 4496a5dc222SDavid du Colombier if(ed == nil) 450*906943f9SDavid du Colombier print("edlinked: nil ed: pc %#p\n", getcallerpc(&ed)); 451*906943f9SDavid du Colombier ed->nexted = ptr2pa(next); 452*906943f9SDavid du Colombier ed->next = next; 4536a5dc222SDavid du Colombier } 4546a5dc222SDavid du Colombier 4556a5dc222SDavid du Colombier static void 456*906943f9SDavid du Colombier setctlhd(Ctlr *ctlr, Ed *ed) 4576a5dc222SDavid du Colombier { 458*906943f9SDavid du Colombier ctlr->ohci->ctlheaded = ptr2pa(ed); 459*906943f9SDavid du Colombier if(ed != nil) 460*906943f9SDavid du Colombier ctlr->ohci->cmdsts |= Sclf; /* reload it on next pass */ 4616a5dc222SDavid du Colombier } 4626a5dc222SDavid du Colombier 4636a5dc222SDavid du Colombier static void 464*906943f9SDavid du Colombier setbulkhd(Ctlr *ctlr, Ed *ed) 4656a5dc222SDavid du Colombier { 466*906943f9SDavid du Colombier ctlr->ohci->bulkheaded = ptr2pa(ed); 467*906943f9SDavid du Colombier if(ed != nil) 468*906943f9SDavid du Colombier ctlr->ohci->cmdsts |= Sblf; /* reload it on next pass */ 469*906943f9SDavid du Colombier } 4706a5dc222SDavid du Colombier 471*906943f9SDavid du Colombier static void 472*906943f9SDavid du Colombier unlinkctl(Ctlr *ctlr, Ed *ed) 473*906943f9SDavid du Colombier { 474*906943f9SDavid du Colombier Ed *this, *prev, *next; 475*906943f9SDavid du Colombier 476*906943f9SDavid du Colombier ctlr->ohci->control &= ~Ccle; 477*906943f9SDavid du Colombier waitSOF(ctlr); 478*906943f9SDavid du Colombier this = ctlhd(ctlr); 479*906943f9SDavid du Colombier ctlr->ohci->ctlcurred = 0; 4806a5dc222SDavid du Colombier prev = nil; 4816a5dc222SDavid du Colombier while(this != nil && this != ed){ 4826a5dc222SDavid du Colombier prev = this; 483*906943f9SDavid du Colombier this = this->next; 4846a5dc222SDavid du Colombier } 4856a5dc222SDavid du Colombier if(this == nil){ 486*906943f9SDavid du Colombier print("unlinkctl: not found\n"); 4876a5dc222SDavid du Colombier return; 4886a5dc222SDavid du Colombier } 489*906943f9SDavid du Colombier next = this->next; 4906a5dc222SDavid du Colombier if(prev == nil) 491*906943f9SDavid du Colombier setctlhd(ctlr, next); 4926a5dc222SDavid du Colombier else 493*906943f9SDavid du Colombier edlinked(prev, next); 494*906943f9SDavid du Colombier ctlr->ohci->control |= Ccle; 495*906943f9SDavid du Colombier edlinked(ed, nil); /* wipe out next field */ 496*906943f9SDavid du Colombier 4976a5dc222SDavid du Colombier } 4986a5dc222SDavid du Colombier 4996a5dc222SDavid du Colombier static void 500*906943f9SDavid du Colombier unlinkbulk(Ctlr *ctlr, Ed *ed) 5016a5dc222SDavid du Colombier { 502*906943f9SDavid du Colombier Ed *this, *prev, *next; 5036a5dc222SDavid du Colombier 504*906943f9SDavid du Colombier ctlr->ohci->control &= ~Cble; 505*906943f9SDavid du Colombier waitSOF(ctlr); 506*906943f9SDavid du Colombier this = bulkhd(ctlr); 507*906943f9SDavid du Colombier ctlr->ohci->bulkcurred = 0; 5086a5dc222SDavid du Colombier prev = nil; 5096a5dc222SDavid du Colombier while(this != nil && this != ed){ 5106a5dc222SDavid du Colombier prev = this; 511*906943f9SDavid du Colombier this = this->next; 5126a5dc222SDavid du Colombier } 513*906943f9SDavid du Colombier if(this == nil){ 514*906943f9SDavid du Colombier print("unlinkbulk: not found\n"); 515*906943f9SDavid du Colombier return; 516*906943f9SDavid du Colombier } 517*906943f9SDavid du Colombier next = this->next; 5186a5dc222SDavid du Colombier if(prev == nil) 519*906943f9SDavid du Colombier setbulkhd(ctlr, next); 5206a5dc222SDavid du Colombier else 521*906943f9SDavid du Colombier edlinked(prev, next); 522*906943f9SDavid du Colombier ctlr->ohci->control |= Cble; 523*906943f9SDavid du Colombier edlinked(ed, nil); /* wipe out next field */ 5246a5dc222SDavid du Colombier } 5256a5dc222SDavid du Colombier 5266a5dc222SDavid du Colombier static void 527*906943f9SDavid du Colombier edsetaddr(Ed *ed, ulong addr) 5286a5dc222SDavid du Colombier { 5296a5dc222SDavid du Colombier ulong ctrl; 5306a5dc222SDavid du Colombier 531*906943f9SDavid du Colombier ctrl = ed->ctrl & ~((Epmax<<7)|Devmax); 532*906943f9SDavid du Colombier ctrl |= (addr & ((Epmax<<7)|Devmax)); 533*906943f9SDavid du Colombier ed->ctrl = ctrl; 5346a5dc222SDavid du Colombier } 5356a5dc222SDavid du Colombier 5366a5dc222SDavid du Colombier static void 537*906943f9SDavid du Colombier edsettog(Ed *ed, int c) 5386a5dc222SDavid du Colombier { 539*906943f9SDavid du Colombier if(c != 0) 540*906943f9SDavid du Colombier ed->head |= Edtoggle; 541*906943f9SDavid du Colombier else 542*906943f9SDavid du Colombier ed->head &= ~Edtoggle; 5436a5dc222SDavid du Colombier } 544*906943f9SDavid du Colombier 545*906943f9SDavid du Colombier static int 546*906943f9SDavid du Colombier edtoggle(Ed *ed) 547*906943f9SDavid du Colombier { 548*906943f9SDavid du Colombier return ed->head & Edtoggle; 549*906943f9SDavid du Colombier } 550*906943f9SDavid du Colombier 551*906943f9SDavid du Colombier static int 552*906943f9SDavid du Colombier edhalted(Ed *ed) 553*906943f9SDavid du Colombier { 554*906943f9SDavid du Colombier return ed->head & Edhalt; 555*906943f9SDavid du Colombier } 556*906943f9SDavid du Colombier 557*906943f9SDavid du Colombier static int 558*906943f9SDavid du Colombier edmaxpkt(Ed *ed) 559*906943f9SDavid du Colombier { 560*906943f9SDavid du Colombier return (ed->ctrl >> Edmpsshift) & Edmpsmask; 5616a5dc222SDavid du Colombier } 5626a5dc222SDavid du Colombier 5636a5dc222SDavid du Colombier static void 564*906943f9SDavid du Colombier edsetmaxpkt(Ed *ed, int m) 5656a5dc222SDavid du Colombier { 566*906943f9SDavid du Colombier ulong c; 5676a5dc222SDavid du Colombier 568*906943f9SDavid du Colombier c = ed->ctrl & ~(Edmpsmask << Edmpsshift); 569*906943f9SDavid du Colombier ed->ctrl = c | ((m&Edmpsmask) << Edmpsshift); 5706a5dc222SDavid du Colombier } 571*906943f9SDavid du Colombier 572*906943f9SDavid du Colombier static int 573*906943f9SDavid du Colombier tderrs(Td *td) 574*906943f9SDavid du Colombier { 575*906943f9SDavid du Colombier return (td->ctrl >> Tdccshift) & Tdccmask; 576*906943f9SDavid du Colombier } 577*906943f9SDavid du Colombier 578*906943f9SDavid du Colombier static int 579*906943f9SDavid du Colombier tdtok(Td *td) 580*906943f9SDavid du Colombier { 581*906943f9SDavid du Colombier return (td->ctrl & Tdtokmask); 582*906943f9SDavid du Colombier } 583*906943f9SDavid du Colombier 584*906943f9SDavid du Colombier static Td* 585*906943f9SDavid du Colombier tdalloc(void) 586*906943f9SDavid du Colombier { 587*906943f9SDavid du Colombier Td *td; 588*906943f9SDavid du Colombier Td *pool; 589*906943f9SDavid du Colombier int i; 590*906943f9SDavid du Colombier 591*906943f9SDavid du Colombier lock(&tdpool); 592*906943f9SDavid du Colombier if(tdpool.free == nil){ 593*906943f9SDavid du Colombier ddprint("ohci: tdalloc %d Tds\n", Incr); 594*906943f9SDavid du Colombier pool = xspanalloc(Incr*sizeof(Td), Align, 0); 595*906943f9SDavid du Colombier if(pool == nil) 596*906943f9SDavid du Colombier panic("tdalloc"); 597*906943f9SDavid du Colombier for(i=Incr; --i>=0;){ 598*906943f9SDavid du Colombier pool[i].next = tdpool.free; 599*906943f9SDavid du Colombier tdpool.free = &pool[i]; 600*906943f9SDavid du Colombier } 601*906943f9SDavid du Colombier tdpool.nalloc += Incr; 602*906943f9SDavid du Colombier tdpool.nfree += Incr; 603*906943f9SDavid du Colombier } 604*906943f9SDavid du Colombier tdpool.ninuse++; 605*906943f9SDavid du Colombier tdpool.nfree--; 606*906943f9SDavid du Colombier td = tdpool.free; 607*906943f9SDavid du Colombier tdpool.free = td->next; 608*906943f9SDavid du Colombier memset(td, 0, sizeof(Td)); 609*906943f9SDavid du Colombier unlock(&tdpool); 610*906943f9SDavid du Colombier 611*906943f9SDavid du Colombier assert(((uintptr)td & 0xF) == 0); 612*906943f9SDavid du Colombier return td; 613*906943f9SDavid du Colombier } 614*906943f9SDavid du Colombier 615*906943f9SDavid du Colombier static void 616*906943f9SDavid du Colombier tdfree(Td *td) 617*906943f9SDavid du Colombier { 618*906943f9SDavid du Colombier if(td == 0) 619*906943f9SDavid du Colombier return; 620*906943f9SDavid du Colombier freeb(td->bp); 621*906943f9SDavid du Colombier td->bp = nil; 622*906943f9SDavid du Colombier lock(&tdpool); 623*906943f9SDavid du Colombier if(td->nexttd == 0x77777777) 624*906943f9SDavid du Colombier panic("ohci: tdfree: double free"); 625*906943f9SDavid du Colombier memset(td, 7, sizeof(Td)); /* poison */ 626*906943f9SDavid du Colombier td->next = tdpool.free; 627*906943f9SDavid du Colombier tdpool.free = td; 628*906943f9SDavid du Colombier tdpool.ninuse--; 629*906943f9SDavid du Colombier tdpool.nfree++; 630*906943f9SDavid du Colombier unlock(&tdpool); 631*906943f9SDavid du Colombier } 632*906943f9SDavid du Colombier 633*906943f9SDavid du Colombier static Ed* 634*906943f9SDavid du Colombier edalloc(void) 635*906943f9SDavid du Colombier { 636*906943f9SDavid du Colombier Ed *ed; 637*906943f9SDavid du Colombier Ed *pool; 638*906943f9SDavid du Colombier int i; 639*906943f9SDavid du Colombier 640*906943f9SDavid du Colombier lock(&edpool); 641*906943f9SDavid du Colombier if(edpool.free == nil){ 642*906943f9SDavid du Colombier ddprint("ohci: edalloc %d Eds\n", Incr); 643*906943f9SDavid du Colombier pool = xspanalloc(Incr*sizeof(Ed), Align, 0); 644*906943f9SDavid du Colombier if(pool == nil) 645*906943f9SDavid du Colombier panic("edalloc"); 646*906943f9SDavid du Colombier for(i=Incr; --i>=0;){ 647*906943f9SDavid du Colombier pool[i].next = edpool.free; 648*906943f9SDavid du Colombier edpool.free = &pool[i]; 649*906943f9SDavid du Colombier } 650*906943f9SDavid du Colombier edpool.nalloc += Incr; 651*906943f9SDavid du Colombier edpool.nfree += Incr; 652*906943f9SDavid du Colombier } 653*906943f9SDavid du Colombier edpool.ninuse++; 654*906943f9SDavid du Colombier edpool.nfree--; 655*906943f9SDavid du Colombier ed = edpool.free; 656*906943f9SDavid du Colombier edpool.free = ed->next; 657*906943f9SDavid du Colombier memset(ed, 0, sizeof(Ed)); 658*906943f9SDavid du Colombier unlock(&edpool); 659*906943f9SDavid du Colombier 660*906943f9SDavid du Colombier return ed; 661*906943f9SDavid du Colombier } 662*906943f9SDavid du Colombier 663*906943f9SDavid du Colombier static void 664*906943f9SDavid du Colombier edfree(Ed *ed) 665*906943f9SDavid du Colombier { 666*906943f9SDavid du Colombier Td *td, *next; 667*906943f9SDavid du Colombier int i; 668*906943f9SDavid du Colombier 669*906943f9SDavid du Colombier if(ed == 0) 670*906943f9SDavid du Colombier return; 671*906943f9SDavid du Colombier i = 0; 672*906943f9SDavid du Colombier for(td = ed->tds; td != nil; td = next){ 673*906943f9SDavid du Colombier next = td->next; 674*906943f9SDavid du Colombier tdfree(td); 675*906943f9SDavid du Colombier if(i++ > 2000){ 676*906943f9SDavid du Colombier print("ohci: bug: ed with more than 2000 tds\n"); 677*906943f9SDavid du Colombier break; 678*906943f9SDavid du Colombier } 679*906943f9SDavid du Colombier } 680*906943f9SDavid du Colombier lock(&edpool); 681*906943f9SDavid du Colombier if(ed->nexted == 0x99999999) 682*906943f9SDavid du Colombier panic("ohci: edfree: double free"); 683*906943f9SDavid du Colombier memset(ed, 9, sizeof(Ed)); /* poison */ 684*906943f9SDavid du Colombier ed->next = edpool.free; 685*906943f9SDavid du Colombier edpool.free = ed; 686*906943f9SDavid du Colombier edpool.ninuse--; 687*906943f9SDavid du Colombier edpool.nfree++; 688*906943f9SDavid du Colombier unlock(&edpool); 689*906943f9SDavid du Colombier ddprint("edfree: ed %#p\n", ed); 6906a5dc222SDavid du Colombier } 6916a5dc222SDavid du Colombier 6926a5dc222SDavid du Colombier /* 6936a5dc222SDavid du Colombier * return smallest power of 2 >= n 6946a5dc222SDavid du Colombier */ 6956a5dc222SDavid du Colombier static int 6966a5dc222SDavid du Colombier flog2(int n) 6976a5dc222SDavid du Colombier { 6986a5dc222SDavid du Colombier int i; 6996a5dc222SDavid du Colombier 7006a5dc222SDavid du Colombier for(i = 0; (1 << i) < n; i++) 7016a5dc222SDavid du Colombier ; 7026a5dc222SDavid du Colombier return i; 7036a5dc222SDavid du Colombier } 7046a5dc222SDavid du Colombier 7056a5dc222SDavid du Colombier /* 7066a5dc222SDavid du Colombier * return smallest power of 2 <= n 7076a5dc222SDavid du Colombier */ 7086a5dc222SDavid du Colombier static int 7096a5dc222SDavid du Colombier flog2lower(int n) 7106a5dc222SDavid du Colombier { 7116a5dc222SDavid du Colombier int i; 7126a5dc222SDavid du Colombier 7136a5dc222SDavid du Colombier for(i = 0; (1 << (i + 1)) <= n; i++) 7146a5dc222SDavid du Colombier ; 7156a5dc222SDavid du Colombier return i; 7166a5dc222SDavid du Colombier } 7176a5dc222SDavid du Colombier 7186a5dc222SDavid du Colombier static int 719*906943f9SDavid du Colombier pickschedq(Qtree *qt, int pollival, ulong bw, ulong limit) 7206a5dc222SDavid du Colombier { 7216a5dc222SDavid du Colombier int i, j, d, upperb, q; 7226a5dc222SDavid du Colombier ulong best, worst, total; 7236a5dc222SDavid du Colombier 724*906943f9SDavid du Colombier d = flog2lower(pollival); 7256a5dc222SDavid du Colombier if(d > qt->depth) 7266a5dc222SDavid du Colombier d = qt->depth; 7276a5dc222SDavid du Colombier q = -1; 7286a5dc222SDavid du Colombier worst = 0; 7296a5dc222SDavid du Colombier best = ~0; 7306a5dc222SDavid du Colombier upperb = (1 << (d+1)) - 1; 7316a5dc222SDavid du Colombier for(i = (1 << d) - 1; i < upperb; i++){ 7326a5dc222SDavid du Colombier total = qt->bw[0]; 7336a5dc222SDavid du Colombier for(j = i; j > 0; j = (j - 1) / 2) 7346a5dc222SDavid du Colombier total += qt->bw[j]; 7356a5dc222SDavid du Colombier if(total < best){ 7366a5dc222SDavid du Colombier best = total; 7376a5dc222SDavid du Colombier q = i; 7386a5dc222SDavid du Colombier } 7396a5dc222SDavid du Colombier if(total > worst) 7406a5dc222SDavid du Colombier worst = total; 7416a5dc222SDavid du Colombier } 7426a5dc222SDavid du Colombier if(worst + bw >= limit) 7436a5dc222SDavid du Colombier return -1; 7446a5dc222SDavid du Colombier return q; 7456a5dc222SDavid du Colombier } 7466a5dc222SDavid du Colombier 7476a5dc222SDavid du Colombier static int 748*906943f9SDavid du Colombier schedq(Ctlr *ctlr, Qio *io, int pollival) 7496a5dc222SDavid du Colombier { 7506a5dc222SDavid du Colombier int q; 751*906943f9SDavid du Colombier Ed *ted; 7526a5dc222SDavid du Colombier 753*906943f9SDavid du Colombier q = pickschedq(ctlr->tree, pollival, io->bw, ~0); 754*906943f9SDavid du Colombier ddqprint("ohci: sched %#p q %d, ival %d, bw %ld\n", io, q, pollival, io->bw); 7556a5dc222SDavid du Colombier if(q < 0){ 756*906943f9SDavid du Colombier print("ohci: no room for ed\n"); 7576a5dc222SDavid du Colombier return -1; 7586a5dc222SDavid du Colombier } 759*906943f9SDavid du Colombier ctlr->tree->bw[q] += io->bw; 760*906943f9SDavid du Colombier ted = ctlr->tree->root[q]; 761*906943f9SDavid du Colombier io->sched = q; 762*906943f9SDavid du Colombier edlinked(io->ed, ted->next); 763*906943f9SDavid du Colombier edlinked(ted, io->ed); 764*906943f9SDavid du Colombier io->ed->inext = ctlr->intrhd; 765*906943f9SDavid du Colombier ctlr->intrhd = io->ed; 7666a5dc222SDavid du Colombier return 0; 7676a5dc222SDavid du Colombier } 7686a5dc222SDavid du Colombier 7696a5dc222SDavid du Colombier static void 770*906943f9SDavid du Colombier unschedq(Ctlr *ctlr, Qio *qio) 7716a5dc222SDavid du Colombier { 7726a5dc222SDavid du Colombier int q; 773*906943f9SDavid du Colombier Ed *prev, *this, *next; 774*906943f9SDavid du Colombier Ed **l; 7756a5dc222SDavid du Colombier 776*906943f9SDavid du Colombier q = qio->sched; 777*906943f9SDavid du Colombier if(q < 0) 7786a5dc222SDavid du Colombier return; 779*906943f9SDavid du Colombier ctlr->tree->bw[q] -= qio->bw; 7806a5dc222SDavid du Colombier 781*906943f9SDavid du Colombier prev = ctlr->tree->root[q]; 782*906943f9SDavid du Colombier this = prev->next; 783*906943f9SDavid du Colombier while(this != nil && this != qio->ed){ 7846a5dc222SDavid du Colombier prev = this; 785*906943f9SDavid du Colombier this = this->next; 7866a5dc222SDavid du Colombier } 7876a5dc222SDavid du Colombier if(this == nil) 788*906943f9SDavid du Colombier print("ohci: unschedq %d: not found\n", q); 7896a5dc222SDavid du Colombier else{ 790*906943f9SDavid du Colombier next = this->next; 791*906943f9SDavid du Colombier edlinked(prev, next); 7926a5dc222SDavid du Colombier } 793*906943f9SDavid du Colombier waitSOF(ctlr); 794*906943f9SDavid du Colombier for(l = &ctlr->intrhd; *l != nil; l = &(*l)->inext) 795*906943f9SDavid du Colombier if(*l == qio->ed){ 796*906943f9SDavid du Colombier *l = (*l)->inext; 797*906943f9SDavid du Colombier return; 798*906943f9SDavid du Colombier } 799*906943f9SDavid du Colombier print("ohci: unschedq: ed %#p not found\n", qio->ed); 8006a5dc222SDavid du Colombier } 8016a5dc222SDavid du Colombier 802*906943f9SDavid du Colombier static char* 803*906943f9SDavid du Colombier seprinttdtok(char *s, char *e, int tok) 8046a5dc222SDavid du Colombier { 805*906943f9SDavid du Colombier switch(tok){ 806*906943f9SDavid du Colombier case Tdtoksetup: 807*906943f9SDavid du Colombier s = seprint(s, e, " setup"); 808*906943f9SDavid du Colombier break; 809*906943f9SDavid du Colombier case Tdtokin: 810*906943f9SDavid du Colombier s = seprint(s, e, " in"); 811*906943f9SDavid du Colombier break; 812*906943f9SDavid du Colombier case Tdtokout: 813*906943f9SDavid du Colombier s = seprint(s, e, " out"); 814*906943f9SDavid du Colombier break; 8156a5dc222SDavid du Colombier } 816*906943f9SDavid du Colombier return s; 817*906943f9SDavid du Colombier } 818*906943f9SDavid du Colombier 819*906943f9SDavid du Colombier 820*906943f9SDavid du Colombier static char* 821*906943f9SDavid du Colombier seprinttd(char *s, char *e, Td *td, int iso) 822*906943f9SDavid du Colombier { 823*906943f9SDavid du Colombier int i; 824*906943f9SDavid du Colombier Block *bp; 825*906943f9SDavid du Colombier 826*906943f9SDavid du Colombier if(td == nil) 827*906943f9SDavid du Colombier return seprint(s, e, "<nil td>\n"); 828*906943f9SDavid du Colombier s = seprint(s, e, "%#p ep %#p ctrl %#p", td, td->ep, td->ctrl); 829*906943f9SDavid du Colombier s = seprint(s, e, " cc=%#ulx", (td->ctrl >> Tdccshift) & Tdccmask); 830*906943f9SDavid du Colombier if(iso == 0){ 831*906943f9SDavid du Colombier if((td->ctrl & Tdround) != 0) 832*906943f9SDavid du Colombier s = seprint(s, e, " rnd"); 833*906943f9SDavid du Colombier s = seprinttdtok(s, e, td->ctrl & Tdtokmask); 834*906943f9SDavid du Colombier if((td->ctrl & Tdusetog) != 0) 835*906943f9SDavid du Colombier s = seprint(s, e, " d%d", (td->ctrl & Tddata1) ? 1 : 0); 836*906943f9SDavid du Colombier else 837*906943f9SDavid du Colombier s = seprint(s, e, " d-"); 838*906943f9SDavid du Colombier s = seprint(s, e, " ec=%uld", (td->ctrl >> Tderrshift) & Tderrmask); 839*906943f9SDavid du Colombier }else{ 840*906943f9SDavid du Colombier s = seprint(s, e, " fc=%uld", (td->ctrl >> Tdfcshift) & Tdfcmask); 841*906943f9SDavid du Colombier s = seprint(s, e, " sf=%uld", td->ctrl & Tdsfmask); 842*906943f9SDavid du Colombier } 843*906943f9SDavid du Colombier s = seprint(s, e, " cbp0 %#p cbp %#p next %#p be %#p %s", 844*906943f9SDavid du Colombier td->cbp0, td->cbp, td->nexttd, td->be, td->last ? "last" : ""); 845*906943f9SDavid du Colombier s = seprint(s, e, "\n\t\t%ld bytes", td->nbytes); 846*906943f9SDavid du Colombier if((bp = td->bp) != nil){ 847*906943f9SDavid du Colombier s = seprint(s, e, " rp %#p wp %#p ", bp->rp, bp->wp); 848*906943f9SDavid du Colombier if(BLEN(bp) > 0) 849*906943f9SDavid du Colombier s = seprintdata(s, e, bp->rp, bp->wp - bp->rp); 850*906943f9SDavid du Colombier } 851*906943f9SDavid du Colombier if(iso == 0) 852*906943f9SDavid du Colombier return seprint(s, e, "\n"); 853*906943f9SDavid du Colombier s = seprint(s, e, "\n\t\t"); 854*906943f9SDavid du Colombier /* we use only offsets[0] */ 855*906943f9SDavid du Colombier i = 0; 856*906943f9SDavid du Colombier s = seprint(s, e, "[%d] %#ux cc=%#ux sz=%ud\n", i, td->offsets[i], 857*906943f9SDavid du Colombier (td->offsets[i] >> Tdiccshift) & Tdiccmask, 858*906943f9SDavid du Colombier td->offsets[i] & 0x7FF); 859*906943f9SDavid du Colombier return s; 860*906943f9SDavid du Colombier } 861*906943f9SDavid du Colombier 862*906943f9SDavid du Colombier static void 863*906943f9SDavid du Colombier dumptd(Td *td, char *p, int iso) 864*906943f9SDavid du Colombier { 865*906943f9SDavid du Colombier static char buf[512]; /* Too much */ 866*906943f9SDavid du Colombier char *s; 867*906943f9SDavid du Colombier 868*906943f9SDavid du Colombier s = seprint(buf, buf+sizeof(buf), "%s: ", p); 869*906943f9SDavid du Colombier s = seprinttd(s, buf+sizeof(buf), td, iso); 870*906943f9SDavid du Colombier if(s > buf && s[-1] != '\n') 871*906943f9SDavid du Colombier s[-1] = '\n'; 872*906943f9SDavid du Colombier print("\t%s", buf); 873*906943f9SDavid du Colombier } 874*906943f9SDavid du Colombier 875*906943f9SDavid du Colombier static void 876*906943f9SDavid du Colombier dumptds(Td *td, char *p, int iso) 877*906943f9SDavid du Colombier { 878*906943f9SDavid du Colombier int i; 879*906943f9SDavid du Colombier 880*906943f9SDavid du Colombier for(i = 0; td != nil; td = td->next){ 881*906943f9SDavid du Colombier dumptd(td, p, iso); 882*906943f9SDavid du Colombier if(td->last) 883*906943f9SDavid du Colombier break; 884*906943f9SDavid du Colombier if(tdtok(td) == Tdtokin && ++i > 2){ 885*906943f9SDavid du Colombier print("\t\t...\n"); 886*906943f9SDavid du Colombier break; 887*906943f9SDavid du Colombier } 888*906943f9SDavid du Colombier } 889*906943f9SDavid du Colombier } 890*906943f9SDavid du Colombier 891*906943f9SDavid du Colombier static void 892*906943f9SDavid du Colombier dumped(Ed *ed) 893*906943f9SDavid du Colombier { 894*906943f9SDavid du Colombier char *buf; 895*906943f9SDavid du Colombier char *s; 896*906943f9SDavid du Colombier char *e; 897*906943f9SDavid du Colombier 898*906943f9SDavid du Colombier if(ed == nil){ 899*906943f9SDavid du Colombier print("<null ed>\n"); 900*906943f9SDavid du Colombier return; 901*906943f9SDavid du Colombier } 902*906943f9SDavid du Colombier buf = malloc(512); 903*906943f9SDavid du Colombier /* no waserror; may want to use from interrupt context */ 904*906943f9SDavid du Colombier if(buf == nil) 905*906943f9SDavid du Colombier return; 906*906943f9SDavid du Colombier e = buf+512; 907*906943f9SDavid du Colombier s = seprint(buf, e, "\ted %#p: ctrl %#p", ed, ed->ctrl); 908*906943f9SDavid du Colombier if((ed->ctrl & Edskip) != 0) 909*906943f9SDavid du Colombier s = seprint(s, e, " skip"); 910*906943f9SDavid du Colombier if((ed->ctrl & Ediso) != 0) 911*906943f9SDavid du Colombier s = seprint(s, e, " iso"); 912*906943f9SDavid du Colombier if((ed->ctrl & Edlow) != 0) 913*906943f9SDavid du Colombier s = seprint(s, e, " low"); 914*906943f9SDavid du Colombier s = seprint(s, e, " d%d", (ed->head & Edtoggle) ? 1 : 0); 915*906943f9SDavid du Colombier if((ed->ctrl & Eddirmask) == Edin) 916*906943f9SDavid du Colombier s = seprint(s, e, " in"); 917*906943f9SDavid du Colombier if((ed->ctrl & Eddirmask) == Edout) 918*906943f9SDavid du Colombier s = seprint(s, e, " out"); 919*906943f9SDavid du Colombier if(edhalted(ed)) 920*906943f9SDavid du Colombier s = seprint(s, e, " hlt"); 921*906943f9SDavid du Colombier s = seprint(s, e, " ep%uld.%uld", (ed->ctrl>>7)&Epmax, ed->ctrl&0x7f); 922*906943f9SDavid du Colombier s = seprint(s, e, " maxpkt %uld", (ed->ctrl>>Edmpsshift)&Edmpsmask); 923*906943f9SDavid du Colombier seprint(s, e, " tail %#p head %#p next %#p\n",ed->tail,ed->head,ed->nexted); 924*906943f9SDavid du Colombier print("%s", buf); 925*906943f9SDavid du Colombier free(buf); 926*906943f9SDavid du Colombier if(ed->tds != nil && (ed->ctrl & Ediso) == 0) 927*906943f9SDavid du Colombier dumptds(ed->tds, "td", 0); 928*906943f9SDavid du Colombier } 929*906943f9SDavid du Colombier 930*906943f9SDavid du Colombier static char* 931*906943f9SDavid du Colombier seprintio(char *s, char *e, Qio *io, char *pref) 932*906943f9SDavid du Colombier { 933*906943f9SDavid du Colombier s = seprint(s, e, "%s qio %#p ed %#p", pref, io, io->ed); 934*906943f9SDavid du Colombier s = seprint(s, e, " tog %d iot %ld err %s id %#ulx", 935*906943f9SDavid du Colombier io->toggle, io->iotime, io->err, io->usbid); 936*906943f9SDavid du Colombier s = seprinttdtok(s, e, io->tok); 937*906943f9SDavid du Colombier s = seprint(s, e, " %s\n", iosname[io->state]); 938*906943f9SDavid du Colombier return s; 939*906943f9SDavid du Colombier } 940*906943f9SDavid du Colombier 941*906943f9SDavid du Colombier static char* 942*906943f9SDavid du Colombier seprintep(char* s, char* e, Ep *ep) 943*906943f9SDavid du Colombier { 944*906943f9SDavid du Colombier Isoio *iso; 945*906943f9SDavid du Colombier Qio *io; 946*906943f9SDavid du Colombier Ctlio *cio; 947*906943f9SDavid du Colombier 948*906943f9SDavid du Colombier if(ep == nil) 949*906943f9SDavid du Colombier return seprint(s, e, "<nil ep>\n"); 950*906943f9SDavid du Colombier if(ep->aux == nil) 951*906943f9SDavid du Colombier return seprint(s, e, "no mdep\n"); 952*906943f9SDavid du Colombier switch(ep->ttype){ 953*906943f9SDavid du Colombier case Tctl: 954*906943f9SDavid du Colombier cio = ep->aux; 955*906943f9SDavid du Colombier s = seprintio(s, e, cio, "c"); 956*906943f9SDavid du Colombier s = seprint(s, e, "\trepl %d ndata %d\n", ep->rhrepl, cio->ndata); 957*906943f9SDavid du Colombier break; 958*906943f9SDavid du Colombier case Tbulk: 959*906943f9SDavid du Colombier case Tintr: 960*906943f9SDavid du Colombier io = ep->aux; 961*906943f9SDavid du Colombier if(ep->mode != OWRITE) 962*906943f9SDavid du Colombier s = seprintio(s, e, &io[OREAD], "r"); 963*906943f9SDavid du Colombier if(ep->mode != OREAD) 964*906943f9SDavid du Colombier s = seprintio(s, e, &io[OWRITE], "w"); 965*906943f9SDavid du Colombier break; 966*906943f9SDavid du Colombier case Tiso: 967*906943f9SDavid du Colombier iso = ep->aux; 968*906943f9SDavid du Colombier s = seprintio(s, e, iso, "w"); 969*906943f9SDavid du Colombier s = seprint(s, e, "\tntds %d avail %d frno %uld left %uld next avail %#p\n", 970*906943f9SDavid du Colombier iso->nframes, iso->navail, iso->frno, iso->left, iso->atds); 971*906943f9SDavid du Colombier break; 972*906943f9SDavid du Colombier } 973*906943f9SDavid du Colombier return s; 974*906943f9SDavid du Colombier } 975*906943f9SDavid du Colombier 976*906943f9SDavid du Colombier static char* 977*906943f9SDavid du Colombier seprintctl(char *s, char *se, ulong ctl) 978*906943f9SDavid du Colombier { 979*906943f9SDavid du Colombier s = seprint(s, se, "en="); 980*906943f9SDavid du Colombier if((ctl&Cple) != 0) 981*906943f9SDavid du Colombier s = seprint(s, se, "p"); 982*906943f9SDavid du Colombier if((ctl&Cie) != 0) 983*906943f9SDavid du Colombier s = seprint(s, se, "i"); 984*906943f9SDavid du Colombier if((ctl&Ccle) != 0) 985*906943f9SDavid du Colombier s = seprint(s, se, "c"); 986*906943f9SDavid du Colombier if((ctl&Cble) != 0) 987*906943f9SDavid du Colombier s = seprint(s, se, "b"); 988*906943f9SDavid du Colombier switch(ctl & Cfsmask){ 989*906943f9SDavid du Colombier case Cfsreset: 990*906943f9SDavid du Colombier return seprint(s, se, " reset"); 991*906943f9SDavid du Colombier case Cfsresume: 992*906943f9SDavid du Colombier return seprint(s, se, " resume"); 993*906943f9SDavid du Colombier case Cfsoper: 994*906943f9SDavid du Colombier return seprint(s, se, " run"); 995*906943f9SDavid du Colombier case Cfssuspend: 996*906943f9SDavid du Colombier return seprint(s, se, " suspend"); 997*906943f9SDavid du Colombier default: 998*906943f9SDavid du Colombier return seprint(s, se, " ???"); 999*906943f9SDavid du Colombier } 1000*906943f9SDavid du Colombier } 1001*906943f9SDavid du Colombier 1002*906943f9SDavid du Colombier static void 1003*906943f9SDavid du Colombier dump(Hci *hp) 1004*906943f9SDavid du Colombier { 1005*906943f9SDavid du Colombier Ctlr *ctlr; 1006*906943f9SDavid du Colombier Ed *ed; 1007*906943f9SDavid du Colombier char cs[20]; 1008*906943f9SDavid du Colombier 1009*906943f9SDavid du Colombier ctlr = hp->aux; 1010*906943f9SDavid du Colombier ilock(ctlr); 1011*906943f9SDavid du Colombier seprintctl(cs, cs+sizeof(cs), ctlr->ohci->control); 1012*906943f9SDavid du Colombier print("ohci ctlr %#p: frno %#ux ctl %#lux %s sts %#lux intr %#lux\n", 1013*906943f9SDavid du Colombier ctlr, ctlr->hcca->framenumber, ctlr->ohci->control, cs, 1014*906943f9SDavid du Colombier ctlr->ohci->cmdsts, ctlr->ohci->intrsts); 1015*906943f9SDavid du Colombier print("ctlhd %#ulx cur %#ulx bulkhd %#ulx cur %#ulx done %#ulx\n", 1016*906943f9SDavid du Colombier ctlr->ohci->ctlheaded, ctlr->ohci->ctlcurred, 1017*906943f9SDavid du Colombier ctlr->ohci->bulkheaded, ctlr->ohci->bulkcurred, 1018*906943f9SDavid du Colombier ctlr->ohci->donehead); 1019*906943f9SDavid du Colombier if(ctlhd(ctlr) != nil) 1020*906943f9SDavid du Colombier print("[ctl]\n"); 1021*906943f9SDavid du Colombier for(ed = ctlhd(ctlr); ed != nil; ed = ed->next) 1022*906943f9SDavid du Colombier dumped(ed); 1023*906943f9SDavid du Colombier if(bulkhd(ctlr) != nil) 1024*906943f9SDavid du Colombier print("[bulk]\n"); 1025*906943f9SDavid du Colombier for(ed = bulkhd(ctlr); ed != nil; ed = ed->next) 1026*906943f9SDavid du Colombier dumped(ed); 1027*906943f9SDavid du Colombier if(ctlr->intrhd != nil) 1028*906943f9SDavid du Colombier print("[intr]\n"); 1029*906943f9SDavid du Colombier for(ed = ctlr->intrhd; ed != nil; ed = ed->inext) 1030*906943f9SDavid du Colombier dumped(ed); 1031*906943f9SDavid du Colombier if(ctlr->tree->root[0]->next != nil) 1032*906943f9SDavid du Colombier print("[iso]"); 1033*906943f9SDavid du Colombier for(ed = ctlr->tree->root[0]->next; ed != nil; ed = ed->next) 1034*906943f9SDavid du Colombier dumped(ed); 1035*906943f9SDavid du Colombier print("%d eds in tree\n", ctlr->ntree); 1036*906943f9SDavid du Colombier iunlock(ctlr); 1037*906943f9SDavid du Colombier lock(&tdpool); 1038*906943f9SDavid du Colombier print("%d tds allocated = %d in use + %d free\n", 1039*906943f9SDavid du Colombier tdpool.nalloc, tdpool.ninuse, tdpool.nfree); 1040*906943f9SDavid du Colombier unlock(&tdpool); 1041*906943f9SDavid du Colombier lock(&edpool); 1042*906943f9SDavid du Colombier print("%d eds allocated = %d in use + %d free\n", 1043*906943f9SDavid du Colombier edpool.nalloc, edpool.ninuse, edpool.nfree); 1044*906943f9SDavid du Colombier unlock(&edpool); 1045*906943f9SDavid du Colombier } 1046*906943f9SDavid du Colombier 1047*906943f9SDavid du Colombier /* 1048*906943f9SDavid du Colombier * Compute size for the next iso Td and setup its 1049*906943f9SDavid du Colombier * descriptor for I/O according to the buffer size. 1050*906943f9SDavid du Colombier */ 1051*906943f9SDavid du Colombier static void 1052*906943f9SDavid du Colombier isodtdinit(Ep *ep, Isoio *iso, Td *td) 1053*906943f9SDavid du Colombier { 1054*906943f9SDavid du Colombier Block *bp; 1055*906943f9SDavid du Colombier long size; 1056*906943f9SDavid du Colombier int i; 1057*906943f9SDavid du Colombier 1058*906943f9SDavid du Colombier bp = td->bp; 1059*906943f9SDavid du Colombier assert(bp != nil && BLEN(bp) == 0); 1060*906943f9SDavid du Colombier size = (ep->hz+iso->left) * ep->pollival / 1000; 1061*906943f9SDavid du Colombier iso->left = (ep->hz+iso->left) * ep->pollival % 1000; 1062*906943f9SDavid du Colombier size *= ep->samplesz; 1063*906943f9SDavid du Colombier if(size > ep->maxpkt){ 1064*906943f9SDavid du Colombier print("ohci: ep%d.%d: size > maxpkt\n", 1065*906943f9SDavid du Colombier ep->dev->nb, ep->nb); 1066*906943f9SDavid du Colombier print("size = %uld max = %ld\n", size, ep->maxpkt); 1067*906943f9SDavid du Colombier size = ep->maxpkt; 1068*906943f9SDavid du Colombier } 1069*906943f9SDavid du Colombier td->nbytes = size; 1070*906943f9SDavid du Colombier memset(bp->wp, 0, size); /* in case we don't fill it on time */ 1071*906943f9SDavid du Colombier td->cbp0 = td->cbp = ptr2pa(bp->rp) & ~0xFFF; 1072*906943f9SDavid du Colombier td->ctrl = TRUNC(iso->frno, Ntdframes); 1073*906943f9SDavid du Colombier td->offsets[0] = (ptr2pa(bp->rp) & 0xFFF); 1074*906943f9SDavid du Colombier td->offsets[0] |= (Tdnotacc << Tdiccshift); 1075*906943f9SDavid du Colombier /* in case the controller checks out the offests... */ 1076*906943f9SDavid du Colombier for(i = 1; i < nelem(td->offsets); i++) 1077*906943f9SDavid du Colombier td->offsets[i] = td->offsets[0]; 1078*906943f9SDavid du Colombier td->be = ptr2pa(bp->rp + size - 1); 1079*906943f9SDavid du Colombier td->ctrl |= (0 << Tdfcshift); /* frame count is 1 */ 1080*906943f9SDavid du Colombier 1081*906943f9SDavid du Colombier iso->frno = TRUNC(iso->frno + ep->pollival, Ntdframes); 1082*906943f9SDavid du Colombier } 1083*906943f9SDavid du Colombier 1084*906943f9SDavid du Colombier /* 1085*906943f9SDavid du Colombier * start I/O on the dummy td and setup a new dummy to fill up. 1086*906943f9SDavid du Colombier */ 1087*906943f9SDavid du Colombier static void 1088*906943f9SDavid du Colombier isoadvance(Ep *ep, Isoio *iso, Td *td) 1089*906943f9SDavid du Colombier { 1090*906943f9SDavid du Colombier Td *dtd; 1091*906943f9SDavid du Colombier 1092*906943f9SDavid du Colombier dtd = iso->atds; 1093*906943f9SDavid du Colombier iso->atds = dtd->anext; 1094*906943f9SDavid du Colombier iso->navail--; 1095*906943f9SDavid du Colombier dtd->anext = nil; 1096*906943f9SDavid du Colombier dtd->bp->wp = dtd->bp->rp; 1097*906943f9SDavid du Colombier dtd->nexttd = 0; 1098*906943f9SDavid du Colombier td->nexttd = ptr2pa(dtd); 1099*906943f9SDavid du Colombier isodtdinit(ep, iso, dtd); 1100*906943f9SDavid du Colombier iso->ed->tail = ptr2pa(dtd); 1101*906943f9SDavid du Colombier } 1102*906943f9SDavid du Colombier 1103*906943f9SDavid du Colombier static int 1104*906943f9SDavid du Colombier isocanwrite(void *a) 1105*906943f9SDavid du Colombier { 1106*906943f9SDavid du Colombier Isoio *iso; 1107*906943f9SDavid du Colombier 1108*906943f9SDavid du Colombier iso = a; 1109*906943f9SDavid du Colombier return iso->state == Qclose || iso->err != nil || 1110*906943f9SDavid du Colombier iso->navail > iso->nframes / 2; 1111*906943f9SDavid du Colombier } 1112*906943f9SDavid du Colombier 1113*906943f9SDavid du Colombier /* 1114*906943f9SDavid du Colombier * Service a completed/failed Td from the done queue. 1115*906943f9SDavid du Colombier * It may be of any transfer type. 1116*906943f9SDavid du Colombier * The queue is not in completion order. 1117*906943f9SDavid du Colombier * (It's actually in reverse completion order). 1118*906943f9SDavid du Colombier * 1119*906943f9SDavid du Colombier * When an error, a short packet, or a last Td is found 1120*906943f9SDavid du Colombier * we awake the process waiting for the transfer. 1121*906943f9SDavid du Colombier * Although later we will process other Tds completed 1122*906943f9SDavid du Colombier * before, epio won't be able to touch the current Td 1123*906943f9SDavid du Colombier * until interrupt returns and releases the lock on the 1124*906943f9SDavid du Colombier * controller. 1125*906943f9SDavid du Colombier */ 1126*906943f9SDavid du Colombier static void 1127*906943f9SDavid du Colombier qhinterrupt(Ctlr *, Ep *ep, Qio *io, Td *td, int) 1128*906943f9SDavid du Colombier { 1129*906943f9SDavid du Colombier Block *bp; 1130*906943f9SDavid du Colombier int mode; 1131*906943f9SDavid du Colombier int err; 1132*906943f9SDavid du Colombier Ed *ed; 1133*906943f9SDavid du Colombier 1134*906943f9SDavid du Colombier ed = io->ed; 1135*906943f9SDavid du Colombier if(io->state != Qrun) 1136*906943f9SDavid du Colombier return; 1137*906943f9SDavid du Colombier if(tdtok(td) == Tdtokin) 1138*906943f9SDavid du Colombier mode = OREAD; 1139*906943f9SDavid du Colombier else 1140*906943f9SDavid du Colombier mode = OWRITE; 1141*906943f9SDavid du Colombier bp = td->bp; 1142*906943f9SDavid du Colombier err = tderrs(td); 1143*906943f9SDavid du Colombier 1144*906943f9SDavid du Colombier switch(err){ 1145*906943f9SDavid du Colombier case Tddataovr: /* Overrun is not an error */ 1146*906943f9SDavid du Colombier case Tdok: 1147*906943f9SDavid du Colombier if(td->cbp != 0) 1148*906943f9SDavid du Colombier panic("ohci: full packet but cbp != 0"); 1149*906943f9SDavid du Colombier break; 1150*906943f9SDavid du Colombier case Tddataund: 1151*906943f9SDavid du Colombier /* short input packets are ok */ 1152*906943f9SDavid du Colombier if(mode == OREAD){ 1153*906943f9SDavid du Colombier if(td->cbp == 0) 1154*906943f9SDavid du Colombier panic("ohci: short packet but cbp == 0"); 1155*906943f9SDavid du Colombier if((td->cbp & ~0xFFF) == (td->cbp0 & ~0xFFF)) 1156*906943f9SDavid du Colombier bp->wp = pa2ptr(td->cbp); 1157*906943f9SDavid du Colombier else 1158*906943f9SDavid du Colombier bp->wp = bp->rp + 0x1000 + (td->cbp&0xFFF); 1159*906943f9SDavid du Colombier if(bp->wp < bp->rp) 1160*906943f9SDavid du Colombier panic("ohci: wp < rp"); 1161*906943f9SDavid du Colombier /* 1162*906943f9SDavid du Colombier * It's ok. clear error and flag as last in xfer. 1163*906943f9SDavid du Colombier * epio must ignore following Tds. 1164*906943f9SDavid du Colombier */ 1165*906943f9SDavid du Colombier td->last = 1; 1166*906943f9SDavid du Colombier td->ctrl &= ~(Tdccmask << Tdccshift); 1167*906943f9SDavid du Colombier break; 1168*906943f9SDavid du Colombier } 1169*906943f9SDavid du Colombier /* else fall; it's an error */ 1170*906943f9SDavid du Colombier case Tdcrc: 1171*906943f9SDavid du Colombier case Tdbitstuff: 1172*906943f9SDavid du Colombier case Tdbadtog: 1173*906943f9SDavid du Colombier case Tdstalled: 1174*906943f9SDavid du Colombier case Tdtmout: 1175*906943f9SDavid du Colombier case Tdpidchk: 1176*906943f9SDavid du Colombier case Tdbadpid: 1177*906943f9SDavid du Colombier bp->wp = bp->rp; /* no bytes in xfer. */ 1178*906943f9SDavid du Colombier io->err = errmsg(err); 1179*906943f9SDavid du Colombier if(debug || ep->debug){ 1180*906943f9SDavid du Colombier print("tdinterrupt: failed err %d (%s)\n", err, io->err); 1181*906943f9SDavid du Colombier dumptd(td, "failed", ed->ctrl & Ediso); 1182*906943f9SDavid du Colombier } 1183*906943f9SDavid du Colombier td->last = 1; 1184*906943f9SDavid du Colombier break; 1185*906943f9SDavid du Colombier default: 1186*906943f9SDavid du Colombier panic("ohci: td cc %ud unknown\n", err); 1187*906943f9SDavid du Colombier } 1188*906943f9SDavid du Colombier 1189*906943f9SDavid du Colombier if(td->last != 0){ 1190*906943f9SDavid du Colombier /* 1191*906943f9SDavid du Colombier * clear td list and halt flag. 1192*906943f9SDavid du Colombier */ 1193*906943f9SDavid du Colombier ed->head = (ed->head & Edtoggle) | ed->tail; 1194*906943f9SDavid du Colombier ed->tds = pa2ptr(ed->tail); 1195*906943f9SDavid du Colombier io->state = Qdone; 1196*906943f9SDavid du Colombier wakeup(io); 1197*906943f9SDavid du Colombier } 1198*906943f9SDavid du Colombier } 1199*906943f9SDavid du Colombier 1200*906943f9SDavid du Colombier /* 1201*906943f9SDavid du Colombier * BUG: Iso input streams are not implemented. 1202*906943f9SDavid du Colombier */ 1203*906943f9SDavid du Colombier static void 1204*906943f9SDavid du Colombier isointerrupt(Ctlr *ctlr, Ep *ep, Qio *io, Td *td, int) 1205*906943f9SDavid du Colombier { 1206*906943f9SDavid du Colombier Isoio *iso; 1207*906943f9SDavid du Colombier Block *bp; 1208*906943f9SDavid du Colombier int err; 1209*906943f9SDavid du Colombier int isoerr; 1210*906943f9SDavid du Colombier Ed *ed; 1211*906943f9SDavid du Colombier 1212*906943f9SDavid du Colombier iso = ep->aux; 1213*906943f9SDavid du Colombier ed = io->ed; 1214*906943f9SDavid du Colombier if(io->state == Qclose) 1215*906943f9SDavid du Colombier return; 1216*906943f9SDavid du Colombier bp = td->bp; 1217*906943f9SDavid du Colombier /* 1218*906943f9SDavid du Colombier * When we get more than half the frames consecutive errors 1219*906943f9SDavid du Colombier * we signal an actual error. Errors in the entire Td are 1220*906943f9SDavid du Colombier * more serious and are always singaled. 1221*906943f9SDavid du Colombier * Errors like overrun are not really errors. In fact, for 1222*906943f9SDavid du Colombier * output, errors cannot be really detected. The driver will 1223*906943f9SDavid du Colombier * hopefully notice I/O errors on input endpoints and detach the device. 1224*906943f9SDavid du Colombier */ 1225*906943f9SDavid du Colombier err = tderrs(td); 1226*906943f9SDavid du Colombier isoerr = (td->offsets[0] >> Tdiccshift) & Tdiccmask; 1227*906943f9SDavid du Colombier if(isoerr == Tdok || isoerr == Tdnotacc) 1228*906943f9SDavid du Colombier iso->nerrs = 0; 1229*906943f9SDavid du Colombier else if(iso->nerrs++ > iso->nframes/2) 1230*906943f9SDavid du Colombier err = Tdstalled; 1231*906943f9SDavid du Colombier if(err != Tdok && err != Tddataovr){ 1232*906943f9SDavid du Colombier bp->wp = bp->rp; 1233*906943f9SDavid du Colombier io->err = errmsg(err); 1234*906943f9SDavid du Colombier if(debug || ep->debug){ 1235*906943f9SDavid du Colombier print("ohci: isointerrupt: ep%d.%d: err %d (%s) frnum 0x%lux\n", 1236*906943f9SDavid du Colombier ep->dev->nb, ep->nb, 1237*906943f9SDavid du Colombier err, errmsg(err), ctlr->ohci->fmnumber); 1238*906943f9SDavid du Colombier dumptd(td, "failed", ed->ctrl & Ediso); 1239*906943f9SDavid du Colombier } 1240*906943f9SDavid du Colombier } 1241*906943f9SDavid du Colombier td->bp->wp = td->bp->rp; 1242*906943f9SDavid du Colombier td->nbytes = 0; 1243*906943f9SDavid du Colombier td->anext = iso->atds; 1244*906943f9SDavid du Colombier iso->atds = td; 1245*906943f9SDavid du Colombier iso->navail++; 1246*906943f9SDavid du Colombier /* 1247*906943f9SDavid du Colombier * If almost all Tds are avail the user is not doing I/O at the 1248*906943f9SDavid du Colombier * required rate. We put another Td in place to keep the polling rate. 1249*906943f9SDavid du Colombier */ 1250*906943f9SDavid du Colombier if(iso->err == nil && iso->navail > iso->nframes - 10) 1251*906943f9SDavid du Colombier isoadvance(ep, iso, pa2ptr(iso->ed->tail)); 1252*906943f9SDavid du Colombier /* 1253*906943f9SDavid du Colombier * If there's enough buffering futher I/O can be done. 1254*906943f9SDavid du Colombier */ 1255*906943f9SDavid du Colombier if(isocanwrite(iso)) 1256*906943f9SDavid du Colombier wakeup(iso); 1257*906943f9SDavid du Colombier } 1258*906943f9SDavid du Colombier 1259*906943f9SDavid du Colombier static void 1260*906943f9SDavid du Colombier interrupt(Ureg *, void *arg) 1261*906943f9SDavid du Colombier { 1262*906943f9SDavid du Colombier Td *td, *ntd; 1263*906943f9SDavid du Colombier Td *td0; 1264*906943f9SDavid du Colombier Hci *hp; 1265*906943f9SDavid du Colombier Ctlr *ctlr; 1266*906943f9SDavid du Colombier ulong status; 1267*906943f9SDavid du Colombier ulong curred; 1268*906943f9SDavid du Colombier int i; 1269*906943f9SDavid du Colombier int frno; 1270*906943f9SDavid du Colombier 1271*906943f9SDavid du Colombier hp = arg; 1272*906943f9SDavid du Colombier ctlr = hp->aux; 1273*906943f9SDavid du Colombier ilock(ctlr); 1274*906943f9SDavid du Colombier status = ctlr->ohci->intrsts; 1275*906943f9SDavid du Colombier status &= ctlr->ohci->intrenable; 1276*906943f9SDavid du Colombier status &= Oc|Rhsc|Fno|Ue|Rd|Sf|Wdh|So; 1277*906943f9SDavid du Colombier frno = TRUNC(ctlr->ohci->fmnumber, Ntdframes); 1278*906943f9SDavid du Colombier if((status & Wdh) != 0){ 1279*906943f9SDavid du Colombier /* lsb of donehead has bit to flag other intrs. */ 1280*906943f9SDavid du Colombier td = pa2ptr(ctlr->hcca->donehead & ~0xF); 1281*906943f9SDavid du Colombier }else 1282*906943f9SDavid du Colombier td = nil; 1283*906943f9SDavid du Colombier td0 = td; 1284*906943f9SDavid du Colombier 1285*906943f9SDavid du Colombier for(i = 0; td != nil && i < 1024; i++){ 1286*906943f9SDavid du Colombier if(0)ddprint("ohci tdinterrupt: td %#p\n", td); 1287*906943f9SDavid du Colombier ntd = pa2ptr(td->nexttd & ~0xF); 1288*906943f9SDavid du Colombier td->nexttd = 0; 1289*906943f9SDavid du Colombier if(td->ep == nil || td->io == nil) 1290*906943f9SDavid du Colombier panic("interrupt: ep %#x io %#x\n", td->ep, td->io); 1291*906943f9SDavid du Colombier ohciinterrupts[td->ep->ttype]++; 1292*906943f9SDavid du Colombier if(td->ep->ttype == Tiso) 1293*906943f9SDavid du Colombier isointerrupt(ctlr, td->ep, td->io, td, frno); 1294*906943f9SDavid du Colombier else 1295*906943f9SDavid du Colombier qhinterrupt(ctlr, td->ep, td->io, td, frno); 1296*906943f9SDavid du Colombier td = ntd; 1297*906943f9SDavid du Colombier } 1298*906943f9SDavid du Colombier if(i == 1024) 1299*906943f9SDavid du Colombier print("ohci: bug: more than 1024 done Tds?\n"); 1300*906943f9SDavid du Colombier 1301*906943f9SDavid du Colombier if(pa2ptr(ctlr->hcca->donehead & ~0xF) != td0) 1302*906943f9SDavid du Colombier print("ohci: bug: donehead changed before ack\n"); 1303*906943f9SDavid du Colombier ctlr->hcca->donehead = 0; 1304*906943f9SDavid du Colombier 1305*906943f9SDavid du Colombier ctlr->ohci->intrsts = status; 1306*906943f9SDavid du Colombier status &= ~Wdh; 1307*906943f9SDavid du Colombier status &= ~Sf; 1308*906943f9SDavid du Colombier if(status & So){ 1309*906943f9SDavid du Colombier print("ohci: sched overrun: too much load\n"); 1310*906943f9SDavid du Colombier ctlr->overrun++; 1311*906943f9SDavid du Colombier status &= ~So; 1312*906943f9SDavid du Colombier } 1313*906943f9SDavid du Colombier if((status & Ue) != 0){ 1314*906943f9SDavid du Colombier curred = ctlr->ohci->periodcurred; 1315*906943f9SDavid du Colombier print("ohci: unrecoverable error frame 0x%.8lux ed 0x%.8lux, " 1316*906943f9SDavid du Colombier "ints %d %d %d %d\n", 1317*906943f9SDavid du Colombier ctlr->ohci->fmnumber, curred, 1318*906943f9SDavid du Colombier ohciinterrupts[Tctl], ohciinterrupts[Tintr], 1319*906943f9SDavid du Colombier ohciinterrupts[Tbulk], ohciinterrupts[Tiso]); 1320*906943f9SDavid du Colombier if(curred != 0) 1321*906943f9SDavid du Colombier dumped(pa2ptr(curred)); 1322*906943f9SDavid du Colombier status &= ~Ue; 1323*906943f9SDavid du Colombier } 1324*906943f9SDavid du Colombier if(status != 0) 1325*906943f9SDavid du Colombier print("ohci interrupt: unhandled sts 0x%.8lux\n", status); 1326*906943f9SDavid du Colombier iunlock(ctlr); 1327*906943f9SDavid du Colombier } 1328*906943f9SDavid du Colombier 1329*906943f9SDavid du Colombier /* 1330*906943f9SDavid du Colombier * The old dummy Td is used to implement the new Td. 1331*906943f9SDavid du Colombier * A new dummy is linked at the end of the old one and 1332*906943f9SDavid du Colombier * returned, to link further Tds if needed. 1333*906943f9SDavid du Colombier */ 1334*906943f9SDavid du Colombier static Td* 1335*906943f9SDavid du Colombier epgettd(Ep *ep, Qio *io, Td **dtdp, int flags, void *a, int count) 1336*906943f9SDavid du Colombier { 1337*906943f9SDavid du Colombier Td *td; 1338*906943f9SDavid du Colombier Td *dtd; 1339*906943f9SDavid du Colombier Block *bp; 1340*906943f9SDavid du Colombier 1341*906943f9SDavid du Colombier if(ep->maxpkt > 0x2000) 1342*906943f9SDavid du Colombier panic("ohci: max packet > two pages"); 1343*906943f9SDavid du Colombier if(ep->maxpkt < count) 1344*906943f9SDavid du Colombier error("maxpkt too short"); 1345*906943f9SDavid du Colombier bp = allocb(ep->maxpkt); /* panics if no mem */ 1346*906943f9SDavid du Colombier assert(bp != nil); 1347*906943f9SDavid du Colombier dtd = *dtdp; 1348*906943f9SDavid du Colombier td = dtd; 1349*906943f9SDavid du Colombier td->bp = bp; 1350*906943f9SDavid du Colombier if(count > 0){ 1351*906943f9SDavid du Colombier td->cbp0 = td->cbp = ptr2pa(bp->wp); 1352*906943f9SDavid du Colombier td->be = ptr2pa(bp->wp + count - 1); 1353*906943f9SDavid du Colombier if(a != nil){ 1354*906943f9SDavid du Colombier validaddr((uintptr)a, count, 0); /* DEBUG */ 1355*906943f9SDavid du Colombier memmove(bp->wp, a, count); 1356*906943f9SDavid du Colombier } 1357*906943f9SDavid du Colombier bp->wp += count; 1358*906943f9SDavid du Colombier } 1359*906943f9SDavid du Colombier td->nbytes = count; 1360*906943f9SDavid du Colombier td->ctrl = io->tok|Tdusetog|io->toggle|flags; 1361*906943f9SDavid du Colombier if(io->toggle == Tddata0) 1362*906943f9SDavid du Colombier io->toggle = Tddata1; 1363*906943f9SDavid du Colombier else 1364*906943f9SDavid du Colombier io->toggle = Tddata0; 1365*906943f9SDavid du Colombier assert(td->ep == ep); 1366*906943f9SDavid du Colombier td->io = io; 1367*906943f9SDavid du Colombier dtd = tdalloc(); /* new dummy */ 1368*906943f9SDavid du Colombier dtd->ep = ep; 1369*906943f9SDavid du Colombier td->nexttd = ptr2pa(dtd); 1370*906943f9SDavid du Colombier td->next = dtd; 1371*906943f9SDavid du Colombier *dtdp = dtd; 1372*906943f9SDavid du Colombier return td; 1373*906943f9SDavid du Colombier } 1374*906943f9SDavid du Colombier 1375*906943f9SDavid du Colombier /* 1376*906943f9SDavid du Colombier * Try to get them idle 1377*906943f9SDavid du Colombier */ 1378*906943f9SDavid du Colombier static void 1379*906943f9SDavid du Colombier aborttds(Qio *io) 1380*906943f9SDavid du Colombier { 1381*906943f9SDavid du Colombier Ed *ed; 1382*906943f9SDavid du Colombier Td *td; 1383*906943f9SDavid du Colombier 1384*906943f9SDavid du Colombier ed = io->ed; 1385*906943f9SDavid du Colombier ed->ctrl |= Edskip; 1386*906943f9SDavid du Colombier for(td = ed->tds; td != nil; td = td->next) 1387*906943f9SDavid du Colombier if(td->bp != nil) 1388*906943f9SDavid du Colombier td->bp->wp = td->bp->rp; 1389*906943f9SDavid du Colombier ed->head = (ed->head&0xF) | ed->tail; 1390*906943f9SDavid du Colombier if((ed->ctrl & Ediso) == 0) 1391*906943f9SDavid du Colombier ed->tds = pa2ptr(ed->tail); 1392*906943f9SDavid du Colombier } 1393*906943f9SDavid du Colombier 1394*906943f9SDavid du Colombier static int 1395*906943f9SDavid du Colombier epiodone(void *a) 1396*906943f9SDavid du Colombier { 1397*906943f9SDavid du Colombier Qio *io; 1398*906943f9SDavid du Colombier 1399*906943f9SDavid du Colombier io = a; 1400*906943f9SDavid du Colombier return io->state != Qrun; 1401*906943f9SDavid du Colombier } 1402*906943f9SDavid du Colombier 1403*906943f9SDavid du Colombier static void 1404*906943f9SDavid du Colombier epiowait(Ctlr *ctlr, Qio *io, int tmout, ulong) 1405*906943f9SDavid du Colombier { 1406*906943f9SDavid du Colombier Ed *ed; 1407*906943f9SDavid du Colombier int timedout; 1408*906943f9SDavid du Colombier 1409*906943f9SDavid du Colombier ed = io->ed; 1410*906943f9SDavid du Colombier if(0)ddqprint("ohci io %#p sleep on ed %#p state %s\n", 1411*906943f9SDavid du Colombier io, ed, iosname[io->state]); 1412*906943f9SDavid du Colombier timedout = 0; 1413*906943f9SDavid du Colombier if(waserror()){ 1414*906943f9SDavid du Colombier dqprint("ohci io %#p ed %#p timed out\n", io, ed); 1415*906943f9SDavid du Colombier timedout++; 1416*906943f9SDavid du Colombier }else{ 1417*906943f9SDavid du Colombier if(tmout == 0) 1418*906943f9SDavid du Colombier sleep(io, epiodone, io); 1419*906943f9SDavid du Colombier else 1420*906943f9SDavid du Colombier tsleep(io, epiodone, io, tmout); 14216a5dc222SDavid du Colombier poperror(); 14226a5dc222SDavid du Colombier } 1423*906943f9SDavid du Colombier ilock(ctlr); 1424*906943f9SDavid du Colombier if(io->state == Qrun) 1425*906943f9SDavid du Colombier timedout = 1; 1426*906943f9SDavid du Colombier else if(io->state != Qdone && io->state != Qclose) 1427*906943f9SDavid du Colombier panic("epio: ed not done and not closed"); 1428*906943f9SDavid du Colombier if(timedout){ 1429*906943f9SDavid du Colombier aborttds(io); 1430*906943f9SDavid du Colombier io->err = "request timed out"; 1431*906943f9SDavid du Colombier iunlock(ctlr); 1432*906943f9SDavid du Colombier if(!waserror()){ 1433*906943f9SDavid du Colombier tsleep(&up->sleep, return0, 0, Abortdelay); 1434*906943f9SDavid du Colombier poperror(); 1435*906943f9SDavid du Colombier } 1436*906943f9SDavid du Colombier ilock(ctlr); 1437*906943f9SDavid du Colombier } 1438*906943f9SDavid du Colombier if(io->state != Qclose) 1439*906943f9SDavid du Colombier io->state = Qidle; 1440*906943f9SDavid du Colombier iunlock(ctlr); 14416a5dc222SDavid du Colombier } 14426a5dc222SDavid du Colombier 14436a5dc222SDavid du Colombier /* 1444*906943f9SDavid du Colombier * Non iso I/O. 1445*906943f9SDavid du Colombier * To make it work for control transfers, the caller may 1446*906943f9SDavid du Colombier * lock the Qio for the entire control transfer. 1447*906943f9SDavid du Colombier * If tmout is not 0 it is a timeout value in ms. 1448*906943f9SDavid du Colombier * 14496a5dc222SDavid du Colombier */ 1450*906943f9SDavid du Colombier static long 1451*906943f9SDavid du Colombier epio(Ep *ep, Qio *io, void *a, long count, int tmout, int mustlock) 1452*906943f9SDavid du Colombier { 1453*906943f9SDavid du Colombier Ed *ed; 1454*906943f9SDavid du Colombier Ctlr *ctlr; 1455*906943f9SDavid du Colombier char buf[80]; 1456*906943f9SDavid du Colombier uchar *c; 1457*906943f9SDavid du Colombier Td *td; 1458*906943f9SDavid du Colombier Td *ltd; 1459*906943f9SDavid du Colombier Td *ntd; 1460*906943f9SDavid du Colombier Td *td0; 1461*906943f9SDavid du Colombier ulong load; 1462*906943f9SDavid du Colombier int last; 1463*906943f9SDavid du Colombier int ntds; 1464*906943f9SDavid du Colombier long tot; 1465*906943f9SDavid du Colombier long n; 1466*906943f9SDavid du Colombier char *err; 1467*906943f9SDavid du Colombier 1468*906943f9SDavid du Colombier ed = io->ed; 1469*906943f9SDavid du Colombier ctlr = ep->hp->aux; 1470*906943f9SDavid du Colombier io->debug = ep->debug; 1471*906943f9SDavid du Colombier ddeprint("ohci: %s ep%d.%d io %#p count %ld\n", 1472*906943f9SDavid du Colombier io->tok == Tdtokin ? "in" : "out", 1473*906943f9SDavid du Colombier ep->dev->nb, ep->nb, io, count); 1474*906943f9SDavid du Colombier if((debug > 1 || ep->debug > 1) && io->tok != Tdtokin){ 1475*906943f9SDavid du Colombier seprintdata(buf, buf+sizeof(buf), a, count); 1476*906943f9SDavid du Colombier print("\t%s\n", buf); 14776a5dc222SDavid du Colombier } 1478*906943f9SDavid du Colombier if(mustlock){ 1479*906943f9SDavid du Colombier qlock(io); 1480*906943f9SDavid du Colombier if(waserror()){ 1481*906943f9SDavid du Colombier qunlock(io); 1482*906943f9SDavid du Colombier nexterror(); 1483*906943f9SDavid du Colombier } 1484*906943f9SDavid du Colombier } 1485*906943f9SDavid du Colombier io->err = nil; 1486*906943f9SDavid du Colombier ilock(ctlr); 1487*906943f9SDavid du Colombier if(io->state == Qclose){ /* Tds released by cancelio */ 1488*906943f9SDavid du Colombier iunlock(ctlr); 1489*906943f9SDavid du Colombier error(io->err ? io->err : Eio); 1490*906943f9SDavid du Colombier } 1491*906943f9SDavid du Colombier if(io->state != Qidle) 1492*906943f9SDavid du Colombier panic("epio: qio not idle"); 1493*906943f9SDavid du Colombier io->state = Qinstall; 1494*906943f9SDavid du Colombier 1495*906943f9SDavid du Colombier c = a; 1496*906943f9SDavid du Colombier ltd = td0 = ed->tds; 1497*906943f9SDavid du Colombier load = tot = 0; 1498*906943f9SDavid du Colombier do{ 1499*906943f9SDavid du Colombier n = ep->maxpkt; 1500*906943f9SDavid du Colombier if(count-tot < n) 1501*906943f9SDavid du Colombier n = count-tot; 1502*906943f9SDavid du Colombier if(io->tok != Tdtokin) 1503*906943f9SDavid du Colombier td = epgettd(ep, io, <d, 0, c+tot, n); 1504*906943f9SDavid du Colombier else 1505*906943f9SDavid du Colombier td = epgettd(ep, io, <d, 0, nil, n); 1506*906943f9SDavid du Colombier tot += n; 1507*906943f9SDavid du Colombier load += ep->load; 1508*906943f9SDavid du Colombier }while(tot < count); 1509*906943f9SDavid du Colombier if(td0 == nil || ltd == nil || td0 == ltd) 1510*906943f9SDavid du Colombier panic("epio: no td"); 1511*906943f9SDavid du Colombier td->last = 1; 1512*906943f9SDavid du Colombier if(debug > 2 || ep->debug > 2) 1513*906943f9SDavid du Colombier dumptds(td0, "put td", ep->ttype == Tiso); 1514*906943f9SDavid du Colombier iunlock(ctlr); 1515*906943f9SDavid du Colombier 1516*906943f9SDavid du Colombier ilock(ctlr); 1517*906943f9SDavid du Colombier if(io->state != Qclose){ 1518*906943f9SDavid du Colombier io->iotime = TK2MS(MACHP(0)->ticks); 1519*906943f9SDavid du Colombier io->state = Qrun; 1520*906943f9SDavid du Colombier ed->tail = ptr2pa(ltd); 1521*906943f9SDavid du Colombier if(ep->ttype == Tctl) 1522*906943f9SDavid du Colombier ctlr->ohci->cmdsts |= Sclf; 1523*906943f9SDavid du Colombier else if(ep->ttype == Tbulk) 1524*906943f9SDavid du Colombier ctlr->ohci->cmdsts |= Sblf; 1525*906943f9SDavid du Colombier } 1526*906943f9SDavid du Colombier iunlock(ctlr); 1527*906943f9SDavid du Colombier 1528*906943f9SDavid du Colombier epiowait(ctlr, io, tmout, load); 1529*906943f9SDavid du Colombier ilock(ctlr); 1530*906943f9SDavid du Colombier if(debug > 1 || ep->debug > 1) 1531*906943f9SDavid du Colombier dumptds(td0, "got td", 0); 1532*906943f9SDavid du Colombier iunlock(ctlr); 1533*906943f9SDavid du Colombier 1534*906943f9SDavid du Colombier tot = 0; 1535*906943f9SDavid du Colombier c = a; 1536*906943f9SDavid du Colombier ntds = last = 0; 1537*906943f9SDavid du Colombier for(td = td0; td != ltd; td = ntd){ 1538*906943f9SDavid du Colombier ntds++; 1539*906943f9SDavid du Colombier /* 1540*906943f9SDavid du Colombier * If the Td is flagged as last we must 1541*906943f9SDavid du Colombier * ignore any following Td. The block may 1542*906943f9SDavid du Colombier * seem to have bytes but interrupt has not seen 1543*906943f9SDavid du Colombier * those Tds through the done queue, and they are void. 1544*906943f9SDavid du Colombier */ 1545*906943f9SDavid du Colombier if(last == 0 && tderrs(td) == Tdok){ 1546*906943f9SDavid du Colombier n = BLEN(td->bp); 1547*906943f9SDavid du Colombier tot += n; 1548*906943f9SDavid du Colombier if(tdtok(td) == Tdtokin && n > 0){ 1549*906943f9SDavid du Colombier memmove(c, td->bp->rp, n); 1550*906943f9SDavid du Colombier c += n; 1551*906943f9SDavid du Colombier } 1552*906943f9SDavid du Colombier } 1553*906943f9SDavid du Colombier last |= td->last; 1554*906943f9SDavid du Colombier ntd = td->next; 1555*906943f9SDavid du Colombier tdfree(td); 1556*906943f9SDavid du Colombier } 1557*906943f9SDavid du Colombier if(edtoggle(ed) == 0) 1558*906943f9SDavid du Colombier io->toggle = Tddata0; 1559*906943f9SDavid du Colombier else 1560*906943f9SDavid du Colombier io->toggle = Tddata1; 1561*906943f9SDavid du Colombier 1562*906943f9SDavid du Colombier err = io->err; 1563*906943f9SDavid du Colombier if(mustlock){ 1564*906943f9SDavid du Colombier qunlock(io); 1565*906943f9SDavid du Colombier poperror(); 1566*906943f9SDavid du Colombier } 1567*906943f9SDavid du Colombier ddeprint("ohci: io %#p: %d tds: return %ld err '%s'\n\n", 1568*906943f9SDavid du Colombier io, ntds, tot, err); 1569*906943f9SDavid du Colombier if(err != nil) 1570*906943f9SDavid du Colombier error(err); 1571*906943f9SDavid du Colombier if(tot < 0) 1572*906943f9SDavid du Colombier error(Eio); 1573*906943f9SDavid du Colombier return tot; 1574*906943f9SDavid du Colombier } 1575*906943f9SDavid du Colombier 1576*906943f9SDavid du Colombier /* 1577*906943f9SDavid du Colombier * halt condition was cleared on the endpoint. update our toggles. 1578*906943f9SDavid du Colombier */ 1579*906943f9SDavid du Colombier static void 1580*906943f9SDavid du Colombier clrhalt(Ep *ep) 1581*906943f9SDavid du Colombier { 1582*906943f9SDavid du Colombier Qio *io; 1583*906943f9SDavid du Colombier 1584*906943f9SDavid du Colombier ep->clrhalt = 0; 1585*906943f9SDavid du Colombier switch(ep->ttype){ 1586*906943f9SDavid du Colombier case Tbulk: 1587*906943f9SDavid du Colombier case Tintr: 1588*906943f9SDavid du Colombier io = ep->aux; 1589*906943f9SDavid du Colombier if(ep->mode != OREAD){ 1590*906943f9SDavid du Colombier qlock(&io[OWRITE]); 1591*906943f9SDavid du Colombier io[OWRITE].toggle = Tddata0; 1592*906943f9SDavid du Colombier deprint("ep clrhalt for io %#p\n", io+OWRITE); 1593*906943f9SDavid du Colombier qunlock(&io[OWRITE]); 1594*906943f9SDavid du Colombier } 1595*906943f9SDavid du Colombier if(ep->mode != OWRITE){ 1596*906943f9SDavid du Colombier qlock(&io[OREAD]); 1597*906943f9SDavid du Colombier io[OREAD].toggle = Tddata0; 1598*906943f9SDavid du Colombier deprint("ep clrhalt for io %#p\n", io+OREAD); 1599*906943f9SDavid du Colombier qunlock(&io[OREAD]); 1600*906943f9SDavid du Colombier } 1601*906943f9SDavid du Colombier break; 16026a5dc222SDavid du Colombier } 16036a5dc222SDavid du Colombier } 16046a5dc222SDavid du Colombier 16056a5dc222SDavid du Colombier static long 1606*906943f9SDavid du Colombier epread(Ep *ep, void *a, long count) 16076a5dc222SDavid du Colombier { 1608*906943f9SDavid du Colombier Ctlio *cio; 1609*906943f9SDavid du Colombier Qio *io; 1610*906943f9SDavid du Colombier char buf[80]; 1611*906943f9SDavid du Colombier ulong delta; 16126a5dc222SDavid du Colombier 1613*906943f9SDavid du Colombier if(ep->aux == nil) 1614*906943f9SDavid du Colombier panic("epread: not open"); 1615*906943f9SDavid du Colombier 1616*906943f9SDavid du Colombier switch(ep->ttype){ 1617*906943f9SDavid du Colombier case Tctl: 1618*906943f9SDavid du Colombier cio = ep->aux; 1619*906943f9SDavid du Colombier qlock(cio); 1620*906943f9SDavid du Colombier if(waserror()){ 1621*906943f9SDavid du Colombier qunlock(cio); 1622*906943f9SDavid du Colombier nexterror(); 16236a5dc222SDavid du Colombier } 1624*906943f9SDavid du Colombier ddeprint("epread ctl ndata %d\n", cio->ndata); 1625*906943f9SDavid du Colombier if(cio->ndata < 0) 1626*906943f9SDavid du Colombier error("request expected"); 1627*906943f9SDavid du Colombier else if(cio->ndata == 0){ 1628*906943f9SDavid du Colombier cio->ndata = -1; 1629*906943f9SDavid du Colombier count = 0; 16306a5dc222SDavid du Colombier }else{ 1631*906943f9SDavid du Colombier if(count > cio->ndata) 1632*906943f9SDavid du Colombier count = cio->ndata; 1633*906943f9SDavid du Colombier if(count > 0) 1634*906943f9SDavid du Colombier memmove(a, cio->data, count); 1635*906943f9SDavid du Colombier /* BUG for big transfers */ 1636*906943f9SDavid du Colombier free(cio->data); 1637*906943f9SDavid du Colombier cio->data = nil; 1638*906943f9SDavid du Colombier cio->ndata = 0; /* signal EOF next time */ 16396a5dc222SDavid du Colombier } 1640*906943f9SDavid du Colombier qunlock(cio); 16416a5dc222SDavid du Colombier poperror(); 1642*906943f9SDavid du Colombier if(debug>1 || ep->debug){ 1643*906943f9SDavid du Colombier seprintdata(buf, buf+sizeof(buf), a, count); 1644*906943f9SDavid du Colombier print("epread: %s\n", buf); 1645*906943f9SDavid du Colombier } 1646*906943f9SDavid du Colombier return count; 1647*906943f9SDavid du Colombier case Tbulk: 1648*906943f9SDavid du Colombier io = ep->aux; 1649*906943f9SDavid du Colombier if(ep->clrhalt) 1650*906943f9SDavid du Colombier clrhalt(ep); 1651*906943f9SDavid du Colombier return epio(ep, &io[OREAD], a, count, Bulktmout, 1); 1652*906943f9SDavid du Colombier case Tintr: 1653*906943f9SDavid du Colombier io = ep->aux; 1654*906943f9SDavid du Colombier delta = TK2MS(MACHP(0)->ticks) - io[OREAD].iotime + 1; 1655*906943f9SDavid du Colombier if(delta < ep->pollival / 2) 1656*906943f9SDavid du Colombier tsleep(&up->sleep, return0, 0, ep->pollival/2 - delta); 1657*906943f9SDavid du Colombier if(ep->clrhalt) 1658*906943f9SDavid du Colombier clrhalt(ep); 1659*906943f9SDavid du Colombier return epio(ep, &io[OREAD], a, count, 0, 1); 1660*906943f9SDavid du Colombier case Tiso: 1661*906943f9SDavid du Colombier panic("ohci: iso read not implemented"); 1662*906943f9SDavid du Colombier break; 1663*906943f9SDavid du Colombier default: 1664*906943f9SDavid du Colombier panic("epread: bad ep ttype %d", ep->ttype); 1665*906943f9SDavid du Colombier } 1666*906943f9SDavid du Colombier return -1; 1667*906943f9SDavid du Colombier } 16686a5dc222SDavid du Colombier 1669*906943f9SDavid du Colombier /* 1670*906943f9SDavid du Colombier * Control transfers are one setup write (data0) 1671*906943f9SDavid du Colombier * plus zero or more reads/writes (data1, data0, ...) 1672*906943f9SDavid du Colombier * plus a final write/read with data1 to ack. 1673*906943f9SDavid du Colombier * For both host to device and device to host we perform 1674*906943f9SDavid du Colombier * the entire transfer when the user writes the request, 1675*906943f9SDavid du Colombier * and keep any data read from the device for a later read. 1676*906943f9SDavid du Colombier * We call epio three times instead of placing all Tds at 1677*906943f9SDavid du Colombier * the same time because doing so leads to crc/tmout errors 1678*906943f9SDavid du Colombier * for some devices. 1679*906943f9SDavid du Colombier * Upon errors on the data phase we must still run the status 1680*906943f9SDavid du Colombier * phase or the device may cease responding in the future. 1681*906943f9SDavid du Colombier */ 1682*906943f9SDavid du Colombier static long 1683*906943f9SDavid du Colombier epctlio(Ep *ep, Ctlio *cio, void *a, long count) 1684*906943f9SDavid du Colombier { 1685*906943f9SDavid du Colombier uchar *c; 1686*906943f9SDavid du Colombier long len; 1687*906943f9SDavid du Colombier 1688*906943f9SDavid du Colombier ddeprint("epctlio: cio %#p ep%d.%d count %ld\n", 1689*906943f9SDavid du Colombier cio, ep->dev->nb, ep->nb, count); 1690*906943f9SDavid du Colombier if(count < Rsetuplen) 1691*906943f9SDavid du Colombier error("short usb command"); 1692*906943f9SDavid du Colombier qlock(cio); 1693*906943f9SDavid du Colombier free(cio->data); 1694*906943f9SDavid du Colombier cio->data = nil; 1695*906943f9SDavid du Colombier cio->ndata = 0; 1696*906943f9SDavid du Colombier if(waserror()){ 1697*906943f9SDavid du Colombier qunlock(cio); 1698*906943f9SDavid du Colombier free(cio->data); 1699*906943f9SDavid du Colombier cio->data = nil; 1700*906943f9SDavid du Colombier cio->ndata = 0; 1701*906943f9SDavid du Colombier nexterror(); 1702*906943f9SDavid du Colombier } 1703*906943f9SDavid du Colombier 1704*906943f9SDavid du Colombier /* set the address if unset and out of configuration state */ 1705*906943f9SDavid du Colombier if(ep->dev->state != Dconfig && cio->usbid == 0){ 1706*906943f9SDavid du Colombier cio->usbid = (ep->nb<<7)|(ep->dev->nb & Devmax); 1707*906943f9SDavid du Colombier edsetaddr(cio->ed, cio->usbid); 1708*906943f9SDavid du Colombier } 1709*906943f9SDavid du Colombier /* adjust maxpkt if the user has learned a different one */ 1710*906943f9SDavid du Colombier if(edmaxpkt(cio->ed) != ep->maxpkt) 1711*906943f9SDavid du Colombier edsetmaxpkt(cio->ed, ep->maxpkt); 1712*906943f9SDavid du Colombier c = a; 1713*906943f9SDavid du Colombier cio->tok = Tdtoksetup; 1714*906943f9SDavid du Colombier cio->toggle = Tddata0; 1715*906943f9SDavid du Colombier if(epio(ep, cio, a, Rsetuplen, Ctltmout, 0) < Rsetuplen) 1716*906943f9SDavid du Colombier error(Eio); 1717*906943f9SDavid du Colombier 1718*906943f9SDavid du Colombier a = c + Rsetuplen; 1719*906943f9SDavid du Colombier count -= Rsetuplen; 1720*906943f9SDavid du Colombier 1721*906943f9SDavid du Colombier cio->toggle = Tddata1; 1722*906943f9SDavid du Colombier if(c[Rtype] & Rd2h){ 1723*906943f9SDavid du Colombier cio->tok = Tdtokin; 1724*906943f9SDavid du Colombier len = GET2(c+Rcount); 1725*906943f9SDavid du Colombier if(len <= 0) 1726*906943f9SDavid du Colombier error("bad length in d2h request"); 1727*906943f9SDavid du Colombier if(len > Maxctllen) 1728*906943f9SDavid du Colombier error("d2h data too large to fit in ohci"); 1729*906943f9SDavid du Colombier a = cio->data = smalloc(len+1); 1730*906943f9SDavid du Colombier }else{ 1731*906943f9SDavid du Colombier cio->tok = Tdtokout; 1732*906943f9SDavid du Colombier len = count; 1733*906943f9SDavid du Colombier } 1734*906943f9SDavid du Colombier if(len > 0) 1735*906943f9SDavid du Colombier if(waserror()) 1736*906943f9SDavid du Colombier len = -1; 1737*906943f9SDavid du Colombier else{ 1738*906943f9SDavid du Colombier len = epio(ep, cio, a, len, Ctltmout, 0); 1739*906943f9SDavid du Colombier poperror(); 1740*906943f9SDavid du Colombier } 1741*906943f9SDavid du Colombier if(c[Rtype] & Rd2h){ 1742*906943f9SDavid du Colombier count = Rsetuplen; 1743*906943f9SDavid du Colombier cio->ndata = len; 1744*906943f9SDavid du Colombier cio->tok = Tdtokout; 1745*906943f9SDavid du Colombier }else{ 1746*906943f9SDavid du Colombier if(len < 0) 1747*906943f9SDavid du Colombier count = -1; 1748*906943f9SDavid du Colombier else 1749*906943f9SDavid du Colombier count = Rsetuplen + len; 1750*906943f9SDavid du Colombier cio->tok = Tdtokin; 1751*906943f9SDavid du Colombier } 1752*906943f9SDavid du Colombier cio->toggle = Tddata1; 1753*906943f9SDavid du Colombier epio(ep, cio, nil, 0, Ctltmout, 0); 1754*906943f9SDavid du Colombier qunlock(cio); 1755*906943f9SDavid du Colombier poperror(); 1756*906943f9SDavid du Colombier ddeprint("epctlio cio %#p return %ld\n", cio, count); 1757*906943f9SDavid du Colombier return count; 1758*906943f9SDavid du Colombier } 1759*906943f9SDavid du Colombier 1760*906943f9SDavid du Colombier /* 1761*906943f9SDavid du Colombier * Put new samples in the dummy Td. 1762*906943f9SDavid du Colombier * BUG: This does only a transfer per Td. We could do up to 8. 1763*906943f9SDavid du Colombier */ 1764*906943f9SDavid du Colombier static long 1765*906943f9SDavid du Colombier putsamples(Ctlr *ctlr, Ep *ep, Isoio *iso, uchar *b, long count) 1766*906943f9SDavid du Colombier { 1767*906943f9SDavid du Colombier Td *td; 1768*906943f9SDavid du Colombier ulong n; 1769*906943f9SDavid du Colombier 1770*906943f9SDavid du Colombier td = pa2ptr(iso->ed->tail); 1771*906943f9SDavid du Colombier n = count; 1772*906943f9SDavid du Colombier if(n > td->nbytes - BLEN(td->bp)) 1773*906943f9SDavid du Colombier n = td->nbytes - BLEN(td->bp); 1774*906943f9SDavid du Colombier assert(td->bp->wp + n <= td->bp->lim); 1775*906943f9SDavid du Colombier memmove(td->bp->wp, b, n); 1776*906943f9SDavid du Colombier td->bp->wp += n; 1777*906943f9SDavid du Colombier if(BLEN(td->bp) == td->nbytes){ /* full Td: activate it */ 1778*906943f9SDavid du Colombier ilock(ctlr); 1779*906943f9SDavid du Colombier isoadvance(ep, iso, td); 1780*906943f9SDavid du Colombier iunlock(ctlr); 1781*906943f9SDavid du Colombier } 1782*906943f9SDavid du Colombier return n; 1783*906943f9SDavid du Colombier } 1784*906943f9SDavid du Colombier 1785*906943f9SDavid du Colombier static long 1786*906943f9SDavid du Colombier episowrite(Ep *ep, void *a, long count) 1787*906943f9SDavid du Colombier { 1788*906943f9SDavid du Colombier Isoio *iso; 1789*906943f9SDavid du Colombier uchar *b; 1790*906943f9SDavid du Colombier Ctlr *ctlr; 1791*906943f9SDavid du Colombier char *err; 1792*906943f9SDavid du Colombier long tot; 1793*906943f9SDavid du Colombier long nw; 1794*906943f9SDavid du Colombier 1795*906943f9SDavid du Colombier ctlr = ep->hp->aux; 1796*906943f9SDavid du Colombier iso = ep->aux; 1797*906943f9SDavid du Colombier iso->debug = ep->debug; 1798*906943f9SDavid du Colombier 1799*906943f9SDavid du Colombier qlock(iso); 1800*906943f9SDavid du Colombier if(waserror()){ 1801*906943f9SDavid du Colombier qunlock(iso); 1802*906943f9SDavid du Colombier nexterror(); 1803*906943f9SDavid du Colombier } 1804*906943f9SDavid du Colombier diprint("ohci: episowrite: %#p ep%d.%d\n", iso, ep->dev->nb, ep->nb); 1805*906943f9SDavid du Colombier ilock(ctlr); 1806*906943f9SDavid du Colombier if(iso->state == Qclose){ 1807*906943f9SDavid du Colombier iunlock(ctlr); 1808*906943f9SDavid du Colombier error(iso->err ? iso->err : Eio); 1809*906943f9SDavid du Colombier } 1810*906943f9SDavid du Colombier iso->state = Qrun; 1811*906943f9SDavid du Colombier b = a; 1812*906943f9SDavid du Colombier for(tot = 0; tot < count; tot += nw){ 1813*906943f9SDavid du Colombier while(isocanwrite(iso) == 0){ 1814*906943f9SDavid du Colombier iunlock(ctlr); 1815*906943f9SDavid du Colombier diprint("ohci: episowrite: %#p sleep\n", iso); 1816*906943f9SDavid du Colombier if(waserror()){ 1817*906943f9SDavid du Colombier if(iso->err == nil) 1818*906943f9SDavid du Colombier iso->err = "I/O timed out"; 1819*906943f9SDavid du Colombier ilock(ctlr); 1820*906943f9SDavid du Colombier break; 1821*906943f9SDavid du Colombier } 1822*906943f9SDavid du Colombier tsleep(iso, isocanwrite, iso, Isotmout); 1823*906943f9SDavid du Colombier poperror(); 1824*906943f9SDavid du Colombier ilock(ctlr); 1825*906943f9SDavid du Colombier } 1826*906943f9SDavid du Colombier err = iso->err; 1827*906943f9SDavid du Colombier iso->err = nil; 1828*906943f9SDavid du Colombier if(iso->state == Qclose || err != nil){ 1829*906943f9SDavid du Colombier iunlock(ctlr); 1830*906943f9SDavid du Colombier error(err ? err : Eio); 1831*906943f9SDavid du Colombier } 1832*906943f9SDavid du Colombier if(iso->state != Qrun) 1833*906943f9SDavid du Colombier panic("episowrite: iso not running"); 1834*906943f9SDavid du Colombier iunlock(ctlr); /* We could page fault here */ 1835*906943f9SDavid du Colombier nw = putsamples(ctlr, ep, iso, b+tot, count-tot); 1836*906943f9SDavid du Colombier ilock(ctlr); 1837*906943f9SDavid du Colombier } 1838*906943f9SDavid du Colombier if(iso->state != Qclose) 1839*906943f9SDavid du Colombier iso->state = Qdone; 1840*906943f9SDavid du Colombier iunlock(ctlr); 1841*906943f9SDavid du Colombier err = iso->err; /* in case it failed early */ 1842*906943f9SDavid du Colombier iso->err = nil; 1843*906943f9SDavid du Colombier qunlock(iso); 1844*906943f9SDavid du Colombier poperror(); 1845*906943f9SDavid du Colombier if(err != nil) 1846*906943f9SDavid du Colombier error(err); 1847*906943f9SDavid du Colombier diprint("ohci: episowrite: %#p %ld bytes\n", iso, tot); 1848*906943f9SDavid du Colombier return tot; 1849*906943f9SDavid du Colombier } 1850*906943f9SDavid du Colombier 1851*906943f9SDavid du Colombier static long 1852*906943f9SDavid du Colombier epwrite(Ep *ep, void *a, long count) 1853*906943f9SDavid du Colombier { 1854*906943f9SDavid du Colombier Qio *io; 1855*906943f9SDavid du Colombier Ctlio *cio; 1856*906943f9SDavid du Colombier ulong delta; 1857*906943f9SDavid du Colombier uchar *b; 1858*906943f9SDavid du Colombier long tot; 1859*906943f9SDavid du Colombier long nw; 1860*906943f9SDavid du Colombier 1861*906943f9SDavid du Colombier if(ep->aux == nil) 1862*906943f9SDavid du Colombier panic("ohci: epwrite: not open"); 1863*906943f9SDavid du Colombier switch(ep->ttype){ 1864*906943f9SDavid du Colombier case Tctl: 1865*906943f9SDavid du Colombier cio = ep->aux; 1866*906943f9SDavid du Colombier return epctlio(ep, cio, a, count); 1867*906943f9SDavid du Colombier case Tbulk: 1868*906943f9SDavid du Colombier io = ep->aux; 1869*906943f9SDavid du Colombier if(ep->clrhalt) 1870*906943f9SDavid du Colombier clrhalt(ep); 1871*906943f9SDavid du Colombier /* 1872*906943f9SDavid du Colombier * Put at most Tdatomic Tds (512 bytes) at a time. 1873*906943f9SDavid du Colombier * Otherwise some devices produce babble errors. 1874*906943f9SDavid du Colombier */ 1875*906943f9SDavid du Colombier b = a; 1876*906943f9SDavid du Colombier for(tot = 0; tot < count ; tot += nw){ 1877*906943f9SDavid du Colombier nw = count - tot; 1878*906943f9SDavid du Colombier if(nw > Tdatomic * ep->maxpkt) 1879*906943f9SDavid du Colombier nw = Tdatomic * ep->maxpkt; 1880*906943f9SDavid du Colombier nw = epio(ep, &io[OWRITE], b+tot, nw, Bulktmout, 1); 1881*906943f9SDavid du Colombier } 1882*906943f9SDavid du Colombier return tot; 1883*906943f9SDavid du Colombier case Tintr: 1884*906943f9SDavid du Colombier io = ep->aux; 1885*906943f9SDavid du Colombier delta = TK2MS(MACHP(0)->ticks) - io[OWRITE].iotime + 1; 1886*906943f9SDavid du Colombier if(delta < ep->pollival) 1887*906943f9SDavid du Colombier tsleep(&up->sleep, return0, 0, ep->pollival - delta); 1888*906943f9SDavid du Colombier if(ep->clrhalt) 1889*906943f9SDavid du Colombier clrhalt(ep); 1890*906943f9SDavid du Colombier return epio(ep, &io[OWRITE], a, count, 0, 1); 1891*906943f9SDavid du Colombier case Tiso: 1892*906943f9SDavid du Colombier return episowrite(ep, a, count); 1893*906943f9SDavid du Colombier default: 1894*906943f9SDavid du Colombier panic("ohci: epwrite: bad ep ttype %d", ep->ttype); 1895*906943f9SDavid du Colombier } 1896*906943f9SDavid du Colombier return -1; 1897*906943f9SDavid du Colombier } 1898*906943f9SDavid du Colombier 1899*906943f9SDavid du Colombier static Ed* 1900*906943f9SDavid du Colombier newed(Ctlr *ctlr, Ep *ep, Qio *io, char *) 1901*906943f9SDavid du Colombier { 1902*906943f9SDavid du Colombier Ed *ed; 1903*906943f9SDavid du Colombier Td *td; 1904*906943f9SDavid du Colombier 1905*906943f9SDavid du Colombier ed = io->ed = edalloc(); /* no errors raised here, really */ 1906*906943f9SDavid du Colombier td = tdalloc(); 1907*906943f9SDavid du Colombier td->ep = ep; 1908*906943f9SDavid du Colombier td->io = io; 1909*906943f9SDavid du Colombier ed->tail = ptr2pa(td); 1910*906943f9SDavid du Colombier ed->head = ptr2pa(td); 1911*906943f9SDavid du Colombier ed->tds = td; 1912*906943f9SDavid du Colombier ed->ep = ep; 1913*906943f9SDavid du Colombier ed->ctrl = (ep->maxpkt & Edmpsmask) << Edmpsshift; 1914*906943f9SDavid du Colombier if(ep->ttype == Tiso) 1915*906943f9SDavid du Colombier ed->ctrl |= Ediso; 1916*906943f9SDavid du Colombier if(waserror()){ 1917*906943f9SDavid du Colombier edfree(ed); 1918*906943f9SDavid du Colombier io->ed = nil; 1919*906943f9SDavid du Colombier nexterror(); 1920*906943f9SDavid du Colombier } 1921*906943f9SDavid du Colombier /* For setup endpoints we start with the config address */ 1922*906943f9SDavid du Colombier if(ep->ttype != Tctl) 1923*906943f9SDavid du Colombier edsetaddr(io->ed, io->usbid); 1924*906943f9SDavid du Colombier if(ep->dev->speed == Lowspeed) 1925*906943f9SDavid du Colombier ed->ctrl |= Edlow; 1926*906943f9SDavid du Colombier switch(io->tok){ 1927*906943f9SDavid du Colombier case Tdtokin: 1928*906943f9SDavid du Colombier ed->ctrl |= Edin; 1929*906943f9SDavid du Colombier break; 1930*906943f9SDavid du Colombier case Tdtokout: 1931*906943f9SDavid du Colombier ed->ctrl |= Edout; 1932*906943f9SDavid du Colombier break; 1933*906943f9SDavid du Colombier default: 1934*906943f9SDavid du Colombier ed->ctrl |= Edtddir; /* Td will say */ 1935*906943f9SDavid du Colombier break; 1936*906943f9SDavid du Colombier } 1937*906943f9SDavid du Colombier 1938*906943f9SDavid du Colombier switch(ep->ttype){ 1939*906943f9SDavid du Colombier case Tctl: 1940*906943f9SDavid du Colombier ilock(ctlr); 1941*906943f9SDavid du Colombier edlinked(ed, ctlhd(ctlr)); 1942*906943f9SDavid du Colombier setctlhd(ctlr, ed); 1943*906943f9SDavid du Colombier iunlock(ctlr); 1944*906943f9SDavid du Colombier break; 1945*906943f9SDavid du Colombier case Tbulk: 1946*906943f9SDavid du Colombier ilock(ctlr); 1947*906943f9SDavid du Colombier edlinked(ed, bulkhd(ctlr)); 1948*906943f9SDavid du Colombier setbulkhd(ctlr, ed); 1949*906943f9SDavid du Colombier iunlock(ctlr); 1950*906943f9SDavid du Colombier break; 1951*906943f9SDavid du Colombier case Tintr: 1952*906943f9SDavid du Colombier case Tiso: 1953*906943f9SDavid du Colombier ilock(ctlr); 1954*906943f9SDavid du Colombier schedq(ctlr, io, ep->pollival); 1955*906943f9SDavid du Colombier iunlock(ctlr); 1956*906943f9SDavid du Colombier break; 1957*906943f9SDavid du Colombier default: 1958*906943f9SDavid du Colombier panic("ohci: newed: bad ttype"); 1959*906943f9SDavid du Colombier } 1960*906943f9SDavid du Colombier poperror(); 1961*906943f9SDavid du Colombier return ed; 1962*906943f9SDavid du Colombier } 1963*906943f9SDavid du Colombier 1964*906943f9SDavid du Colombier static void 1965*906943f9SDavid du Colombier isoopen(Ctlr *ctlr, Ep *ep) 1966*906943f9SDavid du Colombier { 1967*906943f9SDavid du Colombier Td *td; 1968*906943f9SDavid du Colombier Td *edtds; 1969*906943f9SDavid du Colombier Isoio *iso; 1970*906943f9SDavid du Colombier int i; 1971*906943f9SDavid du Colombier 1972*906943f9SDavid du Colombier iso = ep->aux; 1973*906943f9SDavid du Colombier iso->usbid = (ep->nb<<7)|(ep->dev->nb & Devmax); 1974*906943f9SDavid du Colombier iso->bw = ep->hz * ep->samplesz; /* bytes/sec */ 1975*906943f9SDavid du Colombier if(ep->mode != OWRITE){ 1976*906943f9SDavid du Colombier print("ohci: bug: iso input streams not implemented\n"); 1977*906943f9SDavid du Colombier error("ohci iso input streams not implemented"); 1978*906943f9SDavid du Colombier }else 1979*906943f9SDavid du Colombier iso->tok = Tdtokout; 1980*906943f9SDavid du Colombier 1981*906943f9SDavid du Colombier iso->left = 0; 1982*906943f9SDavid du Colombier iso->nerrs = 0; 1983*906943f9SDavid du Colombier iso->frno = TRUNC(ctlr->ohci->fmnumber + 10, Ntdframes); 1984*906943f9SDavid du Colombier iso->nframes = 1000 / ep->pollival; 1985*906943f9SDavid du Colombier if(iso->nframes < 10){ 1986*906943f9SDavid du Colombier print("ohci: isoopen: less than 10 frames; using 10.\n"); 1987*906943f9SDavid du Colombier iso->nframes = 10; 1988*906943f9SDavid du Colombier } 1989*906943f9SDavid du Colombier iso->navail = iso->nframes; 1990*906943f9SDavid du Colombier iso->atds = edtds = nil; 1991*906943f9SDavid du Colombier for(i = 0; i < iso->nframes-1; i++){ /* -1 for dummy */ 1992*906943f9SDavid du Colombier td = tdalloc(); 1993*906943f9SDavid du Colombier td->ep = ep; 1994*906943f9SDavid du Colombier td->io = iso; 1995*906943f9SDavid du Colombier td->bp = allocb(ep->maxpkt); 1996*906943f9SDavid du Colombier td->anext = iso->atds; /* link as avail */ 1997*906943f9SDavid du Colombier iso->atds = td; 1998*906943f9SDavid du Colombier td->next = edtds; 1999*906943f9SDavid du Colombier edtds = td; 2000*906943f9SDavid du Colombier } 2001*906943f9SDavid du Colombier newed(ctlr, ep, iso, "iso"); /* allocates a dummy td */ 2002*906943f9SDavid du Colombier iso->ed->tds->bp = allocb(ep->maxpkt); /* but not its block */ 2003*906943f9SDavid du Colombier iso->ed->tds->next = edtds; 2004*906943f9SDavid du Colombier isodtdinit(ep, iso, iso->ed->tds); 2005*906943f9SDavid du Colombier } 2006*906943f9SDavid du Colombier 2007*906943f9SDavid du Colombier /* 2008*906943f9SDavid du Colombier * Allocate the endpoint and set it up for I/O 2009*906943f9SDavid du Colombier * in the controller. This must follow what's said 2010*906943f9SDavid du Colombier * in Ep regarding configuration, including perhaps 2011*906943f9SDavid du Colombier * the saved toggles (saved on a previous close of 2012*906943f9SDavid du Colombier * the endpoint data file by epclose). 2013*906943f9SDavid du Colombier */ 2014*906943f9SDavid du Colombier static void 2015*906943f9SDavid du Colombier epopen(Ep *ep) 2016*906943f9SDavid du Colombier { 2017*906943f9SDavid du Colombier Ctlr *ctlr; 2018*906943f9SDavid du Colombier Qio *io; 2019*906943f9SDavid du Colombier Ctlio *cio; 2020*906943f9SDavid du Colombier ulong usbid; 2021*906943f9SDavid du Colombier 2022*906943f9SDavid du Colombier ctlr = ep->hp->aux; 2023*906943f9SDavid du Colombier deprint("ohci: epopen ep%d.%d\n", ep->dev->nb, ep->nb); 2024*906943f9SDavid du Colombier if(ep->aux != nil) 2025*906943f9SDavid du Colombier panic("ohci: epopen called with open ep"); 2026*906943f9SDavid du Colombier if(waserror()){ 2027*906943f9SDavid du Colombier free(ep->aux); 2028*906943f9SDavid du Colombier ep->aux = nil; 2029*906943f9SDavid du Colombier nexterror(); 2030*906943f9SDavid du Colombier } 2031*906943f9SDavid du Colombier switch(ep->ttype){ 2032*906943f9SDavid du Colombier case Tnone: 2033*906943f9SDavid du Colombier error("endpoint not configured"); 2034*906943f9SDavid du Colombier case Tiso: 2035*906943f9SDavid du Colombier ep->aux = smalloc(sizeof(Isoio)); 2036*906943f9SDavid du Colombier isoopen(ctlr, ep); 2037*906943f9SDavid du Colombier break; 2038*906943f9SDavid du Colombier case Tctl: 2039*906943f9SDavid du Colombier cio = ep->aux = smalloc(sizeof(Ctlio)); 2040*906943f9SDavid du Colombier cio->debug = ep->debug; 2041*906943f9SDavid du Colombier cio->ndata = -1; 2042*906943f9SDavid du Colombier cio->data = nil; 2043*906943f9SDavid du Colombier cio->tok = -1; /* invalid; Tds will say */ 2044*906943f9SDavid du Colombier if(ep->dev->isroot != 0 && ep->nb == 0) /* root hub */ 2045*906943f9SDavid du Colombier break; 2046*906943f9SDavid du Colombier newed(ctlr, ep, cio, "epc"); 2047*906943f9SDavid du Colombier break; 2048*906943f9SDavid du Colombier case Tbulk: 2049*906943f9SDavid du Colombier ep->pollival = 1; /* assume this; doesn't really matter */ 2050*906943f9SDavid du Colombier /* and fall... */ 2051*906943f9SDavid du Colombier case Tintr: 2052*906943f9SDavid du Colombier io = ep->aux = smalloc(sizeof(Qio)*2); 2053*906943f9SDavid du Colombier io[OREAD].debug = io[OWRITE].debug = ep->debug; 2054*906943f9SDavid du Colombier usbid = (ep->nb<<7)|(ep->dev->nb & Devmax); 2055*906943f9SDavid du Colombier if(ep->mode != OREAD){ 2056*906943f9SDavid du Colombier if(ep->toggle[OWRITE] != 0) 2057*906943f9SDavid du Colombier io[OWRITE].toggle = Tddata1; 2058*906943f9SDavid du Colombier else 2059*906943f9SDavid du Colombier io[OWRITE].toggle = Tddata0; 2060*906943f9SDavid du Colombier io[OWRITE].tok = Tdtokout; 2061*906943f9SDavid du Colombier io[OWRITE].usbid = usbid; 2062*906943f9SDavid du Colombier io[OWRITE].bw = ep->maxpkt*1000/ep->pollival; /* bytes/s */ 2063*906943f9SDavid du Colombier newed(ctlr, ep, io+OWRITE, "epw"); 2064*906943f9SDavid du Colombier } 2065*906943f9SDavid du Colombier if(ep->mode != OWRITE){ 2066*906943f9SDavid du Colombier if(ep->toggle[OREAD] != 0) 2067*906943f9SDavid du Colombier io[OREAD].toggle = Tddata1; 2068*906943f9SDavid du Colombier else 2069*906943f9SDavid du Colombier io[OREAD].toggle = Tddata0; 2070*906943f9SDavid du Colombier io[OREAD].tok = Tdtokin; 2071*906943f9SDavid du Colombier io[OREAD].usbid = usbid; 2072*906943f9SDavid du Colombier io[OREAD].bw = ep->maxpkt*1000/ep->pollival; /* bytes/s */ 2073*906943f9SDavid du Colombier newed(ctlr, ep, io+OREAD, "epr"); 2074*906943f9SDavid du Colombier } 2075*906943f9SDavid du Colombier break; 2076*906943f9SDavid du Colombier } 2077*906943f9SDavid du Colombier deprint("ohci: epopen done:\n"); 2078*906943f9SDavid du Colombier if(debug || ep->debug) 2079*906943f9SDavid du Colombier dump(ep->hp); 2080*906943f9SDavid du Colombier poperror(); 2081*906943f9SDavid du Colombier } 2082*906943f9SDavid du Colombier 2083*906943f9SDavid du Colombier static void 2084*906943f9SDavid du Colombier cancelio(Ep *ep, Qio *io) 2085*906943f9SDavid du Colombier { 2086*906943f9SDavid du Colombier Ed *ed; 2087*906943f9SDavid du Colombier Ctlr *ctlr; 2088*906943f9SDavid du Colombier 2089*906943f9SDavid du Colombier ctlr = ep->hp->aux; 2090*906943f9SDavid du Colombier ed = io->ed; 2091*906943f9SDavid du Colombier 2092*906943f9SDavid du Colombier ilock(ctlr); 2093*906943f9SDavid du Colombier if(io == nil || io->state == Qclose){ 2094*906943f9SDavid du Colombier assert(io->ed == nil); 2095*906943f9SDavid du Colombier iunlock(ctlr); 2096*906943f9SDavid du Colombier return; 2097*906943f9SDavid du Colombier } 2098*906943f9SDavid du Colombier io->state = Qclose; 2099*906943f9SDavid du Colombier io->err = Eio; 2100*906943f9SDavid du Colombier aborttds(io); 2101*906943f9SDavid du Colombier iunlock(ctlr); 2102*906943f9SDavid du Colombier if(!waserror()){ 2103*906943f9SDavid du Colombier tsleep(&up->sleep, return0, 0, Abortdelay); 2104*906943f9SDavid du Colombier poperror(); 2105*906943f9SDavid du Colombier } 2106*906943f9SDavid du Colombier 2107*906943f9SDavid du Colombier wakeup(io); 2108*906943f9SDavid du Colombier qlock(io); 2109*906943f9SDavid du Colombier /* wait for epio if running */ 2110*906943f9SDavid du Colombier qunlock(io); 2111*906943f9SDavid du Colombier 2112*906943f9SDavid du Colombier ilock(ctlr); 2113*906943f9SDavid du Colombier switch(ep->ttype){ 2114*906943f9SDavid du Colombier case Tctl: 2115*906943f9SDavid du Colombier unlinkctl(ctlr, ed); 2116*906943f9SDavid du Colombier break; 2117*906943f9SDavid du Colombier case Tbulk: 2118*906943f9SDavid du Colombier unlinkbulk(ctlr, ed); 2119*906943f9SDavid du Colombier break; 2120*906943f9SDavid du Colombier case Tintr: 2121*906943f9SDavid du Colombier case Tiso: 2122*906943f9SDavid du Colombier unschedq(ctlr, io); 2123*906943f9SDavid du Colombier break; 2124*906943f9SDavid du Colombier default: 2125*906943f9SDavid du Colombier panic("ohci cancelio: bad ttype"); 2126*906943f9SDavid du Colombier } 2127*906943f9SDavid du Colombier iunlock(ctlr); 2128*906943f9SDavid du Colombier edfree(io->ed); 2129*906943f9SDavid du Colombier io->ed = nil; 2130*906943f9SDavid du Colombier } 2131*906943f9SDavid du Colombier 2132*906943f9SDavid du Colombier static void 2133*906943f9SDavid du Colombier epclose(Ep *ep) 2134*906943f9SDavid du Colombier { 2135*906943f9SDavid du Colombier Ctlio *cio; 2136*906943f9SDavid du Colombier Isoio *iso; 2137*906943f9SDavid du Colombier Qio *io; 2138*906943f9SDavid du Colombier 2139*906943f9SDavid du Colombier deprint("ohci: epclose ep%d.%d\n", ep->dev->nb, ep->nb); 2140*906943f9SDavid du Colombier if(ep->aux == nil) 2141*906943f9SDavid du Colombier panic("ohci: epclose called with closed ep"); 2142*906943f9SDavid du Colombier switch(ep->ttype){ 2143*906943f9SDavid du Colombier case Tctl: 2144*906943f9SDavid du Colombier cio = ep->aux; 2145*906943f9SDavid du Colombier cancelio(ep, cio); 2146*906943f9SDavid du Colombier free(cio->data); 2147*906943f9SDavid du Colombier cio->data = nil; 2148*906943f9SDavid du Colombier break; 2149*906943f9SDavid du Colombier case Tbulk: 2150*906943f9SDavid du Colombier case Tintr: 2151*906943f9SDavid du Colombier io = ep->aux; 2152*906943f9SDavid du Colombier if(ep->mode != OWRITE){ 2153*906943f9SDavid du Colombier cancelio(ep, &io[OREAD]); 2154*906943f9SDavid du Colombier if(io[OREAD].toggle == Tddata1) 2155*906943f9SDavid du Colombier ep->toggle[OREAD] = 1; 2156*906943f9SDavid du Colombier } 2157*906943f9SDavid du Colombier if(ep->mode != OREAD){ 2158*906943f9SDavid du Colombier cancelio(ep, &io[OWRITE]); 2159*906943f9SDavid du Colombier if(io[OWRITE].toggle == Tddata1) 2160*906943f9SDavid du Colombier ep->toggle[OWRITE] = 1; 2161*906943f9SDavid du Colombier } 2162*906943f9SDavid du Colombier break; 2163*906943f9SDavid du Colombier case Tiso: 2164*906943f9SDavid du Colombier iso = ep->aux; 2165*906943f9SDavid du Colombier cancelio(ep, iso); 2166*906943f9SDavid du Colombier break; 2167*906943f9SDavid du Colombier default: 2168*906943f9SDavid du Colombier panic("epclose: bad ttype %d", ep->ttype); 2169*906943f9SDavid du Colombier } 2170*906943f9SDavid du Colombier 2171*906943f9SDavid du Colombier deprint("ohci: epclose ep%d.%d: done\n", ep->dev->nb, ep->nb); 2172*906943f9SDavid du Colombier free(ep->aux); 2173*906943f9SDavid du Colombier ep->aux = nil; 2174*906943f9SDavid du Colombier } 2175*906943f9SDavid du Colombier 2176*906943f9SDavid du Colombier static int 2177*906943f9SDavid du Colombier portreset(Hci *hp, int port, int on) 2178*906943f9SDavid du Colombier { 2179*906943f9SDavid du Colombier Ctlr *ctlr; 2180*906943f9SDavid du Colombier 2181*906943f9SDavid du Colombier if(on == 0) 2182*906943f9SDavid du Colombier return 0; 2183*906943f9SDavid du Colombier 2184*906943f9SDavid du Colombier ctlr = hp->aux; 2185*906943f9SDavid du Colombier qlock(&ctlr->resetl); 2186*906943f9SDavid du Colombier if(waserror()){ 2187*906943f9SDavid du Colombier qunlock(&ctlr->resetl); 2188*906943f9SDavid du Colombier nexterror(); 2189*906943f9SDavid du Colombier } 2190*906943f9SDavid du Colombier ilock(ctlr); 2191*906943f9SDavid du Colombier ctlr->ohci->rhportsts[port - 1] = Spp | Spr; 2192*906943f9SDavid du Colombier while((ctlr->ohci->rhportsts[port - 1] & Prsc) == 0){ 2193*906943f9SDavid du Colombier iunlock(ctlr); 2194*906943f9SDavid du Colombier dprint("ohci: portreset, wait for reset complete\n"); 2195*906943f9SDavid du Colombier ilock(ctlr); 2196*906943f9SDavid du Colombier } 2197*906943f9SDavid du Colombier ctlr->ohci->rhportsts[port - 1] = Prsc; 2198*906943f9SDavid du Colombier iunlock(ctlr); 2199*906943f9SDavid du Colombier poperror(); 2200*906943f9SDavid du Colombier qunlock(&ctlr->resetl); 2201*906943f9SDavid du Colombier return 0; 2202*906943f9SDavid du Colombier } 2203*906943f9SDavid du Colombier 2204*906943f9SDavid du Colombier static int 2205*906943f9SDavid du Colombier portenable(Hci *hp, int port, int on) 2206*906943f9SDavid du Colombier { 2207*906943f9SDavid du Colombier Ctlr *ctlr; 2208*906943f9SDavid du Colombier 2209*906943f9SDavid du Colombier ctlr = hp->aux; 2210*906943f9SDavid du Colombier dprint("ohci: %#p port %d enable=%d\n", ctlr->ohci, port, on); 2211*906943f9SDavid du Colombier qlock(&ctlr->resetl); 2212*906943f9SDavid du Colombier if(waserror()){ 2213*906943f9SDavid du Colombier qunlock(&ctlr->resetl); 2214*906943f9SDavid du Colombier nexterror(); 2215*906943f9SDavid du Colombier } 2216*906943f9SDavid du Colombier ilock(ctlr); 2217*906943f9SDavid du Colombier if(on) 2218*906943f9SDavid du Colombier ctlr->ohci->rhportsts[port - 1] = Spe | Spp; 2219*906943f9SDavid du Colombier else 2220*906943f9SDavid du Colombier ctlr->ohci->rhportsts[port - 1] = Cpe; 2221*906943f9SDavid du Colombier iunlock(ctlr); 2222*906943f9SDavid du Colombier tsleep(&up->sleep, return0, 0, Enabledelay); 2223*906943f9SDavid du Colombier poperror(); 2224*906943f9SDavid du Colombier qunlock(&ctlr->resetl); 2225*906943f9SDavid du Colombier return 0; 2226*906943f9SDavid du Colombier } 2227*906943f9SDavid du Colombier 2228*906943f9SDavid du Colombier static int 2229*906943f9SDavid du Colombier portstatus(Hci *hp, int port) 2230*906943f9SDavid du Colombier { 2231*906943f9SDavid du Colombier int v; 2232*906943f9SDavid du Colombier Ctlr *ub; 2233*906943f9SDavid du Colombier ulong ohcistatus; 2234*906943f9SDavid du Colombier 2235*906943f9SDavid du Colombier /* 2236*906943f9SDavid du Colombier * We must return status bits as a 2237*906943f9SDavid du Colombier * get port status hub request would do. 2238*906943f9SDavid du Colombier */ 2239*906943f9SDavid du Colombier ub = hp->aux; 2240*906943f9SDavid du Colombier ohcistatus = ub->ohci->rhportsts[port - 1]; 2241*906943f9SDavid du Colombier v = 0; 2242*906943f9SDavid du Colombier if(ohcistatus & Ccs) 2243*906943f9SDavid du Colombier v |= HPpresent; 2244*906943f9SDavid du Colombier if(ohcistatus & Pes) 2245*906943f9SDavid du Colombier v |= HPenable; 2246*906943f9SDavid du Colombier if(ohcistatus & Pss) 2247*906943f9SDavid du Colombier v |= HPsuspend; 2248*906943f9SDavid du Colombier if(ohcistatus & Prs) 2249*906943f9SDavid du Colombier v |= HPreset; 2250*906943f9SDavid du Colombier else { 2251*906943f9SDavid du Colombier /* port is not in reset; these potential writes are ok */ 2252*906943f9SDavid du Colombier if(ohcistatus & Csc){ 2253*906943f9SDavid du Colombier v |= HPstatuschg; 2254*906943f9SDavid du Colombier ub->ohci->rhportsts[port - 1] = Csc; 2255*906943f9SDavid du Colombier } 2256*906943f9SDavid du Colombier if(ohcistatus & Pesc){ 2257*906943f9SDavid du Colombier v |= HPchange; 2258*906943f9SDavid du Colombier ub->ohci->rhportsts[port - 1] = Pesc; 22596a5dc222SDavid du Colombier } 22606a5dc222SDavid du Colombier } 2261*906943f9SDavid du Colombier if(ohcistatus & Lsda) 2262*906943f9SDavid du Colombier v |= HPslow; 2263*906943f9SDavid du Colombier if(v & (HPstatuschg|HPchange)) 2264*906943f9SDavid du Colombier ddprint("ohci port %d sts %#ulx hub sts %#x\n", port, ohcistatus, v); 2265*906943f9SDavid du Colombier return v; 2266*906943f9SDavid du Colombier } 2267*906943f9SDavid du Colombier 2268*906943f9SDavid du Colombier static void 2269*906943f9SDavid du Colombier dumpohci(Ctlr *ctlr) 2270*906943f9SDavid du Colombier { 2271*906943f9SDavid du Colombier int i; 2272*906943f9SDavid du Colombier ulong *ohci; 2273*906943f9SDavid du Colombier 2274*906943f9SDavid du Colombier ohci = &ctlr->ohci->revision; 2275*906943f9SDavid du Colombier print("ohci registers: \n"); 2276*906943f9SDavid du Colombier for(i = 0; i < sizeof(Ohci)/sizeof(ulong); i++) 2277*906943f9SDavid du Colombier if(i < 3 || ohci[i] != 0) 2278*906943f9SDavid du Colombier print("\t[%#2.2x]\t%#8.8ulx\n", i * 4, ohci[i]); 2279*906943f9SDavid du Colombier print("\n"); 2280*906943f9SDavid du Colombier } 2281*906943f9SDavid du Colombier 2282*906943f9SDavid du Colombier static void 2283*906943f9SDavid du Colombier init(Hci *hp) 2284*906943f9SDavid du Colombier { 2285*906943f9SDavid du Colombier Ctlr *ctlr; 2286*906943f9SDavid du Colombier Ohci *ohci; 2287*906943f9SDavid du Colombier int i; 2288*906943f9SDavid du Colombier ulong ival; 2289*906943f9SDavid du Colombier ulong ctrl; 2290*906943f9SDavid du Colombier ulong fmi; 2291*906943f9SDavid du Colombier 2292*906943f9SDavid du Colombier ctlr = hp->aux; 2293*906943f9SDavid du Colombier dprint("ohci %#p init\n", ctlr->ohci); 2294*906943f9SDavid du Colombier ohci = ctlr->ohci; 2295*906943f9SDavid du Colombier 2296*906943f9SDavid du Colombier fmi = ctlr->ohci->fminterval; 2297*906943f9SDavid du Colombier ctlr->ohci->cmdsts = Shcr; /* reset the block */ 2298*906943f9SDavid du Colombier while(ctlr->ohci->cmdsts & Shcr) 2299*906943f9SDavid du Colombier delay(1); /* wait till reset complete, Ohci says 10us max. */ 2300*906943f9SDavid du Colombier ctlr->ohci->fminterval = fmi; 2301*906943f9SDavid du Colombier 2302*906943f9SDavid du Colombier /* 2303*906943f9SDavid du Colombier * now that soft reset is done we are in suspend state. 2304*906943f9SDavid du Colombier * Setup registers which take in suspend state 2305*906943f9SDavid du Colombier * (will only be here for 2ms). 2306*906943f9SDavid du Colombier */ 2307*906943f9SDavid du Colombier 2308*906943f9SDavid du Colombier ctlr->ohci->hcca = ptr2pa(ctlr->hcca); 2309*906943f9SDavid du Colombier setctlhd(ctlr, nil); 2310*906943f9SDavid du Colombier ctlr->ohci->ctlcurred = 0; 2311*906943f9SDavid du Colombier setbulkhd(ctlr, nil); 2312*906943f9SDavid du Colombier ctlr->ohci->bulkcurred = 0; 2313*906943f9SDavid du Colombier 2314*906943f9SDavid du Colombier ohci->intrenable = Mie | Wdh | Ue; 2315*906943f9SDavid du Colombier ohci->control |= Ccle | Cble | Cple | Cie | Cfsoper; 2316*906943f9SDavid du Colombier 2317*906943f9SDavid du Colombier /* set frame after operational */ 2318*906943f9SDavid du Colombier ival = ohci->fminterval & ~(Fmaxpktmask << Fmaxpktshift); 2319*906943f9SDavid du Colombier ohci->fminterval = ival | (5120 << Fmaxpktshift); 2320*906943f9SDavid du Colombier ohci->rhdesca = Nps; /* no power switching */ 2321*906943f9SDavid du Colombier if(ohci->rhdesca & Nps){ 2322*906943f9SDavid du Colombier dprint("ohci: ports are not power switched\n"); 2323*906943f9SDavid du Colombier }else{ 2324*906943f9SDavid du Colombier dprint("ohci: ports are power switched\n"); 2325*906943f9SDavid du Colombier ohci->rhdesca &= ~Psm; 2326*906943f9SDavid du Colombier ohci->rhsts &= ~Lpsc; 2327*906943f9SDavid du Colombier } 2328*906943f9SDavid du Colombier for(i = 0; i < ctlr->nports; i++) /* paranoia */ 2329*906943f9SDavid du Colombier ohci->rhportsts[i] = 0; /* this has no effect */ 2330*906943f9SDavid du Colombier delay(50); 2331*906943f9SDavid du Colombier 2332*906943f9SDavid du Colombier for(i = 0; i < ctlr->nports; i++) 2333*906943f9SDavid du Colombier ohci->rhportsts[i] = Spp | Spr; 2334*906943f9SDavid du Colombier delay(100); 2335*906943f9SDavid du Colombier 2336*906943f9SDavid du Colombier ctrl = ohci->control; 2337*906943f9SDavid du Colombier if((ctrl & Cfsmask) != Cfsoper){ 2338*906943f9SDavid du Colombier ctrl = (ctrl & ~Cfsmask) | Cfsoper; 2339*906943f9SDavid du Colombier ohci->control = ctrl; 2340*906943f9SDavid du Colombier ohci->rhsts = Sgp; 2341*906943f9SDavid du Colombier } 2342*906943f9SDavid du Colombier 2343*906943f9SDavid du Colombier if(debug > 1) 2344*906943f9SDavid du Colombier dumpohci(ctlr); 2345*906943f9SDavid du Colombier } 2346*906943f9SDavid du Colombier 2347*906943f9SDavid du Colombier static void 2348*906943f9SDavid du Colombier scanpci(void) 2349*906943f9SDavid du Colombier { 2350*906943f9SDavid du Colombier ulong mem; 2351*906943f9SDavid du Colombier Ctlr *ctlr; 2352*906943f9SDavid du Colombier Pcidev *p; 2353*906943f9SDavid du Colombier int i; 2354*906943f9SDavid du Colombier static int already = 0; 2355*906943f9SDavid du Colombier 2356*906943f9SDavid du Colombier if(already) 2357*906943f9SDavid du Colombier return; 2358*906943f9SDavid du Colombier already = 1; 2359*906943f9SDavid du Colombier p = nil; 2360*906943f9SDavid du Colombier while(p = pcimatch(p, 0, 0)) { 2361*906943f9SDavid du Colombier /* 2362*906943f9SDavid du Colombier * Find Ohci controllers (Programming Interface = 0x10). 2363*906943f9SDavid du Colombier */ 2364*906943f9SDavid du Colombier if(p->ccrb != Pcibcserial || p->ccru != Pciscusb || 2365*906943f9SDavid du Colombier p->ccrp != 0x10) 2366*906943f9SDavid du Colombier continue; 2367*906943f9SDavid du Colombier mem = p->mem[0].bar & ~0x0F; 2368*906943f9SDavid du Colombier dprint("ohci: %x/%x port 0x%lux size 0x%x irq %d\n", 2369*906943f9SDavid du Colombier p->vid, p->did, mem, p->mem[0].size, p->intl); 2370*906943f9SDavid du Colombier if(mem == 0){ 2371*906943f9SDavid du Colombier print("ohci: failed to map registers\n"); 2372*906943f9SDavid du Colombier continue; 2373*906943f9SDavid du Colombier } 2374*906943f9SDavid du Colombier if(p->intl == 0xFF || p->intl == 0) { 2375*906943f9SDavid du Colombier print("ohci: no irq assigned for port %#lux\n", mem); 2376*906943f9SDavid du Colombier continue; 2377*906943f9SDavid du Colombier } 2378*906943f9SDavid du Colombier 2379*906943f9SDavid du Colombier ctlr = mallocz(sizeof(Ctlr), 1); 2380*906943f9SDavid du Colombier ctlr->pcidev = p; 2381*906943f9SDavid du Colombier ctlr->ohci = vmap(mem, p->mem[0].size); 2382*906943f9SDavid du Colombier dprint("scanpci: ctlr %#p, ohci %#p\n", ctlr, ctlr->ohci); 2383*906943f9SDavid du Colombier pcisetbme(p); 2384*906943f9SDavid du Colombier pcisetpms(p, 0); 2385*906943f9SDavid du Colombier for(i = 0; i < Nhcis; i++) 2386*906943f9SDavid du Colombier if(ctlrs[i] == nil){ 2387*906943f9SDavid du Colombier ctlrs[i] = ctlr; 2388*906943f9SDavid du Colombier break; 2389*906943f9SDavid du Colombier } 2390*906943f9SDavid du Colombier if(i == Nhcis) 2391*906943f9SDavid du Colombier print("ohci: bug: no more controllers\n"); 2392*906943f9SDavid du Colombier } 2393*906943f9SDavid du Colombier } 2394*906943f9SDavid du Colombier 2395*906943f9SDavid du Colombier static void 2396*906943f9SDavid du Colombier usbdebug(Hci*, int d) 2397*906943f9SDavid du Colombier { 2398*906943f9SDavid du Colombier debug = d; 23996a5dc222SDavid du Colombier } 24006a5dc222SDavid du Colombier 24016a5dc222SDavid du Colombier /* 24026a5dc222SDavid du Colombier * build the periodic scheduling tree: 24036a5dc222SDavid du Colombier * framesize must be a multiple of the tree size 24046a5dc222SDavid du Colombier */ 2405*906943f9SDavid du Colombier static void 2406*906943f9SDavid du Colombier mkqhtree(Ctlr *ctlr) 24076a5dc222SDavid du Colombier { 24086a5dc222SDavid du Colombier int i, n, d, o, leaf0, depth; 2409*906943f9SDavid du Colombier Ed **tree; 2410*906943f9SDavid du Colombier Qtree *qt; 24116a5dc222SDavid du Colombier 24126a5dc222SDavid du Colombier depth = flog2(32); 24136a5dc222SDavid du Colombier n = (1 << (depth+1)) - 1; 24146a5dc222SDavid du Colombier qt = mallocz(sizeof(*qt), 1); 24156a5dc222SDavid du Colombier if(qt == nil) 2416*906943f9SDavid du Colombier panic("usb: can't allocate scheduling tree"); 24176a5dc222SDavid du Colombier qt->nel = n; 24186a5dc222SDavid du Colombier qt->depth = depth; 24196a5dc222SDavid du Colombier qt->bw = mallocz(n * sizeof(qt->bw), 1); 2420*906943f9SDavid du Colombier qt->root = tree = mallocz(n * sizeof(Ed *), 1); 2421*906943f9SDavid du Colombier if(qt->bw == nil || qt->root == nil) 2422*906943f9SDavid du Colombier panic("usb: can't allocate scheduling tree"); 2423*906943f9SDavid du Colombier for(i = 0; i < n; i++){ 2424*906943f9SDavid du Colombier if((tree[i] = edalloc()) == nil) 2425*906943f9SDavid du Colombier panic("mkqhtree"); 2426*906943f9SDavid du Colombier tree[i]->ctrl = (8 << Edmpsshift); /* not needed */ 2427*906943f9SDavid du Colombier tree[i]->ctrl |= Edskip; 2428*906943f9SDavid du Colombier 2429*906943f9SDavid du Colombier if(i > 0) 2430*906943f9SDavid du Colombier edlinked(tree[i], tree[(i-1)/2]); 2431*906943f9SDavid du Colombier else 2432*906943f9SDavid du Colombier edlinked(tree[i], nil); 24336a5dc222SDavid du Colombier } 2434*906943f9SDavid du Colombier ctlr->ntree = i; 2435*906943f9SDavid du Colombier dprint("ohci: tree: %d endpoints allocated\n", i); 24366a5dc222SDavid du Colombier 24376a5dc222SDavid du Colombier /* distribute leaves evenly round the frame list */ 24386a5dc222SDavid du Colombier leaf0 = n / 2; 24396a5dc222SDavid du Colombier for(i = 0; i < 32; i++){ 24406a5dc222SDavid du Colombier o = 0; 24416a5dc222SDavid du Colombier for(d = 0; d < depth; d++){ 24426a5dc222SDavid du Colombier o <<= 1; 24436a5dc222SDavid du Colombier if(i & (1 << d)) 24446a5dc222SDavid du Colombier o |= 1; 24456a5dc222SDavid du Colombier } 24466a5dc222SDavid du Colombier if(leaf0 + o >= n){ 24476a5dc222SDavid du Colombier print("leaf0=%d o=%d i=%d n=%d\n", leaf0, o, i, n); 24486a5dc222SDavid du Colombier break; 24496a5dc222SDavid du Colombier } 2450*906943f9SDavid du Colombier ctlr->hcca->intrtable[i] = ptr2pa(tree[leaf0 + o]); 24516a5dc222SDavid du Colombier } 2452*906943f9SDavid du Colombier ctlr->tree = qt; 24536a5dc222SDavid du Colombier } 24546a5dc222SDavid du Colombier 24556a5dc222SDavid du Colombier static void 2456*906943f9SDavid du Colombier ohcimeminit(Ctlr *ctlr) 24576a5dc222SDavid du Colombier { 2458*906943f9SDavid du Colombier Hcca *hcca; 24596a5dc222SDavid du Colombier 2460*906943f9SDavid du Colombier edfree(edalloc()); /* allocate pools now */ 2461*906943f9SDavid du Colombier tdfree(tdalloc()); 2462*906943f9SDavid du Colombier 2463*906943f9SDavid du Colombier hcca = xspanalloc(sizeof(Hcca), 256, 0); 2464*906943f9SDavid du Colombier if(hcca == nil) 2465*906943f9SDavid du Colombier panic("usbhreset: no memory for Hcca"); 2466*906943f9SDavid du Colombier memset(hcca, 0, sizeof(*hcca)); 2467*906943f9SDavid du Colombier ctlr->hcca = hcca; 2468*906943f9SDavid du Colombier 2469*906943f9SDavid du Colombier mkqhtree(ctlr); 24706a5dc222SDavid du Colombier } 2471*906943f9SDavid du Colombier 2472*906943f9SDavid du Colombier static void 2473*906943f9SDavid du Colombier ohcireset(Ctlr *ctlr) 2474*906943f9SDavid du Colombier { 24756a5dc222SDavid du Colombier ilock(ctlr); 2476*906943f9SDavid du Colombier dprint("ohci %#p reset\n", ctlr->ohci); 24776a5dc222SDavid du Colombier 24786a5dc222SDavid du Colombier /* 24796a5dc222SDavid du Colombier * usually enter here in reset, wait till its through, 24806a5dc222SDavid du Colombier * then do our own so we are on known timing conditions. 2481*906943f9SDavid du Colombier * Is this needed? 24826a5dc222SDavid du Colombier */ 24836a5dc222SDavid du Colombier delay(100); 2484*906943f9SDavid du Colombier ctlr->ohci->control = 0; 2485*906943f9SDavid du Colombier delay(100); 24866a5dc222SDavid du Colombier 24876a5dc222SDavid du Colombier /* legacy support register: turn off lunacy mode */ 2488*906943f9SDavid du Colombier pcicfgw16(ctlr->pcidev, 0xc0, 0x2000); 24896a5dc222SDavid du Colombier 2490*906943f9SDavid du Colombier iunlock(ctlr); 24916a5dc222SDavid du Colombier } 24926a5dc222SDavid du Colombier 2493*906943f9SDavid du Colombier static int 2494*906943f9SDavid du Colombier reset(Hci *hp) 2495*906943f9SDavid du Colombier { 2496*906943f9SDavid du Colombier static Lock resetlck; 2497*906943f9SDavid du Colombier int i; 2498*906943f9SDavid du Colombier Ctlr *ctlr; 2499*906943f9SDavid du Colombier Pcidev *p; 25006a5dc222SDavid du Colombier 2501*906943f9SDavid du Colombier if(getconf("*nousbohci")) 25026a5dc222SDavid du Colombier return -1; 25036a5dc222SDavid du Colombier 2504*906943f9SDavid du Colombier ilock(&resetlck); 2505*906943f9SDavid du Colombier scanpci(); 25066a5dc222SDavid du Colombier 25076a5dc222SDavid du Colombier /* 2508*906943f9SDavid du Colombier * Any adapter matches if no hp->port is supplied, 2509*906943f9SDavid du Colombier * otherwise the ports must match. 25106a5dc222SDavid du Colombier */ 2511*906943f9SDavid du Colombier ctlr = nil; 2512*906943f9SDavid du Colombier for(i = 0; i < Nhcis && ctlrs[i] != nil; i++){ 2513*906943f9SDavid du Colombier ctlr = ctlrs[i]; 2514*906943f9SDavid du Colombier if(ctlr->active == 0) 2515*906943f9SDavid du Colombier if(hp->port == 0 || hp->port == (uintptr)ctlr->ohci){ 2516*906943f9SDavid du Colombier ctlr->active = 1; 2517*906943f9SDavid du Colombier break; 25186a5dc222SDavid du Colombier } 2519*906943f9SDavid du Colombier } 2520*906943f9SDavid du Colombier iunlock(&resetlck); 2521*906943f9SDavid du Colombier if(ctlrs[i] == nil || i == Nhcis) 2522*906943f9SDavid du Colombier return -1; 2523*906943f9SDavid du Colombier if(ctlr->ohci->control == ~0) 2524*906943f9SDavid du Colombier return -1; 2525*906943f9SDavid du Colombier 25266a5dc222SDavid du Colombier 25276a5dc222SDavid du Colombier p = ctlr->pcidev; 2528*906943f9SDavid du Colombier hp->aux = ctlr; 2529*906943f9SDavid du Colombier hp->port = (uintptr)ctlr->ohci; 2530*906943f9SDavid du Colombier hp->irq = p->intl; 2531*906943f9SDavid du Colombier hp->tbdf = p->tbdf; 2532*906943f9SDavid du Colombier ctlr->nports = hp->nports = ctlr->ohci->rhdesca & 0xff; 2533*906943f9SDavid du Colombier 2534*906943f9SDavid du Colombier ohcireset(ctlr); 2535*906943f9SDavid du Colombier ohcimeminit(ctlr); 25366a5dc222SDavid du Colombier 25376a5dc222SDavid du Colombier /* 2538*906943f9SDavid du Colombier * Linkage to the generic HCI driver. 25396a5dc222SDavid du Colombier */ 2540*906943f9SDavid du Colombier hp->init = init; 2541*906943f9SDavid du Colombier hp->dump = dump; 2542*906943f9SDavid du Colombier hp->interrupt = interrupt; 2543*906943f9SDavid du Colombier hp->epopen = epopen; 2544*906943f9SDavid du Colombier hp->epclose = epclose; 2545*906943f9SDavid du Colombier hp->epread = epread; 2546*906943f9SDavid du Colombier hp->epwrite = epwrite; 2547*906943f9SDavid du Colombier hp->seprintep = seprintep; 2548*906943f9SDavid du Colombier hp->portenable = portenable; 2549*906943f9SDavid du Colombier hp->portreset = portreset; 2550*906943f9SDavid du Colombier hp->portstatus = portstatus; 2551*906943f9SDavid du Colombier hp->debug = usbdebug; 2552*906943f9SDavid du Colombier hp->type = "ohci"; 25536a5dc222SDavid du Colombier return 0; 25546a5dc222SDavid du Colombier } 25556a5dc222SDavid du Colombier 25566a5dc222SDavid du Colombier void 25576a5dc222SDavid du Colombier usbohcilink(void) 25586a5dc222SDavid du Colombier { 2559*906943f9SDavid du Colombier addhcitype("ohci", reset); 25606a5dc222SDavid du Colombier } 2561