1ade43d10SDavid du Colombier /* 284860c5dSDavid du Colombier * USB device driver framework. 3906943f9SDavid du Colombier * 4906943f9SDavid du Colombier * This is in charge of providing access to actual HCIs 5906943f9SDavid du Colombier * and providing I/O to the various endpoints of devices. 6906943f9SDavid du Colombier * A separate user program (usbd) is in charge of 7906943f9SDavid du Colombier * enumerating the bus, setting up endpoints and 8906943f9SDavid du Colombier * starting devices (also user programs). 9906943f9SDavid du Colombier * 10906943f9SDavid du Colombier * The interface provided is a violation of the standard: 11906943f9SDavid du Colombier * you're welcome. 12906943f9SDavid du Colombier * 13906943f9SDavid du Colombier * The interface consists of a root directory with several files 14906943f9SDavid du Colombier * plus a directory (epN.M) with two files per endpoint. 15906943f9SDavid du Colombier * A device is represented by its first endpoint, which 16906943f9SDavid du Colombier * is a control endpoint automatically allocated for each device. 17906943f9SDavid du Colombier * Device control endpoints may be used to create new endpoints. 18906943f9SDavid du Colombier * Devices corresponding to hubs may also allocate new devices, 19906943f9SDavid du Colombier * perhaps also hubs. Initially, a hub device is allocated for 20906943f9SDavid du Colombier * each controller present, to represent its root hub. Those can 21906943f9SDavid du Colombier * never be removed. 22906943f9SDavid du Colombier * 23906943f9SDavid du Colombier * All endpoints refer to the first endpoint (epN.0) of the device, 24906943f9SDavid du Colombier * which keeps per-device information, and also to the HCI used 25906943f9SDavid du Colombier * to reach them. Although all endpoints cache that information. 26906943f9SDavid du Colombier * 27906943f9SDavid du Colombier * epN.M/data files permit I/O and are considered DMEXCL. 28906943f9SDavid du Colombier * epN.M/ctl files provide status info and accept control requests. 29906943f9SDavid du Colombier * 30906943f9SDavid du Colombier * Endpoints may be given file names to be listed also at #u, 31906943f9SDavid du Colombier * for those drivers that have nothing to do after configuring the 32906943f9SDavid du Colombier * device and its endpoints. 33906943f9SDavid du Colombier * 34906943f9SDavid du Colombier * Drivers for different controllers are kept at usb[oue]hci.c 35906943f9SDavid du Colombier * It's likely we could factor out much from controllers into 36906943f9SDavid du Colombier * a generic controller driver, the problem is that details 37906943f9SDavid du Colombier * regarding how to handle toggles, tokens, Tds, etc. will 38906943f9SDavid du Colombier * get in the way. Thus, code is probably easier the way it is. 39ade43d10SDavid du Colombier */ 40906943f9SDavid du Colombier 419a747e4fSDavid du Colombier #include "u.h" 429a747e4fSDavid du Colombier #include "../port/lib.h" 439a747e4fSDavid du Colombier #include "mem.h" 449a747e4fSDavid du Colombier #include "dat.h" 459a747e4fSDavid du Colombier #include "fns.h" 469a747e4fSDavid du Colombier #include "io.h" 479a747e4fSDavid du Colombier #include "../port/error.h" 4884860c5dSDavid du Colombier #include "../port/usb.h" 499a747e4fSDavid du Colombier 50906943f9SDavid du Colombier typedef struct Hcitype Hcitype; 519a747e4fSDavid du Colombier 529a747e4fSDavid du Colombier enum 539a747e4fSDavid du Colombier { 54906943f9SDavid du Colombier /* Qid numbers */ 55906943f9SDavid du Colombier Qdir = 0, /* #u */ 56906943f9SDavid du Colombier Qusbdir, /* #u/usb */ 57906943f9SDavid du Colombier Qctl, /* #u/usb/ctl - control requests */ 58906943f9SDavid du Colombier 59906943f9SDavid du Colombier Qep0dir, /* #u/usb/ep0.0 - endpoint 0 dir */ 60906943f9SDavid du Colombier Qep0io, /* #u/usb/ep0.0/data - endpoint 0 I/O */ 61906943f9SDavid du Colombier Qep0ctl, /* #u/usb/ep0.0/ctl - endpoint 0 ctl. */ 62906943f9SDavid du Colombier Qep0dummy, /* give 4 qids to each endpoint */ 63906943f9SDavid du Colombier 64906943f9SDavid du Colombier Qepdir = 0, /* (qid-qep0dir)&3 is one of these */ 65906943f9SDavid du Colombier Qepio, /* to identify which file for the endpoint */ 66906943f9SDavid du Colombier Qepctl, 67906943f9SDavid du Colombier 68906943f9SDavid du Colombier /* ... */ 69906943f9SDavid du Colombier 70906943f9SDavid du Colombier /* Usb ctls. */ 71906943f9SDavid du Colombier CMdebug = 0, /* debug on|off */ 72906943f9SDavid du Colombier CMdump, /* dump (data structures for debug) */ 73906943f9SDavid du Colombier 74906943f9SDavid du Colombier /* Ep. ctls */ 75906943f9SDavid du Colombier CMnew = 0, /* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */ 76906943f9SDavid du Colombier CMnewdev, /* newdev full|low|high portnb (allocate new devices) */ 77906943f9SDavid du Colombier CMhub, /* hub (set the device as a hub) */ 78906943f9SDavid du Colombier CMspeed, /* speed full|low|high|no */ 79906943f9SDavid du Colombier CMmaxpkt, /* maxpkt size */ 80906943f9SDavid du Colombier CMntds, /* ntds nb (max nb. of tds per µframe) */ 81906943f9SDavid du Colombier CMclrhalt, /* clrhalt (halt was cleared on endpoint) */ 82906943f9SDavid du Colombier CMpollival, /* pollival interval (interrupt/iso) */ 83906943f9SDavid du Colombier CMhz, /* hz n (samples/sec; iso) */ 84906943f9SDavid du Colombier CMsamplesz, /* samplesz n (sample size; iso) */ 85906943f9SDavid du Colombier CMinfo, /* info infostr (ke.ep info for humans) */ 86906943f9SDavid du Colombier CMdetach, /* detach (abort I/O forever on this ep). */ 87906943f9SDavid du Colombier CMaddress, /* address (address is assigned) */ 88906943f9SDavid du Colombier CMdebugep, /* debug n (set/clear debug for this ep) */ 89906943f9SDavid du Colombier CMname, /* name str (show up as #u/name as well) */ 90d37e33ffSDavid du Colombier CMtmout, /* timeout n (activate timeouts for ep) */ 91a23bc242SDavid du Colombier CMpreset, /* reset the port */ 92906943f9SDavid du Colombier 93906943f9SDavid du Colombier /* Hub feature selectors */ 94906943f9SDavid du Colombier Rportenable = 1, 95906943f9SDavid du Colombier Rportreset = 4, 96906943f9SDavid du Colombier 979a747e4fSDavid du Colombier }; 989a747e4fSDavid du Colombier 99906943f9SDavid du Colombier struct Hcitype 1009a747e4fSDavid du Colombier { 1019a747e4fSDavid du Colombier char* type; 102906943f9SDavid du Colombier int (*reset)(Hci*); 103906943f9SDavid du Colombier }; 104906943f9SDavid du Colombier 105906943f9SDavid du Colombier #define QID(q) ((int)(q).path) 106906943f9SDavid du Colombier 107906943f9SDavid du Colombier static char Edetach[] = "device is detached"; 108906943f9SDavid du Colombier static char Enotconf[] = "endpoint not configured"; 109906943f9SDavid du Colombier char Estalled[] = "endpoint stalled"; 110906943f9SDavid du Colombier 111906943f9SDavid du Colombier static Cmdtab usbctls[] = 112906943f9SDavid du Colombier { 113906943f9SDavid du Colombier {CMdebug, "debug", 2}, 114906943f9SDavid du Colombier {CMdump, "dump", 1}, 115906943f9SDavid du Colombier }; 116906943f9SDavid du Colombier 117906943f9SDavid du Colombier static Cmdtab epctls[] = 118906943f9SDavid du Colombier { 119906943f9SDavid du Colombier {CMnew, "new", 4}, 120906943f9SDavid du Colombier {CMnewdev, "newdev", 3}, 121906943f9SDavid du Colombier {CMhub, "hub", 1}, 122906943f9SDavid du Colombier {CMspeed, "speed", 2}, 123906943f9SDavid du Colombier {CMmaxpkt, "maxpkt", 2}, 124906943f9SDavid du Colombier {CMntds, "ntds", 2}, 125906943f9SDavid du Colombier {CMpollival, "pollival", 2}, 126906943f9SDavid du Colombier {CMsamplesz, "samplesz", 2}, 127906943f9SDavid du Colombier {CMhz, "hz", 2}, 128906943f9SDavid du Colombier {CMinfo, "info", 0}, 129906943f9SDavid du Colombier {CMdetach, "detach", 1}, 130906943f9SDavid du Colombier {CMaddress, "address", 1}, 131906943f9SDavid du Colombier {CMdebugep, "debug", 2}, 132906943f9SDavid du Colombier {CMclrhalt, "clrhalt", 1}, 133906943f9SDavid du Colombier {CMname, "name", 2}, 134d37e33ffSDavid du Colombier {CMtmout, "timeout", 2}, 135a23bc242SDavid du Colombier {CMpreset, "reset", 1}, 136906943f9SDavid du Colombier }; 137906943f9SDavid du Colombier 138906943f9SDavid du Colombier static Dirtab usbdir[] = 139906943f9SDavid du Colombier { 140906943f9SDavid du Colombier "ctl", {Qctl}, 0, 0666, 141906943f9SDavid du Colombier }; 142906943f9SDavid du Colombier 143906943f9SDavid du Colombier char *usbmodename[] = 144906943f9SDavid du Colombier { 145906943f9SDavid du Colombier [OREAD] "r", 146906943f9SDavid du Colombier [OWRITE] "w", 147906943f9SDavid du Colombier [ORDWR] "rw", 148906943f9SDavid du Colombier }; 149906943f9SDavid du Colombier 150906943f9SDavid du Colombier static char *ttname[] = 151906943f9SDavid du Colombier { 152906943f9SDavid du Colombier [Tnone] "none", 153906943f9SDavid du Colombier [Tctl] "control", 154906943f9SDavid du Colombier [Tiso] "iso", 155906943f9SDavid du Colombier [Tintr] "interrupt", 156906943f9SDavid du Colombier [Tbulk] "bulk", 157906943f9SDavid du Colombier }; 158906943f9SDavid du Colombier 159906943f9SDavid du Colombier static char *spname[] = 160906943f9SDavid du Colombier { 161906943f9SDavid du Colombier [Fullspeed] "full", 162906943f9SDavid du Colombier [Lowspeed] "low", 163906943f9SDavid du Colombier [Highspeed] "high", 164906943f9SDavid du Colombier [Nospeed] "no", 165906943f9SDavid du Colombier }; 166906943f9SDavid du Colombier 167906943f9SDavid du Colombier static int debug; 168906943f9SDavid du Colombier static Hcitype hcitypes[Nhcis]; 169906943f9SDavid du Colombier static Hci* hcis[Nhcis]; 170906943f9SDavid du Colombier static QLock epslck; /* add, del, lookup endpoints */ 171906943f9SDavid du Colombier static Ep* eps[Neps]; /* all endpoints known */ 172906943f9SDavid du Colombier static int epmax; /* 1 + last endpoint index used */ 173906943f9SDavid du Colombier static int usbidgen; /* device address generator */ 174906943f9SDavid du Colombier 175906943f9SDavid du Colombier /* 176906943f9SDavid du Colombier * Is there something like this in a library? should it be? 177906943f9SDavid du Colombier */ 178906943f9SDavid du Colombier char* 179906943f9SDavid du Colombier seprintdata(char *s, char *se, uchar *d, int n) 180906943f9SDavid du Colombier { 18184860c5dSDavid du Colombier int i, l; 182906943f9SDavid du Colombier 183906943f9SDavid du Colombier s = seprint(s, se, " %#p[%d]: ", d, n); 184906943f9SDavid du Colombier l = n; 185906943f9SDavid du Colombier if(l > 10) 186906943f9SDavid du Colombier l = 10; 187906943f9SDavid du Colombier for(i=0; i<l; i++) 188906943f9SDavid du Colombier s = seprint(s, se, " %2.2ux", d[i]); 189906943f9SDavid du Colombier if(l < n) 190906943f9SDavid du Colombier s = seprint(s, se, "..."); 191906943f9SDavid du Colombier return s; 192906943f9SDavid du Colombier } 193906943f9SDavid du Colombier 194906943f9SDavid du Colombier static int 195906943f9SDavid du Colombier name2speed(char *name) 196906943f9SDavid du Colombier { 197906943f9SDavid du Colombier int i; 198906943f9SDavid du Colombier 199906943f9SDavid du Colombier for(i = 0; i < nelem(spname); i++) 200906943f9SDavid du Colombier if(strcmp(name, spname[i]) == 0) 201906943f9SDavid du Colombier return i; 202906943f9SDavid du Colombier return Nospeed; 203906943f9SDavid du Colombier } 204906943f9SDavid du Colombier 205906943f9SDavid du Colombier static int 206906943f9SDavid du Colombier name2ttype(char *name) 207906943f9SDavid du Colombier { 208906943f9SDavid du Colombier int i; 209906943f9SDavid du Colombier 210906943f9SDavid du Colombier for(i = 0; i < nelem(ttname); i++) 211906943f9SDavid du Colombier if(strcmp(name, ttname[i]) == 0) 212906943f9SDavid du Colombier return i; 213906943f9SDavid du Colombier /* may be a std. USB ep. type */ 214906943f9SDavid du Colombier i = strtol(name, nil, 0); 215906943f9SDavid du Colombier switch(i+1){ 216906943f9SDavid du Colombier case Tctl: 217906943f9SDavid du Colombier case Tiso: 218906943f9SDavid du Colombier case Tbulk: 219906943f9SDavid du Colombier case Tintr: 220906943f9SDavid du Colombier return i+1; 221906943f9SDavid du Colombier default: 222906943f9SDavid du Colombier return Tnone; 223906943f9SDavid du Colombier } 224906943f9SDavid du Colombier } 225906943f9SDavid du Colombier 226906943f9SDavid du Colombier static int 227906943f9SDavid du Colombier name2mode(char *mode) 228906943f9SDavid du Colombier { 229906943f9SDavid du Colombier int i; 230906943f9SDavid du Colombier 231906943f9SDavid du Colombier for(i = 0; i < nelem(usbmodename); i++) 232906943f9SDavid du Colombier if(strcmp(mode, usbmodename[i]) == 0) 233906943f9SDavid du Colombier return i; 234906943f9SDavid du Colombier return -1; 235906943f9SDavid du Colombier } 236906943f9SDavid du Colombier 237906943f9SDavid du Colombier static int 238906943f9SDavid du Colombier qid2epidx(int q) 239906943f9SDavid du Colombier { 240906943f9SDavid du Colombier q = (q-Qep0dir)/4; 241906943f9SDavid du Colombier if(q < 0 || q >= epmax || eps[q] == nil) 242906943f9SDavid du Colombier return -1; 243906943f9SDavid du Colombier return q; 244906943f9SDavid du Colombier } 245906943f9SDavid du Colombier 246906943f9SDavid du Colombier static int 247906943f9SDavid du Colombier isqtype(int q, int type) 248906943f9SDavid du Colombier { 249906943f9SDavid du Colombier if(q < Qep0dir) 250906943f9SDavid du Colombier return 0; 251906943f9SDavid du Colombier q -= Qep0dir; 252906943f9SDavid du Colombier return (q & 3) == type; 253906943f9SDavid du Colombier } 2549a747e4fSDavid du Colombier 2559a747e4fSDavid du Colombier void 256906943f9SDavid du Colombier addhcitype(char* t, int (*r)(Hci*)) 2579a747e4fSDavid du Colombier { 2589a747e4fSDavid du Colombier static int ntype; 2599a747e4fSDavid du Colombier 260906943f9SDavid du Colombier if(ntype == Nhcis) 2619a747e4fSDavid du Colombier panic("too many USB host interface types"); 262906943f9SDavid du Colombier hcitypes[ntype].type = t; 263906943f9SDavid du Colombier hcitypes[ntype].reset = r; 2649a747e4fSDavid du Colombier ntype++; 2659a747e4fSDavid du Colombier } 2669a747e4fSDavid du Colombier 267906943f9SDavid du Colombier static char* 268906943f9SDavid du Colombier seprintep(char *s, char *se, Ep *ep, int all) 2699a747e4fSDavid du Colombier { 270a23bc242SDavid du Colombier static char* dsnames[] = { "config", "enabled", "detached", "reset" }; 2719a747e4fSDavid du Colombier Udev *d; 2729a747e4fSDavid du Colombier int i; 273906943f9SDavid du Colombier int di; 2749a747e4fSDavid du Colombier 275906943f9SDavid du Colombier d = ep->dev; 276906943f9SDavid du Colombier 277906943f9SDavid du Colombier qlock(ep); 2789a747e4fSDavid du Colombier if(waserror()){ 279906943f9SDavid du Colombier qunlock(ep); 2809a747e4fSDavid du Colombier nexterror(); 2819a747e4fSDavid du Colombier } 282906943f9SDavid du Colombier di = ep->dev->nb; 283906943f9SDavid du Colombier if(all) 284906943f9SDavid du Colombier s = seprint(s, se, "dev %d ep %d ", di, ep->nb); 285906943f9SDavid du Colombier s = seprint(s, se, "%s", dsnames[ep->dev->state]); 286906943f9SDavid du Colombier s = seprint(s, se, " %s", ttname[ep->ttype]); 287906943f9SDavid du Colombier assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR); 288906943f9SDavid du Colombier s = seprint(s, se, " %s", usbmodename[ep->mode]); 289906943f9SDavid du Colombier s = seprint(s, se, " speed %s", spname[d->speed]); 290906943f9SDavid du Colombier s = seprint(s, se, " maxpkt %ld", ep->maxpkt); 291906943f9SDavid du Colombier s = seprint(s, se, " pollival %ld", ep->pollival); 292906943f9SDavid du Colombier s = seprint(s, se, " samplesz %ld", ep->samplesz); 293906943f9SDavid du Colombier s = seprint(s, se, " hz %ld", ep->hz); 294906943f9SDavid du Colombier s = seprint(s, se, " hub %d", ep->dev->hub); 295906943f9SDavid du Colombier s = seprint(s, se, " port %d", ep->dev->port); 296906943f9SDavid du Colombier if(ep->inuse) 297906943f9SDavid du Colombier s = seprint(s, se, " busy"); 298906943f9SDavid du Colombier else 299906943f9SDavid du Colombier s = seprint(s, se, " idle"); 300906943f9SDavid du Colombier if(all){ 301906943f9SDavid du Colombier s = seprint(s, se, " load %uld", ep->load); 302906943f9SDavid du Colombier s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep); 303906943f9SDavid du Colombier s = seprint(s, se, " idx %d", ep->idx); 304906943f9SDavid du Colombier if(ep->name != nil) 305906943f9SDavid du Colombier s = seprint(s, se, " name '%s'", ep->name); 306d37e33ffSDavid du Colombier if(ep->tmout != 0) 307d37e33ffSDavid du Colombier s = seprint(s, se, " tmout"); 308906943f9SDavid du Colombier if(ep == ep->ep0){ 309906943f9SDavid du Colombier s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno); 310906943f9SDavid du Colombier s = seprint(s, se, " eps:"); 311906943f9SDavid du Colombier for(i = 0; i < nelem(d->eps); i++) 312906943f9SDavid du Colombier if(d->eps[i] != nil) 313906943f9SDavid du Colombier s = seprint(s, se, " ep%d.%d", di, i); 3149a747e4fSDavid du Colombier } 315906943f9SDavid du Colombier } 316906943f9SDavid du Colombier if(ep->info != nil) 317c8cbc0e9SDavid du Colombier s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type); 318906943f9SDavid du Colombier else 319906943f9SDavid du Colombier s = seprint(s, se, "\n"); 320906943f9SDavid du Colombier qunlock(ep); 3219a747e4fSDavid du Colombier poperror(); 322906943f9SDavid du Colombier return s; 323906943f9SDavid du Colombier } 324906943f9SDavid du Colombier 325906943f9SDavid du Colombier static Ep* 326906943f9SDavid du Colombier epalloc(Hci *hp) 327906943f9SDavid du Colombier { 328906943f9SDavid du Colombier Ep *ep; 329906943f9SDavid du Colombier int i; 330906943f9SDavid du Colombier 331*305b51b8SDavid du Colombier ep = smalloc(sizeof(Ep)); 332906943f9SDavid du Colombier ep->ref = 1; 333906943f9SDavid du Colombier qlock(&epslck); 334906943f9SDavid du Colombier for(i = 0; i < Neps; i++) 335906943f9SDavid du Colombier if(eps[i] == nil) 336906943f9SDavid du Colombier break; 337906943f9SDavid du Colombier if(i == Neps){ 338906943f9SDavid du Colombier qunlock(&epslck); 339906943f9SDavid du Colombier free(ep); 340906943f9SDavid du Colombier print("usb: bug: too few endpoints.\n"); 341906943f9SDavid du Colombier return nil; 342906943f9SDavid du Colombier } 343906943f9SDavid du Colombier ep->idx = i; 344906943f9SDavid du Colombier if(epmax <= i) 345906943f9SDavid du Colombier epmax = i+1; 346906943f9SDavid du Colombier eps[i] = ep; 347906943f9SDavid du Colombier ep->hp = hp; 348906943f9SDavid du Colombier ep->maxpkt = 8; 349906943f9SDavid du Colombier ep->ntds = 1; 350906943f9SDavid du Colombier ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */ 351906943f9SDavid du Colombier qunlock(&epslck); 352906943f9SDavid du Colombier return ep; 353906943f9SDavid du Colombier } 354906943f9SDavid du Colombier 355906943f9SDavid du Colombier static Ep* 356906943f9SDavid du Colombier getep(int i) 357906943f9SDavid du Colombier { 358906943f9SDavid du Colombier Ep *ep; 359906943f9SDavid du Colombier 360906943f9SDavid du Colombier if(i < 0 || i >= epmax || eps[i] == nil) 361906943f9SDavid du Colombier return nil; 362906943f9SDavid du Colombier qlock(&epslck); 363906943f9SDavid du Colombier ep = eps[i]; 364906943f9SDavid du Colombier if(ep != nil) 365906943f9SDavid du Colombier incref(ep); 366906943f9SDavid du Colombier qunlock(&epslck); 367906943f9SDavid du Colombier return ep; 3689a747e4fSDavid du Colombier } 3699a747e4fSDavid du Colombier 3709a747e4fSDavid du Colombier static void 371906943f9SDavid du Colombier putep(Ep *ep) 372906943f9SDavid du Colombier { 373906943f9SDavid du Colombier Udev *d; 374906943f9SDavid du Colombier 375906943f9SDavid du Colombier if(ep != nil && decref(ep) == 0){ 376906943f9SDavid du Colombier d = ep->dev; 377906943f9SDavid du Colombier deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep); 378906943f9SDavid du Colombier qlock(&epslck); 379906943f9SDavid du Colombier eps[ep->idx] = nil; 380906943f9SDavid du Colombier if(ep->idx == epmax-1) 381906943f9SDavid du Colombier epmax--; 382906943f9SDavid du Colombier if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen) 383906943f9SDavid du Colombier usbidgen--; 384906943f9SDavid du Colombier qunlock(&epslck); 385906943f9SDavid du Colombier if(d != nil){ 386906943f9SDavid du Colombier qlock(ep->ep0); 387906943f9SDavid du Colombier d->eps[ep->nb] = nil; 388906943f9SDavid du Colombier qunlock(ep->ep0); 389906943f9SDavid du Colombier } 390906943f9SDavid du Colombier if(ep->ep0 != ep){ 391906943f9SDavid du Colombier putep(ep->ep0); 392906943f9SDavid du Colombier ep->ep0 = nil; 393906943f9SDavid du Colombier } 394906943f9SDavid du Colombier free(ep->info); 395906943f9SDavid du Colombier free(ep->name); 396906943f9SDavid du Colombier free(ep); 397906943f9SDavid du Colombier } 398906943f9SDavid du Colombier } 399906943f9SDavid du Colombier 400906943f9SDavid du Colombier static void 401906943f9SDavid du Colombier dumpeps(void) 4029a747e4fSDavid du Colombier { 4039a747e4fSDavid du Colombier int i; 404906943f9SDavid du Colombier static char buf[512]; 405906943f9SDavid du Colombier char *s; 406906943f9SDavid du Colombier char *e; 407906943f9SDavid du Colombier Ep *ep; 4089a747e4fSDavid du Colombier 409906943f9SDavid du Colombier print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps); 410906943f9SDavid du Colombier for(i = 0; i < epmax; i++){ 411906943f9SDavid du Colombier s = buf; 412906943f9SDavid du Colombier e = buf+sizeof(buf); 413906943f9SDavid du Colombier ep = getep(i); 414906943f9SDavid du Colombier if(ep != nil){ 415906943f9SDavid du Colombier if(waserror()){ 416906943f9SDavid du Colombier putep(ep); 417906943f9SDavid du Colombier nexterror(); 4189a747e4fSDavid du Colombier } 419906943f9SDavid du Colombier s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb); 420906943f9SDavid du Colombier seprintep(s, e, ep, 1); 421906943f9SDavid du Colombier print("%s", buf); 422906943f9SDavid du Colombier ep->hp->seprintep(buf, e, ep); 423906943f9SDavid du Colombier print("%s", buf); 424906943f9SDavid du Colombier poperror(); 425906943f9SDavid du Colombier putep(ep); 426906943f9SDavid du Colombier } 427906943f9SDavid du Colombier } 428906943f9SDavid du Colombier print("usb dump hcis:\n"); 429906943f9SDavid du Colombier for(i = 0; i < Nhcis; i++) 430906943f9SDavid du Colombier if(hcis[i] != nil) 431906943f9SDavid du Colombier hcis[i]->dump(hcis[i]); 432906943f9SDavid du Colombier } 433906943f9SDavid du Colombier 434906943f9SDavid du Colombier static int 435906943f9SDavid du Colombier newusbid(Hci *) 436906943f9SDavid du Colombier { 437906943f9SDavid du Colombier int id; 438906943f9SDavid du Colombier 439906943f9SDavid du Colombier qlock(&epslck); 440906943f9SDavid du Colombier id = ++usbidgen; 441906943f9SDavid du Colombier if(id >= 0x7F) 442906943f9SDavid du Colombier print("#u: too many device addresses; reuse them more\n"); 443906943f9SDavid du Colombier qunlock(&epslck); 444906943f9SDavid du Colombier return id; 445906943f9SDavid du Colombier } 446906943f9SDavid du Colombier 447906943f9SDavid du Colombier /* 448906943f9SDavid du Colombier * Create endpoint 0 for a new device 449906943f9SDavid du Colombier */ 450906943f9SDavid du Colombier static Ep* 451906943f9SDavid du Colombier newdev(Hci *hp, int ishub, int isroot) 452906943f9SDavid du Colombier { 453906943f9SDavid du Colombier Ep *ep; 454906943f9SDavid du Colombier Udev *d; 455906943f9SDavid du Colombier 456906943f9SDavid du Colombier ep = epalloc(hp); 457*305b51b8SDavid du Colombier d = ep->dev = smalloc(sizeof(Udev)); 458906943f9SDavid du Colombier d->nb = newusbid(hp); 459906943f9SDavid du Colombier d->eps[0] = ep; 460906943f9SDavid du Colombier ep->nb = 0; 461906943f9SDavid du Colombier ep->toggle[0] = ep->toggle[1] = 0; 462906943f9SDavid du Colombier d->ishub = ishub; 463906943f9SDavid du Colombier d->isroot = isroot; 464906943f9SDavid du Colombier if(hp->highspeed != 0) 465906943f9SDavid du Colombier d->speed = Highspeed; 466906943f9SDavid du Colombier else 467906943f9SDavid du Colombier d->speed = Fullspeed; 468906943f9SDavid du Colombier d->state = Dconfig; /* address not yet set */ 469906943f9SDavid du Colombier ep->dev = d; 470906943f9SDavid du Colombier ep->ep0 = ep; /* no ref counted here */ 471906943f9SDavid du Colombier ep->ttype = Tctl; 472d37e33ffSDavid du Colombier ep->tmout = Xfertmout; 473906943f9SDavid du Colombier ep->mode = ORDWR; 474906943f9SDavid du Colombier dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep); 475906943f9SDavid du Colombier return ep; 476906943f9SDavid du Colombier } 477906943f9SDavid du Colombier 478906943f9SDavid du Colombier /* 479906943f9SDavid du Colombier * Create a new endpoint for the device 480906943f9SDavid du Colombier * accessed via the given endpoint 0. 481906943f9SDavid du Colombier */ 482906943f9SDavid du Colombier static Ep* 483906943f9SDavid du Colombier newdevep(Ep *ep, int i, int tt, int mode) 484906943f9SDavid du Colombier { 485906943f9SDavid du Colombier Ep *nep; 486906943f9SDavid du Colombier Udev *d; 487906943f9SDavid du Colombier 488906943f9SDavid du Colombier d = ep->dev; 489906943f9SDavid du Colombier if(d->eps[i] != nil) 490906943f9SDavid du Colombier error("endpoint already in use"); 491906943f9SDavid du Colombier nep = epalloc(ep->hp); 492906943f9SDavid du Colombier incref(ep); 493906943f9SDavid du Colombier d->eps[i] = nep; 494906943f9SDavid du Colombier nep->nb = i; 495906943f9SDavid du Colombier nep->toggle[0] = nep->toggle[1] = 0; 496906943f9SDavid du Colombier nep->ep0 = ep; 497906943f9SDavid du Colombier nep->dev = ep->dev; 498906943f9SDavid du Colombier nep->mode = mode; 499906943f9SDavid du Colombier nep->ttype = tt; 500906943f9SDavid du Colombier nep->debug = ep->debug; 501d37e33ffSDavid du Colombier /* set defaults */ 502d37e33ffSDavid du Colombier switch(tt){ 503d37e33ffSDavid du Colombier case Tctl: 504d37e33ffSDavid du Colombier nep->tmout = Xfertmout; 505d37e33ffSDavid du Colombier break; 506d37e33ffSDavid du Colombier case Tintr: 507906943f9SDavid du Colombier nep->pollival = 10; 508d37e33ffSDavid du Colombier break; 509d37e33ffSDavid du Colombier case Tiso: 510d37e33ffSDavid du Colombier nep->tmout = Xfertmout; 511d37e33ffSDavid du Colombier nep->pollival = 10; 512906943f9SDavid du Colombier nep->samplesz = 4; 513906943f9SDavid du Colombier nep->hz = 44100; 514d37e33ffSDavid du Colombier break; 515906943f9SDavid du Colombier } 516906943f9SDavid du Colombier deprint("newdevep ep%d.%d %#p\n", d->nb, nep->nb, nep); 517906943f9SDavid du Colombier return ep; 518906943f9SDavid du Colombier } 519906943f9SDavid du Colombier 520906943f9SDavid du Colombier static int 521906943f9SDavid du Colombier epdataperm(int mode) 522906943f9SDavid du Colombier { 523906943f9SDavid du Colombier 524906943f9SDavid du Colombier switch(mode){ 525906943f9SDavid du Colombier case OREAD: 526906943f9SDavid du Colombier return 0440|DMEXCL; 527906943f9SDavid du Colombier break; 528906943f9SDavid du Colombier case OWRITE: 529906943f9SDavid du Colombier return 0220|DMEXCL; 530906943f9SDavid du Colombier break; 531906943f9SDavid du Colombier default: 532906943f9SDavid du Colombier return 0660|DMEXCL; 5339a747e4fSDavid du Colombier } 5349a747e4fSDavid du Colombier } 5359a747e4fSDavid du Colombier 5369a747e4fSDavid du Colombier static int 5379a747e4fSDavid du Colombier usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) 5389a747e4fSDavid du Colombier { 5399a747e4fSDavid du Colombier Qid q; 540906943f9SDavid du Colombier Dirtab *dir; 541906943f9SDavid du Colombier int perm; 542906943f9SDavid du Colombier char *se; 543906943f9SDavid du Colombier Ep *ep; 544906943f9SDavid du Colombier int nb; 545906943f9SDavid du Colombier int mode; 5469a747e4fSDavid du Colombier 547906943f9SDavid du Colombier if(0)ddprint("usbgen q %#x s %d...", QID(c->qid), s); 5489a747e4fSDavid du Colombier if(s == DEVDOTDOT){ 549906943f9SDavid du Colombier if(QID(c->qid) <= Qusbdir){ 550906943f9SDavid du Colombier mkqid(&q, Qdir, 0, QTDIR); 551906943f9SDavid du Colombier devdir(c, q, "#u", 0, eve, 0555, dp); 552906943f9SDavid du Colombier }else{ 553906943f9SDavid du Colombier mkqid(&q, Qusbdir, 0, QTDIR); 554906943f9SDavid du Colombier devdir(c, q, "usb", 0, eve, 0555, dp); 555906943f9SDavid du Colombier } 556906943f9SDavid du Colombier if(0)ddprint("ok\n"); 5579a747e4fSDavid du Colombier return 1; 5589a747e4fSDavid du Colombier } 5599a747e4fSDavid du Colombier 560906943f9SDavid du Colombier switch(QID(c->qid)){ 561906943f9SDavid du Colombier case Qdir: /* list #u */ 562906943f9SDavid du Colombier if(s == 0){ 563906943f9SDavid du Colombier mkqid(&q, Qusbdir, 0, QTDIR); 564906943f9SDavid du Colombier devdir(c, q, "usb", 0, eve, 0555, dp); 565906943f9SDavid du Colombier if(0)ddprint("ok\n"); 5669a747e4fSDavid du Colombier return 1; 5679a747e4fSDavid du Colombier } 568906943f9SDavid du Colombier s--; 569906943f9SDavid du Colombier if(s < 0 || s >= epmax) 570906943f9SDavid du Colombier goto Fail; 571906943f9SDavid du Colombier ep = getep(s); 572906943f9SDavid du Colombier if(ep == nil || ep->name == nil){ 573906943f9SDavid du Colombier if(ep != nil) 574906943f9SDavid du Colombier putep(ep); 575906943f9SDavid du Colombier if(0)ddprint("skip\n"); 5769a747e4fSDavid du Colombier return 0; 577906943f9SDavid du Colombier } 578906943f9SDavid du Colombier if(waserror()){ 579906943f9SDavid du Colombier putep(ep); 580906943f9SDavid du Colombier nexterror(); 581906943f9SDavid du Colombier } 582906943f9SDavid du Colombier mkqid(&q, Qep0io+s*4, 0, QTFILE); 583906943f9SDavid du Colombier devdir(c, q, ep->name, 0, eve, epdataperm(ep->mode), dp); 584906943f9SDavid du Colombier putep(ep); 585906943f9SDavid du Colombier poperror(); 586906943f9SDavid du Colombier if(0)ddprint("ok\n"); 5879a747e4fSDavid du Colombier return 1; 5889a747e4fSDavid du Colombier 589906943f9SDavid du Colombier case Qusbdir: /* list #u/usb */ 590906943f9SDavid du Colombier Usbdir: 591906943f9SDavid du Colombier if(s < nelem(usbdir)){ 592906943f9SDavid du Colombier dir = &usbdir[s]; 593906943f9SDavid du Colombier mkqid(&q, dir->qid.path, 0, QTFILE); 594906943f9SDavid du Colombier devdir(c, q, dir->name, dir->length, eve, dir->perm, dp); 595906943f9SDavid du Colombier if(0)ddprint("ok\n"); 5969a747e4fSDavid du Colombier return 1; 5979a747e4fSDavid du Colombier } 598906943f9SDavid du Colombier s -= nelem(usbdir); 599906943f9SDavid du Colombier if(s < 0 || s >= epmax) 600906943f9SDavid du Colombier goto Fail; 601906943f9SDavid du Colombier ep = getep(s); 602906943f9SDavid du Colombier if(ep == nil){ 603906943f9SDavid du Colombier if(0)ddprint("skip\n"); 6049a747e4fSDavid du Colombier return 0; 605906943f9SDavid du Colombier } 606906943f9SDavid du Colombier if(waserror()){ 607906943f9SDavid du Colombier putep(ep); 608906943f9SDavid du Colombier nexterror(); 609906943f9SDavid du Colombier } 610906943f9SDavid du Colombier se = up->genbuf+sizeof(up->genbuf); 611906943f9SDavid du Colombier seprint(up->genbuf, se, "ep%d.%d", ep->dev->nb, ep->nb); 612906943f9SDavid du Colombier mkqid(&q, Qep0dir+4*s, 0, QTDIR); 613906943f9SDavid du Colombier putep(ep); 614906943f9SDavid du Colombier poperror(); 615906943f9SDavid du Colombier devdir(c, q, up->genbuf, 0, eve, 0755, dp); 616906943f9SDavid du Colombier if(0)ddprint("ok\n"); 617906943f9SDavid du Colombier return 1; 618906943f9SDavid du Colombier 619906943f9SDavid du Colombier case Qctl: 620906943f9SDavid du Colombier s = 0; 621906943f9SDavid du Colombier goto Usbdir; 622906943f9SDavid du Colombier 623906943f9SDavid du Colombier default: /* list #u/usb/epN.M */ 624906943f9SDavid du Colombier nb = qid2epidx(QID(c->qid)); 625906943f9SDavid du Colombier ep = getep(nb); 626906943f9SDavid du Colombier if(ep == nil) 627906943f9SDavid du Colombier goto Fail; 628906943f9SDavid du Colombier mode = ep->mode; 629906943f9SDavid du Colombier putep(ep); 630906943f9SDavid du Colombier if(isqtype(QID(c->qid), Qepdir)){ 631906943f9SDavid du Colombier Epdir: 632906943f9SDavid du Colombier switch(s){ 633906943f9SDavid du Colombier case 0: 634906943f9SDavid du Colombier mkqid(&q, Qep0io+nb*4, 0, QTFILE); 635906943f9SDavid du Colombier perm = epdataperm(mode); 636906943f9SDavid du Colombier devdir(c, q, "data", 0, eve, perm, dp); 6379a747e4fSDavid du Colombier break; 638906943f9SDavid du Colombier case 1: 639906943f9SDavid du Colombier mkqid(&q, Qep0ctl+nb*4, 0, QTFILE); 640906943f9SDavid du Colombier devdir(c, q, "ctl", 0, eve, 0664, dp); 6419a747e4fSDavid du Colombier break; 6429a747e4fSDavid du Colombier default: 643906943f9SDavid du Colombier goto Fail; 6449a747e4fSDavid du Colombier } 645906943f9SDavid du Colombier }else if(isqtype(QID(c->qid), Qepctl)){ 646906943f9SDavid du Colombier s = 1; 647906943f9SDavid du Colombier goto Epdir; 648906943f9SDavid du Colombier }else{ 649906943f9SDavid du Colombier s = 0; 650906943f9SDavid du Colombier goto Epdir; 651906943f9SDavid du Colombier } 652906943f9SDavid du Colombier if(0)ddprint("ok\n"); 6539a747e4fSDavid du Colombier return 1; 6549a747e4fSDavid du Colombier } 655906943f9SDavid du Colombier Fail: 656906943f9SDavid du Colombier if(0)ddprint("fail\n"); 657906943f9SDavid du Colombier return -1; 658906943f9SDavid du Colombier } 659906943f9SDavid du Colombier 660906943f9SDavid du Colombier static Hci* 661906943f9SDavid du Colombier hciprobe(int cardno, int ctlrno) 6629a747e4fSDavid du Colombier { 663906943f9SDavid du Colombier Hci *hp; 664906943f9SDavid du Colombier char *type; 665e4ac449cSDavid du Colombier char name[64]; 666906943f9SDavid du Colombier static int epnb = 1; /* guess the endpoint nb. for the controller */ 6679a747e4fSDavid du Colombier 668906943f9SDavid du Colombier ddprint("hciprobe %d %d\n", cardno, ctlrno); 669*305b51b8SDavid du Colombier hp = smalloc(sizeof(Hci)); 670906943f9SDavid du Colombier hp->ctlrno = ctlrno; 671906943f9SDavid du Colombier hp->tbdf = BUSUNKNOWN; 6729a747e4fSDavid du Colombier 6739a747e4fSDavid du Colombier if(cardno < 0){ 674906943f9SDavid du Colombier if(isaconfig("usb", ctlrno, hp) == 0){ 675906943f9SDavid du Colombier free(hp); 6769a747e4fSDavid du Colombier return nil; 6779a747e4fSDavid du Colombier } 678906943f9SDavid du Colombier for(cardno = 0; cardno < Nhcis; cardno++){ 679906943f9SDavid du Colombier if(hcitypes[cardno].type == nil) 680906943f9SDavid du Colombier break; 681906943f9SDavid du Colombier type = hp->type; 6829a747e4fSDavid du Colombier if(type==nil || *type==0) 6839a747e4fSDavid du Colombier type = "uhci"; 684906943f9SDavid du Colombier if(cistrcmp(hcitypes[cardno].type, type) == 0) 6859a747e4fSDavid du Colombier break; 6869a747e4fSDavid du Colombier } 6879a747e4fSDavid du Colombier } 6889a747e4fSDavid du Colombier 689c8cbc0e9SDavid du Colombier if(cardno >= Nhcis || hcitypes[cardno].type == nil){ 690c8cbc0e9SDavid du Colombier free(hp); 691c8cbc0e9SDavid du Colombier return nil; 692c8cbc0e9SDavid du Colombier } 693c8cbc0e9SDavid du Colombier dprint("%s...", hcitypes[cardno].type); 694c8cbc0e9SDavid du Colombier if(hcitypes[cardno].reset(hp) < 0){ 695906943f9SDavid du Colombier free(hp); 6969a747e4fSDavid du Colombier return nil; 6979a747e4fSDavid du Colombier } 6989a747e4fSDavid du Colombier 6999a747e4fSDavid du Colombier /* 7009a747e4fSDavid du Colombier * IRQ2 doesn't really exist, it's used to gang the interrupt 7019a747e4fSDavid du Colombier * controllers together. A device set to IRQ2 will appear on 7029a747e4fSDavid du Colombier * the second interrupt controller as IRQ9. 7039a747e4fSDavid du Colombier */ 704906943f9SDavid du Colombier if(hp->irq == 2) 705906943f9SDavid du Colombier hp->irq = 9; 706906943f9SDavid du Colombier snprint(name, sizeof(name), "usb%s", hcitypes[cardno].type); 707906943f9SDavid du Colombier intrenable(hp->irq, hp->interrupt, hp, hp->tbdf, name); 7089a747e4fSDavid du Colombier 709e4ac449cSDavid du Colombier /* 710e4ac449cSDavid du Colombier * modern machines have too many usb controllers to list on 711e4ac449cSDavid du Colombier * the console. 712e4ac449cSDavid du Colombier */ 713a23bc242SDavid du Colombier dprint("#u/usb/ep%d.0: %s: port 0x%luX irq %d\n", 714a23bc242SDavid du Colombier epnb, hcitypes[cardno].type, hp->port, hp->irq); 715e4ac449cSDavid du Colombier epnb++; 716906943f9SDavid du Colombier return hp; 7179a747e4fSDavid du Colombier } 7189a747e4fSDavid du Colombier 7199a747e4fSDavid du Colombier static void 7209a747e4fSDavid du Colombier usbreset(void) 7219a747e4fSDavid du Colombier { 7229a747e4fSDavid du Colombier int cardno, ctlrno; 723906943f9SDavid du Colombier Hci *hp; 7249a747e4fSDavid du Colombier 725ea58ad6fSDavid du Colombier if(getconf("*nousbprobe")) 726ea58ad6fSDavid du Colombier return; 727906943f9SDavid du Colombier dprint("usbreset\n"); 728ea58ad6fSDavid du Colombier 729906943f9SDavid du Colombier for(ctlrno = 0; ctlrno < Nhcis; ctlrno++) 730906943f9SDavid du Colombier if((hp = hciprobe(-1, ctlrno)) != nil) 731906943f9SDavid du Colombier hcis[ctlrno] = hp; 7329a747e4fSDavid du Colombier cardno = ctlrno = 0; 733906943f9SDavid du Colombier while(cardno < Nhcis && ctlrno < Nhcis && hcitypes[cardno].type != nil) 734906943f9SDavid du Colombier if(hcis[ctlrno] != nil) 7359a747e4fSDavid du Colombier ctlrno++; 736906943f9SDavid du Colombier else{ 737906943f9SDavid du Colombier hp = hciprobe(cardno, ctlrno); 738906943f9SDavid du Colombier if(hp == nil) 7399a747e4fSDavid du Colombier cardno++; 740906943f9SDavid du Colombier hcis[ctlrno++] = hp; 7419a747e4fSDavid du Colombier } 742906943f9SDavid du Colombier if(hcis[Nhcis-1] != nil) 74384860c5dSDavid du Colombier print("usbreset: bug: Nhcis (%d) too small\n", Nhcis); 7449a747e4fSDavid du Colombier } 7459a747e4fSDavid du Colombier 746906943f9SDavid du Colombier static void 7479a747e4fSDavid du Colombier usbinit(void) 7489a747e4fSDavid du Colombier { 749906943f9SDavid du Colombier Hci *hp; 7509a747e4fSDavid du Colombier int ctlrno; 751906943f9SDavid du Colombier Ep *d; 752906943f9SDavid du Colombier char info[40]; 7539a747e4fSDavid du Colombier 754906943f9SDavid du Colombier dprint("usbinit\n"); 755906943f9SDavid du Colombier for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){ 756906943f9SDavid du Colombier hp = hcis[ctlrno]; 757906943f9SDavid du Colombier if(hp != nil){ 758906943f9SDavid du Colombier if(hp->init != nil) 759906943f9SDavid du Colombier hp->init(hp); 760906943f9SDavid du Colombier d = newdev(hp, 1, 1); /* new root hub */ 761906943f9SDavid du Colombier d->dev->state = Denabled; /* although addr == 0 */ 762906943f9SDavid du Colombier d->maxpkt = 64; 763906943f9SDavid du Colombier snprint(info, sizeof(info), "ports %d", hp->nports); 764906943f9SDavid du Colombier kstrdup(&d->info, info); 765906943f9SDavid du Colombier } 7669a747e4fSDavid du Colombier } 7679a747e4fSDavid du Colombier } 7689a747e4fSDavid du Colombier 769906943f9SDavid du Colombier static Chan* 7709a747e4fSDavid du Colombier usbattach(char *spec) 7719a747e4fSDavid du Colombier { 772906943f9SDavid du Colombier return devattach(L'u', spec); 7739a747e4fSDavid du Colombier } 7749a747e4fSDavid du Colombier 7759a747e4fSDavid du Colombier static Walkqid* 7769a747e4fSDavid du Colombier usbwalk(Chan *c, Chan *nc, char **name, int nname) 7779a747e4fSDavid du Colombier { 7789a747e4fSDavid du Colombier return devwalk(c, nc, name, nname, nil, 0, usbgen); 7799a747e4fSDavid du Colombier } 7809a747e4fSDavid du Colombier 7819a747e4fSDavid du Colombier static int 7829a747e4fSDavid du Colombier usbstat(Chan *c, uchar *db, int n) 7839a747e4fSDavid du Colombier { 7849a747e4fSDavid du Colombier return devstat(c, db, n, nil, 0, usbgen); 7859a747e4fSDavid du Colombier } 7869a747e4fSDavid du Colombier 787906943f9SDavid du Colombier /* 788906943f9SDavid du Colombier * µs for the given transfer, for bandwidth allocation. 789906943f9SDavid du Colombier * This is a very rough worst case for what 5.11.3 790906943f9SDavid du Colombier * of the usb 2.0 spec says. 791906943f9SDavid du Colombier * Also, we are using maxpkt and not actual transfer sizes. 792906943f9SDavid du Colombier * Only when we are sure we 793906943f9SDavid du Colombier * are not exceeding b/w might we consider adjusting it. 794906943f9SDavid du Colombier */ 795906943f9SDavid du Colombier static ulong 796906943f9SDavid du Colombier usbload(int speed, int maxpkt) 797906943f9SDavid du Colombier { 798906943f9SDavid du Colombier enum{ Hostns = 1000, Hubns = 333 }; 799906943f9SDavid du Colombier ulong l; 800906943f9SDavid du Colombier ulong bs; 801906943f9SDavid du Colombier 802906943f9SDavid du Colombier l = 0; 803906943f9SDavid du Colombier bs = 10UL * maxpkt; 804906943f9SDavid du Colombier switch(speed){ 805906943f9SDavid du Colombier case Highspeed: 806906943f9SDavid du Colombier l = 55*8*2 + 2 * (3 + bs) + Hostns; 807906943f9SDavid du Colombier break; 808906943f9SDavid du Colombier case Fullspeed: 809906943f9SDavid du Colombier l = 9107 + 84 * (4 + bs) + Hostns; 810906943f9SDavid du Colombier break; 811906943f9SDavid du Colombier case Lowspeed: 812906943f9SDavid du Colombier l = 64107 + 2 * Hubns + 667 * (3 + bs) + Hostns; 813906943f9SDavid du Colombier break; 814906943f9SDavid du Colombier default: 815906943f9SDavid du Colombier print("usbload: bad speed %d\n", speed); 816906943f9SDavid du Colombier /* let it run */ 817906943f9SDavid du Colombier } 818906943f9SDavid du Colombier return l / 1000UL; /* in µs */ 819906943f9SDavid du Colombier } 820906943f9SDavid du Colombier 821906943f9SDavid du Colombier static Chan* 8229a747e4fSDavid du Colombier usbopen(Chan *c, int omode) 8239a747e4fSDavid du Colombier { 824906943f9SDavid du Colombier int q; 825906943f9SDavid du Colombier Ep *ep; 826906943f9SDavid du Colombier int mode; 8279a747e4fSDavid du Colombier 828906943f9SDavid du Colombier mode = openmode(omode); 829906943f9SDavid du Colombier q = QID(c->qid); 830906943f9SDavid du Colombier 831906943f9SDavid du Colombier if(q >= Qep0dir && qid2epidx(q) < 0) 832906943f9SDavid du Colombier error(Eio); 833906943f9SDavid du Colombier if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir)) 8349a747e4fSDavid du Colombier return devopen(c, omode, nil, 0, usbgen); 8359a747e4fSDavid du Colombier 836906943f9SDavid du Colombier ep = getep(qid2epidx(q)); 837906943f9SDavid du Colombier if(ep == nil) 838906943f9SDavid du Colombier error(Eio); 839906943f9SDavid du Colombier deprint("usbopen q %#x fid %d omode %d\n", q, c->fid, mode); 8409a747e4fSDavid du Colombier if(waserror()){ 841906943f9SDavid du Colombier putep(ep); 8429a747e4fSDavid du Colombier nexterror(); 8439a747e4fSDavid du Colombier } 844906943f9SDavid du Colombier qlock(ep); 845906943f9SDavid du Colombier if(ep->inuse){ 846906943f9SDavid du Colombier qunlock(ep); 8479a747e4fSDavid du Colombier error(Einuse); 848906943f9SDavid du Colombier } 849906943f9SDavid du Colombier ep->inuse = 1; 850906943f9SDavid du Colombier qunlock(ep); 851906943f9SDavid du Colombier if(waserror()){ 852906943f9SDavid du Colombier ep->inuse = 0; 853906943f9SDavid du Colombier nexterror(); 854906943f9SDavid du Colombier } 855906943f9SDavid du Colombier if(mode != OREAD && ep->mode == OREAD) 856906943f9SDavid du Colombier error(Eperm); 857906943f9SDavid du Colombier if(mode != OWRITE && ep->mode == OWRITE) 858906943f9SDavid du Colombier error(Eperm); 859906943f9SDavid du Colombier if(ep->ttype == Tnone) 860906943f9SDavid du Colombier error(Enotconf); 861906943f9SDavid du Colombier ep->clrhalt = 0; 862906943f9SDavid du Colombier ep->rhrepl = -1; 863906943f9SDavid du Colombier if(ep->load == 0) 864906943f9SDavid du Colombier ep->load = usbload(ep->dev->speed, ep->maxpkt); 865906943f9SDavid du Colombier ep->hp->epopen(ep); 8669a747e4fSDavid du Colombier 867906943f9SDavid du Colombier poperror(); /* ep->inuse */ 868906943f9SDavid du Colombier poperror(); /* don't putep(): ref kept for fid using the ep. */ 869ade43d10SDavid du Colombier 870906943f9SDavid du Colombier c->mode = mode; 8719a747e4fSDavid du Colombier c->flag |= COPEN; 8729a747e4fSDavid du Colombier c->offset = 0; 873906943f9SDavid du Colombier c->aux = nil; /* paranoia */ 8749a747e4fSDavid du Colombier return c; 8759a747e4fSDavid du Colombier } 8769a747e4fSDavid du Colombier 877906943f9SDavid du Colombier static void 878906943f9SDavid du Colombier epclose(Ep *ep) 8799a747e4fSDavid du Colombier { 880906943f9SDavid du Colombier qlock(ep); 8819a747e4fSDavid du Colombier if(waserror()){ 882906943f9SDavid du Colombier qunlock(ep); 8839a747e4fSDavid du Colombier nexterror(); 8849a747e4fSDavid du Colombier } 885906943f9SDavid du Colombier if(ep->inuse){ 886906943f9SDavid du Colombier ep->hp->epclose(ep); 887906943f9SDavid du Colombier ep->inuse = 0; 888906943f9SDavid du Colombier } 889906943f9SDavid du Colombier qunlock(ep); 890906943f9SDavid du Colombier poperror(); 891906943f9SDavid du Colombier } 892906943f9SDavid du Colombier 893906943f9SDavid du Colombier static void 894906943f9SDavid du Colombier usbclose(Chan *c) 895906943f9SDavid du Colombier { 896906943f9SDavid du Colombier int q; 897906943f9SDavid du Colombier Ep *ep; 898906943f9SDavid du Colombier 899906943f9SDavid du Colombier q = QID(c->qid); 900906943f9SDavid du Colombier if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir)) 901906943f9SDavid du Colombier return; 902906943f9SDavid du Colombier 903906943f9SDavid du Colombier ep = getep(qid2epidx(q)); 904906943f9SDavid du Colombier if(ep == nil) 905906943f9SDavid du Colombier return; 906906943f9SDavid du Colombier deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref); 907906943f9SDavid du Colombier if(waserror()){ 908906943f9SDavid du Colombier putep(ep); 909906943f9SDavid du Colombier nexterror(); 910906943f9SDavid du Colombier } 9119a747e4fSDavid du Colombier if(c->flag & COPEN){ 912906943f9SDavid du Colombier free(c->aux); 913906943f9SDavid du Colombier c->aux = nil; 914906943f9SDavid du Colombier epclose(ep); 915906943f9SDavid du Colombier putep(ep); /* release ref kept since usbopen */ 916906943f9SDavid du Colombier c->flag &= ~COPEN; 9179a747e4fSDavid du Colombier } 9189a747e4fSDavid du Colombier poperror(); 919906943f9SDavid du Colombier putep(ep); 9209a747e4fSDavid du Colombier } 9219a747e4fSDavid du Colombier 922906943f9SDavid du Colombier static long 923906943f9SDavid du Colombier ctlread(Chan *c, void *a, long n, vlong offset) 9249a747e4fSDavid du Colombier { 925906943f9SDavid du Colombier int q; 926906943f9SDavid du Colombier char *s; 927906943f9SDavid du Colombier char *us; 928906943f9SDavid du Colombier char *se; 929906943f9SDavid du Colombier Ep *ep; 930906943f9SDavid du Colombier int i; 9319a747e4fSDavid du Colombier 932906943f9SDavid du Colombier q = QID(c->qid); 933906943f9SDavid du Colombier us = s = smalloc(READSTR); 934906943f9SDavid du Colombier se = s + READSTR; 935906943f9SDavid du Colombier if(waserror()){ 936906943f9SDavid du Colombier free(us); 937906943f9SDavid du Colombier nexterror(); 9389a747e4fSDavid du Colombier } 939906943f9SDavid du Colombier if(q == Qctl) 940906943f9SDavid du Colombier for(i = 0; i < epmax; i++){ 941906943f9SDavid du Colombier ep = getep(i); 942906943f9SDavid du Colombier if(ep != nil){ 943906943f9SDavid du Colombier if(waserror()){ 944906943f9SDavid du Colombier putep(ep); 945906943f9SDavid du Colombier nexterror(); 946906943f9SDavid du Colombier } 947906943f9SDavid du Colombier s = seprint(s, se, "ep%d.%d ", ep->dev->nb, ep->nb); 948906943f9SDavid du Colombier s = seprintep(s, se, ep, 0); 949906943f9SDavid du Colombier poperror(); 950906943f9SDavid du Colombier } 951906943f9SDavid du Colombier putep(ep); 952906943f9SDavid du Colombier } 953906943f9SDavid du Colombier else{ 954906943f9SDavid du Colombier ep = getep(qid2epidx(q)); 955906943f9SDavid du Colombier if(ep == nil) 956906943f9SDavid du Colombier error(Eio); 957906943f9SDavid du Colombier if(waserror()){ 958906943f9SDavid du Colombier putep(ep); 959906943f9SDavid du Colombier nexterror(); 960906943f9SDavid du Colombier } 961906943f9SDavid du Colombier if(c->aux != nil){ 962906943f9SDavid du Colombier /* After a new endpoint request we read 963906943f9SDavid du Colombier * the new endpoint name back. 964906943f9SDavid du Colombier */ 965906943f9SDavid du Colombier strecpy(s, se, c->aux); 966906943f9SDavid du Colombier free(c->aux); 967906943f9SDavid du Colombier c->aux = nil; 968906943f9SDavid du Colombier }else 969906943f9SDavid du Colombier seprintep(s, se, ep, 0); 970906943f9SDavid du Colombier poperror(); 971906943f9SDavid du Colombier putep(ep); 972906943f9SDavid du Colombier } 973906943f9SDavid du Colombier n = readstr(offset, a, n, us); 974906943f9SDavid du Colombier poperror(); 975906943f9SDavid du Colombier free(us); 976906943f9SDavid du Colombier return n; 9779a747e4fSDavid du Colombier } 9789a747e4fSDavid du Colombier 979906943f9SDavid du Colombier /* 980906943f9SDavid du Colombier * Fake root hub emulation. 981906943f9SDavid du Colombier */ 982906943f9SDavid du Colombier static long 983906943f9SDavid du Colombier rhubread(Ep *ep, void *a, long n) 984906943f9SDavid du Colombier { 985906943f9SDavid du Colombier char *b; 986906943f9SDavid du Colombier 987906943f9SDavid du Colombier if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2) 988906943f9SDavid du Colombier return -1; 989906943f9SDavid du Colombier if(ep->rhrepl < 0) 990906943f9SDavid du Colombier return -1; 991906943f9SDavid du Colombier 992906943f9SDavid du Colombier b = a; 993906943f9SDavid du Colombier memset(b, 0, n); 994906943f9SDavid du Colombier PUT2(b, ep->rhrepl); 995906943f9SDavid du Colombier ep->rhrepl = -1; 996906943f9SDavid du Colombier return n; 997906943f9SDavid du Colombier } 998906943f9SDavid du Colombier 999906943f9SDavid du Colombier static long 1000906943f9SDavid du Colombier rhubwrite(Ep *ep, void *a, long n) 1001906943f9SDavid du Colombier { 1002906943f9SDavid du Colombier uchar *s; 1003906943f9SDavid du Colombier int cmd; 1004906943f9SDavid du Colombier int feature; 1005906943f9SDavid du Colombier int port; 1006906943f9SDavid du Colombier Hci *hp; 1007906943f9SDavid du Colombier 1008906943f9SDavid du Colombier if(ep->dev == nil || ep->dev->isroot == 0 || ep->nb != 0) 1009906943f9SDavid du Colombier return -1; 1010906943f9SDavid du Colombier if(n != Rsetuplen) 1011906943f9SDavid du Colombier error("root hub is a toy hub"); 1012906943f9SDavid du Colombier ep->rhrepl = -1; 1013906943f9SDavid du Colombier s = a; 1014906943f9SDavid du Colombier if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother)) 1015906943f9SDavid du Colombier error("root hub is a toy hub"); 1016906943f9SDavid du Colombier hp = ep->hp; 1017906943f9SDavid du Colombier cmd = s[Rreq]; 1018906943f9SDavid du Colombier feature = GET2(s+Rvalue); 1019906943f9SDavid du Colombier port = GET2(s+Rindex); 1020906943f9SDavid du Colombier if(port < 1 || port > hp->nports) 1021906943f9SDavid du Colombier error("bad hub port number"); 1022906943f9SDavid du Colombier switch(feature){ 1023906943f9SDavid du Colombier case Rportenable: 1024906943f9SDavid du Colombier ep->rhrepl = hp->portenable(hp, port, cmd == Rsetfeature); 1025906943f9SDavid du Colombier break; 1026906943f9SDavid du Colombier case Rportreset: 1027906943f9SDavid du Colombier ep->rhrepl = hp->portreset(hp, port, cmd == Rsetfeature); 1028906943f9SDavid du Colombier break; 1029906943f9SDavid du Colombier case Rgetstatus: 1030906943f9SDavid du Colombier ep->rhrepl = hp->portstatus(hp, port); 1031906943f9SDavid du Colombier break; 1032906943f9SDavid du Colombier default: 1033906943f9SDavid du Colombier ep->rhrepl = 0; 1034906943f9SDavid du Colombier } 1035906943f9SDavid du Colombier return n; 1036906943f9SDavid du Colombier } 1037906943f9SDavid du Colombier 1038906943f9SDavid du Colombier static long 10399a747e4fSDavid du Colombier usbread(Chan *c, void *a, long n, vlong offset) 10409a747e4fSDavid du Colombier { 1041906943f9SDavid du Colombier int q; 1042906943f9SDavid du Colombier Ep *ep; 1043906943f9SDavid du Colombier int nr; 1044906943f9SDavid du Colombier 1045906943f9SDavid du Colombier q = QID(c->qid); 10469a747e4fSDavid du Colombier 10479a747e4fSDavid du Colombier if(c->qid.type == QTDIR) 10489a747e4fSDavid du Colombier return devdirread(c, a, n, nil, 0, usbgen); 10499a747e4fSDavid du Colombier 1050906943f9SDavid du Colombier if(q == Qctl || isqtype(q, Qepctl)) 1051906943f9SDavid du Colombier return ctlread(c, a, n, offset); 10529a747e4fSDavid du Colombier 1053906943f9SDavid du Colombier ep = getep(qid2epidx(q)); 1054906943f9SDavid du Colombier if(ep == nil) 10559a747e4fSDavid du Colombier error(Eio); 10569a747e4fSDavid du Colombier if(waserror()){ 1057906943f9SDavid du Colombier putep(ep); 10589a747e4fSDavid du Colombier nexterror(); 10599a747e4fSDavid du Colombier } 1060906943f9SDavid du Colombier if(ep->dev->state == Ddetach) 1061906943f9SDavid du Colombier error(Edetach); 1062906943f9SDavid du Colombier if(ep->mode == OWRITE || ep->inuse == 0) 1063906943f9SDavid du Colombier error(Ebadusefd); 1064906943f9SDavid du Colombier switch(ep->ttype){ 1065906943f9SDavid du Colombier case Tnone: 1066906943f9SDavid du Colombier error("endpoint not configured"); 1067906943f9SDavid du Colombier case Tctl: 1068906943f9SDavid du Colombier nr = rhubread(ep, a, n); 1069906943f9SDavid du Colombier if(nr >= 0){ 1070906943f9SDavid du Colombier n = nr; 107102c76561SDavid du Colombier break; 10729a747e4fSDavid du Colombier } 1073906943f9SDavid du Colombier /* else fall */ 1074906943f9SDavid du Colombier default: 1075906943f9SDavid du Colombier ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",q,c->fid,n,offset); 1076906943f9SDavid du Colombier n = ep->hp->epread(ep, a, n); 1077906943f9SDavid du Colombier break; 10789a747e4fSDavid du Colombier } 10799a747e4fSDavid du Colombier poperror(); 1080906943f9SDavid du Colombier putep(ep); 10819a747e4fSDavid du Colombier return n; 10829a747e4fSDavid du Colombier } 10839a747e4fSDavid du Colombier 1084906943f9SDavid du Colombier static long 1085906943f9SDavid du Colombier pow2(int n) 10869a747e4fSDavid du Colombier { 108784860c5dSDavid du Colombier return 1 << n; 1088906943f9SDavid du Colombier } 1089906943f9SDavid du Colombier 1090906943f9SDavid du Colombier static void 1091906943f9SDavid du Colombier setmaxpkt(Ep *ep, char* s) 1092906943f9SDavid du Colombier { 1093906943f9SDavid du Colombier long spp; /* samples per packet */ 1094906943f9SDavid du Colombier 1095906943f9SDavid du Colombier if(ep->dev->speed == Highspeed) 1096906943f9SDavid du Colombier spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000; 1097906943f9SDavid du Colombier else 1098906943f9SDavid du Colombier spp = (ep->hz * ep->pollival + 999) / 1000; 1099906943f9SDavid du Colombier ep->maxpkt = spp * ep->samplesz; 1100906943f9SDavid du Colombier deprint("usb: %s: setmaxpkt: hz %ld poll %ld" 1101906943f9SDavid du Colombier " ntds %d %s speed -> spp %ld maxpkt %ld\n", s, 1102906943f9SDavid du Colombier ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed], 1103906943f9SDavid du Colombier spp, ep->maxpkt); 1104906943f9SDavid du Colombier if(ep->maxpkt > 1024){ 1105906943f9SDavid du Colombier print("usb: %s: maxpkt %ld > 1024. truncating\n", s, ep->maxpkt); 1106906943f9SDavid du Colombier ep->maxpkt = 1024; 1107906943f9SDavid du Colombier } 1108906943f9SDavid du Colombier } 1109906943f9SDavid du Colombier 1110906943f9SDavid du Colombier /* 1111906943f9SDavid du Colombier * Many endpoint ctls. simply update the portable representation 1112906943f9SDavid du Colombier * of the endpoint. The actual controller driver will look 1113906943f9SDavid du Colombier * at them to setup the endpoints as dictated. 1114906943f9SDavid du Colombier */ 1115906943f9SDavid du Colombier static long 1116906943f9SDavid du Colombier epctl(Ep *ep, Chan *c, void *a, long n) 1117906943f9SDavid du Colombier { 111884860c5dSDavid du Colombier int i, l, mode, nb, tt; 111984860c5dSDavid du Colombier char *b, *s; 112084860c5dSDavid du Colombier Cmdbuf *cb; 112184860c5dSDavid du Colombier Cmdtab *ct; 1122906943f9SDavid du Colombier Ep *nep; 11239a747e4fSDavid du Colombier Udev *d; 112484860c5dSDavid du Colombier static char *Info = "info "; 1125906943f9SDavid du Colombier 1126906943f9SDavid du Colombier d = ep->dev; 1127906943f9SDavid du Colombier 1128906943f9SDavid du Colombier cb = parsecmd(a, n); 1129906943f9SDavid du Colombier if(waserror()){ 1130906943f9SDavid du Colombier free(cb); 1131906943f9SDavid du Colombier nexterror(); 1132906943f9SDavid du Colombier } 1133906943f9SDavid du Colombier ct = lookupcmd(cb, epctls, nelem(epctls)); 1134906943f9SDavid du Colombier if(ct == nil) 1135906943f9SDavid du Colombier error(Ebadctl); 1136906943f9SDavid du Colombier i = ct->index; 1137a23bc242SDavid du Colombier if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset) 1138906943f9SDavid du Colombier if(ep != ep->ep0) 1139906943f9SDavid du Colombier error("allowed only on a setup endpoint"); 1140906943f9SDavid du Colombier if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname) 1141906943f9SDavid du Colombier if(ep != ep->ep0 && ep->inuse != 0) 1142906943f9SDavid du Colombier error("must configure before using"); 1143906943f9SDavid du Colombier switch(i){ 1144906943f9SDavid du Colombier case CMnew: 1145906943f9SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 1146906943f9SDavid du Colombier nb = strtol(cb->f[1], nil, 0); 1147906943f9SDavid du Colombier if(nb < 0 || nb >= Ndeveps) 1148906943f9SDavid du Colombier error("bad endpoint number"); 1149906943f9SDavid du Colombier tt = name2ttype(cb->f[2]); 1150906943f9SDavid du Colombier if(tt == Tnone) 1151906943f9SDavid du Colombier error("unknown endpoint type"); 1152906943f9SDavid du Colombier mode = name2mode(cb->f[3]); 1153906943f9SDavid du Colombier if(mode < 0) 1154906943f9SDavid du Colombier error("unknown i/o mode"); 1155906943f9SDavid du Colombier newdevep(ep, nb, tt, mode); 1156906943f9SDavid du Colombier break; 1157906943f9SDavid du Colombier case CMnewdev: 1158906943f9SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 1159906943f9SDavid du Colombier if(ep != ep->ep0 || d->ishub == 0) 1160906943f9SDavid du Colombier error("not a hub setup endpoint"); 1161906943f9SDavid du Colombier l = name2speed(cb->f[1]); 1162906943f9SDavid du Colombier if(l == Nospeed) 1163906943f9SDavid du Colombier error("speed must be full|low|high"); 1164906943f9SDavid du Colombier nep = newdev(ep->hp, 0, 0); 1165906943f9SDavid du Colombier nep->dev->speed = l; 1166906943f9SDavid du Colombier if(nep->dev->speed != Lowspeed) 1167906943f9SDavid du Colombier nep->maxpkt = 64; /* assume full speed */ 1168906943f9SDavid du Colombier nep->dev->hub = d->nb; 1169906943f9SDavid du Colombier nep->dev->port = atoi(cb->f[2]); 1170906943f9SDavid du Colombier /* next read request will read 1171906943f9SDavid du Colombier * the name for the new endpoint 1172906943f9SDavid du Colombier */ 1173906943f9SDavid du Colombier l = sizeof(up->genbuf); 1174906943f9SDavid du Colombier snprint(up->genbuf, l, "ep%d.%d", nep->dev->nb, nep->nb); 1175906943f9SDavid du Colombier kstrdup(&c->aux, up->genbuf); 1176906943f9SDavid du Colombier break; 1177906943f9SDavid du Colombier case CMhub: 1178906943f9SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 1179906943f9SDavid du Colombier d->ishub = 1; 1180906943f9SDavid du Colombier break; 1181906943f9SDavid du Colombier case CMspeed: 1182906943f9SDavid du Colombier l = name2speed(cb->f[1]); 1183906943f9SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l); 1184906943f9SDavid du Colombier if(l == Nospeed) 1185906943f9SDavid du Colombier error("speed must be full|low|high"); 1186906943f9SDavid du Colombier qlock(ep->ep0); 1187906943f9SDavid du Colombier d->speed = l; 1188906943f9SDavid du Colombier qunlock(ep->ep0); 1189906943f9SDavid du Colombier break; 1190906943f9SDavid du Colombier case CMmaxpkt: 1191906943f9SDavid du Colombier l = strtoul(cb->f[1], nil, 0); 1192906943f9SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l); 1193906943f9SDavid du Colombier if(l < 1 || l > 1024) 1194906943f9SDavid du Colombier error("maxpkt not in [1:1024]"); 1195906943f9SDavid du Colombier qlock(ep); 1196906943f9SDavid du Colombier ep->maxpkt = l; 1197906943f9SDavid du Colombier qunlock(ep); 1198906943f9SDavid du Colombier break; 1199906943f9SDavid du Colombier case CMntds: 1200906943f9SDavid du Colombier l = strtoul(cb->f[1], nil, 0); 1201906943f9SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l); 1202906943f9SDavid du Colombier if(l < 1 || l > 3) 1203906943f9SDavid du Colombier error("ntds not in [1:3]"); 1204906943f9SDavid du Colombier qlock(ep); 1205906943f9SDavid du Colombier ep->ntds = l; 1206906943f9SDavid du Colombier qunlock(ep); 1207906943f9SDavid du Colombier break; 1208906943f9SDavid du Colombier case CMpollival: 1209906943f9SDavid du Colombier if(ep->ttype != Tintr && ep->ttype != Tiso) 1210906943f9SDavid du Colombier error("not an intr or iso endpoint"); 1211906943f9SDavid du Colombier l = strtoul(cb->f[1], nil, 0); 1212906943f9SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l); 1213906943f9SDavid du Colombier if(ep->ttype == Tiso || 1214906943f9SDavid du Colombier (ep->ttype == Tintr && ep->dev->speed == Highspeed)){ 1215906943f9SDavid du Colombier if(l < 1 || l > 16) 1216906943f9SDavid du Colombier error("pollival power not in [1:16]"); 1217906943f9SDavid du Colombier l = pow2(l-1); 1218906943f9SDavid du Colombier }else 1219906943f9SDavid du Colombier if(l < 1 || l > 255) 1220906943f9SDavid du Colombier error("pollival not in [1:255]"); 1221906943f9SDavid du Colombier qlock(ep); 1222906943f9SDavid du Colombier ep->pollival = l; 1223906943f9SDavid du Colombier if(ep->ttype == Tiso) 1224906943f9SDavid du Colombier setmaxpkt(ep, "pollival"); 1225906943f9SDavid du Colombier qunlock(ep); 1226906943f9SDavid du Colombier break; 1227906943f9SDavid du Colombier case CMsamplesz: 1228906943f9SDavid du Colombier if(ep->ttype != Tiso) 1229906943f9SDavid du Colombier error("not an iso endpoint"); 1230906943f9SDavid du Colombier l = strtoul(cb->f[1], nil, 0); 1231906943f9SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l); 1232906943f9SDavid du Colombier if(l <= 0 || l > 8) 1233906943f9SDavid du Colombier error("samplesz not in [1:8]"); 1234906943f9SDavid du Colombier qlock(ep); 1235906943f9SDavid du Colombier ep->samplesz = l; 1236906943f9SDavid du Colombier setmaxpkt(ep, "samplesz"); 1237906943f9SDavid du Colombier qunlock(ep); 1238906943f9SDavid du Colombier break; 1239906943f9SDavid du Colombier case CMhz: 1240906943f9SDavid du Colombier if(ep->ttype != Tiso) 1241906943f9SDavid du Colombier error("not an iso endpoint"); 1242906943f9SDavid du Colombier l = strtoul(cb->f[1], nil, 0); 1243906943f9SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l); 1244906943f9SDavid du Colombier if(l <= 0 || l > 100000) 1245906943f9SDavid du Colombier error("hz not in [1:100000]"); 1246906943f9SDavid du Colombier qlock(ep); 1247906943f9SDavid du Colombier ep->hz = l; 1248906943f9SDavid du Colombier setmaxpkt(ep, "hz"); 1249906943f9SDavid du Colombier qunlock(ep); 1250906943f9SDavid du Colombier break; 1251906943f9SDavid du Colombier case CMclrhalt: 1252906943f9SDavid du Colombier qlock(ep); 1253906943f9SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 1254906943f9SDavid du Colombier ep->clrhalt = 1; 1255906943f9SDavid du Colombier qunlock(ep); 1256906943f9SDavid du Colombier break; 1257906943f9SDavid du Colombier case CMinfo: 1258906943f9SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 1259906943f9SDavid du Colombier l = strlen(Info); 1260906943f9SDavid du Colombier s = a; 1261906943f9SDavid du Colombier if(n < l+2 || strncmp(Info, s, l) != 0) 1262906943f9SDavid du Colombier error(Ebadctl); 1263906943f9SDavid du Colombier if(n > 1024) 1264906943f9SDavid du Colombier n = 1024; 1265906943f9SDavid du Colombier b = smalloc(n); 1266906943f9SDavid du Colombier memmove(b, s+l, n-l); 1267906943f9SDavid du Colombier b[n-l] = 0; 1268906943f9SDavid du Colombier if(b[n-l-1] == '\n') 1269906943f9SDavid du Colombier b[n-l-1] = 0; 1270906943f9SDavid du Colombier qlock(ep); 1271906943f9SDavid du Colombier free(ep->info); 1272906943f9SDavid du Colombier ep->info = b; 1273906943f9SDavid du Colombier qunlock(ep); 1274906943f9SDavid du Colombier break; 1275906943f9SDavid du Colombier case CMaddress: 1276906943f9SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 1277906943f9SDavid du Colombier ep->dev->state = Denabled; 1278906943f9SDavid du Colombier break; 1279906943f9SDavid du Colombier case CMdetach: 1280906943f9SDavid du Colombier if(ep->dev->isroot != 0) 1281906943f9SDavid du Colombier error("can't detach a root hub"); 1282906943f9SDavid du Colombier deprint("usb epctl %s ep%d.%d\n", 1283906943f9SDavid du Colombier cb->f[0], ep->dev->nb, ep->nb); 1284906943f9SDavid du Colombier ep->dev->state = Ddetach; 1285906943f9SDavid du Colombier /* Release file system ref. for its endpoints */ 1286906943f9SDavid du Colombier for(i = 0; i < nelem(ep->dev->eps); i++) 1287906943f9SDavid du Colombier putep(ep->dev->eps[i]); 1288906943f9SDavid du Colombier break; 1289906943f9SDavid du Colombier case CMdebugep: 1290906943f9SDavid du Colombier if(strcmp(cb->f[1], "on") == 0) 1291906943f9SDavid du Colombier ep->debug = 1; 1292906943f9SDavid du Colombier else if(strcmp(cb->f[1], "off") == 0) 1293906943f9SDavid du Colombier ep->debug = 0; 1294906943f9SDavid du Colombier else 1295906943f9SDavid du Colombier ep->debug = strtoul(cb->f[1], nil, 0); 1296906943f9SDavid du Colombier print("usb: ep%d.%d debug %d\n", 1297906943f9SDavid du Colombier ep->dev->nb, ep->nb, ep->debug); 1298906943f9SDavid du Colombier break; 1299906943f9SDavid du Colombier case CMname: 1300906943f9SDavid du Colombier deprint("usb epctl %s %s\n", cb->f[0], cb->f[1]); 1301906943f9SDavid du Colombier validname(cb->f[1], 0); 1302906943f9SDavid du Colombier kstrdup(&ep->name, cb->f[1]); 1303906943f9SDavid du Colombier break; 1304d37e33ffSDavid du Colombier case CMtmout: 1305d37e33ffSDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 1306d37e33ffSDavid du Colombier if(ep->ttype == Tiso || ep->ttype == Tctl) 1307d37e33ffSDavid du Colombier error("ctl ignored for this endpoint type"); 1308d37e33ffSDavid du Colombier ep->tmout = strtoul(cb->f[1], nil, 0); 1309d37e33ffSDavid du Colombier if(ep->tmout != 0 && ep->tmout < Xfertmout) 1310d37e33ffSDavid du Colombier ep->tmout = Xfertmout; 1311d37e33ffSDavid du Colombier break; 1312a23bc242SDavid du Colombier case CMpreset: 1313a23bc242SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 1314a23bc242SDavid du Colombier if(ep->ttype != Tctl) 1315a23bc242SDavid du Colombier error("not a control endpoint"); 1316a23bc242SDavid du Colombier if(ep->dev->state != Denabled) 1317a23bc242SDavid du Colombier error("forbidden on devices not enabled"); 1318a23bc242SDavid du Colombier ep->dev->state = Dreset; 1319a23bc242SDavid du Colombier break; 1320906943f9SDavid du Colombier default: 1321c9b6d007SDavid du Colombier panic("usb: unknown epctl %d", ct->index); 1322906943f9SDavid du Colombier } 1323906943f9SDavid du Colombier free(cb); 1324906943f9SDavid du Colombier poperror(); 1325906943f9SDavid du Colombier return n; 1326906943f9SDavid du Colombier } 1327906943f9SDavid du Colombier 1328906943f9SDavid du Colombier static long 1329906943f9SDavid du Colombier usbctl(void *a, long n) 1330906943f9SDavid du Colombier { 1331906943f9SDavid du Colombier Cmdtab *ct; 1332906943f9SDavid du Colombier Cmdbuf *cb; 1333906943f9SDavid du Colombier Ep *ep; 1334906943f9SDavid du Colombier int i; 1335906943f9SDavid du Colombier 1336906943f9SDavid du Colombier cb = parsecmd(a, n); 1337906943f9SDavid du Colombier if(waserror()){ 1338906943f9SDavid du Colombier free(cb); 1339906943f9SDavid du Colombier nexterror(); 1340906943f9SDavid du Colombier } 1341906943f9SDavid du Colombier ct = lookupcmd(cb, usbctls, nelem(usbctls)); 1342906943f9SDavid du Colombier dprint("usb ctl %s\n", cb->f[0]); 1343906943f9SDavid du Colombier switch(ct->index){ 1344906943f9SDavid du Colombier case CMdebug: 1345906943f9SDavid du Colombier if(strcmp(cb->f[1], "on") == 0) 1346906943f9SDavid du Colombier debug = 1; 1347906943f9SDavid du Colombier else if(strcmp(cb->f[1], "off") == 0) 1348906943f9SDavid du Colombier debug = 0; 1349906943f9SDavid du Colombier else 1350906943f9SDavid du Colombier debug = strtol(cb->f[1], nil, 0); 1351906943f9SDavid du Colombier print("usb: debug %d\n", debug); 1352906943f9SDavid du Colombier for(i = 0; i < epmax; i++) 1353906943f9SDavid du Colombier if((ep = getep(i)) != nil){ 1354906943f9SDavid du Colombier ep->hp->debug(ep->hp, debug); 1355906943f9SDavid du Colombier putep(ep); 1356906943f9SDavid du Colombier } 1357906943f9SDavid du Colombier break; 1358906943f9SDavid du Colombier case CMdump: 1359906943f9SDavid du Colombier dumpeps(); 1360906943f9SDavid du Colombier break; 1361906943f9SDavid du Colombier } 1362906943f9SDavid du Colombier free(cb); 1363906943f9SDavid du Colombier poperror(); 1364906943f9SDavid du Colombier return n; 1365906943f9SDavid du Colombier } 1366906943f9SDavid du Colombier 1367906943f9SDavid du Colombier static long 1368906943f9SDavid du Colombier ctlwrite(Chan *c, void *a, long n) 1369906943f9SDavid du Colombier { 1370906943f9SDavid du Colombier int q; 1371906943f9SDavid du Colombier Ep *ep; 1372906943f9SDavid du Colombier 1373906943f9SDavid du Colombier q = QID(c->qid); 1374906943f9SDavid du Colombier if(q == Qctl) 1375906943f9SDavid du Colombier return usbctl(a, n); 1376906943f9SDavid du Colombier 1377906943f9SDavid du Colombier ep = getep(qid2epidx(q)); 1378906943f9SDavid du Colombier if(ep == nil) 1379906943f9SDavid du Colombier error(Eio); 1380906943f9SDavid du Colombier if(waserror()){ 1381906943f9SDavid du Colombier putep(ep); 1382906943f9SDavid du Colombier nexterror(); 1383906943f9SDavid du Colombier } 1384906943f9SDavid du Colombier if(ep->dev->state == Ddetach) 1385906943f9SDavid du Colombier error(Edetach); 1386906943f9SDavid du Colombier if(isqtype(q, Qepctl) && c->aux != nil){ 1387906943f9SDavid du Colombier /* Be sure we don't keep a cloned ep name */ 1388906943f9SDavid du Colombier free(c->aux); 1389906943f9SDavid du Colombier c->aux = nil; 1390906943f9SDavid du Colombier error("read, not write, expected"); 1391906943f9SDavid du Colombier } 1392906943f9SDavid du Colombier n = epctl(ep, c, a, n); 1393906943f9SDavid du Colombier putep(ep); 1394906943f9SDavid du Colombier poperror(); 1395906943f9SDavid du Colombier return n; 1396906943f9SDavid du Colombier } 1397906943f9SDavid du Colombier 1398906943f9SDavid du Colombier static long 1399906943f9SDavid du Colombier usbwrite(Chan *c, void *a, long n, vlong off) 1400906943f9SDavid du Colombier { 140184860c5dSDavid du Colombier int nr, q; 1402906943f9SDavid du Colombier Ep *ep; 14039a747e4fSDavid du Colombier 14049a747e4fSDavid du Colombier if(c->qid.type == QTDIR) 140545d5944cSDavid du Colombier error(Eisdir); 14069a747e4fSDavid du Colombier 1407906943f9SDavid du Colombier q = QID(c->qid); 14089a747e4fSDavid du Colombier 1409906943f9SDavid du Colombier if(q == Qctl || isqtype(q, Qepctl)) 1410906943f9SDavid du Colombier return ctlwrite(c, a, n); 14119a747e4fSDavid du Colombier 1412906943f9SDavid du Colombier ep = getep(qid2epidx(q)); 1413906943f9SDavid du Colombier if(ep == nil) 14149a747e4fSDavid du Colombier error(Eio); 1415906943f9SDavid du Colombier if(waserror()){ 1416906943f9SDavid du Colombier putep(ep); 1417906943f9SDavid du Colombier nexterror(); 1418906943f9SDavid du Colombier } 1419906943f9SDavid du Colombier if(ep->dev->state == Ddetach) 1420906943f9SDavid du Colombier error(Edetach); 1421906943f9SDavid du Colombier if(ep->mode == OREAD || ep->inuse == 0) 1422906943f9SDavid du Colombier error(Ebadusefd); 14239a747e4fSDavid du Colombier 1424906943f9SDavid du Colombier switch(ep->ttype){ 1425906943f9SDavid du Colombier case Tnone: 1426906943f9SDavid du Colombier error("endpoint not configured"); 1427906943f9SDavid du Colombier case Tctl: 1428906943f9SDavid du Colombier nr = rhubwrite(ep, a, n); 1429906943f9SDavid du Colombier if(nr >= 0){ 1430906943f9SDavid du Colombier n = nr; 14319a747e4fSDavid du Colombier break; 14329a747e4fSDavid du Colombier } 1433906943f9SDavid du Colombier /* else fall */ 1434906943f9SDavid du Colombier default: 1435906943f9SDavid du Colombier ddeprint("\nusbwrite q %#x fid %d cnt %ld off %lld\n",q, c->fid, n, off); 1436906943f9SDavid du Colombier ep->hp->epwrite(ep, a, n); 1437906943f9SDavid du Colombier } 1438906943f9SDavid du Colombier putep(ep); 1439906943f9SDavid du Colombier poperror(); 14409a747e4fSDavid du Colombier return n; 14419a747e4fSDavid du Colombier } 14429a747e4fSDavid du Colombier 1443c8cbc0e9SDavid du Colombier void 1444c8cbc0e9SDavid du Colombier usbshutdown(void) 1445c8cbc0e9SDavid du Colombier { 1446c8cbc0e9SDavid du Colombier Hci *hp; 1447c8cbc0e9SDavid du Colombier int i; 1448c8cbc0e9SDavid du Colombier 1449c8cbc0e9SDavid du Colombier for(i = 0; i < Nhcis; i++){ 1450c8cbc0e9SDavid du Colombier hp = hcis[i]; 1451c8cbc0e9SDavid du Colombier if(hp == nil) 1452c8cbc0e9SDavid du Colombier continue; 1453c8cbc0e9SDavid du Colombier if(hp->shutdown == nil) 1454c8cbc0e9SDavid du Colombier print("#u: no shutdown function for %s\n", hp->type); 1455c8cbc0e9SDavid du Colombier else 1456c8cbc0e9SDavid du Colombier hp->shutdown(hp); 1457c8cbc0e9SDavid du Colombier } 1458c8cbc0e9SDavid du Colombier } 1459c8cbc0e9SDavid du Colombier 14609a747e4fSDavid du Colombier Dev usbdevtab = { 1461906943f9SDavid du Colombier L'u', 14629a747e4fSDavid du Colombier "usb", 14639a747e4fSDavid du Colombier 14649a747e4fSDavid du Colombier usbreset, 14659a747e4fSDavid du Colombier usbinit, 1466c8cbc0e9SDavid du Colombier usbshutdown, 14679a747e4fSDavid du Colombier usbattach, 14689a747e4fSDavid du Colombier usbwalk, 14699a747e4fSDavid du Colombier usbstat, 14709a747e4fSDavid du Colombier usbopen, 14719a747e4fSDavid du Colombier devcreate, 14729a747e4fSDavid du Colombier usbclose, 14739a747e4fSDavid du Colombier usbread, 14749a747e4fSDavid du Colombier devbread, 14759a747e4fSDavid du Colombier usbwrite, 14769a747e4fSDavid du Colombier devbwrite, 14779a747e4fSDavid du Colombier devremove, 14789a747e4fSDavid du Colombier devwstat, 14799a747e4fSDavid du Colombier }; 1480