18e32b400SDavid du Colombier /* 2add6b5c5SDavid du Colombier * USB device driver framework. 38e32b400SDavid du Colombier * 48e32b400SDavid du Colombier * This is in charge of providing access to actual HCIs 58e32b400SDavid du Colombier * and providing I/O to the various endpoints of devices. 68e32b400SDavid du Colombier * A separate user program (usbd) is in charge of 78e32b400SDavid du Colombier * enumerating the bus, setting up endpoints and 88e32b400SDavid du Colombier * starting devices (also user programs). 98e32b400SDavid du Colombier * 108e32b400SDavid du Colombier * The interface provided is a violation of the standard: 118e32b400SDavid du Colombier * you're welcome. 128e32b400SDavid du Colombier * 138e32b400SDavid du Colombier * The interface consists of a root directory with several files 148e32b400SDavid du Colombier * plus a directory (epN.M) with two files per endpoint. 158e32b400SDavid du Colombier * A device is represented by its first endpoint, which 168e32b400SDavid du Colombier * is a control endpoint automatically allocated for each device. 178e32b400SDavid du Colombier * Device control endpoints may be used to create new endpoints. 188e32b400SDavid du Colombier * Devices corresponding to hubs may also allocate new devices, 198e32b400SDavid du Colombier * perhaps also hubs. Initially, a hub device is allocated for 208e32b400SDavid du Colombier * each controller present, to represent its root hub. Those can 218e32b400SDavid du Colombier * never be removed. 228e32b400SDavid du Colombier * 238e32b400SDavid du Colombier * All endpoints refer to the first endpoint (epN.0) of the device, 248e32b400SDavid du Colombier * which keeps per-device information, and also to the HCI used 258e32b400SDavid du Colombier * to reach them. Although all endpoints cache that information. 268e32b400SDavid du Colombier * 278e32b400SDavid du Colombier * epN.M/data files permit I/O and are considered DMEXCL. 288e32b400SDavid du Colombier * epN.M/ctl files provide status info and accept control requests. 298e32b400SDavid du Colombier * 308e32b400SDavid du Colombier * Endpoints may be given file names to be listed also at #u, 318e32b400SDavid du Colombier * for those drivers that have nothing to do after configuring the 328e32b400SDavid du Colombier * device and its endpoints. 338e32b400SDavid du Colombier * 348e32b400SDavid du Colombier * Drivers for different controllers are kept at usb[oue]hci.c 358e32b400SDavid du Colombier * It's likely we could factor out much from controllers into 368e32b400SDavid du Colombier * a generic controller driver, the problem is that details 378e32b400SDavid du Colombier * regarding how to handle toggles, tokens, Tds, etc. will 388e32b400SDavid du Colombier * get in the way. Thus, code is probably easier the way it is. 398e32b400SDavid du Colombier */ 408e32b400SDavid du Colombier 418e32b400SDavid du Colombier #include "u.h" 428e32b400SDavid du Colombier #include "../port/lib.h" 438e32b400SDavid du Colombier #include "mem.h" 448e32b400SDavid du Colombier #include "dat.h" 458e32b400SDavid du Colombier #include "fns.h" 468e32b400SDavid du Colombier #include "io.h" 478e32b400SDavid du Colombier #include "../port/error.h" 4884860c5dSDavid du Colombier #include "../port/usb.h" 498e32b400SDavid du Colombier 508e32b400SDavid du Colombier typedef struct Hcitype Hcitype; 518e32b400SDavid du Colombier 528e32b400SDavid du Colombier enum 538e32b400SDavid du Colombier { 548e32b400SDavid du Colombier /* Qid numbers */ 558e32b400SDavid du Colombier Qdir = 0, /* #u */ 568e32b400SDavid du Colombier Qusbdir, /* #u/usb */ 578e32b400SDavid du Colombier Qctl, /* #u/usb/ctl - control requests */ 588e32b400SDavid du Colombier 598e32b400SDavid du Colombier Qep0dir, /* #u/usb/ep0.0 - endpoint 0 dir */ 608e32b400SDavid du Colombier Qep0io, /* #u/usb/ep0.0/data - endpoint 0 I/O */ 618e32b400SDavid du Colombier Qep0ctl, /* #u/usb/ep0.0/ctl - endpoint 0 ctl. */ 628e32b400SDavid du Colombier Qep0dummy, /* give 4 qids to each endpoint */ 638e32b400SDavid du Colombier 648e32b400SDavid du Colombier Qepdir = 0, /* (qid-qep0dir)&3 is one of these */ 658e32b400SDavid du Colombier Qepio, /* to identify which file for the endpoint */ 668e32b400SDavid du Colombier Qepctl, 678e32b400SDavid du Colombier 688e32b400SDavid du Colombier /* ... */ 698e32b400SDavid du Colombier 708e32b400SDavid du Colombier /* Usb ctls. */ 718e32b400SDavid du Colombier CMdebug = 0, /* debug on|off */ 728e32b400SDavid du Colombier CMdump, /* dump (data structures for debug) */ 738e32b400SDavid du Colombier 748e32b400SDavid du Colombier /* Ep. ctls */ 758e32b400SDavid du Colombier CMnew = 0, /* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */ 768e32b400SDavid du Colombier CMnewdev, /* newdev full|low|high portnb (allocate new devices) */ 778e32b400SDavid du Colombier CMhub, /* hub (set the device as a hub) */ 788e32b400SDavid du Colombier CMspeed, /* speed full|low|high|no */ 798e32b400SDavid du Colombier CMmaxpkt, /* maxpkt size */ 808e32b400SDavid du Colombier CMntds, /* ntds nb (max nb. of tds per µframe) */ 818e32b400SDavid du Colombier CMclrhalt, /* clrhalt (halt was cleared on endpoint) */ 828e32b400SDavid du Colombier CMpollival, /* pollival interval (interrupt/iso) */ 838e32b400SDavid du Colombier CMhz, /* hz n (samples/sec; iso) */ 848e32b400SDavid du Colombier CMsamplesz, /* samplesz n (sample size; iso) */ 858e32b400SDavid du Colombier CMinfo, /* info infostr (ke.ep info for humans) */ 868e32b400SDavid du Colombier CMdetach, /* detach (abort I/O forever on this ep). */ 878e32b400SDavid du Colombier CMaddress, /* address (address is assigned) */ 888e32b400SDavid du Colombier CMdebugep, /* debug n (set/clear debug for this ep) */ 898e32b400SDavid du Colombier CMname, /* name str (show up as #u/name as well) */ 908e32b400SDavid du Colombier CMtmout, /* timeout n (activate timeouts for ep) */ 918e32b400SDavid du Colombier CMpreset, /* reset the port */ 928e32b400SDavid du Colombier 938e32b400SDavid du Colombier /* Hub feature selectors */ 948e32b400SDavid du Colombier Rportenable = 1, 958e32b400SDavid du Colombier Rportreset = 4, 968e32b400SDavid du Colombier 978e32b400SDavid du Colombier }; 988e32b400SDavid du Colombier 998e32b400SDavid du Colombier struct Hcitype 1008e32b400SDavid du Colombier { 1018e32b400SDavid du Colombier char* type; 1028e32b400SDavid du Colombier int (*reset)(Hci*); 1038e32b400SDavid du Colombier }; 1048e32b400SDavid du Colombier 1058e32b400SDavid du Colombier #define QID(q) ((int)(q).path) 1068e32b400SDavid du Colombier 1078e32b400SDavid du Colombier static char Edetach[] = "device is detached"; 1088e32b400SDavid du Colombier static char Enotconf[] = "endpoint not configured"; 1098e32b400SDavid du Colombier char Estalled[] = "endpoint stalled"; 1108e32b400SDavid du Colombier 1118e32b400SDavid du Colombier static Cmdtab usbctls[] = 1128e32b400SDavid du Colombier { 1138e32b400SDavid du Colombier {CMdebug, "debug", 2}, 1148e32b400SDavid du Colombier {CMdump, "dump", 1}, 1158e32b400SDavid du Colombier }; 1168e32b400SDavid du Colombier 1178e32b400SDavid du Colombier static Cmdtab epctls[] = 1188e32b400SDavid du Colombier { 1198e32b400SDavid du Colombier {CMnew, "new", 4}, 1208e32b400SDavid du Colombier {CMnewdev, "newdev", 3}, 1218e32b400SDavid du Colombier {CMhub, "hub", 1}, 1228e32b400SDavid du Colombier {CMspeed, "speed", 2}, 1238e32b400SDavid du Colombier {CMmaxpkt, "maxpkt", 2}, 1248e32b400SDavid du Colombier {CMntds, "ntds", 2}, 1258e32b400SDavid du Colombier {CMpollival, "pollival", 2}, 1268e32b400SDavid du Colombier {CMsamplesz, "samplesz", 2}, 1278e32b400SDavid du Colombier {CMhz, "hz", 2}, 1288e32b400SDavid du Colombier {CMinfo, "info", 0}, 1298e32b400SDavid du Colombier {CMdetach, "detach", 1}, 1308e32b400SDavid du Colombier {CMaddress, "address", 1}, 1318e32b400SDavid du Colombier {CMdebugep, "debug", 2}, 1328e32b400SDavid du Colombier {CMclrhalt, "clrhalt", 1}, 1338e32b400SDavid du Colombier {CMname, "name", 2}, 1348e32b400SDavid du Colombier {CMtmout, "timeout", 2}, 1358e32b400SDavid du Colombier {CMpreset, "reset", 1}, 1368e32b400SDavid du Colombier }; 1378e32b400SDavid du Colombier 1388e32b400SDavid du Colombier static Dirtab usbdir[] = 1398e32b400SDavid du Colombier { 1408e32b400SDavid du Colombier "ctl", {Qctl}, 0, 0666, 1418e32b400SDavid du Colombier }; 1428e32b400SDavid du Colombier 1438e32b400SDavid du Colombier char *usbmodename[] = 1448e32b400SDavid du Colombier { 1458e32b400SDavid du Colombier [OREAD] "r", 1468e32b400SDavid du Colombier [OWRITE] "w", 1478e32b400SDavid du Colombier [ORDWR] "rw", 1488e32b400SDavid du Colombier }; 1498e32b400SDavid du Colombier 1508e32b400SDavid du Colombier static char *ttname[] = 1518e32b400SDavid du Colombier { 1528e32b400SDavid du Colombier [Tnone] "none", 1538e32b400SDavid du Colombier [Tctl] "control", 1548e32b400SDavid du Colombier [Tiso] "iso", 1558e32b400SDavid du Colombier [Tintr] "interrupt", 1568e32b400SDavid du Colombier [Tbulk] "bulk", 1578e32b400SDavid du Colombier }; 1588e32b400SDavid du Colombier 1598e32b400SDavid du Colombier static char *spname[] = 1608e32b400SDavid du Colombier { 1618e32b400SDavid du Colombier [Fullspeed] "full", 1628e32b400SDavid du Colombier [Lowspeed] "low", 1638e32b400SDavid du Colombier [Highspeed] "high", 1648e32b400SDavid du Colombier [Nospeed] "no", 1658e32b400SDavid du Colombier }; 1668e32b400SDavid du Colombier 167c8a340cdSDavid du Colombier static int debug; 1688e32b400SDavid du Colombier static Hcitype hcitypes[Nhcis]; 1698e32b400SDavid du Colombier static Hci* hcis[Nhcis]; 1708e32b400SDavid du Colombier static QLock epslck; /* add, del, lookup endpoints */ 1718e32b400SDavid du Colombier static Ep* eps[Neps]; /* all endpoints known */ 1728e32b400SDavid du Colombier static int epmax; /* 1 + last endpoint index used */ 1738e32b400SDavid du Colombier static int usbidgen; /* device address generator */ 1748e32b400SDavid du Colombier 1758e32b400SDavid du Colombier /* 1768e32b400SDavid du Colombier * Is there something like this in a library? should it be? 1778e32b400SDavid du Colombier */ 1788e32b400SDavid du Colombier char* 1798e32b400SDavid du Colombier seprintdata(char *s, char *se, uchar *d, int n) 1808e32b400SDavid du Colombier { 1818e32b400SDavid du Colombier int i, l; 1828e32b400SDavid du Colombier 1838e32b400SDavid du Colombier s = seprint(s, se, " %#p[%d]: ", d, n); 1848e32b400SDavid du Colombier l = n; 1858e32b400SDavid du Colombier if(l > 10) 1868e32b400SDavid du Colombier l = 10; 1878e32b400SDavid du Colombier for(i=0; i<l; i++) 1888e32b400SDavid du Colombier s = seprint(s, se, " %2.2ux", d[i]); 1898e32b400SDavid du Colombier if(l < n) 1908e32b400SDavid du Colombier s = seprint(s, se, "..."); 1918e32b400SDavid du Colombier return s; 1928e32b400SDavid du Colombier } 1938e32b400SDavid du Colombier 1948e32b400SDavid du Colombier static int 1958e32b400SDavid du Colombier name2speed(char *name) 1968e32b400SDavid du Colombier { 1978e32b400SDavid du Colombier int i; 1988e32b400SDavid du Colombier 1998e32b400SDavid du Colombier for(i = 0; i < nelem(spname); i++) 2008e32b400SDavid du Colombier if(strcmp(name, spname[i]) == 0) 2018e32b400SDavid du Colombier return i; 2028e32b400SDavid du Colombier return Nospeed; 2038e32b400SDavid du Colombier } 2048e32b400SDavid du Colombier 2058e32b400SDavid du Colombier static int 2068e32b400SDavid du Colombier name2ttype(char *name) 2078e32b400SDavid du Colombier { 2088e32b400SDavid du Colombier int i; 2098e32b400SDavid du Colombier 2108e32b400SDavid du Colombier for(i = 0; i < nelem(ttname); i++) 2118e32b400SDavid du Colombier if(strcmp(name, ttname[i]) == 0) 2128e32b400SDavid du Colombier return i; 2138e32b400SDavid du Colombier /* may be a std. USB ep. type */ 2148e32b400SDavid du Colombier i = strtol(name, nil, 0); 2158e32b400SDavid du Colombier switch(i+1){ 2168e32b400SDavid du Colombier case Tctl: 2178e32b400SDavid du Colombier case Tiso: 2188e32b400SDavid du Colombier case Tbulk: 2198e32b400SDavid du Colombier case Tintr: 2208e32b400SDavid du Colombier return i+1; 2218e32b400SDavid du Colombier default: 2228e32b400SDavid du Colombier return Tnone; 2238e32b400SDavid du Colombier } 2248e32b400SDavid du Colombier } 2258e32b400SDavid du Colombier 2268e32b400SDavid du Colombier static int 2278e32b400SDavid du Colombier name2mode(char *mode) 2288e32b400SDavid du Colombier { 2298e32b400SDavid du Colombier int i; 2308e32b400SDavid du Colombier 2318e32b400SDavid du Colombier for(i = 0; i < nelem(usbmodename); i++) 2328e32b400SDavid du Colombier if(strcmp(mode, usbmodename[i]) == 0) 2338e32b400SDavid du Colombier return i; 2348e32b400SDavid du Colombier return -1; 2358e32b400SDavid du Colombier } 2368e32b400SDavid du Colombier 2378e32b400SDavid du Colombier static int 2388e32b400SDavid du Colombier qid2epidx(int q) 2398e32b400SDavid du Colombier { 2408e32b400SDavid du Colombier q = (q-Qep0dir)/4; 2418e32b400SDavid du Colombier if(q < 0 || q >= epmax || eps[q] == nil) 2428e32b400SDavid du Colombier return -1; 2438e32b400SDavid du Colombier return q; 2448e32b400SDavid du Colombier } 2458e32b400SDavid du Colombier 2468e32b400SDavid du Colombier static int 2478e32b400SDavid du Colombier isqtype(int q, int type) 2488e32b400SDavid du Colombier { 2498e32b400SDavid du Colombier if(q < Qep0dir) 2508e32b400SDavid du Colombier return 0; 2518e32b400SDavid du Colombier q -= Qep0dir; 2528e32b400SDavid du Colombier return (q & 3) == type; 2538e32b400SDavid du Colombier } 2548e32b400SDavid du Colombier 2558e32b400SDavid du Colombier void 2568e32b400SDavid du Colombier addhcitype(char* t, int (*r)(Hci*)) 2578e32b400SDavid du Colombier { 2588e32b400SDavid du Colombier static int ntype; 2598e32b400SDavid du Colombier 2608e32b400SDavid du Colombier if(ntype == Nhcis) 2618e32b400SDavid du Colombier panic("too many USB host interface types"); 2628e32b400SDavid du Colombier hcitypes[ntype].type = t; 2638e32b400SDavid du Colombier hcitypes[ntype].reset = r; 2648e32b400SDavid du Colombier ntype++; 2658e32b400SDavid du Colombier } 2668e32b400SDavid du Colombier 2678e32b400SDavid du Colombier static char* 2688e32b400SDavid du Colombier seprintep(char *s, char *se, Ep *ep, int all) 2698e32b400SDavid du Colombier { 2708e32b400SDavid du Colombier static char* dsnames[] = { "config", "enabled", "detached", "reset" }; 2718e32b400SDavid du Colombier Udev *d; 2728e32b400SDavid du Colombier int i; 2738e32b400SDavid du Colombier int di; 2748e32b400SDavid du Colombier 2758e32b400SDavid du Colombier d = ep->dev; 2768e32b400SDavid du Colombier 2778e32b400SDavid du Colombier qlock(ep); 2788e32b400SDavid du Colombier if(waserror()){ 2798e32b400SDavid du Colombier qunlock(ep); 2808e32b400SDavid du Colombier nexterror(); 2818e32b400SDavid du Colombier } 2828e32b400SDavid du Colombier di = ep->dev->nb; 2838e32b400SDavid du Colombier if(all) 2848e32b400SDavid du Colombier s = seprint(s, se, "dev %d ep %d ", di, ep->nb); 2858e32b400SDavid du Colombier s = seprint(s, se, "%s", dsnames[ep->dev->state]); 2868e32b400SDavid du Colombier s = seprint(s, se, " %s", ttname[ep->ttype]); 2878e32b400SDavid du Colombier assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR); 2888e32b400SDavid du Colombier s = seprint(s, se, " %s", usbmodename[ep->mode]); 2898e32b400SDavid du Colombier s = seprint(s, se, " speed %s", spname[d->speed]); 2908e32b400SDavid du Colombier s = seprint(s, se, " maxpkt %ld", ep->maxpkt); 2918e32b400SDavid du Colombier s = seprint(s, se, " pollival %ld", ep->pollival); 2928e32b400SDavid du Colombier s = seprint(s, se, " samplesz %ld", ep->samplesz); 2938e32b400SDavid du Colombier s = seprint(s, se, " hz %ld", ep->hz); 2948e32b400SDavid du Colombier s = seprint(s, se, " hub %d", ep->dev->hub); 2958e32b400SDavid du Colombier s = seprint(s, se, " port %d", ep->dev->port); 2968e32b400SDavid du Colombier if(ep->inuse) 2978e32b400SDavid du Colombier s = seprint(s, se, " busy"); 2988e32b400SDavid du Colombier else 2998e32b400SDavid du Colombier s = seprint(s, se, " idle"); 3008e32b400SDavid du Colombier if(all){ 3018e32b400SDavid du Colombier s = seprint(s, se, " load %uld", ep->load); 3028e32b400SDavid du Colombier s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep); 3038e32b400SDavid du Colombier s = seprint(s, se, " idx %d", ep->idx); 3048e32b400SDavid du Colombier if(ep->name != nil) 3058e32b400SDavid du Colombier s = seprint(s, se, " name '%s'", ep->name); 3068e32b400SDavid du Colombier if(ep->tmout != 0) 3078e32b400SDavid du Colombier s = seprint(s, se, " tmout"); 3088e32b400SDavid du Colombier if(ep == ep->ep0){ 3098e32b400SDavid du Colombier s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno); 3108e32b400SDavid du Colombier s = seprint(s, se, " eps:"); 3118e32b400SDavid du Colombier for(i = 0; i < nelem(d->eps); i++) 3128e32b400SDavid du Colombier if(d->eps[i] != nil) 3138e32b400SDavid du Colombier s = seprint(s, se, " ep%d.%d", di, i); 3148e32b400SDavid du Colombier } 3158e32b400SDavid du Colombier } 3168e32b400SDavid du Colombier if(ep->info != nil) 3178e32b400SDavid du Colombier s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type); 3188e32b400SDavid du Colombier else 3198e32b400SDavid du Colombier s = seprint(s, se, "\n"); 3208e32b400SDavid du Colombier qunlock(ep); 3218e32b400SDavid du Colombier poperror(); 3228e32b400SDavid du Colombier return s; 3238e32b400SDavid du Colombier } 3248e32b400SDavid du Colombier 3258e32b400SDavid du Colombier static Ep* 3268e32b400SDavid du Colombier epalloc(Hci *hp) 3278e32b400SDavid du Colombier { 3288e32b400SDavid du Colombier Ep *ep; 3298e32b400SDavid du Colombier int i; 3308e32b400SDavid du Colombier 331*305b51b8SDavid du Colombier ep = smalloc(sizeof(Ep)); 3328e32b400SDavid du Colombier ep->ref = 1; 3338e32b400SDavid du Colombier qlock(&epslck); 3348e32b400SDavid du Colombier for(i = 0; i < Neps; i++) 3358e32b400SDavid du Colombier if(eps[i] == nil) 3368e32b400SDavid du Colombier break; 3378e32b400SDavid du Colombier if(i == Neps){ 3388e32b400SDavid du Colombier qunlock(&epslck); 3398e32b400SDavid du Colombier free(ep); 3408e32b400SDavid du Colombier print("usb: bug: too few endpoints.\n"); 3418e32b400SDavid du Colombier return nil; 3428e32b400SDavid du Colombier } 3438e32b400SDavid du Colombier ep->idx = i; 3448e32b400SDavid du Colombier if(epmax <= i) 3458e32b400SDavid du Colombier epmax = i+1; 3468e32b400SDavid du Colombier eps[i] = ep; 3478e32b400SDavid du Colombier ep->hp = hp; 3488e32b400SDavid du Colombier ep->maxpkt = 8; 3498e32b400SDavid du Colombier ep->ntds = 1; 3508e32b400SDavid du Colombier ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */ 3518e32b400SDavid du Colombier qunlock(&epslck); 3528e32b400SDavid du Colombier return ep; 3538e32b400SDavid du Colombier } 3548e32b400SDavid du Colombier 3558e32b400SDavid du Colombier static Ep* 3568e32b400SDavid du Colombier getep(int i) 3578e32b400SDavid du Colombier { 3588e32b400SDavid du Colombier Ep *ep; 3598e32b400SDavid du Colombier 3608e32b400SDavid du Colombier if(i < 0 || i >= epmax || eps[i] == nil) 3618e32b400SDavid du Colombier return nil; 3628e32b400SDavid du Colombier qlock(&epslck); 3638e32b400SDavid du Colombier ep = eps[i]; 3648e32b400SDavid du Colombier if(ep != nil) 3658e32b400SDavid du Colombier incref(ep); 3668e32b400SDavid du Colombier qunlock(&epslck); 3678e32b400SDavid du Colombier return ep; 3688e32b400SDavid du Colombier } 3698e32b400SDavid du Colombier 3708e32b400SDavid du Colombier static void 3718e32b400SDavid du Colombier putep(Ep *ep) 3728e32b400SDavid du Colombier { 3738e32b400SDavid du Colombier Udev *d; 3748e32b400SDavid du Colombier 3758e32b400SDavid du Colombier if(ep != nil && decref(ep) == 0){ 3768e32b400SDavid du Colombier d = ep->dev; 3778e32b400SDavid du Colombier deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep); 3788e32b400SDavid du Colombier qlock(&epslck); 3798e32b400SDavid du Colombier eps[ep->idx] = nil; 3808e32b400SDavid du Colombier if(ep->idx == epmax-1) 3818e32b400SDavid du Colombier epmax--; 3828e32b400SDavid du Colombier if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen) 3838e32b400SDavid du Colombier usbidgen--; 3848e32b400SDavid du Colombier qunlock(&epslck); 3858e32b400SDavid du Colombier if(d != nil){ 3868e32b400SDavid du Colombier qlock(ep->ep0); 3878e32b400SDavid du Colombier d->eps[ep->nb] = nil; 3888e32b400SDavid du Colombier qunlock(ep->ep0); 3898e32b400SDavid du Colombier } 3908e32b400SDavid du Colombier if(ep->ep0 != ep){ 3918e32b400SDavid du Colombier putep(ep->ep0); 3928e32b400SDavid du Colombier ep->ep0 = nil; 3938e32b400SDavid du Colombier } 3948e32b400SDavid du Colombier free(ep->info); 3958e32b400SDavid du Colombier free(ep->name); 3968e32b400SDavid du Colombier free(ep); 3978e32b400SDavid du Colombier } 3988e32b400SDavid du Colombier } 3998e32b400SDavid du Colombier 4008e32b400SDavid du Colombier static void 4018e32b400SDavid du Colombier dumpeps(void) 4028e32b400SDavid du Colombier { 4038e32b400SDavid du Colombier int i; 4048e32b400SDavid du Colombier static char buf[512]; 4058e32b400SDavid du Colombier char *s; 4068e32b400SDavid du Colombier char *e; 4078e32b400SDavid du Colombier Ep *ep; 4088e32b400SDavid du Colombier 4098e32b400SDavid du Colombier print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps); 4108e32b400SDavid du Colombier for(i = 0; i < epmax; i++){ 4118e32b400SDavid du Colombier s = buf; 4128e32b400SDavid du Colombier e = buf+sizeof(buf); 4138e32b400SDavid du Colombier ep = getep(i); 4148e32b400SDavid du Colombier if(ep != nil){ 4158e32b400SDavid du Colombier if(waserror()){ 4168e32b400SDavid du Colombier putep(ep); 4178e32b400SDavid du Colombier nexterror(); 4188e32b400SDavid du Colombier } 4198e32b400SDavid du Colombier s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb); 4208e32b400SDavid du Colombier seprintep(s, e, ep, 1); 4218e32b400SDavid du Colombier print("%s", buf); 4228e32b400SDavid du Colombier ep->hp->seprintep(buf, e, ep); 4238e32b400SDavid du Colombier print("%s", buf); 4248e32b400SDavid du Colombier poperror(); 4258e32b400SDavid du Colombier putep(ep); 4268e32b400SDavid du Colombier } 4278e32b400SDavid du Colombier } 4288e32b400SDavid du Colombier print("usb dump hcis:\n"); 4298e32b400SDavid du Colombier for(i = 0; i < Nhcis; i++) 4308e32b400SDavid du Colombier if(hcis[i] != nil) 4318e32b400SDavid du Colombier hcis[i]->dump(hcis[i]); 4328e32b400SDavid du Colombier } 4338e32b400SDavid du Colombier 4348e32b400SDavid du Colombier static int 4358e32b400SDavid du Colombier newusbid(Hci *) 4368e32b400SDavid du Colombier { 4378e32b400SDavid du Colombier int id; 4388e32b400SDavid du Colombier 4398e32b400SDavid du Colombier qlock(&epslck); 4408e32b400SDavid du Colombier id = ++usbidgen; 4418e32b400SDavid du Colombier if(id >= 0x7F) 4428e32b400SDavid du Colombier print("#u: too many device addresses; reuse them more\n"); 4438e32b400SDavid du Colombier qunlock(&epslck); 4448e32b400SDavid du Colombier return id; 4458e32b400SDavid du Colombier } 4468e32b400SDavid du Colombier 4478e32b400SDavid du Colombier /* 4488e32b400SDavid du Colombier * Create endpoint 0 for a new device 4498e32b400SDavid du Colombier */ 4508e32b400SDavid du Colombier static Ep* 4518e32b400SDavid du Colombier newdev(Hci *hp, int ishub, int isroot) 4528e32b400SDavid du Colombier { 4538e32b400SDavid du Colombier Ep *ep; 4548e32b400SDavid du Colombier Udev *d; 4558e32b400SDavid du Colombier 4568e32b400SDavid du Colombier ep = epalloc(hp); 457*305b51b8SDavid du Colombier d = ep->dev = smalloc(sizeof(Udev)); 4588e32b400SDavid du Colombier d->nb = newusbid(hp); 4598e32b400SDavid du Colombier d->eps[0] = ep; 4608e32b400SDavid du Colombier ep->nb = 0; 4618e32b400SDavid du Colombier ep->toggle[0] = ep->toggle[1] = 0; 4628e32b400SDavid du Colombier d->ishub = ishub; 4638e32b400SDavid du Colombier d->isroot = isroot; 4648e32b400SDavid du Colombier if(hp->highspeed != 0) 4658e32b400SDavid du Colombier d->speed = Highspeed; 4668e32b400SDavid du Colombier else 4678e32b400SDavid du Colombier d->speed = Fullspeed; 4688e32b400SDavid du Colombier d->state = Dconfig; /* address not yet set */ 4698e32b400SDavid du Colombier ep->dev = d; 4708e32b400SDavid du Colombier ep->ep0 = ep; /* no ref counted here */ 4718e32b400SDavid du Colombier ep->ttype = Tctl; 4728e32b400SDavid du Colombier ep->tmout = Xfertmout; 4738e32b400SDavid du Colombier ep->mode = ORDWR; 4748e32b400SDavid du Colombier dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep); 4758e32b400SDavid du Colombier return ep; 4768e32b400SDavid du Colombier } 4778e32b400SDavid du Colombier 4788e32b400SDavid du Colombier /* 4798e32b400SDavid du Colombier * Create a new endpoint for the device 4808e32b400SDavid du Colombier * accessed via the given endpoint 0. 4818e32b400SDavid du Colombier */ 4828e32b400SDavid du Colombier static Ep* 4838e32b400SDavid du Colombier newdevep(Ep *ep, int i, int tt, int mode) 4848e32b400SDavid du Colombier { 4858e32b400SDavid du Colombier Ep *nep; 4868e32b400SDavid du Colombier Udev *d; 4878e32b400SDavid du Colombier 4888e32b400SDavid du Colombier d = ep->dev; 4898e32b400SDavid du Colombier if(d->eps[i] != nil) 4908e32b400SDavid du Colombier error("endpoint already in use"); 4918e32b400SDavid du Colombier nep = epalloc(ep->hp); 4928e32b400SDavid du Colombier incref(ep); 4938e32b400SDavid du Colombier d->eps[i] = nep; 4948e32b400SDavid du Colombier nep->nb = i; 4958e32b400SDavid du Colombier nep->toggle[0] = nep->toggle[1] = 0; 4968e32b400SDavid du Colombier nep->ep0 = ep; 4978e32b400SDavid du Colombier nep->dev = ep->dev; 4988e32b400SDavid du Colombier nep->mode = mode; 4998e32b400SDavid du Colombier nep->ttype = tt; 5008e32b400SDavid du Colombier nep->debug = ep->debug; 5018e32b400SDavid du Colombier /* set defaults */ 5028e32b400SDavid du Colombier switch(tt){ 5038e32b400SDavid du Colombier case Tctl: 5048e32b400SDavid du Colombier nep->tmout = Xfertmout; 5058e32b400SDavid du Colombier break; 5068e32b400SDavid du Colombier case Tintr: 5078e32b400SDavid du Colombier nep->pollival = 10; 5088e32b400SDavid du Colombier break; 5098e32b400SDavid du Colombier case Tiso: 5108e32b400SDavid du Colombier nep->tmout = Xfertmout; 5118e32b400SDavid du Colombier nep->pollival = 10; 5128e32b400SDavid du Colombier nep->samplesz = 4; 5138e32b400SDavid du Colombier nep->hz = 44100; 5148e32b400SDavid du Colombier break; 5158e32b400SDavid du Colombier } 5168e32b400SDavid du Colombier deprint("newdevep ep%d.%d %#p\n", d->nb, nep->nb, nep); 5178e32b400SDavid du Colombier return ep; 5188e32b400SDavid du Colombier } 5198e32b400SDavid du Colombier 5208e32b400SDavid du Colombier static int 5218e32b400SDavid du Colombier epdataperm(int mode) 5228e32b400SDavid du Colombier { 5238e32b400SDavid du Colombier 5248e32b400SDavid du Colombier switch(mode){ 5258e32b400SDavid du Colombier case OREAD: 5268e32b400SDavid du Colombier return 0440|DMEXCL; 5278e32b400SDavid du Colombier break; 5288e32b400SDavid du Colombier case OWRITE: 5298e32b400SDavid du Colombier return 0220|DMEXCL; 5308e32b400SDavid du Colombier break; 5318e32b400SDavid du Colombier default: 5328e32b400SDavid du Colombier return 0660|DMEXCL; 5338e32b400SDavid du Colombier } 5348e32b400SDavid du Colombier } 5358e32b400SDavid du Colombier 5368e32b400SDavid du Colombier static int 5378e32b400SDavid du Colombier usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) 5388e32b400SDavid du Colombier { 5398e32b400SDavid du Colombier Qid q; 5408e32b400SDavid du Colombier Dirtab *dir; 5418e32b400SDavid du Colombier int perm; 5428e32b400SDavid du Colombier char *se; 5438e32b400SDavid du Colombier Ep *ep; 5448e32b400SDavid du Colombier int nb; 5458e32b400SDavid du Colombier int mode; 5468e32b400SDavid du Colombier 5478e32b400SDavid du Colombier if(0)ddprint("usbgen q %#x s %d...", QID(c->qid), s); 5488e32b400SDavid du Colombier if(s == DEVDOTDOT){ 5498e32b400SDavid du Colombier if(QID(c->qid) <= Qusbdir){ 5508e32b400SDavid du Colombier mkqid(&q, Qdir, 0, QTDIR); 5518e32b400SDavid du Colombier devdir(c, q, "#u", 0, eve, 0555, dp); 5528e32b400SDavid du Colombier }else{ 5538e32b400SDavid du Colombier mkqid(&q, Qusbdir, 0, QTDIR); 5548e32b400SDavid du Colombier devdir(c, q, "usb", 0, eve, 0555, dp); 5558e32b400SDavid du Colombier } 5568e32b400SDavid du Colombier if(0)ddprint("ok\n"); 5578e32b400SDavid du Colombier return 1; 5588e32b400SDavid du Colombier } 5598e32b400SDavid du Colombier 5608e32b400SDavid du Colombier switch(QID(c->qid)){ 5618e32b400SDavid du Colombier case Qdir: /* list #u */ 5628e32b400SDavid du Colombier if(s == 0){ 5638e32b400SDavid du Colombier mkqid(&q, Qusbdir, 0, QTDIR); 5648e32b400SDavid du Colombier devdir(c, q, "usb", 0, eve, 0555, dp); 5658e32b400SDavid du Colombier if(0)ddprint("ok\n"); 5668e32b400SDavid du Colombier return 1; 5678e32b400SDavid du Colombier } 5688e32b400SDavid du Colombier s--; 5698e32b400SDavid du Colombier if(s < 0 || s >= epmax) 5708e32b400SDavid du Colombier goto Fail; 5718e32b400SDavid du Colombier ep = getep(s); 5728e32b400SDavid du Colombier if(ep == nil || ep->name == nil){ 5738e32b400SDavid du Colombier if(ep != nil) 5748e32b400SDavid du Colombier putep(ep); 5758e32b400SDavid du Colombier if(0)ddprint("skip\n"); 5768e32b400SDavid du Colombier return 0; 5778e32b400SDavid du Colombier } 5788e32b400SDavid du Colombier if(waserror()){ 5798e32b400SDavid du Colombier putep(ep); 5808e32b400SDavid du Colombier nexterror(); 5818e32b400SDavid du Colombier } 5828e32b400SDavid du Colombier mkqid(&q, Qep0io+s*4, 0, QTFILE); 5838e32b400SDavid du Colombier devdir(c, q, ep->name, 0, eve, epdataperm(ep->mode), dp); 5848e32b400SDavid du Colombier putep(ep); 5858e32b400SDavid du Colombier poperror(); 5868e32b400SDavid du Colombier if(0)ddprint("ok\n"); 5878e32b400SDavid du Colombier return 1; 5888e32b400SDavid du Colombier 5898e32b400SDavid du Colombier case Qusbdir: /* list #u/usb */ 5908e32b400SDavid du Colombier Usbdir: 5918e32b400SDavid du Colombier if(s < nelem(usbdir)){ 5928e32b400SDavid du Colombier dir = &usbdir[s]; 5938e32b400SDavid du Colombier mkqid(&q, dir->qid.path, 0, QTFILE); 5948e32b400SDavid du Colombier devdir(c, q, dir->name, dir->length, eve, dir->perm, dp); 5958e32b400SDavid du Colombier if(0)ddprint("ok\n"); 5968e32b400SDavid du Colombier return 1; 5978e32b400SDavid du Colombier } 5988e32b400SDavid du Colombier s -= nelem(usbdir); 5998e32b400SDavid du Colombier if(s < 0 || s >= epmax) 6008e32b400SDavid du Colombier goto Fail; 6018e32b400SDavid du Colombier ep = getep(s); 6028e32b400SDavid du Colombier if(ep == nil){ 6038e32b400SDavid du Colombier if(0)ddprint("skip\n"); 6048e32b400SDavid du Colombier return 0; 6058e32b400SDavid du Colombier } 6068e32b400SDavid du Colombier if(waserror()){ 6078e32b400SDavid du Colombier putep(ep); 6088e32b400SDavid du Colombier nexterror(); 6098e32b400SDavid du Colombier } 6108e32b400SDavid du Colombier se = up->genbuf+sizeof(up->genbuf); 6118e32b400SDavid du Colombier seprint(up->genbuf, se, "ep%d.%d", ep->dev->nb, ep->nb); 6128e32b400SDavid du Colombier mkqid(&q, Qep0dir+4*s, 0, QTDIR); 6138e32b400SDavid du Colombier putep(ep); 6148e32b400SDavid du Colombier poperror(); 6158e32b400SDavid du Colombier devdir(c, q, up->genbuf, 0, eve, 0755, dp); 6168e32b400SDavid du Colombier if(0)ddprint("ok\n"); 6178e32b400SDavid du Colombier return 1; 6188e32b400SDavid du Colombier 6198e32b400SDavid du Colombier case Qctl: 6208e32b400SDavid du Colombier s = 0; 6218e32b400SDavid du Colombier goto Usbdir; 6228e32b400SDavid du Colombier 6238e32b400SDavid du Colombier default: /* list #u/usb/epN.M */ 6248e32b400SDavid du Colombier nb = qid2epidx(QID(c->qid)); 6258e32b400SDavid du Colombier ep = getep(nb); 6268e32b400SDavid du Colombier if(ep == nil) 6278e32b400SDavid du Colombier goto Fail; 6288e32b400SDavid du Colombier mode = ep->mode; 6298e32b400SDavid du Colombier putep(ep); 6308e32b400SDavid du Colombier if(isqtype(QID(c->qid), Qepdir)){ 6318e32b400SDavid du Colombier Epdir: 6328e32b400SDavid du Colombier switch(s){ 6338e32b400SDavid du Colombier case 0: 6348e32b400SDavid du Colombier mkqid(&q, Qep0io+nb*4, 0, QTFILE); 6358e32b400SDavid du Colombier perm = epdataperm(mode); 6368e32b400SDavid du Colombier devdir(c, q, "data", 0, eve, perm, dp); 6378e32b400SDavid du Colombier break; 6388e32b400SDavid du Colombier case 1: 6398e32b400SDavid du Colombier mkqid(&q, Qep0ctl+nb*4, 0, QTFILE); 6408e32b400SDavid du Colombier devdir(c, q, "ctl", 0, eve, 0664, dp); 6418e32b400SDavid du Colombier break; 6428e32b400SDavid du Colombier default: 6438e32b400SDavid du Colombier goto Fail; 6448e32b400SDavid du Colombier } 6458e32b400SDavid du Colombier }else if(isqtype(QID(c->qid), Qepctl)){ 6468e32b400SDavid du Colombier s = 1; 6478e32b400SDavid du Colombier goto Epdir; 6488e32b400SDavid du Colombier }else{ 6498e32b400SDavid du Colombier s = 0; 6508e32b400SDavid du Colombier goto Epdir; 6518e32b400SDavid du Colombier } 6528e32b400SDavid du Colombier if(0)ddprint("ok\n"); 6538e32b400SDavid du Colombier return 1; 6548e32b400SDavid du Colombier } 6558e32b400SDavid du Colombier Fail: 6568e32b400SDavid du Colombier if(0)ddprint("fail\n"); 6578e32b400SDavid du Colombier return -1; 6588e32b400SDavid du Colombier } 6598e32b400SDavid du Colombier 6608e32b400SDavid du Colombier static Hci* 6618e32b400SDavid du Colombier hciprobe(int cardno, int ctlrno) 6628e32b400SDavid du Colombier { 6638e32b400SDavid du Colombier Hci *hp; 6648e32b400SDavid du Colombier char *type; 6658e32b400SDavid du Colombier char name[64]; 6668e32b400SDavid du Colombier static int epnb = 1; /* guess the endpoint nb. for the controller */ 6678e32b400SDavid du Colombier 6688e32b400SDavid du Colombier ddprint("hciprobe %d %d\n", cardno, ctlrno); 669*305b51b8SDavid du Colombier hp = smalloc(sizeof(Hci)); 6708e32b400SDavid du Colombier hp->ctlrno = ctlrno; 6718e32b400SDavid du Colombier 672c8a340cdSDavid du Colombier if(cardno < 0) 6738e32b400SDavid du Colombier for(cardno = 0; cardno < Nhcis; cardno++){ 6748e32b400SDavid du Colombier if(hcitypes[cardno].type == nil) 6758e32b400SDavid du Colombier break; 6768e32b400SDavid du Colombier type = hp->type; 6778e32b400SDavid du Colombier if(type==nil || *type==0) 6788e32b400SDavid du Colombier type = "uhci"; 6798e32b400SDavid du Colombier if(cistrcmp(hcitypes[cardno].type, type) == 0) 6808e32b400SDavid du Colombier break; 6818e32b400SDavid du Colombier } 6828e32b400SDavid du Colombier 6838e32b400SDavid du Colombier if(cardno >= Nhcis || hcitypes[cardno].type == nil){ 6848e32b400SDavid du Colombier free(hp); 6858e32b400SDavid du Colombier return nil; 6868e32b400SDavid du Colombier } 6878e32b400SDavid du Colombier dprint("%s...", hcitypes[cardno].type); 6888e32b400SDavid du Colombier if(hcitypes[cardno].reset(hp) < 0){ 6898e32b400SDavid du Colombier free(hp); 6908e32b400SDavid du Colombier return nil; 6918e32b400SDavid du Colombier } 6928e32b400SDavid du Colombier 6938e32b400SDavid du Colombier snprint(name, sizeof(name), "usb%s", hcitypes[cardno].type); 6948e32b400SDavid du Colombier intrenable(hp->irq, hp->interrupt, hp, UNKNOWN, name); 6958e32b400SDavid du Colombier 696fe25a076SDavid du Colombier print("#u/usb/ep%d.0: %s: port %#luX irq %d\n", 6978e32b400SDavid du Colombier epnb, hcitypes[cardno].type, hp->port, hp->irq); 6988e32b400SDavid du Colombier epnb++; 6998e32b400SDavid du Colombier 7008e32b400SDavid du Colombier return hp; 7018e32b400SDavid du Colombier } 7028e32b400SDavid du Colombier 7038e32b400SDavid du Colombier static void 7048e32b400SDavid du Colombier usbreset(void) 7058e32b400SDavid du Colombier { 7068e32b400SDavid du Colombier int cardno, ctlrno; 7078e32b400SDavid du Colombier Hci *hp; 7088e32b400SDavid du Colombier 7098e32b400SDavid du Colombier dprint("usbreset\n"); 7108e32b400SDavid du Colombier 7118e32b400SDavid du Colombier for(ctlrno = 0; ctlrno < Nhcis; ctlrno++) 7128e32b400SDavid du Colombier if((hp = hciprobe(-1, ctlrno)) != nil) 7138e32b400SDavid du Colombier hcis[ctlrno] = hp; 7148e32b400SDavid du Colombier cardno = ctlrno = 0; 7158e32b400SDavid du Colombier while(cardno < Nhcis && ctlrno < Nhcis && hcitypes[cardno].type != nil) 7168e32b400SDavid du Colombier if(hcis[ctlrno] != nil) 7178e32b400SDavid du Colombier ctlrno++; 7188e32b400SDavid du Colombier else{ 7198e32b400SDavid du Colombier hp = hciprobe(cardno, ctlrno); 7208e32b400SDavid du Colombier if(hp == nil) 7218e32b400SDavid du Colombier cardno++; 7228e32b400SDavid du Colombier hcis[ctlrno++] = hp; 7238e32b400SDavid du Colombier } 7248e32b400SDavid du Colombier if(hcis[Nhcis-1] != nil) 7258e32b400SDavid du Colombier print("usbreset: bug: Nhcis too small\n"); 7268e32b400SDavid du Colombier } 7278e32b400SDavid du Colombier 7288e32b400SDavid du Colombier static void 7298e32b400SDavid du Colombier usbinit(void) 7308e32b400SDavid du Colombier { 7318e32b400SDavid du Colombier Hci *hp; 7328e32b400SDavid du Colombier int ctlrno; 7338e32b400SDavid du Colombier Ep *d; 7348e32b400SDavid du Colombier char info[40]; 7358e32b400SDavid du Colombier 7368e32b400SDavid du Colombier dprint("usbinit\n"); 7378e32b400SDavid du Colombier for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){ 7388e32b400SDavid du Colombier hp = hcis[ctlrno]; 7398e32b400SDavid du Colombier if(hp != nil){ 7408e32b400SDavid du Colombier if(hp->init != nil) 7418e32b400SDavid du Colombier hp->init(hp); 7428e32b400SDavid du Colombier d = newdev(hp, 1, 1); /* new root hub */ 7438e32b400SDavid du Colombier d->dev->state = Denabled; /* although addr == 0 */ 7448e32b400SDavid du Colombier d->maxpkt = 64; 7458e32b400SDavid du Colombier snprint(info, sizeof(info), "ports %d", hp->nports); 7468e32b400SDavid du Colombier kstrdup(&d->info, info); 7478e32b400SDavid du Colombier } 7488e32b400SDavid du Colombier } 7498e32b400SDavid du Colombier } 7508e32b400SDavid du Colombier 7518e32b400SDavid du Colombier static Chan* 7528e32b400SDavid du Colombier usbattach(char *spec) 7538e32b400SDavid du Colombier { 7548e32b400SDavid du Colombier return devattach(L'u', spec); 7558e32b400SDavid du Colombier } 7568e32b400SDavid du Colombier 7578e32b400SDavid du Colombier static Walkqid* 7588e32b400SDavid du Colombier usbwalk(Chan *c, Chan *nc, char **name, int nname) 7598e32b400SDavid du Colombier { 7608e32b400SDavid du Colombier return devwalk(c, nc, name, nname, nil, 0, usbgen); 7618e32b400SDavid du Colombier } 7628e32b400SDavid du Colombier 7638e32b400SDavid du Colombier static int 7648e32b400SDavid du Colombier usbstat(Chan *c, uchar *db, int n) 7658e32b400SDavid du Colombier { 7668e32b400SDavid du Colombier return devstat(c, db, n, nil, 0, usbgen); 7678e32b400SDavid du Colombier } 7688e32b400SDavid du Colombier 7698e32b400SDavid du Colombier /* 7708e32b400SDavid du Colombier * µs for the given transfer, for bandwidth allocation. 7718e32b400SDavid du Colombier * This is a very rough worst case for what 5.11.3 7728e32b400SDavid du Colombier * of the usb 2.0 spec says. 7738e32b400SDavid du Colombier * Also, we are using maxpkt and not actual transfer sizes. 7748e32b400SDavid du Colombier * Only when we are sure we 7758e32b400SDavid du Colombier * are not exceeding b/w might we consider adjusting it. 7768e32b400SDavid du Colombier */ 7778e32b400SDavid du Colombier static ulong 7788e32b400SDavid du Colombier usbload(int speed, int maxpkt) 7798e32b400SDavid du Colombier { 7808e32b400SDavid du Colombier enum{ Hostns = 1000, Hubns = 333 }; 7818e32b400SDavid du Colombier ulong l; 7828e32b400SDavid du Colombier ulong bs; 7838e32b400SDavid du Colombier 7848e32b400SDavid du Colombier l = 0; 7858e32b400SDavid du Colombier bs = 10UL * maxpkt; 7868e32b400SDavid du Colombier switch(speed){ 7878e32b400SDavid du Colombier case Highspeed: 7888e32b400SDavid du Colombier l = 55*8*2 + 2 * (3 + bs) + Hostns; 7898e32b400SDavid du Colombier break; 7908e32b400SDavid du Colombier case Fullspeed: 7918e32b400SDavid du Colombier l = 9107 + 84 * (4 + bs) + Hostns; 7928e32b400SDavid du Colombier break; 7938e32b400SDavid du Colombier case Lowspeed: 7948e32b400SDavid du Colombier l = 64107 + 2 * Hubns + 667 * (3 + bs) + Hostns; 7958e32b400SDavid du Colombier break; 7968e32b400SDavid du Colombier default: 7978e32b400SDavid du Colombier print("usbload: bad speed %d\n", speed); 7988e32b400SDavid du Colombier /* let it run */ 7998e32b400SDavid du Colombier } 8008e32b400SDavid du Colombier return l / 1000UL; /* in µs */ 8018e32b400SDavid du Colombier } 8028e32b400SDavid du Colombier 8038e32b400SDavid du Colombier static Chan* 8048e32b400SDavid du Colombier usbopen(Chan *c, int omode) 8058e32b400SDavid du Colombier { 8068e32b400SDavid du Colombier int q; 8078e32b400SDavid du Colombier Ep *ep; 8088e32b400SDavid du Colombier int mode; 8098e32b400SDavid du Colombier 8108e32b400SDavid du Colombier mode = openmode(omode); 8118e32b400SDavid du Colombier q = QID(c->qid); 8128e32b400SDavid du Colombier 8138e32b400SDavid du Colombier if(q >= Qep0dir && qid2epidx(q) < 0) 8148e32b400SDavid du Colombier error(Eio); 8158e32b400SDavid du Colombier if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir)) 8168e32b400SDavid du Colombier return devopen(c, omode, nil, 0, usbgen); 8178e32b400SDavid du Colombier 8188e32b400SDavid du Colombier ep = getep(qid2epidx(q)); 8198e32b400SDavid du Colombier if(ep == nil) 8208e32b400SDavid du Colombier error(Eio); 8218e32b400SDavid du Colombier deprint("usbopen q %#x fid %d omode %d\n", q, c->fid, mode); 8228e32b400SDavid du Colombier if(waserror()){ 8238e32b400SDavid du Colombier putep(ep); 8248e32b400SDavid du Colombier nexterror(); 8258e32b400SDavid du Colombier } 8268e32b400SDavid du Colombier qlock(ep); 8278e32b400SDavid du Colombier if(ep->inuse){ 8288e32b400SDavid du Colombier qunlock(ep); 8298e32b400SDavid du Colombier error(Einuse); 8308e32b400SDavid du Colombier } 8318e32b400SDavid du Colombier ep->inuse = 1; 8328e32b400SDavid du Colombier qunlock(ep); 8338e32b400SDavid du Colombier if(waserror()){ 8348e32b400SDavid du Colombier ep->inuse = 0; 8358e32b400SDavid du Colombier nexterror(); 8368e32b400SDavid du Colombier } 8378e32b400SDavid du Colombier if(mode != OREAD && ep->mode == OREAD) 8388e32b400SDavid du Colombier error(Eperm); 8398e32b400SDavid du Colombier if(mode != OWRITE && ep->mode == OWRITE) 8408e32b400SDavid du Colombier error(Eperm); 8418e32b400SDavid du Colombier if(ep->ttype == Tnone) 8428e32b400SDavid du Colombier error(Enotconf); 8438e32b400SDavid du Colombier ep->clrhalt = 0; 8448e32b400SDavid du Colombier ep->rhrepl = -1; 8458e32b400SDavid du Colombier if(ep->load == 0) 8468e32b400SDavid du Colombier ep->load = usbload(ep->dev->speed, ep->maxpkt); 8478e32b400SDavid du Colombier ep->hp->epopen(ep); 8488e32b400SDavid du Colombier 8498e32b400SDavid du Colombier poperror(); /* ep->inuse */ 8508e32b400SDavid du Colombier poperror(); /* don't putep(): ref kept for fid using the ep. */ 8518e32b400SDavid du Colombier 8528e32b400SDavid du Colombier c->mode = mode; 8538e32b400SDavid du Colombier c->flag |= COPEN; 8548e32b400SDavid du Colombier c->offset = 0; 8558e32b400SDavid du Colombier c->aux = nil; /* paranoia */ 8568e32b400SDavid du Colombier return c; 8578e32b400SDavid du Colombier } 8588e32b400SDavid du Colombier 8598e32b400SDavid du Colombier static void 8608e32b400SDavid du Colombier epclose(Ep *ep) 8618e32b400SDavid du Colombier { 8628e32b400SDavid du Colombier qlock(ep); 8638e32b400SDavid du Colombier if(waserror()){ 8648e32b400SDavid du Colombier qunlock(ep); 8658e32b400SDavid du Colombier nexterror(); 8668e32b400SDavid du Colombier } 8678e32b400SDavid du Colombier if(ep->inuse){ 8688e32b400SDavid du Colombier ep->hp->epclose(ep); 8698e32b400SDavid du Colombier ep->inuse = 0; 8708e32b400SDavid du Colombier } 8718e32b400SDavid du Colombier qunlock(ep); 8728e32b400SDavid du Colombier poperror(); 8738e32b400SDavid du Colombier } 8748e32b400SDavid du Colombier 8758e32b400SDavid du Colombier static void 8768e32b400SDavid du Colombier usbclose(Chan *c) 8778e32b400SDavid du Colombier { 8788e32b400SDavid du Colombier int q; 8798e32b400SDavid du Colombier Ep *ep; 8808e32b400SDavid du Colombier 8818e32b400SDavid du Colombier q = QID(c->qid); 8828e32b400SDavid du Colombier if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir)) 8838e32b400SDavid du Colombier return; 8848e32b400SDavid du Colombier 8858e32b400SDavid du Colombier ep = getep(qid2epidx(q)); 8868e32b400SDavid du Colombier if(ep == nil) 8878e32b400SDavid du Colombier return; 8888e32b400SDavid du Colombier deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref); 8898e32b400SDavid du Colombier if(waserror()){ 8908e32b400SDavid du Colombier putep(ep); 8918e32b400SDavid du Colombier nexterror(); 8928e32b400SDavid du Colombier } 8938e32b400SDavid du Colombier if(c->flag & COPEN){ 8948e32b400SDavid du Colombier free(c->aux); 8958e32b400SDavid du Colombier c->aux = nil; 8968e32b400SDavid du Colombier epclose(ep); 8978e32b400SDavid du Colombier putep(ep); /* release ref kept since usbopen */ 8988e32b400SDavid du Colombier c->flag &= ~COPEN; 8998e32b400SDavid du Colombier } 9008e32b400SDavid du Colombier poperror(); 9018e32b400SDavid du Colombier putep(ep); 9028e32b400SDavid du Colombier } 9038e32b400SDavid du Colombier 9048e32b400SDavid du Colombier static long 9058e32b400SDavid du Colombier ctlread(Chan *c, void *a, long n, vlong offset) 9068e32b400SDavid du Colombier { 9078e32b400SDavid du Colombier int q; 9088e32b400SDavid du Colombier char *s; 9098e32b400SDavid du Colombier char *us; 9108e32b400SDavid du Colombier char *se; 9118e32b400SDavid du Colombier Ep *ep; 9128e32b400SDavid du Colombier int i; 9138e32b400SDavid du Colombier 9148e32b400SDavid du Colombier q = QID(c->qid); 9158e32b400SDavid du Colombier us = s = smalloc(READSTR); 9168e32b400SDavid du Colombier se = s + READSTR; 9178e32b400SDavid du Colombier if(waserror()){ 9188e32b400SDavid du Colombier free(us); 9198e32b400SDavid du Colombier nexterror(); 9208e32b400SDavid du Colombier } 9218e32b400SDavid du Colombier if(q == Qctl) 9228e32b400SDavid du Colombier for(i = 0; i < epmax; i++){ 9238e32b400SDavid du Colombier ep = getep(i); 9248e32b400SDavid du Colombier if(ep != nil){ 9258e32b400SDavid du Colombier if(waserror()){ 9268e32b400SDavid du Colombier putep(ep); 9278e32b400SDavid du Colombier nexterror(); 9288e32b400SDavid du Colombier } 9298e32b400SDavid du Colombier s = seprint(s, se, "ep%d.%d ", ep->dev->nb, ep->nb); 9308e32b400SDavid du Colombier s = seprintep(s, se, ep, 0); 9318e32b400SDavid du Colombier poperror(); 9328e32b400SDavid du Colombier } 9338e32b400SDavid du Colombier putep(ep); 9348e32b400SDavid du Colombier } 9358e32b400SDavid du Colombier else{ 9368e32b400SDavid du Colombier ep = getep(qid2epidx(q)); 9378e32b400SDavid du Colombier if(ep == nil) 9388e32b400SDavid du Colombier error(Eio); 9398e32b400SDavid du Colombier if(waserror()){ 9408e32b400SDavid du Colombier putep(ep); 9418e32b400SDavid du Colombier nexterror(); 9428e32b400SDavid du Colombier } 9438e32b400SDavid du Colombier if(c->aux != nil){ 9448e32b400SDavid du Colombier /* After a new endpoint request we read 9458e32b400SDavid du Colombier * the new endpoint name back. 9468e32b400SDavid du Colombier */ 9478e32b400SDavid du Colombier strecpy(s, se, c->aux); 9488e32b400SDavid du Colombier free(c->aux); 9498e32b400SDavid du Colombier c->aux = nil; 9508e32b400SDavid du Colombier }else 9518e32b400SDavid du Colombier seprintep(s, se, ep, 0); 9528e32b400SDavid du Colombier poperror(); 9538e32b400SDavid du Colombier putep(ep); 9548e32b400SDavid du Colombier } 9558e32b400SDavid du Colombier n = readstr(offset, a, n, us); 9568e32b400SDavid du Colombier poperror(); 9578e32b400SDavid du Colombier free(us); 9588e32b400SDavid du Colombier return n; 9598e32b400SDavid du Colombier } 9608e32b400SDavid du Colombier 9618e32b400SDavid du Colombier /* 9628e32b400SDavid du Colombier * Fake root hub emulation. 9638e32b400SDavid du Colombier */ 9648e32b400SDavid du Colombier static long 9658e32b400SDavid du Colombier rhubread(Ep *ep, void *a, long n) 9668e32b400SDavid du Colombier { 9678e32b400SDavid du Colombier char *b; 9688e32b400SDavid du Colombier 9698e32b400SDavid du Colombier if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2) 9708e32b400SDavid du Colombier return -1; 9718e32b400SDavid du Colombier if(ep->rhrepl < 0) 9728e32b400SDavid du Colombier return -1; 9738e32b400SDavid du Colombier 9748e32b400SDavid du Colombier b = a; 9758e32b400SDavid du Colombier memset(b, 0, n); 9768e32b400SDavid du Colombier PUT2(b, ep->rhrepl); 9778e32b400SDavid du Colombier ep->rhrepl = -1; 9788e32b400SDavid du Colombier return n; 9798e32b400SDavid du Colombier } 9808e32b400SDavid du Colombier 9818e32b400SDavid du Colombier static long 9828e32b400SDavid du Colombier rhubwrite(Ep *ep, void *a, long n) 9838e32b400SDavid du Colombier { 9848e32b400SDavid du Colombier uchar *s; 9858e32b400SDavid du Colombier int cmd; 9868e32b400SDavid du Colombier int feature; 9878e32b400SDavid du Colombier int port; 9888e32b400SDavid du Colombier Hci *hp; 9898e32b400SDavid du Colombier 9908e32b400SDavid du Colombier if(ep->dev == nil || ep->dev->isroot == 0 || ep->nb != 0) 9918e32b400SDavid du Colombier return -1; 9928e32b400SDavid du Colombier if(n != Rsetuplen) 9938e32b400SDavid du Colombier error("root hub is a toy hub"); 9948e32b400SDavid du Colombier ep->rhrepl = -1; 9958e32b400SDavid du Colombier s = a; 9968e32b400SDavid du Colombier if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother)) 9978e32b400SDavid du Colombier error("root hub is a toy hub"); 9988e32b400SDavid du Colombier hp = ep->hp; 9998e32b400SDavid du Colombier cmd = s[Rreq]; 10008e32b400SDavid du Colombier feature = GET2(s+Rvalue); 10018e32b400SDavid du Colombier port = GET2(s+Rindex); 10028e32b400SDavid du Colombier if(port < 1 || port > hp->nports) 10038e32b400SDavid du Colombier error("bad hub port number"); 10048e32b400SDavid du Colombier switch(feature){ 10058e32b400SDavid du Colombier case Rportenable: 10068e32b400SDavid du Colombier ep->rhrepl = hp->portenable(hp, port, cmd == Rsetfeature); 10078e32b400SDavid du Colombier break; 10088e32b400SDavid du Colombier case Rportreset: 10098e32b400SDavid du Colombier ep->rhrepl = hp->portreset(hp, port, cmd == Rsetfeature); 10108e32b400SDavid du Colombier break; 10118e32b400SDavid du Colombier case Rgetstatus: 10128e32b400SDavid du Colombier ep->rhrepl = hp->portstatus(hp, port); 10138e32b400SDavid du Colombier break; 10148e32b400SDavid du Colombier default: 10158e32b400SDavid du Colombier ep->rhrepl = 0; 10168e32b400SDavid du Colombier } 10178e32b400SDavid du Colombier return n; 10188e32b400SDavid du Colombier } 10198e32b400SDavid du Colombier 10208e32b400SDavid du Colombier static long 10218e32b400SDavid du Colombier usbread(Chan *c, void *a, long n, vlong offset) 10228e32b400SDavid du Colombier { 10238e32b400SDavid du Colombier int q; 10248e32b400SDavid du Colombier Ep *ep; 10258e32b400SDavid du Colombier int nr; 10268e32b400SDavid du Colombier 10278e32b400SDavid du Colombier q = QID(c->qid); 10288e32b400SDavid du Colombier 10298e32b400SDavid du Colombier if(c->qid.type == QTDIR) 10308e32b400SDavid du Colombier return devdirread(c, a, n, nil, 0, usbgen); 10318e32b400SDavid du Colombier 10328e32b400SDavid du Colombier if(q == Qctl || isqtype(q, Qepctl)) 10338e32b400SDavid du Colombier return ctlread(c, a, n, offset); 10348e32b400SDavid du Colombier 10358e32b400SDavid du Colombier ep = getep(qid2epidx(q)); 10368e32b400SDavid du Colombier if(ep == nil) 10378e32b400SDavid du Colombier error(Eio); 10388e32b400SDavid du Colombier if(waserror()){ 10398e32b400SDavid du Colombier putep(ep); 10408e32b400SDavid du Colombier nexterror(); 10418e32b400SDavid du Colombier } 10428e32b400SDavid du Colombier if(ep->dev->state == Ddetach) 10438e32b400SDavid du Colombier error(Edetach); 10448e32b400SDavid du Colombier if(ep->mode == OWRITE || ep->inuse == 0) 10458e32b400SDavid du Colombier error(Ebadusefd); 10468e32b400SDavid du Colombier switch(ep->ttype){ 10478e32b400SDavid du Colombier case Tnone: 10488e32b400SDavid du Colombier error("endpoint not configured"); 10498e32b400SDavid du Colombier case Tctl: 10508e32b400SDavid du Colombier nr = rhubread(ep, a, n); 10518e32b400SDavid du Colombier if(nr >= 0){ 10528e32b400SDavid du Colombier n = nr; 10538e32b400SDavid du Colombier break; 10548e32b400SDavid du Colombier } 10558e32b400SDavid du Colombier /* else fall */ 10568e32b400SDavid du Colombier default: 10578e32b400SDavid du Colombier ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",q,c->fid,n,offset); 10588e32b400SDavid du Colombier n = ep->hp->epread(ep, a, n); 10598e32b400SDavid du Colombier break; 10608e32b400SDavid du Colombier } 10618e32b400SDavid du Colombier poperror(); 10628e32b400SDavid du Colombier putep(ep); 10638e32b400SDavid du Colombier return n; 10648e32b400SDavid du Colombier } 10658e32b400SDavid du Colombier 10668e32b400SDavid du Colombier static long 10678e32b400SDavid du Colombier pow2(int n) 10688e32b400SDavid du Colombier { 1069add6b5c5SDavid du Colombier return 1 << n; 10708e32b400SDavid du Colombier } 10718e32b400SDavid du Colombier 10728e32b400SDavid du Colombier static void 10738e32b400SDavid du Colombier setmaxpkt(Ep *ep, char* s) 10748e32b400SDavid du Colombier { 10758e32b400SDavid du Colombier long spp; /* samples per packet */ 10768e32b400SDavid du Colombier 10778e32b400SDavid du Colombier if(ep->dev->speed == Highspeed) 10788e32b400SDavid du Colombier spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000; 10798e32b400SDavid du Colombier else 10808e32b400SDavid du Colombier spp = (ep->hz * ep->pollival + 999) / 1000; 10818e32b400SDavid du Colombier ep->maxpkt = spp * ep->samplesz; 10828e32b400SDavid du Colombier deprint("usb: %s: setmaxpkt: hz %ld poll %ld" 10838e32b400SDavid du Colombier " ntds %d %s speed -> spp %ld maxpkt %ld\n", s, 10848e32b400SDavid du Colombier ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed], 10858e32b400SDavid du Colombier spp, ep->maxpkt); 10868e32b400SDavid du Colombier if(ep->maxpkt > 1024){ 10878e32b400SDavid du Colombier print("usb: %s: maxpkt %ld > 1024. truncating\n", s, ep->maxpkt); 10888e32b400SDavid du Colombier ep->maxpkt = 1024; 10898e32b400SDavid du Colombier } 10908e32b400SDavid du Colombier } 10918e32b400SDavid du Colombier 10928e32b400SDavid du Colombier /* 10938e32b400SDavid du Colombier * Many endpoint ctls. simply update the portable representation 10948e32b400SDavid du Colombier * of the endpoint. The actual controller driver will look 10958e32b400SDavid du Colombier * at them to setup the endpoints as dictated. 10968e32b400SDavid du Colombier */ 10978e32b400SDavid du Colombier static long 10988e32b400SDavid du Colombier epctl(Ep *ep, Chan *c, void *a, long n) 10998e32b400SDavid du Colombier { 1100add6b5c5SDavid du Colombier int i, l, mode, nb, tt; 1101add6b5c5SDavid du Colombier char *b, *s; 1102add6b5c5SDavid du Colombier Cmdbuf *cb; 1103add6b5c5SDavid du Colombier Cmdtab *ct; 11048e32b400SDavid du Colombier Ep *nep; 11058e32b400SDavid du Colombier Udev *d; 1106add6b5c5SDavid du Colombier static char *Info = "info "; 11078e32b400SDavid du Colombier 11088e32b400SDavid du Colombier d = ep->dev; 11098e32b400SDavid du Colombier 11108e32b400SDavid du Colombier cb = parsecmd(a, n); 11118e32b400SDavid du Colombier if(waserror()){ 11128e32b400SDavid du Colombier free(cb); 11138e32b400SDavid du Colombier nexterror(); 11148e32b400SDavid du Colombier } 11158e32b400SDavid du Colombier ct = lookupcmd(cb, epctls, nelem(epctls)); 11168e32b400SDavid du Colombier if(ct == nil) 11178e32b400SDavid du Colombier error(Ebadctl); 11188e32b400SDavid du Colombier i = ct->index; 11198e32b400SDavid du Colombier if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset) 11208e32b400SDavid du Colombier if(ep != ep->ep0) 11218e32b400SDavid du Colombier error("allowed only on a setup endpoint"); 11228e32b400SDavid du Colombier if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname) 11238e32b400SDavid du Colombier if(ep != ep->ep0 && ep->inuse != 0) 11248e32b400SDavid du Colombier error("must configure before using"); 11258e32b400SDavid du Colombier switch(i){ 11268e32b400SDavid du Colombier case CMnew: 11278e32b400SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 11288e32b400SDavid du Colombier nb = strtol(cb->f[1], nil, 0); 11298e32b400SDavid du Colombier if(nb < 0 || nb >= Ndeveps) 11308e32b400SDavid du Colombier error("bad endpoint number"); 11318e32b400SDavid du Colombier tt = name2ttype(cb->f[2]); 11328e32b400SDavid du Colombier if(tt == Tnone) 11338e32b400SDavid du Colombier error("unknown endpoint type"); 11348e32b400SDavid du Colombier mode = name2mode(cb->f[3]); 11358e32b400SDavid du Colombier if(mode < 0) 11368e32b400SDavid du Colombier error("unknown i/o mode"); 11378e32b400SDavid du Colombier newdevep(ep, nb, tt, mode); 11388e32b400SDavid du Colombier break; 11398e32b400SDavid du Colombier case CMnewdev: 11408e32b400SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 11418e32b400SDavid du Colombier if(ep != ep->ep0 || d->ishub == 0) 11428e32b400SDavid du Colombier error("not a hub setup endpoint"); 11438e32b400SDavid du Colombier l = name2speed(cb->f[1]); 11448e32b400SDavid du Colombier if(l == Nospeed) 11458e32b400SDavid du Colombier error("speed must be full|low|high"); 11468e32b400SDavid du Colombier nep = newdev(ep->hp, 0, 0); 11478e32b400SDavid du Colombier nep->dev->speed = l; 11488e32b400SDavid du Colombier if(nep->dev->speed != Lowspeed) 11498e32b400SDavid du Colombier nep->maxpkt = 64; /* assume full speed */ 11508e32b400SDavid du Colombier nep->dev->hub = d->nb; 11518e32b400SDavid du Colombier nep->dev->port = atoi(cb->f[2]); 11528e32b400SDavid du Colombier /* next read request will read 11538e32b400SDavid du Colombier * the name for the new endpoint 11548e32b400SDavid du Colombier */ 11558e32b400SDavid du Colombier l = sizeof(up->genbuf); 11568e32b400SDavid du Colombier snprint(up->genbuf, l, "ep%d.%d", nep->dev->nb, nep->nb); 11578e32b400SDavid du Colombier kstrdup(&c->aux, up->genbuf); 11588e32b400SDavid du Colombier break; 11598e32b400SDavid du Colombier case CMhub: 11608e32b400SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 11618e32b400SDavid du Colombier d->ishub = 1; 11628e32b400SDavid du Colombier break; 11638e32b400SDavid du Colombier case CMspeed: 11648e32b400SDavid du Colombier l = name2speed(cb->f[1]); 11658e32b400SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l); 11668e32b400SDavid du Colombier if(l == Nospeed) 11678e32b400SDavid du Colombier error("speed must be full|low|high"); 11688e32b400SDavid du Colombier qlock(ep->ep0); 11698e32b400SDavid du Colombier d->speed = l; 11708e32b400SDavid du Colombier qunlock(ep->ep0); 11718e32b400SDavid du Colombier break; 11728e32b400SDavid du Colombier case CMmaxpkt: 11738e32b400SDavid du Colombier l = strtoul(cb->f[1], nil, 0); 11748e32b400SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l); 11758e32b400SDavid du Colombier if(l < 1 || l > 1024) 11768e32b400SDavid du Colombier error("maxpkt not in [1:1024]"); 11778e32b400SDavid du Colombier qlock(ep); 11788e32b400SDavid du Colombier ep->maxpkt = l; 11798e32b400SDavid du Colombier qunlock(ep); 11808e32b400SDavid du Colombier break; 11818e32b400SDavid du Colombier case CMntds: 11828e32b400SDavid du Colombier l = strtoul(cb->f[1], nil, 0); 11838e32b400SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l); 11848e32b400SDavid du Colombier if(l < 1 || l > 3) 11858e32b400SDavid du Colombier error("ntds not in [1:3]"); 11868e32b400SDavid du Colombier qlock(ep); 11878e32b400SDavid du Colombier ep->ntds = l; 11888e32b400SDavid du Colombier qunlock(ep); 11898e32b400SDavid du Colombier break; 11908e32b400SDavid du Colombier case CMpollival: 11918e32b400SDavid du Colombier if(ep->ttype != Tintr && ep->ttype != Tiso) 11928e32b400SDavid du Colombier error("not an intr or iso endpoint"); 11938e32b400SDavid du Colombier l = strtoul(cb->f[1], nil, 0); 11948e32b400SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l); 11958e32b400SDavid du Colombier if(ep->ttype == Tiso || 11968e32b400SDavid du Colombier (ep->ttype == Tintr && ep->dev->speed == Highspeed)){ 11978e32b400SDavid du Colombier if(l < 1 || l > 16) 11988e32b400SDavid du Colombier error("pollival power not in [1:16]"); 11998e32b400SDavid du Colombier l = pow2(l-1); 12008e32b400SDavid du Colombier }else 12018e32b400SDavid du Colombier if(l < 1 || l > 255) 12028e32b400SDavid du Colombier error("pollival not in [1:255]"); 12038e32b400SDavid du Colombier qlock(ep); 12048e32b400SDavid du Colombier ep->pollival = l; 12058e32b400SDavid du Colombier if(ep->ttype == Tiso) 12068e32b400SDavid du Colombier setmaxpkt(ep, "pollival"); 12078e32b400SDavid du Colombier qunlock(ep); 12088e32b400SDavid du Colombier break; 12098e32b400SDavid du Colombier case CMsamplesz: 12108e32b400SDavid du Colombier if(ep->ttype != Tiso) 12118e32b400SDavid du Colombier error("not an iso endpoint"); 12128e32b400SDavid du Colombier l = strtoul(cb->f[1], nil, 0); 12138e32b400SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l); 12148e32b400SDavid du Colombier if(l <= 0 || l > 8) 12158e32b400SDavid du Colombier error("samplesz not in [1:8]"); 12168e32b400SDavid du Colombier qlock(ep); 12178e32b400SDavid du Colombier ep->samplesz = l; 12188e32b400SDavid du Colombier setmaxpkt(ep, "samplesz"); 12198e32b400SDavid du Colombier qunlock(ep); 12208e32b400SDavid du Colombier break; 12218e32b400SDavid du Colombier case CMhz: 12228e32b400SDavid du Colombier if(ep->ttype != Tiso) 12238e32b400SDavid du Colombier error("not an iso endpoint"); 12248e32b400SDavid du Colombier l = strtoul(cb->f[1], nil, 0); 12258e32b400SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l); 12268e32b400SDavid du Colombier if(l <= 0 || l > 100000) 12278e32b400SDavid du Colombier error("hz not in [1:100000]"); 12288e32b400SDavid du Colombier qlock(ep); 12298e32b400SDavid du Colombier ep->hz = l; 12308e32b400SDavid du Colombier setmaxpkt(ep, "hz"); 12318e32b400SDavid du Colombier qunlock(ep); 12328e32b400SDavid du Colombier break; 12338e32b400SDavid du Colombier case CMclrhalt: 12348e32b400SDavid du Colombier qlock(ep); 12358e32b400SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 12368e32b400SDavid du Colombier ep->clrhalt = 1; 12378e32b400SDavid du Colombier qunlock(ep); 12388e32b400SDavid du Colombier break; 12398e32b400SDavid du Colombier case CMinfo: 12408e32b400SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 12418e32b400SDavid du Colombier l = strlen(Info); 12428e32b400SDavid du Colombier s = a; 12438e32b400SDavid du Colombier if(n < l+2 || strncmp(Info, s, l) != 0) 12448e32b400SDavid du Colombier error(Ebadctl); 12458e32b400SDavid du Colombier if(n > 1024) 12468e32b400SDavid du Colombier n = 1024; 12478e32b400SDavid du Colombier b = smalloc(n); 12488e32b400SDavid du Colombier memmove(b, s+l, n-l); 12498e32b400SDavid du Colombier b[n-l] = 0; 12508e32b400SDavid du Colombier if(b[n-l-1] == '\n') 12518e32b400SDavid du Colombier b[n-l-1] = 0; 12528e32b400SDavid du Colombier qlock(ep); 12538e32b400SDavid du Colombier free(ep->info); 12548e32b400SDavid du Colombier ep->info = b; 12558e32b400SDavid du Colombier qunlock(ep); 12568e32b400SDavid du Colombier break; 12578e32b400SDavid du Colombier case CMaddress: 12588e32b400SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 12598e32b400SDavid du Colombier ep->dev->state = Denabled; 12608e32b400SDavid du Colombier break; 12618e32b400SDavid du Colombier case CMdetach: 12628e32b400SDavid du Colombier if(ep->dev->isroot != 0) 12638e32b400SDavid du Colombier error("can't detach a root hub"); 12648e32b400SDavid du Colombier deprint("usb epctl %s ep%d.%d\n", 12658e32b400SDavid du Colombier cb->f[0], ep->dev->nb, ep->nb); 12668e32b400SDavid du Colombier ep->dev->state = Ddetach; 12678e32b400SDavid du Colombier /* Release file system ref. for its endpoints */ 12688e32b400SDavid du Colombier for(i = 0; i < nelem(ep->dev->eps); i++) 12698e32b400SDavid du Colombier putep(ep->dev->eps[i]); 12708e32b400SDavid du Colombier break; 12718e32b400SDavid du Colombier case CMdebugep: 12728e32b400SDavid du Colombier if(strcmp(cb->f[1], "on") == 0) 12738e32b400SDavid du Colombier ep->debug = 1; 12748e32b400SDavid du Colombier else if(strcmp(cb->f[1], "off") == 0) 12758e32b400SDavid du Colombier ep->debug = 0; 12768e32b400SDavid du Colombier else 12778e32b400SDavid du Colombier ep->debug = strtoul(cb->f[1], nil, 0); 12788e32b400SDavid du Colombier print("usb: ep%d.%d debug %d\n", 12798e32b400SDavid du Colombier ep->dev->nb, ep->nb, ep->debug); 12808e32b400SDavid du Colombier break; 12818e32b400SDavid du Colombier case CMname: 12828e32b400SDavid du Colombier deprint("usb epctl %s %s\n", cb->f[0], cb->f[1]); 12838e32b400SDavid du Colombier validname(cb->f[1], 0); 12848e32b400SDavid du Colombier kstrdup(&ep->name, cb->f[1]); 12858e32b400SDavid du Colombier break; 12868e32b400SDavid du Colombier case CMtmout: 12878e32b400SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 12888e32b400SDavid du Colombier if(ep->ttype == Tiso || ep->ttype == Tctl) 12898e32b400SDavid du Colombier error("ctl ignored for this endpoint type"); 12908e32b400SDavid du Colombier ep->tmout = strtoul(cb->f[1], nil, 0); 12918e32b400SDavid du Colombier if(ep->tmout != 0 && ep->tmout < Xfertmout) 12928e32b400SDavid du Colombier ep->tmout = Xfertmout; 12938e32b400SDavid du Colombier break; 12948e32b400SDavid du Colombier case CMpreset: 12958e32b400SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 12968e32b400SDavid du Colombier if(ep->ttype != Tctl) 12978e32b400SDavid du Colombier error("not a control endpoint"); 12988e32b400SDavid du Colombier if(ep->dev->state != Denabled) 12998e32b400SDavid du Colombier error("forbidden on devices not enabled"); 13008e32b400SDavid du Colombier ep->dev->state = Dreset; 13018e32b400SDavid du Colombier break; 13028e32b400SDavid du Colombier default: 13038e32b400SDavid du Colombier panic("usb: unknown epctl %d", ct->index); 13048e32b400SDavid du Colombier } 13058e32b400SDavid du Colombier free(cb); 13068e32b400SDavid du Colombier poperror(); 13078e32b400SDavid du Colombier return n; 13088e32b400SDavid du Colombier } 13098e32b400SDavid du Colombier 13108e32b400SDavid du Colombier static long 13118e32b400SDavid du Colombier usbctl(void *a, long n) 13128e32b400SDavid du Colombier { 13138e32b400SDavid du Colombier Cmdtab *ct; 13148e32b400SDavid du Colombier Cmdbuf *cb; 13158e32b400SDavid du Colombier Ep *ep; 13168e32b400SDavid du Colombier int i; 13178e32b400SDavid du Colombier 13188e32b400SDavid du Colombier cb = parsecmd(a, n); 13198e32b400SDavid du Colombier if(waserror()){ 13208e32b400SDavid du Colombier free(cb); 13218e32b400SDavid du Colombier nexterror(); 13228e32b400SDavid du Colombier } 13238e32b400SDavid du Colombier ct = lookupcmd(cb, usbctls, nelem(usbctls)); 13248e32b400SDavid du Colombier dprint("usb ctl %s\n", cb->f[0]); 13258e32b400SDavid du Colombier switch(ct->index){ 13268e32b400SDavid du Colombier case CMdebug: 13278e32b400SDavid du Colombier if(strcmp(cb->f[1], "on") == 0) 13288e32b400SDavid du Colombier debug = 1; 13298e32b400SDavid du Colombier else if(strcmp(cb->f[1], "off") == 0) 13308e32b400SDavid du Colombier debug = 0; 13318e32b400SDavid du Colombier else 13328e32b400SDavid du Colombier debug = strtol(cb->f[1], nil, 0); 13338e32b400SDavid du Colombier print("usb: debug %d\n", debug); 13348e32b400SDavid du Colombier for(i = 0; i < epmax; i++) 13358e32b400SDavid du Colombier if((ep = getep(i)) != nil){ 13368e32b400SDavid du Colombier ep->hp->debug(ep->hp, debug); 13378e32b400SDavid du Colombier putep(ep); 13388e32b400SDavid du Colombier } 13398e32b400SDavid du Colombier break; 13408e32b400SDavid du Colombier case CMdump: 13418e32b400SDavid du Colombier dumpeps(); 13428e32b400SDavid du Colombier break; 13438e32b400SDavid du Colombier } 13448e32b400SDavid du Colombier free(cb); 13458e32b400SDavid du Colombier poperror(); 13468e32b400SDavid du Colombier return n; 13478e32b400SDavid du Colombier } 13488e32b400SDavid du Colombier 13498e32b400SDavid du Colombier static long 13508e32b400SDavid du Colombier ctlwrite(Chan *c, void *a, long n) 13518e32b400SDavid du Colombier { 13528e32b400SDavid du Colombier int q; 13538e32b400SDavid du Colombier Ep *ep; 13548e32b400SDavid du Colombier 13558e32b400SDavid du Colombier q = QID(c->qid); 13568e32b400SDavid du Colombier if(q == Qctl) 13578e32b400SDavid du Colombier return usbctl(a, n); 13588e32b400SDavid du Colombier 13598e32b400SDavid du Colombier ep = getep(qid2epidx(q)); 13608e32b400SDavid du Colombier if(ep == nil) 13618e32b400SDavid du Colombier error(Eio); 13628e32b400SDavid du Colombier if(waserror()){ 13638e32b400SDavid du Colombier putep(ep); 13648e32b400SDavid du Colombier nexterror(); 13658e32b400SDavid du Colombier } 13668e32b400SDavid du Colombier if(ep->dev->state == Ddetach) 13678e32b400SDavid du Colombier error(Edetach); 13688e32b400SDavid du Colombier if(isqtype(q, Qepctl) && c->aux != nil){ 13698e32b400SDavid du Colombier /* Be sure we don't keep a cloned ep name */ 13708e32b400SDavid du Colombier free(c->aux); 13718e32b400SDavid du Colombier c->aux = nil; 13728e32b400SDavid du Colombier error("read, not write, expected"); 13738e32b400SDavid du Colombier } 13748e32b400SDavid du Colombier n = epctl(ep, c, a, n); 13758e32b400SDavid du Colombier putep(ep); 13768e32b400SDavid du Colombier poperror(); 13778e32b400SDavid du Colombier return n; 13788e32b400SDavid du Colombier } 13798e32b400SDavid du Colombier 13808e32b400SDavid du Colombier static long 13818e32b400SDavid du Colombier usbwrite(Chan *c, void *a, long n, vlong off) 13828e32b400SDavid du Colombier { 1383add6b5c5SDavid du Colombier int nr, q; 13848e32b400SDavid du Colombier Ep *ep; 13858e32b400SDavid du Colombier 13868e32b400SDavid du Colombier if(c->qid.type == QTDIR) 13878e32b400SDavid du Colombier error(Eisdir); 13888e32b400SDavid du Colombier 13898e32b400SDavid du Colombier q = QID(c->qid); 13908e32b400SDavid du Colombier 13918e32b400SDavid du Colombier if(q == Qctl || isqtype(q, Qepctl)) 13928e32b400SDavid du Colombier return ctlwrite(c, a, n); 13938e32b400SDavid du Colombier 13948e32b400SDavid du Colombier ep = getep(qid2epidx(q)); 13958e32b400SDavid du Colombier if(ep == nil) 13968e32b400SDavid du Colombier error(Eio); 13978e32b400SDavid du Colombier if(waserror()){ 13988e32b400SDavid du Colombier putep(ep); 13998e32b400SDavid du Colombier nexterror(); 14008e32b400SDavid du Colombier } 14018e32b400SDavid du Colombier if(ep->dev->state == Ddetach) 14028e32b400SDavid du Colombier error(Edetach); 14038e32b400SDavid du Colombier if(ep->mode == OREAD || ep->inuse == 0) 14048e32b400SDavid du Colombier error(Ebadusefd); 14058e32b400SDavid du Colombier 14068e32b400SDavid du Colombier switch(ep->ttype){ 14078e32b400SDavid du Colombier case Tnone: 14088e32b400SDavid du Colombier error("endpoint not configured"); 14098e32b400SDavid du Colombier case Tctl: 14108e32b400SDavid du Colombier nr = rhubwrite(ep, a, n); 14118e32b400SDavid du Colombier if(nr >= 0){ 14128e32b400SDavid du Colombier n = nr; 14138e32b400SDavid du Colombier break; 14148e32b400SDavid du Colombier } 14158e32b400SDavid du Colombier /* else fall */ 14168e32b400SDavid du Colombier default: 14178e32b400SDavid du Colombier ddeprint("\nusbwrite q %#x fid %d cnt %ld off %lld\n",q, c->fid, n, off); 14188e32b400SDavid du Colombier ep->hp->epwrite(ep, a, n); 14198e32b400SDavid du Colombier } 14208e32b400SDavid du Colombier putep(ep); 14218e32b400SDavid du Colombier poperror(); 14228e32b400SDavid du Colombier return n; 14238e32b400SDavid du Colombier } 14248e32b400SDavid du Colombier 14258e32b400SDavid du Colombier void 14268e32b400SDavid du Colombier usbshutdown(void) 14278e32b400SDavid du Colombier { 14288e32b400SDavid du Colombier Hci *hp; 14298e32b400SDavid du Colombier int i; 14308e32b400SDavid du Colombier 14318e32b400SDavid du Colombier for(i = 0; i < Nhcis; i++){ 14328e32b400SDavid du Colombier hp = hcis[i]; 14338e32b400SDavid du Colombier if(hp == nil) 14348e32b400SDavid du Colombier continue; 14358e32b400SDavid du Colombier if(hp->shutdown == nil) 14368e32b400SDavid du Colombier print("#u: no shutdown function for %s\n", hp->type); 14378e32b400SDavid du Colombier else 14388e32b400SDavid du Colombier hp->shutdown(hp); 14398e32b400SDavid du Colombier } 14408e32b400SDavid du Colombier } 14418e32b400SDavid du Colombier 14428e32b400SDavid du Colombier Dev usbdevtab = { 14438e32b400SDavid du Colombier L'u', 14448e32b400SDavid du Colombier "usb", 14458e32b400SDavid du Colombier 14468e32b400SDavid du Colombier usbreset, 14478e32b400SDavid du Colombier usbinit, 14488e32b400SDavid du Colombier usbshutdown, 14498e32b400SDavid du Colombier usbattach, 14508e32b400SDavid du Colombier usbwalk, 14518e32b400SDavid du Colombier usbstat, 14528e32b400SDavid du Colombier usbopen, 14538e32b400SDavid du Colombier devcreate, 14548e32b400SDavid du Colombier usbclose, 14558e32b400SDavid du Colombier usbread, 14568e32b400SDavid du Colombier devbread, 14578e32b400SDavid du Colombier usbwrite, 14588e32b400SDavid du Colombier devbwrite, 14598e32b400SDavid du Colombier devremove, 14608e32b400SDavid du Colombier devwstat, 14618e32b400SDavid du Colombier }; 1462