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