1154abd99SDavid du Colombier /* 2c8a340cdSDavid du Colombier * USB device driver framework. 3154abd99SDavid du Colombier * 4154abd99SDavid du Colombier * This is in charge of providing access to actual HCIs 5154abd99SDavid du Colombier * and providing I/O to the various endpoints of devices. 6154abd99SDavid du Colombier * A separate user program (usbd) is in charge of 7154abd99SDavid du Colombier * enumerating the bus, setting up endpoints and 8154abd99SDavid du Colombier * starting devices (also user programs). 9154abd99SDavid du Colombier * 10154abd99SDavid du Colombier * The interface provided is a violation of the standard: 11154abd99SDavid du Colombier * you're welcome. 12154abd99SDavid du Colombier * 13154abd99SDavid du Colombier * The interface consists of a root directory with several files 14154abd99SDavid du Colombier * plus a directory (epN.M) with two files per endpoint. 15154abd99SDavid du Colombier * A device is represented by its first endpoint, which 16154abd99SDavid du Colombier * is a control endpoint automatically allocated for each device. 17154abd99SDavid du Colombier * Device control endpoints may be used to create new endpoints. 18154abd99SDavid du Colombier * Devices corresponding to hubs may also allocate new devices, 19154abd99SDavid du Colombier * perhaps also hubs. Initially, a hub device is allocated for 20154abd99SDavid du Colombier * each controller present, to represent its root hub. Those can 21154abd99SDavid du Colombier * never be removed. 22154abd99SDavid du Colombier * 23154abd99SDavid du Colombier * All endpoints refer to the first endpoint (epN.0) of the device, 24154abd99SDavid du Colombier * which keeps per-device information, and also to the HCI used 25154abd99SDavid du Colombier * to reach them. Although all endpoints cache that information. 26154abd99SDavid du Colombier * 27154abd99SDavid du Colombier * epN.M/data files permit I/O and are considered DMEXCL. 28154abd99SDavid du Colombier * epN.M/ctl files provide status info and accept control requests. 29154abd99SDavid du Colombier * 30154abd99SDavid du Colombier * Endpoints may be given file names to be listed also at #u, 31154abd99SDavid du Colombier * for those drivers that have nothing to do after configuring the 32154abd99SDavid du Colombier * device and its endpoints. 33154abd99SDavid du Colombier * 34154abd99SDavid du Colombier * Drivers for different controllers are kept at usb[oue]hci.c 35154abd99SDavid du Colombier * It's likely we could factor out much from controllers into 36154abd99SDavid du Colombier * a generic controller driver, the problem is that details 37154abd99SDavid du Colombier * regarding how to handle toggles, tokens, Tds, etc. will 38154abd99SDavid du Colombier * get in the way. Thus, code is probably easier the way it is. 39154abd99SDavid du Colombier */ 40154abd99SDavid du Colombier 41154abd99SDavid du Colombier #include "u.h" 42154abd99SDavid du Colombier #include "../port/lib.h" 43154abd99SDavid du Colombier #include "mem.h" 44154abd99SDavid du Colombier #include "dat.h" 45154abd99SDavid du Colombier #include "fns.h" 46154abd99SDavid du Colombier #include "io.h" 47154abd99SDavid du Colombier #include "../port/error.h" 4884860c5dSDavid du Colombier #include "../port/usb.h" 49154abd99SDavid du Colombier 50154abd99SDavid du Colombier typedef struct Hcitype Hcitype; 51154abd99SDavid du Colombier 52154abd99SDavid du Colombier enum 53154abd99SDavid du Colombier { 54154abd99SDavid du Colombier /* Qid numbers */ 55154abd99SDavid du Colombier Qdir = 0, /* #u */ 56154abd99SDavid du Colombier Qusbdir, /* #u/usb */ 57154abd99SDavid du Colombier Qctl, /* #u/usb/ctl - control requests */ 58154abd99SDavid du Colombier 59154abd99SDavid du Colombier Qep0dir, /* #u/usb/ep0.0 - endpoint 0 dir */ 60154abd99SDavid du Colombier Qep0io, /* #u/usb/ep0.0/data - endpoint 0 I/O */ 61154abd99SDavid du Colombier Qep0ctl, /* #u/usb/ep0.0/ctl - endpoint 0 ctl. */ 62154abd99SDavid du Colombier Qep0dummy, /* give 4 qids to each endpoint */ 63154abd99SDavid du Colombier 64154abd99SDavid du Colombier Qepdir = 0, /* (qid-qep0dir)&3 is one of these */ 65154abd99SDavid du Colombier Qepio, /* to identify which file for the endpoint */ 66154abd99SDavid du Colombier Qepctl, 67154abd99SDavid du Colombier 68154abd99SDavid du Colombier /* ... */ 69154abd99SDavid du Colombier 70154abd99SDavid du Colombier /* Usb ctls. */ 71154abd99SDavid du Colombier CMdebug = 0, /* debug on|off */ 72154abd99SDavid du Colombier CMdump, /* dump (data structures for debug) */ 73154abd99SDavid du Colombier 74154abd99SDavid du Colombier /* Ep. ctls */ 75154abd99SDavid du Colombier CMnew = 0, /* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */ 76154abd99SDavid du Colombier CMnewdev, /* newdev full|low|high portnb (allocate new devices) */ 77154abd99SDavid du Colombier CMhub, /* hub (set the device as a hub) */ 78154abd99SDavid du Colombier CMspeed, /* speed full|low|high|no */ 79154abd99SDavid du Colombier CMmaxpkt, /* maxpkt size */ 80154abd99SDavid du Colombier CMntds, /* ntds nb (max nb. of tds per µframe) */ 81154abd99SDavid du Colombier CMclrhalt, /* clrhalt (halt was cleared on endpoint) */ 82154abd99SDavid du Colombier CMpollival, /* pollival interval (interrupt/iso) */ 83154abd99SDavid du Colombier CMhz, /* hz n (samples/sec; iso) */ 84154abd99SDavid du Colombier CMsamplesz, /* samplesz n (sample size; iso) */ 85154abd99SDavid du Colombier CMinfo, /* info infostr (ke.ep info for humans) */ 86154abd99SDavid du Colombier CMdetach, /* detach (abort I/O forever on this ep). */ 87154abd99SDavid du Colombier CMaddress, /* address (address is assigned) */ 88154abd99SDavid du Colombier CMdebugep, /* debug n (set/clear debug for this ep) */ 89154abd99SDavid du Colombier CMname, /* name str (show up as #u/name as well) */ 90cd52453fSDavid du Colombier CMtmout, /* timeout n (activate timeouts for ep) */ 91cd52453fSDavid du Colombier CMpreset, /* reset the port */ 92154abd99SDavid du Colombier 93154abd99SDavid du Colombier /* Hub feature selectors */ 94154abd99SDavid du Colombier Rportenable = 1, 95154abd99SDavid du Colombier Rportreset = 4, 96154abd99SDavid du Colombier 97154abd99SDavid du Colombier }; 98154abd99SDavid du Colombier 99154abd99SDavid du Colombier struct Hcitype 100154abd99SDavid du Colombier { 101154abd99SDavid du Colombier char* type; 102154abd99SDavid du Colombier int (*reset)(Hci*); 103154abd99SDavid du Colombier }; 104154abd99SDavid du Colombier 105154abd99SDavid du Colombier #define QID(q) ((int)(q).path) 106154abd99SDavid du Colombier 107154abd99SDavid du Colombier static char Edetach[] = "device is detached"; 108154abd99SDavid du Colombier static char Enotconf[] = "endpoint not configured"; 109154abd99SDavid du Colombier char Estalled[] = "endpoint stalled"; 110154abd99SDavid du Colombier 111154abd99SDavid du Colombier static Cmdtab usbctls[] = 112154abd99SDavid du Colombier { 113154abd99SDavid du Colombier {CMdebug, "debug", 2}, 114154abd99SDavid du Colombier {CMdump, "dump", 1}, 115154abd99SDavid du Colombier }; 116154abd99SDavid du Colombier 117154abd99SDavid du Colombier static Cmdtab epctls[] = 118154abd99SDavid du Colombier { 119154abd99SDavid du Colombier {CMnew, "new", 4}, 120154abd99SDavid du Colombier {CMnewdev, "newdev", 3}, 121154abd99SDavid du Colombier {CMhub, "hub", 1}, 122154abd99SDavid du Colombier {CMspeed, "speed", 2}, 123154abd99SDavid du Colombier {CMmaxpkt, "maxpkt", 2}, 124154abd99SDavid du Colombier {CMntds, "ntds", 2}, 125154abd99SDavid du Colombier {CMpollival, "pollival", 2}, 126154abd99SDavid du Colombier {CMsamplesz, "samplesz", 2}, 127154abd99SDavid du Colombier {CMhz, "hz", 2}, 128154abd99SDavid du Colombier {CMinfo, "info", 0}, 129154abd99SDavid du Colombier {CMdetach, "detach", 1}, 130154abd99SDavid du Colombier {CMaddress, "address", 1}, 131154abd99SDavid du Colombier {CMdebugep, "debug", 2}, 132154abd99SDavid du Colombier {CMclrhalt, "clrhalt", 1}, 133154abd99SDavid du Colombier {CMname, "name", 2}, 134cd52453fSDavid du Colombier {CMtmout, "timeout", 2}, 135cd52453fSDavid du Colombier {CMpreset, "reset", 1}, 136154abd99SDavid du Colombier }; 137154abd99SDavid du Colombier 138154abd99SDavid du Colombier static Dirtab usbdir[] = 139154abd99SDavid du Colombier { 140154abd99SDavid du Colombier "ctl", {Qctl}, 0, 0666, 141154abd99SDavid du Colombier }; 142154abd99SDavid du Colombier 143154abd99SDavid du Colombier char *usbmodename[] = 144154abd99SDavid du Colombier { 145154abd99SDavid du Colombier [OREAD] "r", 146154abd99SDavid du Colombier [OWRITE] "w", 147154abd99SDavid du Colombier [ORDWR] "rw", 148154abd99SDavid du Colombier }; 149154abd99SDavid du Colombier 150154abd99SDavid du Colombier static char *ttname[] = 151154abd99SDavid du Colombier { 152154abd99SDavid du Colombier [Tnone] "none", 153154abd99SDavid du Colombier [Tctl] "control", 154154abd99SDavid du Colombier [Tiso] "iso", 155154abd99SDavid du Colombier [Tintr] "interrupt", 156154abd99SDavid du Colombier [Tbulk] "bulk", 157154abd99SDavid du Colombier }; 158154abd99SDavid du Colombier 159154abd99SDavid du Colombier static char *spname[] = 160154abd99SDavid du Colombier { 161154abd99SDavid du Colombier [Fullspeed] "full", 162154abd99SDavid du Colombier [Lowspeed] "low", 163154abd99SDavid du Colombier [Highspeed] "high", 164154abd99SDavid du Colombier [Nospeed] "no", 165154abd99SDavid du Colombier }; 166154abd99SDavid du Colombier 167154abd99SDavid du Colombier static int debug; 168154abd99SDavid du Colombier static Hcitype hcitypes[Nhcis]; 169154abd99SDavid du Colombier static Hci* hcis[Nhcis]; 170154abd99SDavid du Colombier static QLock epslck; /* add, del, lookup endpoints */ 171154abd99SDavid du Colombier static Ep* eps[Neps]; /* all endpoints known */ 172154abd99SDavid du Colombier static int epmax; /* 1 + last endpoint index used */ 173154abd99SDavid du Colombier static int usbidgen; /* device address generator */ 174154abd99SDavid du Colombier 175154abd99SDavid du Colombier /* 176154abd99SDavid du Colombier * Is there something like this in a library? should it be? 177154abd99SDavid du Colombier */ 178154abd99SDavid du Colombier char* 179154abd99SDavid du Colombier seprintdata(char *s, char *se, uchar *d, int n) 180154abd99SDavid du Colombier { 181c8a340cdSDavid du Colombier int i, l; 182154abd99SDavid du Colombier 183154abd99SDavid du Colombier s = seprint(s, se, " %#p[%d]: ", d, n); 184154abd99SDavid du Colombier l = n; 185154abd99SDavid du Colombier if(l > 10) 186154abd99SDavid du Colombier l = 10; 187154abd99SDavid du Colombier for(i=0; i<l; i++) 188154abd99SDavid du Colombier s = seprint(s, se, " %2.2ux", d[i]); 189154abd99SDavid du Colombier if(l < n) 190154abd99SDavid du Colombier s = seprint(s, se, "..."); 191154abd99SDavid du Colombier return s; 192154abd99SDavid du Colombier } 193154abd99SDavid du Colombier 194154abd99SDavid du Colombier static int 195154abd99SDavid du Colombier name2speed(char *name) 196154abd99SDavid du Colombier { 197154abd99SDavid du Colombier int i; 198154abd99SDavid du Colombier 199154abd99SDavid du Colombier for(i = 0; i < nelem(spname); i++) 200154abd99SDavid du Colombier if(strcmp(name, spname[i]) == 0) 201154abd99SDavid du Colombier return i; 202154abd99SDavid du Colombier return Nospeed; 203154abd99SDavid du Colombier } 204154abd99SDavid du Colombier 205154abd99SDavid du Colombier static int 206154abd99SDavid du Colombier name2ttype(char *name) 207154abd99SDavid du Colombier { 208154abd99SDavid du Colombier int i; 209154abd99SDavid du Colombier 210154abd99SDavid du Colombier for(i = 0; i < nelem(ttname); i++) 211154abd99SDavid du Colombier if(strcmp(name, ttname[i]) == 0) 212154abd99SDavid du Colombier return i; 213154abd99SDavid du Colombier /* may be a std. USB ep. type */ 214154abd99SDavid du Colombier i = strtol(name, nil, 0); 215154abd99SDavid du Colombier switch(i+1){ 216154abd99SDavid du Colombier case Tctl: 217154abd99SDavid du Colombier case Tiso: 218154abd99SDavid du Colombier case Tbulk: 219154abd99SDavid du Colombier case Tintr: 220154abd99SDavid du Colombier return i+1; 221154abd99SDavid du Colombier default: 222154abd99SDavid du Colombier return Tnone; 223154abd99SDavid du Colombier } 224154abd99SDavid du Colombier } 225154abd99SDavid du Colombier 226154abd99SDavid du Colombier static int 227154abd99SDavid du Colombier name2mode(char *mode) 228154abd99SDavid du Colombier { 229154abd99SDavid du Colombier int i; 230154abd99SDavid du Colombier 231154abd99SDavid du Colombier for(i = 0; i < nelem(usbmodename); i++) 232154abd99SDavid du Colombier if(strcmp(mode, usbmodename[i]) == 0) 233154abd99SDavid du Colombier return i; 234154abd99SDavid du Colombier return -1; 235154abd99SDavid du Colombier } 236154abd99SDavid du Colombier 237154abd99SDavid du Colombier static int 238154abd99SDavid du Colombier qid2epidx(int q) 239154abd99SDavid du Colombier { 240154abd99SDavid du Colombier q = (q-Qep0dir)/4; 241154abd99SDavid du Colombier if(q < 0 || q >= epmax || eps[q] == nil) 242154abd99SDavid du Colombier return -1; 243154abd99SDavid du Colombier return q; 244154abd99SDavid du Colombier } 245154abd99SDavid du Colombier 246154abd99SDavid du Colombier static int 247154abd99SDavid du Colombier isqtype(int q, int type) 248154abd99SDavid du Colombier { 249154abd99SDavid du Colombier if(q < Qep0dir) 250154abd99SDavid du Colombier return 0; 251154abd99SDavid du Colombier q -= Qep0dir; 252154abd99SDavid du Colombier return (q & 3) == type; 253154abd99SDavid du Colombier } 254154abd99SDavid du Colombier 255154abd99SDavid du Colombier void 256154abd99SDavid du Colombier addhcitype(char* t, int (*r)(Hci*)) 257154abd99SDavid du Colombier { 258154abd99SDavid du Colombier static int ntype; 259154abd99SDavid du Colombier 260154abd99SDavid du Colombier if(ntype == Nhcis) 261154abd99SDavid du Colombier panic("too many USB host interface types"); 262154abd99SDavid du Colombier hcitypes[ntype].type = t; 263154abd99SDavid du Colombier hcitypes[ntype].reset = r; 264154abd99SDavid du Colombier ntype++; 265154abd99SDavid du Colombier } 266154abd99SDavid du Colombier 267154abd99SDavid du Colombier static char* 268154abd99SDavid du Colombier seprintep(char *s, char *se, Ep *ep, int all) 269154abd99SDavid du Colombier { 270cd52453fSDavid du Colombier static char* dsnames[] = { "config", "enabled", "detached", "reset" }; 271154abd99SDavid du Colombier Udev *d; 272154abd99SDavid du Colombier int i; 273154abd99SDavid du Colombier int di; 274154abd99SDavid du Colombier 275154abd99SDavid du Colombier d = ep->dev; 276154abd99SDavid du Colombier 277154abd99SDavid du Colombier qlock(ep); 278154abd99SDavid du Colombier if(waserror()){ 279154abd99SDavid du Colombier qunlock(ep); 280154abd99SDavid du Colombier nexterror(); 281154abd99SDavid du Colombier } 282154abd99SDavid du Colombier di = ep->dev->nb; 283154abd99SDavid du Colombier if(all) 284154abd99SDavid du Colombier s = seprint(s, se, "dev %d ep %d ", di, ep->nb); 285154abd99SDavid du Colombier s = seprint(s, se, "%s", dsnames[ep->dev->state]); 286154abd99SDavid du Colombier s = seprint(s, se, " %s", ttname[ep->ttype]); 287154abd99SDavid du Colombier assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR); 288154abd99SDavid du Colombier s = seprint(s, se, " %s", usbmodename[ep->mode]); 289154abd99SDavid du Colombier s = seprint(s, se, " speed %s", spname[d->speed]); 290154abd99SDavid du Colombier s = seprint(s, se, " maxpkt %ld", ep->maxpkt); 291154abd99SDavid du Colombier s = seprint(s, se, " pollival %ld", ep->pollival); 292154abd99SDavid du Colombier s = seprint(s, se, " samplesz %ld", ep->samplesz); 293154abd99SDavid du Colombier s = seprint(s, se, " hz %ld", ep->hz); 294154abd99SDavid du Colombier s = seprint(s, se, " hub %d", ep->dev->hub); 295154abd99SDavid du Colombier s = seprint(s, se, " port %d", ep->dev->port); 296154abd99SDavid du Colombier if(ep->inuse) 297154abd99SDavid du Colombier s = seprint(s, se, " busy"); 298154abd99SDavid du Colombier else 299154abd99SDavid du Colombier s = seprint(s, se, " idle"); 300154abd99SDavid du Colombier if(all){ 301154abd99SDavid du Colombier s = seprint(s, se, " load %uld", ep->load); 302154abd99SDavid du Colombier s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep); 303154abd99SDavid du Colombier s = seprint(s, se, " idx %d", ep->idx); 304154abd99SDavid du Colombier if(ep->name != nil) 305154abd99SDavid du Colombier s = seprint(s, se, " name '%s'", ep->name); 306cd52453fSDavid du Colombier if(ep->tmout != 0) 307cd52453fSDavid du Colombier s = seprint(s, se, " tmout"); 308154abd99SDavid du Colombier if(ep == ep->ep0){ 309154abd99SDavid du Colombier s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno); 310154abd99SDavid du Colombier s = seprint(s, se, " eps:"); 311154abd99SDavid du Colombier for(i = 0; i < nelem(d->eps); i++) 312154abd99SDavid du Colombier if(d->eps[i] != nil) 313154abd99SDavid du Colombier s = seprint(s, se, " ep%d.%d", di, i); 314154abd99SDavid du Colombier } 315154abd99SDavid du Colombier } 316154abd99SDavid du Colombier if(ep->info != nil) 317cd52453fSDavid du Colombier s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type); 318154abd99SDavid du Colombier else 319154abd99SDavid du Colombier s = seprint(s, se, "\n"); 320154abd99SDavid du Colombier qunlock(ep); 321154abd99SDavid du Colombier poperror(); 322154abd99SDavid du Colombier return s; 323154abd99SDavid du Colombier } 324154abd99SDavid du Colombier 325154abd99SDavid du Colombier static Ep* 326154abd99SDavid du Colombier epalloc(Hci *hp) 327154abd99SDavid du Colombier { 328154abd99SDavid du Colombier Ep *ep; 329154abd99SDavid du Colombier int i; 330154abd99SDavid du Colombier 331*305b51b8SDavid du Colombier ep = smalloc(sizeof(Ep)); 332154abd99SDavid du Colombier ep->ref = 1; 333154abd99SDavid du Colombier qlock(&epslck); 334154abd99SDavid du Colombier for(i = 0; i < Neps; i++) 335154abd99SDavid du Colombier if(eps[i] == nil) 336154abd99SDavid du Colombier break; 337154abd99SDavid du Colombier if(i == Neps){ 338154abd99SDavid du Colombier qunlock(&epslck); 339154abd99SDavid du Colombier free(ep); 340154abd99SDavid du Colombier print("usb: bug: too few endpoints.\n"); 341154abd99SDavid du Colombier return nil; 342154abd99SDavid du Colombier } 343154abd99SDavid du Colombier ep->idx = i; 344154abd99SDavid du Colombier if(epmax <= i) 345154abd99SDavid du Colombier epmax = i+1; 346154abd99SDavid du Colombier eps[i] = ep; 347154abd99SDavid du Colombier ep->hp = hp; 348154abd99SDavid du Colombier ep->maxpkt = 8; 349154abd99SDavid du Colombier ep->ntds = 1; 350154abd99SDavid du Colombier ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */ 351154abd99SDavid du Colombier qunlock(&epslck); 352154abd99SDavid du Colombier return ep; 353154abd99SDavid du Colombier } 354154abd99SDavid du Colombier 355154abd99SDavid du Colombier static Ep* 356154abd99SDavid du Colombier getep(int i) 357154abd99SDavid du Colombier { 358154abd99SDavid du Colombier Ep *ep; 359154abd99SDavid du Colombier 360154abd99SDavid du Colombier if(i < 0 || i >= epmax || eps[i] == nil) 361154abd99SDavid du Colombier return nil; 362154abd99SDavid du Colombier qlock(&epslck); 363154abd99SDavid du Colombier ep = eps[i]; 364154abd99SDavid du Colombier if(ep != nil) 365154abd99SDavid du Colombier incref(ep); 366154abd99SDavid du Colombier qunlock(&epslck); 367154abd99SDavid du Colombier return ep; 368154abd99SDavid du Colombier } 369154abd99SDavid du Colombier 370154abd99SDavid du Colombier static void 371154abd99SDavid du Colombier putep(Ep *ep) 372154abd99SDavid du Colombier { 373154abd99SDavid du Colombier Udev *d; 374154abd99SDavid du Colombier 375154abd99SDavid du Colombier if(ep != nil && decref(ep) == 0){ 376154abd99SDavid du Colombier d = ep->dev; 377154abd99SDavid du Colombier deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep); 378154abd99SDavid du Colombier qlock(&epslck); 379154abd99SDavid du Colombier eps[ep->idx] = nil; 380154abd99SDavid du Colombier if(ep->idx == epmax-1) 381154abd99SDavid du Colombier epmax--; 382154abd99SDavid du Colombier if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen) 383154abd99SDavid du Colombier usbidgen--; 384154abd99SDavid du Colombier qunlock(&epslck); 385154abd99SDavid du Colombier if(d != nil){ 386154abd99SDavid du Colombier qlock(ep->ep0); 387154abd99SDavid du Colombier d->eps[ep->nb] = nil; 388154abd99SDavid du Colombier qunlock(ep->ep0); 389154abd99SDavid du Colombier } 390154abd99SDavid du Colombier if(ep->ep0 != ep){ 391154abd99SDavid du Colombier putep(ep->ep0); 392154abd99SDavid du Colombier ep->ep0 = nil; 393154abd99SDavid du Colombier } 394154abd99SDavid du Colombier free(ep->info); 395154abd99SDavid du Colombier free(ep->name); 396154abd99SDavid du Colombier free(ep); 397154abd99SDavid du Colombier } 398154abd99SDavid du Colombier } 399154abd99SDavid du Colombier 400154abd99SDavid du Colombier static void 401154abd99SDavid du Colombier dumpeps(void) 402154abd99SDavid du Colombier { 403154abd99SDavid du Colombier int i; 404154abd99SDavid du Colombier static char buf[512]; 405154abd99SDavid du Colombier char *s; 406154abd99SDavid du Colombier char *e; 407154abd99SDavid du Colombier Ep *ep; 408154abd99SDavid du Colombier 409154abd99SDavid du Colombier print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps); 410154abd99SDavid du Colombier for(i = 0; i < epmax; i++){ 411154abd99SDavid du Colombier s = buf; 412154abd99SDavid du Colombier e = buf+sizeof(buf); 413154abd99SDavid du Colombier ep = getep(i); 414154abd99SDavid du Colombier if(ep != nil){ 415154abd99SDavid du Colombier if(waserror()){ 416154abd99SDavid du Colombier putep(ep); 417154abd99SDavid du Colombier nexterror(); 418154abd99SDavid du Colombier } 419154abd99SDavid du Colombier s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb); 420154abd99SDavid du Colombier seprintep(s, e, ep, 1); 421154abd99SDavid du Colombier print("%s", buf); 422154abd99SDavid du Colombier ep->hp->seprintep(buf, e, ep); 423154abd99SDavid du Colombier print("%s", buf); 424154abd99SDavid du Colombier poperror(); 425154abd99SDavid du Colombier putep(ep); 426154abd99SDavid du Colombier } 427154abd99SDavid du Colombier } 428154abd99SDavid du Colombier print("usb dump hcis:\n"); 429154abd99SDavid du Colombier for(i = 0; i < Nhcis; i++) 430154abd99SDavid du Colombier if(hcis[i] != nil) 431154abd99SDavid du Colombier hcis[i]->dump(hcis[i]); 432154abd99SDavid du Colombier } 433154abd99SDavid du Colombier 434154abd99SDavid du Colombier static int 435154abd99SDavid du Colombier newusbid(Hci *) 436154abd99SDavid du Colombier { 437154abd99SDavid du Colombier int id; 438154abd99SDavid du Colombier 439154abd99SDavid du Colombier qlock(&epslck); 440154abd99SDavid du Colombier id = ++usbidgen; 441154abd99SDavid du Colombier if(id >= 0x7F) 442154abd99SDavid du Colombier print("#u: too many device addresses; reuse them more\n"); 443154abd99SDavid du Colombier qunlock(&epslck); 444154abd99SDavid du Colombier return id; 445154abd99SDavid du Colombier } 446154abd99SDavid du Colombier 447154abd99SDavid du Colombier /* 448154abd99SDavid du Colombier * Create endpoint 0 for a new device 449154abd99SDavid du Colombier */ 450154abd99SDavid du Colombier static Ep* 451154abd99SDavid du Colombier newdev(Hci *hp, int ishub, int isroot) 452154abd99SDavid du Colombier { 453154abd99SDavid du Colombier Ep *ep; 454154abd99SDavid du Colombier Udev *d; 455154abd99SDavid du Colombier 456154abd99SDavid du Colombier ep = epalloc(hp); 457*305b51b8SDavid du Colombier d = ep->dev = smalloc(sizeof(Udev)); 458154abd99SDavid du Colombier d->nb = newusbid(hp); 459154abd99SDavid du Colombier d->eps[0] = ep; 460154abd99SDavid du Colombier ep->nb = 0; 461154abd99SDavid du Colombier ep->toggle[0] = ep->toggle[1] = 0; 462154abd99SDavid du Colombier d->ishub = ishub; 463154abd99SDavid du Colombier d->isroot = isroot; 464154abd99SDavid du Colombier if(hp->highspeed != 0) 465154abd99SDavid du Colombier d->speed = Highspeed; 466154abd99SDavid du Colombier else 467154abd99SDavid du Colombier d->speed = Fullspeed; 468154abd99SDavid du Colombier d->state = Dconfig; /* address not yet set */ 469154abd99SDavid du Colombier ep->dev = d; 470154abd99SDavid du Colombier ep->ep0 = ep; /* no ref counted here */ 471154abd99SDavid du Colombier ep->ttype = Tctl; 472cd52453fSDavid du Colombier ep->tmout = Xfertmout; 473154abd99SDavid du Colombier ep->mode = ORDWR; 474154abd99SDavid du Colombier dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep); 475154abd99SDavid du Colombier return ep; 476154abd99SDavid du Colombier } 477154abd99SDavid du Colombier 478154abd99SDavid du Colombier /* 479154abd99SDavid du Colombier * Create a new endpoint for the device 480154abd99SDavid du Colombier * accessed via the given endpoint 0. 481154abd99SDavid du Colombier */ 482154abd99SDavid du Colombier static Ep* 483154abd99SDavid du Colombier newdevep(Ep *ep, int i, int tt, int mode) 484154abd99SDavid du Colombier { 485154abd99SDavid du Colombier Ep *nep; 486154abd99SDavid du Colombier Udev *d; 487154abd99SDavid du Colombier 488154abd99SDavid du Colombier d = ep->dev; 489154abd99SDavid du Colombier if(d->eps[i] != nil) 490154abd99SDavid du Colombier error("endpoint already in use"); 491154abd99SDavid du Colombier nep = epalloc(ep->hp); 492154abd99SDavid du Colombier incref(ep); 493154abd99SDavid du Colombier d->eps[i] = nep; 494154abd99SDavid du Colombier nep->nb = i; 495154abd99SDavid du Colombier nep->toggle[0] = nep->toggle[1] = 0; 496154abd99SDavid du Colombier nep->ep0 = ep; 497154abd99SDavid du Colombier nep->dev = ep->dev; 498154abd99SDavid du Colombier nep->mode = mode; 499154abd99SDavid du Colombier nep->ttype = tt; 500154abd99SDavid du Colombier nep->debug = ep->debug; 501cd52453fSDavid du Colombier /* set defaults */ 502cd52453fSDavid du Colombier switch(tt){ 503cd52453fSDavid du Colombier case Tctl: 504cd52453fSDavid du Colombier nep->tmout = Xfertmout; 505cd52453fSDavid du Colombier break; 506cd52453fSDavid du Colombier case Tintr: 507154abd99SDavid du Colombier nep->pollival = 10; 508cd52453fSDavid du Colombier break; 509cd52453fSDavid du Colombier case Tiso: 510cd52453fSDavid du Colombier nep->tmout = Xfertmout; 511cd52453fSDavid du Colombier nep->pollival = 10; 512154abd99SDavid du Colombier nep->samplesz = 4; 513154abd99SDavid du Colombier nep->hz = 44100; 514cd52453fSDavid du Colombier break; 515154abd99SDavid du Colombier } 516154abd99SDavid du Colombier deprint("newdevep ep%d.%d %#p\n", d->nb, nep->nb, nep); 517154abd99SDavid du Colombier return ep; 518154abd99SDavid du Colombier } 519154abd99SDavid du Colombier 520154abd99SDavid du Colombier static int 521154abd99SDavid du Colombier epdataperm(int mode) 522154abd99SDavid du Colombier { 523154abd99SDavid du Colombier 524154abd99SDavid du Colombier switch(mode){ 525154abd99SDavid du Colombier case OREAD: 526154abd99SDavid du Colombier return 0440|DMEXCL; 527154abd99SDavid du Colombier break; 528154abd99SDavid du Colombier case OWRITE: 529154abd99SDavid du Colombier return 0220|DMEXCL; 530154abd99SDavid du Colombier break; 531154abd99SDavid du Colombier default: 532154abd99SDavid du Colombier return 0660|DMEXCL; 533154abd99SDavid du Colombier } 534154abd99SDavid du Colombier } 535154abd99SDavid du Colombier 536154abd99SDavid du Colombier static int 537154abd99SDavid du Colombier usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) 538154abd99SDavid du Colombier { 539154abd99SDavid du Colombier Qid q; 540154abd99SDavid du Colombier Dirtab *dir; 541154abd99SDavid du Colombier int perm; 542154abd99SDavid du Colombier char *se; 543154abd99SDavid du Colombier Ep *ep; 544154abd99SDavid du Colombier int nb; 545154abd99SDavid du Colombier int mode; 546154abd99SDavid du Colombier 547154abd99SDavid du Colombier if(0)ddprint("usbgen q %#x s %d...", QID(c->qid), s); 548154abd99SDavid du Colombier if(s == DEVDOTDOT){ 549154abd99SDavid du Colombier if(QID(c->qid) <= Qusbdir){ 550154abd99SDavid du Colombier mkqid(&q, Qdir, 0, QTDIR); 551154abd99SDavid du Colombier devdir(c, q, "#u", 0, eve, 0555, dp); 552154abd99SDavid du Colombier }else{ 553154abd99SDavid du Colombier mkqid(&q, Qusbdir, 0, QTDIR); 554154abd99SDavid du Colombier devdir(c, q, "usb", 0, eve, 0555, dp); 555154abd99SDavid du Colombier } 556154abd99SDavid du Colombier if(0)ddprint("ok\n"); 557154abd99SDavid du Colombier return 1; 558154abd99SDavid du Colombier } 559154abd99SDavid du Colombier 560154abd99SDavid du Colombier switch(QID(c->qid)){ 561154abd99SDavid du Colombier case Qdir: /* list #u */ 562154abd99SDavid du Colombier if(s == 0){ 563154abd99SDavid du Colombier mkqid(&q, Qusbdir, 0, QTDIR); 564154abd99SDavid du Colombier devdir(c, q, "usb", 0, eve, 0555, dp); 565154abd99SDavid du Colombier if(0)ddprint("ok\n"); 566154abd99SDavid du Colombier return 1; 567154abd99SDavid du Colombier } 568154abd99SDavid du Colombier s--; 569154abd99SDavid du Colombier if(s < 0 || s >= epmax) 570154abd99SDavid du Colombier goto Fail; 571154abd99SDavid du Colombier ep = getep(s); 572154abd99SDavid du Colombier if(ep == nil || ep->name == nil){ 573154abd99SDavid du Colombier if(ep != nil) 574154abd99SDavid du Colombier putep(ep); 575154abd99SDavid du Colombier if(0)ddprint("skip\n"); 576154abd99SDavid du Colombier return 0; 577154abd99SDavid du Colombier } 578154abd99SDavid du Colombier if(waserror()){ 579154abd99SDavid du Colombier putep(ep); 580154abd99SDavid du Colombier nexterror(); 581154abd99SDavid du Colombier } 582154abd99SDavid du Colombier mkqid(&q, Qep0io+s*4, 0, QTFILE); 583154abd99SDavid du Colombier devdir(c, q, ep->name, 0, eve, epdataperm(ep->mode), dp); 584154abd99SDavid du Colombier putep(ep); 585154abd99SDavid du Colombier poperror(); 586154abd99SDavid du Colombier if(0)ddprint("ok\n"); 587154abd99SDavid du Colombier return 1; 588154abd99SDavid du Colombier 589154abd99SDavid du Colombier case Qusbdir: /* list #u/usb */ 590154abd99SDavid du Colombier Usbdir: 591154abd99SDavid du Colombier if(s < nelem(usbdir)){ 592154abd99SDavid du Colombier dir = &usbdir[s]; 593154abd99SDavid du Colombier mkqid(&q, dir->qid.path, 0, QTFILE); 594154abd99SDavid du Colombier devdir(c, q, dir->name, dir->length, eve, dir->perm, dp); 595154abd99SDavid du Colombier if(0)ddprint("ok\n"); 596154abd99SDavid du Colombier return 1; 597154abd99SDavid du Colombier } 598154abd99SDavid du Colombier s -= nelem(usbdir); 599154abd99SDavid du Colombier if(s < 0 || s >= epmax) 600154abd99SDavid du Colombier goto Fail; 601154abd99SDavid du Colombier ep = getep(s); 602154abd99SDavid du Colombier if(ep == nil){ 603154abd99SDavid du Colombier if(0)ddprint("skip\n"); 604154abd99SDavid du Colombier return 0; 605154abd99SDavid du Colombier } 606154abd99SDavid du Colombier if(waserror()){ 607154abd99SDavid du Colombier putep(ep); 608154abd99SDavid du Colombier nexterror(); 609154abd99SDavid du Colombier } 610154abd99SDavid du Colombier se = up->genbuf+sizeof(up->genbuf); 611154abd99SDavid du Colombier seprint(up->genbuf, se, "ep%d.%d", ep->dev->nb, ep->nb); 612154abd99SDavid du Colombier mkqid(&q, Qep0dir+4*s, 0, QTDIR); 613154abd99SDavid du Colombier putep(ep); 614154abd99SDavid du Colombier poperror(); 615154abd99SDavid du Colombier devdir(c, q, up->genbuf, 0, eve, 0755, dp); 616154abd99SDavid du Colombier if(0)ddprint("ok\n"); 617154abd99SDavid du Colombier return 1; 618154abd99SDavid du Colombier 619154abd99SDavid du Colombier case Qctl: 620154abd99SDavid du Colombier s = 0; 621154abd99SDavid du Colombier goto Usbdir; 622154abd99SDavid du Colombier 623154abd99SDavid du Colombier default: /* list #u/usb/epN.M */ 624154abd99SDavid du Colombier nb = qid2epidx(QID(c->qid)); 625154abd99SDavid du Colombier ep = getep(nb); 626154abd99SDavid du Colombier if(ep == nil) 627154abd99SDavid du Colombier goto Fail; 628154abd99SDavid du Colombier mode = ep->mode; 629154abd99SDavid du Colombier putep(ep); 630154abd99SDavid du Colombier if(isqtype(QID(c->qid), Qepdir)){ 631154abd99SDavid du Colombier Epdir: 632154abd99SDavid du Colombier switch(s){ 633154abd99SDavid du Colombier case 0: 634154abd99SDavid du Colombier mkqid(&q, Qep0io+nb*4, 0, QTFILE); 635154abd99SDavid du Colombier perm = epdataperm(mode); 636154abd99SDavid du Colombier devdir(c, q, "data", 0, eve, perm, dp); 637154abd99SDavid du Colombier break; 638154abd99SDavid du Colombier case 1: 639154abd99SDavid du Colombier mkqid(&q, Qep0ctl+nb*4, 0, QTFILE); 640154abd99SDavid du Colombier devdir(c, q, "ctl", 0, eve, 0664, dp); 641154abd99SDavid du Colombier break; 642154abd99SDavid du Colombier default: 643154abd99SDavid du Colombier goto Fail; 644154abd99SDavid du Colombier } 645154abd99SDavid du Colombier }else if(isqtype(QID(c->qid), Qepctl)){ 646154abd99SDavid du Colombier s = 1; 647154abd99SDavid du Colombier goto Epdir; 648154abd99SDavid du Colombier }else{ 649154abd99SDavid du Colombier s = 0; 650154abd99SDavid du Colombier goto Epdir; 651154abd99SDavid du Colombier } 652154abd99SDavid du Colombier if(0)ddprint("ok\n"); 653154abd99SDavid du Colombier return 1; 654154abd99SDavid du Colombier } 655154abd99SDavid du Colombier Fail: 656154abd99SDavid du Colombier if(0)ddprint("fail\n"); 657154abd99SDavid du Colombier return -1; 658154abd99SDavid du Colombier } 659154abd99SDavid du Colombier 660154abd99SDavid du Colombier static Hci* 661154abd99SDavid du Colombier hciprobe(int cardno, int ctlrno) 662154abd99SDavid du Colombier { 663154abd99SDavid du Colombier Hci *hp; 664154abd99SDavid du Colombier char *type; 665154abd99SDavid du Colombier char name[64]; 666154abd99SDavid du Colombier static int epnb = 1; /* guess the endpoint nb. for the controller */ 667154abd99SDavid du Colombier 668154abd99SDavid du Colombier ddprint("hciprobe %d %d\n", cardno, ctlrno); 669*305b51b8SDavid du Colombier hp = smalloc(sizeof(Hci)); 670154abd99SDavid du Colombier hp->ctlrno = ctlrno; 671154abd99SDavid du Colombier hp->tbdf = BUSUNKNOWN; 672154abd99SDavid du Colombier 673154abd99SDavid du Colombier if(cardno < 0) 674154abd99SDavid du Colombier for(cardno = 0; cardno < Nhcis; cardno++){ 675154abd99SDavid du Colombier if(hcitypes[cardno].type == nil) 676154abd99SDavid du Colombier break; 677154abd99SDavid du Colombier type = hp->type; 678154abd99SDavid du Colombier if(type==nil || *type==0) 679154abd99SDavid du Colombier type = "uhci"; 680154abd99SDavid du Colombier if(cistrcmp(hcitypes[cardno].type, type) == 0) 681154abd99SDavid du Colombier break; 682154abd99SDavid du Colombier } 683154abd99SDavid du Colombier 684154abd99SDavid du Colombier if(cardno >= Nhcis || hcitypes[cardno].type == nil){ 685154abd99SDavid du Colombier free(hp); 686154abd99SDavid du Colombier return nil; 687154abd99SDavid du Colombier } 688154abd99SDavid du Colombier dprint("%s...", hcitypes[cardno].type); 689154abd99SDavid du Colombier if(hcitypes[cardno].reset(hp) < 0){ 690154abd99SDavid du Colombier free(hp); 691154abd99SDavid du Colombier return nil; 692154abd99SDavid du Colombier } 693154abd99SDavid du Colombier 694154abd99SDavid du Colombier snprint(name, sizeof(name), "usb%s", hcitypes[cardno].type); 695154abd99SDavid du Colombier intrenable(Irqlo, hp->irq, hp->interrupt, hp, name); 6969f2e9820SDavid du Colombier print("#u/usb/ep%d.0: %s: port %#luX irq %d\n", 697cd52453fSDavid du Colombier epnb, hcitypes[cardno].type, hp->port, hp->irq); 698cd52453fSDavid du Colombier epnb++; 699154abd99SDavid du Colombier return hp; 700154abd99SDavid du Colombier } 701154abd99SDavid du Colombier 702154abd99SDavid du Colombier static void 703154abd99SDavid du Colombier usbreset(void) 704154abd99SDavid du Colombier { 705154abd99SDavid du Colombier int cardno, ctlrno; 706154abd99SDavid du Colombier Hci *hp; 707154abd99SDavid du Colombier 708154abd99SDavid du Colombier dprint("usbreset\n"); 709154abd99SDavid du Colombier 710154abd99SDavid du Colombier for(ctlrno = 0; ctlrno < Nhcis; ctlrno++) 711154abd99SDavid du Colombier if((hp = hciprobe(-1, ctlrno)) != nil) 712154abd99SDavid du Colombier hcis[ctlrno] = hp; 713154abd99SDavid du Colombier cardno = ctlrno = 0; 714154abd99SDavid du Colombier while(cardno < Nhcis && ctlrno < Nhcis && hcitypes[cardno].type != nil) 715154abd99SDavid du Colombier if(hcis[ctlrno] != nil) 716154abd99SDavid du Colombier ctlrno++; 717154abd99SDavid du Colombier else{ 718154abd99SDavid du Colombier hp = hciprobe(cardno, ctlrno); 719154abd99SDavid du Colombier if(hp == nil) 720154abd99SDavid du Colombier cardno++; 721154abd99SDavid du Colombier hcis[ctlrno++] = hp; 722154abd99SDavid du Colombier } 723154abd99SDavid du Colombier if(hcis[Nhcis-1] != nil) 724154abd99SDavid du Colombier print("usbreset: bug: Nhcis too small\n"); 725154abd99SDavid du Colombier } 726154abd99SDavid du Colombier 727154abd99SDavid du Colombier static void 728154abd99SDavid du Colombier usbinit(void) 729154abd99SDavid du Colombier { 730154abd99SDavid du Colombier Hci *hp; 731154abd99SDavid du Colombier int ctlrno; 732154abd99SDavid du Colombier Ep *d; 733154abd99SDavid du Colombier char info[40]; 734154abd99SDavid du Colombier 735154abd99SDavid du Colombier dprint("usbinit\n"); 736154abd99SDavid du Colombier for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){ 737154abd99SDavid du Colombier hp = hcis[ctlrno]; 738154abd99SDavid du Colombier if(hp != nil){ 739154abd99SDavid du Colombier if(hp->init != nil) 740154abd99SDavid du Colombier hp->init(hp); 741154abd99SDavid du Colombier d = newdev(hp, 1, 1); /* new root hub */ 742154abd99SDavid du Colombier d->dev->state = Denabled; /* although addr == 0 */ 743154abd99SDavid du Colombier d->maxpkt = 64; 744154abd99SDavid du Colombier snprint(info, sizeof(info), "ports %d", hp->nports); 745154abd99SDavid du Colombier kstrdup(&d->info, info); 746154abd99SDavid du Colombier } 747154abd99SDavid du Colombier } 748154abd99SDavid du Colombier } 749154abd99SDavid du Colombier 750154abd99SDavid du Colombier static Chan* 751154abd99SDavid du Colombier usbattach(char *spec) 752154abd99SDavid du Colombier { 753154abd99SDavid du Colombier return devattach(L'u', spec); 754154abd99SDavid du Colombier } 755154abd99SDavid du Colombier 756154abd99SDavid du Colombier static Walkqid* 757154abd99SDavid du Colombier usbwalk(Chan *c, Chan *nc, char **name, int nname) 758154abd99SDavid du Colombier { 759154abd99SDavid du Colombier return devwalk(c, nc, name, nname, nil, 0, usbgen); 760154abd99SDavid du Colombier } 761154abd99SDavid du Colombier 762154abd99SDavid du Colombier static int 763154abd99SDavid du Colombier usbstat(Chan *c, uchar *db, int n) 764154abd99SDavid du Colombier { 765154abd99SDavid du Colombier return devstat(c, db, n, nil, 0, usbgen); 766154abd99SDavid du Colombier } 767154abd99SDavid du Colombier 768154abd99SDavid du Colombier /* 769154abd99SDavid du Colombier * µs for the given transfer, for bandwidth allocation. 770154abd99SDavid du Colombier * This is a very rough worst case for what 5.11.3 771154abd99SDavid du Colombier * of the usb 2.0 spec says. 772154abd99SDavid du Colombier * Also, we are using maxpkt and not actual transfer sizes. 773154abd99SDavid du Colombier * Only when we are sure we 774154abd99SDavid du Colombier * are not exceeding b/w might we consider adjusting it. 775154abd99SDavid du Colombier */ 776154abd99SDavid du Colombier static ulong 777154abd99SDavid du Colombier usbload(int speed, int maxpkt) 778154abd99SDavid du Colombier { 779154abd99SDavid du Colombier enum{ Hostns = 1000, Hubns = 333 }; 780154abd99SDavid du Colombier ulong l; 781154abd99SDavid du Colombier ulong bs; 782154abd99SDavid du Colombier 783154abd99SDavid du Colombier l = 0; 784154abd99SDavid du Colombier bs = 10UL * maxpkt; 785154abd99SDavid du Colombier switch(speed){ 786154abd99SDavid du Colombier case Highspeed: 787154abd99SDavid du Colombier l = 55*8*2 + 2 * (3 + bs) + Hostns; 788154abd99SDavid du Colombier break; 789154abd99SDavid du Colombier case Fullspeed: 790154abd99SDavid du Colombier l = 9107 + 84 * (4 + bs) + Hostns; 791154abd99SDavid du Colombier break; 792154abd99SDavid du Colombier case Lowspeed: 793154abd99SDavid du Colombier l = 64107 + 2 * Hubns + 667 * (3 + bs) + Hostns; 794154abd99SDavid du Colombier break; 795154abd99SDavid du Colombier default: 796154abd99SDavid du Colombier print("usbload: bad speed %d\n", speed); 797154abd99SDavid du Colombier /* let it run */ 798154abd99SDavid du Colombier } 799154abd99SDavid du Colombier return l / 1000UL; /* in µs */ 800154abd99SDavid du Colombier } 801154abd99SDavid du Colombier 802154abd99SDavid du Colombier static Chan* 803154abd99SDavid du Colombier usbopen(Chan *c, int omode) 804154abd99SDavid du Colombier { 805154abd99SDavid du Colombier int q; 806154abd99SDavid du Colombier Ep *ep; 807154abd99SDavid du Colombier int mode; 808154abd99SDavid du Colombier 809154abd99SDavid du Colombier mode = openmode(omode); 810154abd99SDavid du Colombier q = QID(c->qid); 811154abd99SDavid du Colombier 812154abd99SDavid du Colombier if(q >= Qep0dir && qid2epidx(q) < 0) 813154abd99SDavid du Colombier error(Eio); 814154abd99SDavid du Colombier if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir)) 815154abd99SDavid du Colombier return devopen(c, omode, nil, 0, usbgen); 816154abd99SDavid du Colombier 817154abd99SDavid du Colombier ep = getep(qid2epidx(q)); 818154abd99SDavid du Colombier if(ep == nil) 819154abd99SDavid du Colombier error(Eio); 820154abd99SDavid du Colombier deprint("usbopen q %#x fid %d omode %d\n", q, c->fid, mode); 821154abd99SDavid du Colombier if(waserror()){ 822154abd99SDavid du Colombier putep(ep); 823154abd99SDavid du Colombier nexterror(); 824154abd99SDavid du Colombier } 825154abd99SDavid du Colombier qlock(ep); 826154abd99SDavid du Colombier if(ep->inuse){ 827154abd99SDavid du Colombier qunlock(ep); 828154abd99SDavid du Colombier error(Einuse); 829154abd99SDavid du Colombier } 830154abd99SDavid du Colombier ep->inuse = 1; 831154abd99SDavid du Colombier qunlock(ep); 832154abd99SDavid du Colombier if(waserror()){ 833154abd99SDavid du Colombier ep->inuse = 0; 834154abd99SDavid du Colombier nexterror(); 835154abd99SDavid du Colombier } 836154abd99SDavid du Colombier if(mode != OREAD && ep->mode == OREAD) 837154abd99SDavid du Colombier error(Eperm); 838154abd99SDavid du Colombier if(mode != OWRITE && ep->mode == OWRITE) 839154abd99SDavid du Colombier error(Eperm); 840154abd99SDavid du Colombier if(ep->ttype == Tnone) 841154abd99SDavid du Colombier error(Enotconf); 842154abd99SDavid du Colombier ep->clrhalt = 0; 843154abd99SDavid du Colombier ep->rhrepl = -1; 844154abd99SDavid du Colombier if(ep->load == 0) 845154abd99SDavid du Colombier ep->load = usbload(ep->dev->speed, ep->maxpkt); 846154abd99SDavid du Colombier ep->hp->epopen(ep); 847154abd99SDavid du Colombier 848154abd99SDavid du Colombier poperror(); /* ep->inuse */ 849154abd99SDavid du Colombier poperror(); /* don't putep(): ref kept for fid using the ep. */ 850154abd99SDavid du Colombier 851154abd99SDavid du Colombier c->mode = mode; 852154abd99SDavid du Colombier c->flag |= COPEN; 853154abd99SDavid du Colombier c->offset = 0; 854154abd99SDavid du Colombier c->aux = nil; /* paranoia */ 855154abd99SDavid du Colombier return c; 856154abd99SDavid du Colombier } 857154abd99SDavid du Colombier 858154abd99SDavid du Colombier static void 859154abd99SDavid du Colombier epclose(Ep *ep) 860154abd99SDavid du Colombier { 861154abd99SDavid du Colombier qlock(ep); 862154abd99SDavid du Colombier if(waserror()){ 863154abd99SDavid du Colombier qunlock(ep); 864154abd99SDavid du Colombier nexterror(); 865154abd99SDavid du Colombier } 866154abd99SDavid du Colombier if(ep->inuse){ 867154abd99SDavid du Colombier ep->hp->epclose(ep); 868154abd99SDavid du Colombier ep->inuse = 0; 869154abd99SDavid du Colombier } 870154abd99SDavid du Colombier qunlock(ep); 871154abd99SDavid du Colombier poperror(); 872154abd99SDavid du Colombier } 873154abd99SDavid du Colombier 874154abd99SDavid du Colombier static void 875154abd99SDavid du Colombier usbclose(Chan *c) 876154abd99SDavid du Colombier { 877154abd99SDavid du Colombier int q; 878154abd99SDavid du Colombier Ep *ep; 879154abd99SDavid du Colombier 880154abd99SDavid du Colombier q = QID(c->qid); 881154abd99SDavid du Colombier if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir)) 882154abd99SDavid du Colombier return; 883154abd99SDavid du Colombier 884154abd99SDavid du Colombier ep = getep(qid2epidx(q)); 885154abd99SDavid du Colombier if(ep == nil) 886154abd99SDavid du Colombier return; 887154abd99SDavid du Colombier deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref); 888154abd99SDavid du Colombier if(waserror()){ 889154abd99SDavid du Colombier putep(ep); 890154abd99SDavid du Colombier nexterror(); 891154abd99SDavid du Colombier } 892154abd99SDavid du Colombier if(c->flag & COPEN){ 893154abd99SDavid du Colombier free(c->aux); 894154abd99SDavid du Colombier c->aux = nil; 895154abd99SDavid du Colombier epclose(ep); 896154abd99SDavid du Colombier putep(ep); /* release ref kept since usbopen */ 897154abd99SDavid du Colombier c->flag &= ~COPEN; 898154abd99SDavid du Colombier } 899154abd99SDavid du Colombier poperror(); 900154abd99SDavid du Colombier putep(ep); 901154abd99SDavid du Colombier } 902154abd99SDavid du Colombier 903154abd99SDavid du Colombier static long 904154abd99SDavid du Colombier ctlread(Chan *c, void *a, long n, vlong offset) 905154abd99SDavid du Colombier { 906154abd99SDavid du Colombier int q; 907154abd99SDavid du Colombier char *s; 908154abd99SDavid du Colombier char *us; 909154abd99SDavid du Colombier char *se; 910154abd99SDavid du Colombier Ep *ep; 911154abd99SDavid du Colombier int i; 912154abd99SDavid du Colombier 913154abd99SDavid du Colombier q = QID(c->qid); 914154abd99SDavid du Colombier us = s = smalloc(READSTR); 915154abd99SDavid du Colombier se = s + READSTR; 916154abd99SDavid du Colombier if(waserror()){ 917154abd99SDavid du Colombier free(us); 918154abd99SDavid du Colombier nexterror(); 919154abd99SDavid du Colombier } 920154abd99SDavid du Colombier if(q == Qctl) 921154abd99SDavid du Colombier for(i = 0; i < epmax; i++){ 922154abd99SDavid du Colombier ep = getep(i); 923154abd99SDavid du Colombier if(ep != nil){ 924154abd99SDavid du Colombier if(waserror()){ 925154abd99SDavid du Colombier putep(ep); 926154abd99SDavid du Colombier nexterror(); 927154abd99SDavid du Colombier } 928154abd99SDavid du Colombier s = seprint(s, se, "ep%d.%d ", ep->dev->nb, ep->nb); 929154abd99SDavid du Colombier s = seprintep(s, se, ep, 0); 930154abd99SDavid du Colombier poperror(); 931154abd99SDavid du Colombier } 932154abd99SDavid du Colombier putep(ep); 933154abd99SDavid du Colombier } 934154abd99SDavid du Colombier else{ 935154abd99SDavid du Colombier ep = getep(qid2epidx(q)); 936154abd99SDavid du Colombier if(ep == nil) 937154abd99SDavid du Colombier error(Eio); 938154abd99SDavid du Colombier if(waserror()){ 939154abd99SDavid du Colombier putep(ep); 940154abd99SDavid du Colombier nexterror(); 941154abd99SDavid du Colombier } 942154abd99SDavid du Colombier if(c->aux != nil){ 943154abd99SDavid du Colombier /* After a new endpoint request we read 944154abd99SDavid du Colombier * the new endpoint name back. 945154abd99SDavid du Colombier */ 946154abd99SDavid du Colombier strecpy(s, se, c->aux); 947154abd99SDavid du Colombier free(c->aux); 948154abd99SDavid du Colombier c->aux = nil; 949154abd99SDavid du Colombier }else 950154abd99SDavid du Colombier seprintep(s, se, ep, 0); 951154abd99SDavid du Colombier poperror(); 952154abd99SDavid du Colombier putep(ep); 953154abd99SDavid du Colombier } 954154abd99SDavid du Colombier n = readstr(offset, a, n, us); 955154abd99SDavid du Colombier poperror(); 956154abd99SDavid du Colombier free(us); 957154abd99SDavid du Colombier return n; 958154abd99SDavid du Colombier } 959154abd99SDavid du Colombier 960154abd99SDavid du Colombier /* 961154abd99SDavid du Colombier * Fake root hub emulation. 962154abd99SDavid du Colombier */ 963154abd99SDavid du Colombier static long 964154abd99SDavid du Colombier rhubread(Ep *ep, void *a, long n) 965154abd99SDavid du Colombier { 966154abd99SDavid du Colombier char *b; 967154abd99SDavid du Colombier 968154abd99SDavid du Colombier if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2) 969154abd99SDavid du Colombier return -1; 970154abd99SDavid du Colombier if(ep->rhrepl < 0) 971154abd99SDavid du Colombier return -1; 972154abd99SDavid du Colombier 973154abd99SDavid du Colombier b = a; 974154abd99SDavid du Colombier memset(b, 0, n); 975154abd99SDavid du Colombier PUT2(b, ep->rhrepl); 976154abd99SDavid du Colombier ep->rhrepl = -1; 977154abd99SDavid du Colombier return n; 978154abd99SDavid du Colombier } 979154abd99SDavid du Colombier 980154abd99SDavid du Colombier static long 981154abd99SDavid du Colombier rhubwrite(Ep *ep, void *a, long n) 982154abd99SDavid du Colombier { 983154abd99SDavid du Colombier uchar *s; 984154abd99SDavid du Colombier int cmd; 985154abd99SDavid du Colombier int feature; 986154abd99SDavid du Colombier int port; 987154abd99SDavid du Colombier Hci *hp; 988154abd99SDavid du Colombier 989154abd99SDavid du Colombier if(ep->dev == nil || ep->dev->isroot == 0 || ep->nb != 0) 990154abd99SDavid du Colombier return -1; 991154abd99SDavid du Colombier if(n != Rsetuplen) 992154abd99SDavid du Colombier error("root hub is a toy hub"); 993154abd99SDavid du Colombier ep->rhrepl = -1; 994154abd99SDavid du Colombier s = a; 995154abd99SDavid du Colombier if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother)) 996154abd99SDavid du Colombier error("root hub is a toy hub"); 997154abd99SDavid du Colombier hp = ep->hp; 998154abd99SDavid du Colombier cmd = s[Rreq]; 999154abd99SDavid du Colombier feature = GET2(s+Rvalue); 1000154abd99SDavid du Colombier port = GET2(s+Rindex); 1001154abd99SDavid du Colombier if(port < 1 || port > hp->nports) 1002154abd99SDavid du Colombier error("bad hub port number"); 1003154abd99SDavid du Colombier switch(feature){ 1004154abd99SDavid du Colombier case Rportenable: 1005154abd99SDavid du Colombier ep->rhrepl = hp->portenable(hp, port, cmd == Rsetfeature); 1006154abd99SDavid du Colombier break; 1007154abd99SDavid du Colombier case Rportreset: 1008154abd99SDavid du Colombier ep->rhrepl = hp->portreset(hp, port, cmd == Rsetfeature); 1009154abd99SDavid du Colombier break; 1010154abd99SDavid du Colombier case Rgetstatus: 1011154abd99SDavid du Colombier ep->rhrepl = hp->portstatus(hp, port); 1012154abd99SDavid du Colombier break; 1013154abd99SDavid du Colombier default: 1014154abd99SDavid du Colombier ep->rhrepl = 0; 1015154abd99SDavid du Colombier } 1016154abd99SDavid du Colombier return n; 1017154abd99SDavid du Colombier } 1018154abd99SDavid du Colombier 1019154abd99SDavid du Colombier static long 1020154abd99SDavid du Colombier usbread(Chan *c, void *a, long n, vlong offset) 1021154abd99SDavid du Colombier { 1022154abd99SDavid du Colombier int q; 1023154abd99SDavid du Colombier Ep *ep; 1024154abd99SDavid du Colombier int nr; 1025154abd99SDavid du Colombier 1026154abd99SDavid du Colombier q = QID(c->qid); 1027154abd99SDavid du Colombier 1028154abd99SDavid du Colombier if(c->qid.type == QTDIR) 1029154abd99SDavid du Colombier return devdirread(c, a, n, nil, 0, usbgen); 1030154abd99SDavid du Colombier 1031154abd99SDavid du Colombier if(q == Qctl || isqtype(q, Qepctl)) 1032154abd99SDavid du Colombier return ctlread(c, a, n, offset); 1033154abd99SDavid du Colombier 1034154abd99SDavid du Colombier ep = getep(qid2epidx(q)); 1035154abd99SDavid du Colombier if(ep == nil) 1036154abd99SDavid du Colombier error(Eio); 1037154abd99SDavid du Colombier if(waserror()){ 1038154abd99SDavid du Colombier putep(ep); 1039154abd99SDavid du Colombier nexterror(); 1040154abd99SDavid du Colombier } 1041154abd99SDavid du Colombier if(ep->dev->state == Ddetach) 1042154abd99SDavid du Colombier error(Edetach); 1043154abd99SDavid du Colombier if(ep->mode == OWRITE || ep->inuse == 0) 1044154abd99SDavid du Colombier error(Ebadusefd); 1045154abd99SDavid du Colombier switch(ep->ttype){ 1046154abd99SDavid du Colombier case Tnone: 1047154abd99SDavid du Colombier error("endpoint not configured"); 1048154abd99SDavid du Colombier case Tctl: 1049154abd99SDavid du Colombier nr = rhubread(ep, a, n); 1050154abd99SDavid du Colombier if(nr >= 0){ 1051154abd99SDavid du Colombier n = nr; 1052154abd99SDavid du Colombier break; 1053154abd99SDavid du Colombier } 1054154abd99SDavid du Colombier /* else fall */ 1055154abd99SDavid du Colombier default: 1056154abd99SDavid du Colombier ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",q,c->fid,n,offset); 1057154abd99SDavid du Colombier n = ep->hp->epread(ep, a, n); 1058154abd99SDavid du Colombier break; 1059154abd99SDavid du Colombier } 1060154abd99SDavid du Colombier poperror(); 1061154abd99SDavid du Colombier putep(ep); 1062154abd99SDavid du Colombier return n; 1063154abd99SDavid du Colombier } 1064154abd99SDavid du Colombier 1065154abd99SDavid du Colombier static long 1066154abd99SDavid du Colombier pow2(int n) 1067154abd99SDavid du Colombier { 1068c8a340cdSDavid du Colombier return 1 << n; 1069154abd99SDavid du Colombier } 1070154abd99SDavid du Colombier 1071154abd99SDavid du Colombier static void 1072154abd99SDavid du Colombier setmaxpkt(Ep *ep, char* s) 1073154abd99SDavid du Colombier { 1074154abd99SDavid du Colombier long spp; /* samples per packet */ 1075154abd99SDavid du Colombier 1076154abd99SDavid du Colombier if(ep->dev->speed == Highspeed) 1077154abd99SDavid du Colombier spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000; 1078154abd99SDavid du Colombier else 1079154abd99SDavid du Colombier spp = (ep->hz * ep->pollival + 999) / 1000; 1080154abd99SDavid du Colombier ep->maxpkt = spp * ep->samplesz; 1081154abd99SDavid du Colombier deprint("usb: %s: setmaxpkt: hz %ld poll %ld" 1082154abd99SDavid du Colombier " ntds %d %s speed -> spp %ld maxpkt %ld\n", s, 1083154abd99SDavid du Colombier ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed], 1084154abd99SDavid du Colombier spp, ep->maxpkt); 1085154abd99SDavid du Colombier if(ep->maxpkt > 1024){ 1086154abd99SDavid du Colombier print("usb: %s: maxpkt %ld > 1024. truncating\n", s, ep->maxpkt); 1087154abd99SDavid du Colombier ep->maxpkt = 1024; 1088154abd99SDavid du Colombier } 1089154abd99SDavid du Colombier } 1090154abd99SDavid du Colombier 1091154abd99SDavid du Colombier /* 1092154abd99SDavid du Colombier * Many endpoint ctls. simply update the portable representation 1093154abd99SDavid du Colombier * of the endpoint. The actual controller driver will look 1094154abd99SDavid du Colombier * at them to setup the endpoints as dictated. 1095154abd99SDavid du Colombier */ 1096154abd99SDavid du Colombier static long 1097154abd99SDavid du Colombier epctl(Ep *ep, Chan *c, void *a, long n) 1098154abd99SDavid du Colombier { 1099c8a340cdSDavid du Colombier int i, l, mode, nb, tt; 1100c8a340cdSDavid du Colombier char *b, *s; 1101c8a340cdSDavid du Colombier Cmdbuf *cb; 1102c8a340cdSDavid du Colombier Cmdtab *ct; 1103154abd99SDavid du Colombier Ep *nep; 1104154abd99SDavid du Colombier Udev *d; 1105c8a340cdSDavid du Colombier static char *Info = "info "; 1106154abd99SDavid du Colombier 1107154abd99SDavid du Colombier d = ep->dev; 1108154abd99SDavid du Colombier 1109154abd99SDavid du Colombier cb = parsecmd(a, n); 1110154abd99SDavid du Colombier if(waserror()){ 1111154abd99SDavid du Colombier free(cb); 1112154abd99SDavid du Colombier nexterror(); 1113154abd99SDavid du Colombier } 1114154abd99SDavid du Colombier ct = lookupcmd(cb, epctls, nelem(epctls)); 1115154abd99SDavid du Colombier if(ct == nil) 1116154abd99SDavid du Colombier error(Ebadctl); 1117154abd99SDavid du Colombier i = ct->index; 1118cd52453fSDavid du Colombier if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset) 1119154abd99SDavid du Colombier if(ep != ep->ep0) 1120154abd99SDavid du Colombier error("allowed only on a setup endpoint"); 1121154abd99SDavid du Colombier if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname) 1122154abd99SDavid du Colombier if(ep != ep->ep0 && ep->inuse != 0) 1123154abd99SDavid du Colombier error("must configure before using"); 1124154abd99SDavid du Colombier switch(i){ 1125154abd99SDavid du Colombier case CMnew: 1126154abd99SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 1127154abd99SDavid du Colombier nb = strtol(cb->f[1], nil, 0); 1128154abd99SDavid du Colombier if(nb < 0 || nb >= Ndeveps) 1129154abd99SDavid du Colombier error("bad endpoint number"); 1130154abd99SDavid du Colombier tt = name2ttype(cb->f[2]); 1131154abd99SDavid du Colombier if(tt == Tnone) 1132154abd99SDavid du Colombier error("unknown endpoint type"); 1133154abd99SDavid du Colombier mode = name2mode(cb->f[3]); 1134154abd99SDavid du Colombier if(mode < 0) 1135154abd99SDavid du Colombier error("unknown i/o mode"); 1136154abd99SDavid du Colombier newdevep(ep, nb, tt, mode); 1137154abd99SDavid du Colombier break; 1138154abd99SDavid du Colombier case CMnewdev: 1139154abd99SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 1140154abd99SDavid du Colombier if(ep != ep->ep0 || d->ishub == 0) 1141154abd99SDavid du Colombier error("not a hub setup endpoint"); 1142154abd99SDavid du Colombier l = name2speed(cb->f[1]); 1143154abd99SDavid du Colombier if(l == Nospeed) 1144154abd99SDavid du Colombier error("speed must be full|low|high"); 1145154abd99SDavid du Colombier nep = newdev(ep->hp, 0, 0); 1146154abd99SDavid du Colombier nep->dev->speed = l; 1147154abd99SDavid du Colombier if(nep->dev->speed != Lowspeed) 1148154abd99SDavid du Colombier nep->maxpkt = 64; /* assume full speed */ 1149154abd99SDavid du Colombier nep->dev->hub = d->nb; 1150154abd99SDavid du Colombier nep->dev->port = atoi(cb->f[2]); 1151154abd99SDavid du Colombier /* next read request will read 1152154abd99SDavid du Colombier * the name for the new endpoint 1153154abd99SDavid du Colombier */ 1154154abd99SDavid du Colombier l = sizeof(up->genbuf); 1155154abd99SDavid du Colombier snprint(up->genbuf, l, "ep%d.%d", nep->dev->nb, nep->nb); 1156154abd99SDavid du Colombier kstrdup(&c->aux, up->genbuf); 1157154abd99SDavid du Colombier break; 1158154abd99SDavid du Colombier case CMhub: 1159154abd99SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 1160154abd99SDavid du Colombier d->ishub = 1; 1161154abd99SDavid du Colombier break; 1162154abd99SDavid du Colombier case CMspeed: 1163154abd99SDavid du Colombier l = name2speed(cb->f[1]); 1164154abd99SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l); 1165154abd99SDavid du Colombier if(l == Nospeed) 1166154abd99SDavid du Colombier error("speed must be full|low|high"); 1167154abd99SDavid du Colombier qlock(ep->ep0); 1168154abd99SDavid du Colombier d->speed = l; 1169154abd99SDavid du Colombier qunlock(ep->ep0); 1170154abd99SDavid du Colombier break; 1171154abd99SDavid du Colombier case CMmaxpkt: 1172154abd99SDavid du Colombier l = strtoul(cb->f[1], nil, 0); 1173154abd99SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l); 1174154abd99SDavid du Colombier if(l < 1 || l > 1024) 1175154abd99SDavid du Colombier error("maxpkt not in [1:1024]"); 1176154abd99SDavid du Colombier qlock(ep); 1177154abd99SDavid du Colombier ep->maxpkt = l; 1178154abd99SDavid du Colombier qunlock(ep); 1179154abd99SDavid du Colombier break; 1180154abd99SDavid du Colombier case CMntds: 1181154abd99SDavid du Colombier l = strtoul(cb->f[1], nil, 0); 1182154abd99SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l); 1183154abd99SDavid du Colombier if(l < 1 || l > 3) 1184154abd99SDavid du Colombier error("ntds not in [1:3]"); 1185154abd99SDavid du Colombier qlock(ep); 1186154abd99SDavid du Colombier ep->ntds = l; 1187154abd99SDavid du Colombier qunlock(ep); 1188154abd99SDavid du Colombier break; 1189154abd99SDavid du Colombier case CMpollival: 1190154abd99SDavid du Colombier if(ep->ttype != Tintr && ep->ttype != Tiso) 1191154abd99SDavid du Colombier error("not an intr or iso endpoint"); 1192154abd99SDavid du Colombier l = strtoul(cb->f[1], nil, 0); 1193154abd99SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l); 1194154abd99SDavid du Colombier if(ep->ttype == Tiso || 1195154abd99SDavid du Colombier (ep->ttype == Tintr && ep->dev->speed == Highspeed)){ 1196154abd99SDavid du Colombier if(l < 1 || l > 16) 1197154abd99SDavid du Colombier error("pollival power not in [1:16]"); 1198154abd99SDavid du Colombier l = pow2(l-1); 1199154abd99SDavid du Colombier }else 1200154abd99SDavid du Colombier if(l < 1 || l > 255) 1201154abd99SDavid du Colombier error("pollival not in [1:255]"); 1202154abd99SDavid du Colombier qlock(ep); 1203154abd99SDavid du Colombier ep->pollival = l; 1204154abd99SDavid du Colombier if(ep->ttype == Tiso) 1205154abd99SDavid du Colombier setmaxpkt(ep, "pollival"); 1206154abd99SDavid du Colombier qunlock(ep); 1207154abd99SDavid du Colombier break; 1208154abd99SDavid du Colombier case CMsamplesz: 1209154abd99SDavid du Colombier if(ep->ttype != Tiso) 1210154abd99SDavid du Colombier error("not an iso endpoint"); 1211154abd99SDavid du Colombier l = strtoul(cb->f[1], nil, 0); 1212154abd99SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l); 1213154abd99SDavid du Colombier if(l <= 0 || l > 8) 1214154abd99SDavid du Colombier error("samplesz not in [1:8]"); 1215154abd99SDavid du Colombier qlock(ep); 1216154abd99SDavid du Colombier ep->samplesz = l; 1217154abd99SDavid du Colombier setmaxpkt(ep, "samplesz"); 1218154abd99SDavid du Colombier qunlock(ep); 1219154abd99SDavid du Colombier break; 1220154abd99SDavid du Colombier case CMhz: 1221154abd99SDavid du Colombier if(ep->ttype != Tiso) 1222154abd99SDavid du Colombier error("not an iso endpoint"); 1223154abd99SDavid du Colombier l = strtoul(cb->f[1], nil, 0); 1224154abd99SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l); 1225154abd99SDavid du Colombier if(l <= 0 || l > 100000) 1226154abd99SDavid du Colombier error("hz not in [1:100000]"); 1227154abd99SDavid du Colombier qlock(ep); 1228154abd99SDavid du Colombier ep->hz = l; 1229154abd99SDavid du Colombier setmaxpkt(ep, "hz"); 1230154abd99SDavid du Colombier qunlock(ep); 1231154abd99SDavid du Colombier break; 1232154abd99SDavid du Colombier case CMclrhalt: 1233154abd99SDavid du Colombier qlock(ep); 1234154abd99SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 1235154abd99SDavid du Colombier ep->clrhalt = 1; 1236154abd99SDavid du Colombier qunlock(ep); 1237154abd99SDavid du Colombier break; 1238154abd99SDavid du Colombier case CMinfo: 1239154abd99SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 1240154abd99SDavid du Colombier l = strlen(Info); 1241154abd99SDavid du Colombier s = a; 1242154abd99SDavid du Colombier if(n < l+2 || strncmp(Info, s, l) != 0) 1243154abd99SDavid du Colombier error(Ebadctl); 1244154abd99SDavid du Colombier if(n > 1024) 1245154abd99SDavid du Colombier n = 1024; 1246154abd99SDavid du Colombier b = smalloc(n); 1247154abd99SDavid du Colombier memmove(b, s+l, n-l); 1248154abd99SDavid du Colombier b[n-l] = 0; 1249154abd99SDavid du Colombier if(b[n-l-1] == '\n') 1250154abd99SDavid du Colombier b[n-l-1] = 0; 1251154abd99SDavid du Colombier qlock(ep); 1252154abd99SDavid du Colombier free(ep->info); 1253154abd99SDavid du Colombier ep->info = b; 1254154abd99SDavid du Colombier qunlock(ep); 1255154abd99SDavid du Colombier break; 1256154abd99SDavid du Colombier case CMaddress: 1257154abd99SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 1258154abd99SDavid du Colombier ep->dev->state = Denabled; 1259154abd99SDavid du Colombier break; 1260154abd99SDavid du Colombier case CMdetach: 1261154abd99SDavid du Colombier if(ep->dev->isroot != 0) 1262154abd99SDavid du Colombier error("can't detach a root hub"); 1263154abd99SDavid du Colombier deprint("usb epctl %s ep%d.%d\n", 1264154abd99SDavid du Colombier cb->f[0], ep->dev->nb, ep->nb); 1265154abd99SDavid du Colombier ep->dev->state = Ddetach; 1266154abd99SDavid du Colombier /* Release file system ref. for its endpoints */ 1267154abd99SDavid du Colombier for(i = 0; i < nelem(ep->dev->eps); i++) 1268154abd99SDavid du Colombier putep(ep->dev->eps[i]); 1269154abd99SDavid du Colombier break; 1270154abd99SDavid du Colombier case CMdebugep: 1271154abd99SDavid du Colombier if(strcmp(cb->f[1], "on") == 0) 1272154abd99SDavid du Colombier ep->debug = 1; 1273154abd99SDavid du Colombier else if(strcmp(cb->f[1], "off") == 0) 1274154abd99SDavid du Colombier ep->debug = 0; 1275154abd99SDavid du Colombier else 1276154abd99SDavid du Colombier ep->debug = strtoul(cb->f[1], nil, 0); 1277154abd99SDavid du Colombier print("usb: ep%d.%d debug %d\n", 1278154abd99SDavid du Colombier ep->dev->nb, ep->nb, ep->debug); 1279154abd99SDavid du Colombier break; 1280154abd99SDavid du Colombier case CMname: 1281154abd99SDavid du Colombier deprint("usb epctl %s %s\n", cb->f[0], cb->f[1]); 1282154abd99SDavid du Colombier validname(cb->f[1], 0); 1283154abd99SDavid du Colombier kstrdup(&ep->name, cb->f[1]); 1284154abd99SDavid du Colombier break; 1285cd52453fSDavid du Colombier case CMtmout: 1286cd52453fSDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 1287cd52453fSDavid du Colombier if(ep->ttype == Tiso || ep->ttype == Tctl) 1288cd52453fSDavid du Colombier error("ctl ignored for this endpoint type"); 1289cd52453fSDavid du Colombier ep->tmout = strtoul(cb->f[1], nil, 0); 1290cd52453fSDavid du Colombier if(ep->tmout != 0 && ep->tmout < Xfertmout) 1291cd52453fSDavid du Colombier ep->tmout = Xfertmout; 1292cd52453fSDavid du Colombier break; 1293cd52453fSDavid du Colombier case CMpreset: 1294cd52453fSDavid du Colombier deprint("usb epctl %s\n", cb->f[0]); 1295cd52453fSDavid du Colombier if(ep->ttype != Tctl) 1296cd52453fSDavid du Colombier error("not a control endpoint"); 1297cd52453fSDavid du Colombier if(ep->dev->state != Denabled) 1298cd52453fSDavid du Colombier error("forbidden on devices not enabled"); 1299cd52453fSDavid du Colombier ep->dev->state = Dreset; 1300cd52453fSDavid du Colombier break; 1301154abd99SDavid du Colombier default: 1302154abd99SDavid du Colombier panic("usb: unknown epctl %d", ct->index); 1303154abd99SDavid du Colombier } 1304154abd99SDavid du Colombier free(cb); 1305154abd99SDavid du Colombier poperror(); 1306154abd99SDavid du Colombier return n; 1307154abd99SDavid du Colombier } 1308154abd99SDavid du Colombier 1309154abd99SDavid du Colombier static long 1310154abd99SDavid du Colombier usbctl(void *a, long n) 1311154abd99SDavid du Colombier { 1312154abd99SDavid du Colombier Cmdtab *ct; 1313154abd99SDavid du Colombier Cmdbuf *cb; 1314154abd99SDavid du Colombier Ep *ep; 1315154abd99SDavid du Colombier int i; 1316154abd99SDavid du Colombier 1317154abd99SDavid du Colombier cb = parsecmd(a, n); 1318154abd99SDavid du Colombier if(waserror()){ 1319154abd99SDavid du Colombier free(cb); 1320154abd99SDavid du Colombier nexterror(); 1321154abd99SDavid du Colombier } 1322154abd99SDavid du Colombier ct = lookupcmd(cb, usbctls, nelem(usbctls)); 1323154abd99SDavid du Colombier dprint("usb ctl %s\n", cb->f[0]); 1324154abd99SDavid du Colombier switch(ct->index){ 1325154abd99SDavid du Colombier case CMdebug: 1326154abd99SDavid du Colombier if(strcmp(cb->f[1], "on") == 0) 1327154abd99SDavid du Colombier debug = 1; 1328154abd99SDavid du Colombier else if(strcmp(cb->f[1], "off") == 0) 1329154abd99SDavid du Colombier debug = 0; 1330154abd99SDavid du Colombier else 1331154abd99SDavid du Colombier debug = strtol(cb->f[1], nil, 0); 1332154abd99SDavid du Colombier print("usb: debug %d\n", debug); 1333154abd99SDavid du Colombier for(i = 0; i < epmax; i++) 1334154abd99SDavid du Colombier if((ep = getep(i)) != nil){ 1335154abd99SDavid du Colombier ep->hp->debug(ep->hp, debug); 1336154abd99SDavid du Colombier putep(ep); 1337154abd99SDavid du Colombier } 1338154abd99SDavid du Colombier break; 1339154abd99SDavid du Colombier case CMdump: 1340154abd99SDavid du Colombier dumpeps(); 1341154abd99SDavid du Colombier break; 1342154abd99SDavid du Colombier } 1343154abd99SDavid du Colombier free(cb); 1344154abd99SDavid du Colombier poperror(); 1345154abd99SDavid du Colombier return n; 1346154abd99SDavid du Colombier } 1347154abd99SDavid du Colombier 1348154abd99SDavid du Colombier static long 1349154abd99SDavid du Colombier ctlwrite(Chan *c, void *a, long n) 1350154abd99SDavid du Colombier { 1351154abd99SDavid du Colombier int q; 1352154abd99SDavid du Colombier Ep *ep; 1353154abd99SDavid du Colombier 1354154abd99SDavid du Colombier q = QID(c->qid); 1355154abd99SDavid du Colombier if(q == Qctl) 1356154abd99SDavid du Colombier return usbctl(a, n); 1357154abd99SDavid du Colombier 1358154abd99SDavid du Colombier ep = getep(qid2epidx(q)); 1359154abd99SDavid du Colombier if(ep == nil) 1360154abd99SDavid du Colombier error(Eio); 1361154abd99SDavid du Colombier if(waserror()){ 1362154abd99SDavid du Colombier putep(ep); 1363154abd99SDavid du Colombier nexterror(); 1364154abd99SDavid du Colombier } 1365154abd99SDavid du Colombier if(ep->dev->state == Ddetach) 1366154abd99SDavid du Colombier error(Edetach); 1367154abd99SDavid du Colombier if(isqtype(q, Qepctl) && c->aux != nil){ 1368154abd99SDavid du Colombier /* Be sure we don't keep a cloned ep name */ 1369154abd99SDavid du Colombier free(c->aux); 1370154abd99SDavid du Colombier c->aux = nil; 1371154abd99SDavid du Colombier error("read, not write, expected"); 1372154abd99SDavid du Colombier } 1373154abd99SDavid du Colombier n = epctl(ep, c, a, n); 1374154abd99SDavid du Colombier putep(ep); 1375154abd99SDavid du Colombier poperror(); 1376154abd99SDavid du Colombier return n; 1377154abd99SDavid du Colombier } 1378154abd99SDavid du Colombier 1379154abd99SDavid du Colombier static long 1380154abd99SDavid du Colombier usbwrite(Chan *c, void *a, long n, vlong off) 1381154abd99SDavid du Colombier { 1382c8a340cdSDavid du Colombier int nr, q; 1383154abd99SDavid du Colombier Ep *ep; 1384154abd99SDavid du Colombier 1385154abd99SDavid du Colombier if(c->qid.type == QTDIR) 1386154abd99SDavid du Colombier error(Eisdir); 1387154abd99SDavid du Colombier 1388154abd99SDavid du Colombier q = QID(c->qid); 1389154abd99SDavid du Colombier 1390154abd99SDavid du Colombier if(q == Qctl || isqtype(q, Qepctl)) 1391154abd99SDavid du Colombier return ctlwrite(c, a, n); 1392154abd99SDavid du Colombier 1393154abd99SDavid du Colombier ep = getep(qid2epidx(q)); 1394154abd99SDavid du Colombier if(ep == nil) 1395154abd99SDavid du Colombier error(Eio); 1396154abd99SDavid du Colombier if(waserror()){ 1397154abd99SDavid du Colombier putep(ep); 1398154abd99SDavid du Colombier nexterror(); 1399154abd99SDavid du Colombier } 1400154abd99SDavid du Colombier if(ep->dev->state == Ddetach) 1401154abd99SDavid du Colombier error(Edetach); 1402154abd99SDavid du Colombier if(ep->mode == OREAD || ep->inuse == 0) 1403154abd99SDavid du Colombier error(Ebadusefd); 1404154abd99SDavid du Colombier 1405154abd99SDavid du Colombier switch(ep->ttype){ 1406154abd99SDavid du Colombier case Tnone: 1407154abd99SDavid du Colombier error("endpoint not configured"); 1408154abd99SDavid du Colombier case Tctl: 1409154abd99SDavid du Colombier nr = rhubwrite(ep, a, n); 1410154abd99SDavid du Colombier if(nr >= 0){ 1411154abd99SDavid du Colombier n = nr; 1412154abd99SDavid du Colombier break; 1413154abd99SDavid du Colombier } 1414154abd99SDavid du Colombier /* else fall */ 1415154abd99SDavid du Colombier default: 1416154abd99SDavid du Colombier ddeprint("\nusbwrite q %#x fid %d cnt %ld off %lld\n",q, c->fid, n, off); 1417154abd99SDavid du Colombier ep->hp->epwrite(ep, a, n); 1418154abd99SDavid du Colombier } 1419154abd99SDavid du Colombier putep(ep); 1420154abd99SDavid du Colombier poperror(); 1421154abd99SDavid du Colombier return n; 1422154abd99SDavid du Colombier } 1423154abd99SDavid du Colombier 1424154abd99SDavid du Colombier void 1425154abd99SDavid du Colombier usbshutdown(void) 1426154abd99SDavid du Colombier { 1427cd52453fSDavid du Colombier Hci *hp; 1428154abd99SDavid du Colombier int i; 1429154abd99SDavid du Colombier 1430154abd99SDavid du Colombier for(i = 0; i < Nhcis; i++){ 1431cd52453fSDavid du Colombier hp = hcis[i]; 1432cd52453fSDavid du Colombier if(hp == nil) 1433154abd99SDavid du Colombier continue; 1434cd52453fSDavid du Colombier if(hp->shutdown == nil) 1435cd52453fSDavid du Colombier print("#u: no shutdown function for %s\n", hp->type); 1436cd52453fSDavid du Colombier else 1437cd52453fSDavid du Colombier hp->shutdown(hp); 1438154abd99SDavid du Colombier } 1439154abd99SDavid du Colombier } 1440154abd99SDavid du Colombier 1441154abd99SDavid du Colombier Dev usbdevtab = { 1442154abd99SDavid du Colombier L'u', 1443154abd99SDavid du Colombier "usb", 1444154abd99SDavid du Colombier 1445154abd99SDavid du Colombier usbreset, 1446154abd99SDavid du Colombier usbinit, 1447154abd99SDavid du Colombier usbshutdown, 1448154abd99SDavid du Colombier usbattach, 1449154abd99SDavid du Colombier usbwalk, 1450154abd99SDavid du Colombier usbstat, 1451154abd99SDavid du Colombier usbopen, 1452154abd99SDavid du Colombier devcreate, 1453154abd99SDavid du Colombier usbclose, 1454154abd99SDavid du Colombier usbread, 1455154abd99SDavid du Colombier devbread, 1456154abd99SDavid du Colombier usbwrite, 1457154abd99SDavid du Colombier devbwrite, 1458154abd99SDavid du Colombier devremove, 1459154abd99SDavid du Colombier devwstat, 1460154abd99SDavid du Colombier }; 1461