15d9de2d3SDavid du Colombier /*
25d9de2d3SDavid du Colombier * USB device driver framework.
35d9de2d3SDavid du Colombier *
45d9de2d3SDavid du Colombier * This is in charge of providing access to actual HCIs
55d9de2d3SDavid du Colombier * and providing I/O to the various endpoints of devices.
65d9de2d3SDavid du Colombier * A separate user program (usbd) is in charge of
75d9de2d3SDavid du Colombier * enumerating the bus, setting up endpoints and
85d9de2d3SDavid du Colombier * starting devices (also user programs).
95d9de2d3SDavid du Colombier *
105d9de2d3SDavid du Colombier * The interface provided is a violation of the standard:
115d9de2d3SDavid du Colombier * you're welcome.
125d9de2d3SDavid du Colombier *
135d9de2d3SDavid du Colombier * The interface consists of a root directory with several files
145d9de2d3SDavid du Colombier * plus a directory (epN.M) with two files per endpoint.
155d9de2d3SDavid du Colombier * A device is represented by its first endpoint, which
165d9de2d3SDavid du Colombier * is a control endpoint automatically allocated for each device.
175d9de2d3SDavid du Colombier * Device control endpoints may be used to create new endpoints.
185d9de2d3SDavid du Colombier * Devices corresponding to hubs may also allocate new devices,
195d9de2d3SDavid du Colombier * perhaps also hubs. Initially, a hub device is allocated for
205d9de2d3SDavid du Colombier * each controller present, to represent its root hub. Those can
215d9de2d3SDavid du Colombier * never be removed.
225d9de2d3SDavid du Colombier *
235d9de2d3SDavid du Colombier * All endpoints refer to the first endpoint (epN.0) of the device,
245d9de2d3SDavid du Colombier * which keeps per-device information, and also to the HCI used
255d9de2d3SDavid du Colombier * to reach them. Although all endpoints cache that information.
265d9de2d3SDavid du Colombier *
275d9de2d3SDavid du Colombier * epN.M/data files permit I/O and are considered DMEXCL.
285d9de2d3SDavid du Colombier * epN.M/ctl files provide status info and accept control requests.
295d9de2d3SDavid du Colombier *
305d9de2d3SDavid du Colombier * Endpoints may be given file names to be listed also at #u,
315d9de2d3SDavid du Colombier * for those drivers that have nothing to do after configuring the
325d9de2d3SDavid du Colombier * device and its endpoints.
335d9de2d3SDavid du Colombier *
345d9de2d3SDavid du Colombier * Drivers for different controllers are kept at usb[oue]hci.c
355d9de2d3SDavid du Colombier * It's likely we could factor out much from controllers into
365d9de2d3SDavid du Colombier * a generic controller driver, the problem is that details
375d9de2d3SDavid du Colombier * regarding how to handle toggles, tokens, Tds, etc. will
385d9de2d3SDavid du Colombier * get in the way. Thus, code is probably easier the way it is.
395d9de2d3SDavid du Colombier */
405d9de2d3SDavid du Colombier
415d9de2d3SDavid du Colombier #include "u.h"
425d9de2d3SDavid du Colombier #include "../port/lib.h"
435d9de2d3SDavid du Colombier #include "mem.h"
445d9de2d3SDavid du Colombier #include "dat.h"
455d9de2d3SDavid du Colombier #include "fns.h"
465d9de2d3SDavid du Colombier #include "io.h"
475d9de2d3SDavid du Colombier #include "../port/error.h"
485d9de2d3SDavid du Colombier #include "../port/usb.h"
495d9de2d3SDavid du Colombier
505d9de2d3SDavid du Colombier typedef struct Hcitype Hcitype;
515d9de2d3SDavid du Colombier
525d9de2d3SDavid du Colombier enum
535d9de2d3SDavid du Colombier {
545d9de2d3SDavid du Colombier /* Qid numbers */
555d9de2d3SDavid du Colombier Qdir = 0, /* #u */
565d9de2d3SDavid du Colombier Qusbdir, /* #u/usb */
575d9de2d3SDavid du Colombier Qctl, /* #u/usb/ctl - control requests */
585d9de2d3SDavid du Colombier
595d9de2d3SDavid du Colombier Qep0dir, /* #u/usb/ep0.0 - endpoint 0 dir */
605d9de2d3SDavid du Colombier Qep0io, /* #u/usb/ep0.0/data - endpoint 0 I/O */
615d9de2d3SDavid du Colombier Qep0ctl, /* #u/usb/ep0.0/ctl - endpoint 0 ctl. */
625d9de2d3SDavid du Colombier Qep0dummy, /* give 4 qids to each endpoint */
635d9de2d3SDavid du Colombier
645d9de2d3SDavid du Colombier Qepdir = 0, /* (qid-qep0dir)&3 is one of these */
655d9de2d3SDavid du Colombier Qepio, /* to identify which file for the endpoint */
665d9de2d3SDavid du Colombier Qepctl,
675d9de2d3SDavid du Colombier
685d9de2d3SDavid du Colombier /* ... */
695d9de2d3SDavid du Colombier
705d9de2d3SDavid du Colombier /* Usb ctls. */
715d9de2d3SDavid du Colombier CMdebug = 0, /* debug on|off */
725d9de2d3SDavid du Colombier CMdump, /* dump (data structures for debug) */
735d9de2d3SDavid du Colombier
745d9de2d3SDavid du Colombier /* Ep. ctls */
755d9de2d3SDavid du Colombier CMnew = 0, /* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */
765d9de2d3SDavid du Colombier CMnewdev, /* newdev full|low|high portnb (allocate new devices) */
775d9de2d3SDavid du Colombier CMhub, /* hub (set the device as a hub) */
785d9de2d3SDavid du Colombier CMspeed, /* speed full|low|high|no */
795d9de2d3SDavid du Colombier CMmaxpkt, /* maxpkt size */
805d9de2d3SDavid du Colombier CMntds, /* ntds nb (max nb. of tds per µframe) */
815d9de2d3SDavid du Colombier CMclrhalt, /* clrhalt (halt was cleared on endpoint) */
825d9de2d3SDavid du Colombier CMpollival, /* pollival interval (interrupt/iso) */
835d9de2d3SDavid du Colombier CMhz, /* hz n (samples/sec; iso) */
845d9de2d3SDavid du Colombier CMsamplesz, /* samplesz n (sample size; iso) */
855d9de2d3SDavid du Colombier CMinfo, /* info infostr (ke.ep info for humans) */
865d9de2d3SDavid du Colombier CMdetach, /* detach (abort I/O forever on this ep). */
875d9de2d3SDavid du Colombier CMaddress, /* address (address is assigned) */
885d9de2d3SDavid du Colombier CMdebugep, /* debug n (set/clear debug for this ep) */
895d9de2d3SDavid du Colombier CMname, /* name str (show up as #u/name as well) */
905d9de2d3SDavid du Colombier CMtmout, /* timeout n (activate timeouts for ep) */
915d9de2d3SDavid du Colombier CMpreset, /* reset the port */
925d9de2d3SDavid du Colombier
935d9de2d3SDavid du Colombier /* Hub feature selectors */
945d9de2d3SDavid du Colombier Rportenable = 1,
955d9de2d3SDavid du Colombier Rportreset = 4,
965d9de2d3SDavid du Colombier
975d9de2d3SDavid du Colombier };
985d9de2d3SDavid du Colombier
995d9de2d3SDavid du Colombier struct Hcitype
1005d9de2d3SDavid du Colombier {
1015d9de2d3SDavid du Colombier char* type;
1025d9de2d3SDavid du Colombier int (*reset)(Hci*);
1035d9de2d3SDavid du Colombier };
1045d9de2d3SDavid du Colombier
1055d9de2d3SDavid du Colombier #define QID(q) ((int)(q).path)
1065d9de2d3SDavid du Colombier
1075d9de2d3SDavid du Colombier static Cmdtab usbctls[] =
1085d9de2d3SDavid du Colombier {
1095d9de2d3SDavid du Colombier {CMdebug, "debug", 2},
1105d9de2d3SDavid du Colombier {CMdump, "dump", 1},
1115d9de2d3SDavid du Colombier };
1125d9de2d3SDavid du Colombier
1135d9de2d3SDavid du Colombier static Cmdtab epctls[] =
1145d9de2d3SDavid du Colombier {
1155d9de2d3SDavid du Colombier {CMnew, "new", 4},
1165d9de2d3SDavid du Colombier {CMnewdev, "newdev", 3},
1175d9de2d3SDavid du Colombier {CMhub, "hub", 1},
1185d9de2d3SDavid du Colombier {CMspeed, "speed", 2},
1195d9de2d3SDavid du Colombier {CMmaxpkt, "maxpkt", 2},
1205d9de2d3SDavid du Colombier {CMntds, "ntds", 2},
1215d9de2d3SDavid du Colombier {CMpollival, "pollival", 2},
1225d9de2d3SDavid du Colombier {CMsamplesz, "samplesz", 2},
1235d9de2d3SDavid du Colombier {CMhz, "hz", 2},
1245d9de2d3SDavid du Colombier {CMinfo, "info", 0},
1255d9de2d3SDavid du Colombier {CMdetach, "detach", 1},
1265d9de2d3SDavid du Colombier {CMaddress, "address", 1},
1275d9de2d3SDavid du Colombier {CMdebugep, "debug", 2},
1285d9de2d3SDavid du Colombier {CMclrhalt, "clrhalt", 1},
1295d9de2d3SDavid du Colombier {CMname, "name", 2},
1305d9de2d3SDavid du Colombier {CMtmout, "timeout", 2},
1315d9de2d3SDavid du Colombier {CMpreset, "reset", 1},
1325d9de2d3SDavid du Colombier };
1335d9de2d3SDavid du Colombier
1345d9de2d3SDavid du Colombier static Dirtab usbdir[] =
1355d9de2d3SDavid du Colombier {
1365d9de2d3SDavid du Colombier "ctl", {Qctl}, 0, 0666,
1375d9de2d3SDavid du Colombier };
1385d9de2d3SDavid du Colombier
1395d9de2d3SDavid du Colombier char *usbmodename[] =
1405d9de2d3SDavid du Colombier {
1415d9de2d3SDavid du Colombier [OREAD] "r",
1425d9de2d3SDavid du Colombier [OWRITE] "w",
1435d9de2d3SDavid du Colombier [ORDWR] "rw",
1445d9de2d3SDavid du Colombier };
1455d9de2d3SDavid du Colombier
1465d9de2d3SDavid du Colombier static char *ttname[] =
1475d9de2d3SDavid du Colombier {
1485d9de2d3SDavid du Colombier [Tnone] "none",
1495d9de2d3SDavid du Colombier [Tctl] "control",
1505d9de2d3SDavid du Colombier [Tiso] "iso",
1515d9de2d3SDavid du Colombier [Tintr] "interrupt",
1525d9de2d3SDavid du Colombier [Tbulk] "bulk",
1535d9de2d3SDavid du Colombier };
1545d9de2d3SDavid du Colombier
1555d9de2d3SDavid du Colombier static char *spname[] =
1565d9de2d3SDavid du Colombier {
1575d9de2d3SDavid du Colombier [Fullspeed] "full",
1585d9de2d3SDavid du Colombier [Lowspeed] "low",
1595d9de2d3SDavid du Colombier [Highspeed] "high",
1605d9de2d3SDavid du Colombier [Nospeed] "no",
1615d9de2d3SDavid du Colombier };
1625d9de2d3SDavid du Colombier
1635d9de2d3SDavid du Colombier static int debug;
1645d9de2d3SDavid du Colombier static Hcitype hcitypes[Nhcis];
1655d9de2d3SDavid du Colombier static Hci* hcis[Nhcis];
1665d9de2d3SDavid du Colombier static QLock epslck; /* add, del, lookup endpoints */
1675d9de2d3SDavid du Colombier static Ep* eps[Neps]; /* all endpoints known */
1685d9de2d3SDavid du Colombier static int epmax; /* 1 + last endpoint index used */
1695d9de2d3SDavid du Colombier static int usbidgen; /* device address generator */
1705d9de2d3SDavid du Colombier
1715d9de2d3SDavid du Colombier /*
1725d9de2d3SDavid du Colombier * Is there something like this in a library? should it be?
1735d9de2d3SDavid du Colombier */
1745d9de2d3SDavid du Colombier char*
seprintdata(char * s,char * se,uchar * d,int n)1755d9de2d3SDavid du Colombier seprintdata(char *s, char *se, uchar *d, int n)
1765d9de2d3SDavid du Colombier {
1775d9de2d3SDavid du Colombier int i, l;
1785d9de2d3SDavid du Colombier
1795d9de2d3SDavid du Colombier s = seprint(s, se, " %#p[%d]: ", d, n);
1805d9de2d3SDavid du Colombier l = n;
1815d9de2d3SDavid du Colombier if(l > 10)
1825d9de2d3SDavid du Colombier l = 10;
1835d9de2d3SDavid du Colombier for(i=0; i<l; i++)
1845d9de2d3SDavid du Colombier s = seprint(s, se, " %2.2ux", d[i]);
1855d9de2d3SDavid du Colombier if(l < n)
1865d9de2d3SDavid du Colombier s = seprint(s, se, "...");
1875d9de2d3SDavid du Colombier return s;
1885d9de2d3SDavid du Colombier }
1895d9de2d3SDavid du Colombier
1905d9de2d3SDavid du Colombier static int
name2speed(char * name)1915d9de2d3SDavid du Colombier name2speed(char *name)
1925d9de2d3SDavid du Colombier {
1935d9de2d3SDavid du Colombier int i;
1945d9de2d3SDavid du Colombier
1955d9de2d3SDavid du Colombier for(i = 0; i < nelem(spname); i++)
1965d9de2d3SDavid du Colombier if(strcmp(name, spname[i]) == 0)
1975d9de2d3SDavid du Colombier return i;
1985d9de2d3SDavid du Colombier return Nospeed;
1995d9de2d3SDavid du Colombier }
2005d9de2d3SDavid du Colombier
2015d9de2d3SDavid du Colombier static int
name2ttype(char * name)2025d9de2d3SDavid du Colombier name2ttype(char *name)
2035d9de2d3SDavid du Colombier {
2045d9de2d3SDavid du Colombier int i;
2055d9de2d3SDavid du Colombier
2065d9de2d3SDavid du Colombier for(i = 0; i < nelem(ttname); i++)
2075d9de2d3SDavid du Colombier if(strcmp(name, ttname[i]) == 0)
2085d9de2d3SDavid du Colombier return i;
2095d9de2d3SDavid du Colombier /* may be a std. USB ep. type */
2105d9de2d3SDavid du Colombier i = strtol(name, nil, 0);
2115d9de2d3SDavid du Colombier switch(i+1){
2125d9de2d3SDavid du Colombier case Tctl:
2135d9de2d3SDavid du Colombier case Tiso:
2145d9de2d3SDavid du Colombier case Tbulk:
2155d9de2d3SDavid du Colombier case Tintr:
2165d9de2d3SDavid du Colombier return i+1;
2175d9de2d3SDavid du Colombier default:
2185d9de2d3SDavid du Colombier return Tnone;
2195d9de2d3SDavid du Colombier }
2205d9de2d3SDavid du Colombier }
2215d9de2d3SDavid du Colombier
2225d9de2d3SDavid du Colombier static int
name2mode(char * mode)2235d9de2d3SDavid du Colombier name2mode(char *mode)
2245d9de2d3SDavid du Colombier {
2255d9de2d3SDavid du Colombier int i;
2265d9de2d3SDavid du Colombier
2275d9de2d3SDavid du Colombier for(i = 0; i < nelem(usbmodename); i++)
2285d9de2d3SDavid du Colombier if(strcmp(mode, usbmodename[i]) == 0)
2295d9de2d3SDavid du Colombier return i;
2305d9de2d3SDavid du Colombier return -1;
2315d9de2d3SDavid du Colombier }
2325d9de2d3SDavid du Colombier
2335d9de2d3SDavid du Colombier static int
qid2epidx(int q)2345d9de2d3SDavid du Colombier qid2epidx(int q)
2355d9de2d3SDavid du Colombier {
2365d9de2d3SDavid du Colombier q = (q-Qep0dir)/4;
2375d9de2d3SDavid du Colombier if(q < 0 || q >= epmax || eps[q] == nil)
2385d9de2d3SDavid du Colombier return -1;
2395d9de2d3SDavid du Colombier return q;
2405d9de2d3SDavid du Colombier }
2415d9de2d3SDavid du Colombier
2425d9de2d3SDavid du Colombier static int
isqtype(int q,int type)2435d9de2d3SDavid du Colombier isqtype(int q, int type)
2445d9de2d3SDavid du Colombier {
2455d9de2d3SDavid du Colombier if(q < Qep0dir)
2465d9de2d3SDavid du Colombier return 0;
2475d9de2d3SDavid du Colombier q -= Qep0dir;
2485d9de2d3SDavid du Colombier return (q & 3) == type;
2495d9de2d3SDavid du Colombier }
2505d9de2d3SDavid du Colombier
2515d9de2d3SDavid du Colombier void
addhcitype(char * t,int (* r)(Hci *))2525d9de2d3SDavid du Colombier addhcitype(char* t, int (*r)(Hci*))
2535d9de2d3SDavid du Colombier {
2545d9de2d3SDavid du Colombier static int ntype;
2555d9de2d3SDavid du Colombier
2565d9de2d3SDavid du Colombier if(ntype == Nhcis)
2575d9de2d3SDavid du Colombier panic("too many USB host interface types");
2585d9de2d3SDavid du Colombier hcitypes[ntype].type = t;
2595d9de2d3SDavid du Colombier hcitypes[ntype].reset = r;
2605d9de2d3SDavid du Colombier ntype++;
2615d9de2d3SDavid du Colombier }
2625d9de2d3SDavid du Colombier
2635d9de2d3SDavid du Colombier static char*
seprintep(char * s,char * se,Ep * ep,int all)2645d9de2d3SDavid du Colombier seprintep(char *s, char *se, Ep *ep, int all)
2655d9de2d3SDavid du Colombier {
2665d9de2d3SDavid du Colombier static char* dsnames[] = { "config", "enabled", "detached", "reset" };
267*b4d1cf41SDavid du Colombier Udev *d;
268*b4d1cf41SDavid du Colombier int i;
269*b4d1cf41SDavid du Colombier int di;
2705d9de2d3SDavid du Colombier
2715d9de2d3SDavid du Colombier d = ep->dev;
2725d9de2d3SDavid du Colombier
2735d9de2d3SDavid du Colombier qlock(ep);
2745d9de2d3SDavid du Colombier if(waserror()){
2755d9de2d3SDavid du Colombier qunlock(ep);
2765d9de2d3SDavid du Colombier nexterror();
2775d9de2d3SDavid du Colombier }
2785d9de2d3SDavid du Colombier di = ep->dev->nb;
2795d9de2d3SDavid du Colombier if(all)
2805d9de2d3SDavid du Colombier s = seprint(s, se, "dev %d ep %d ", di, ep->nb);
2815d9de2d3SDavid du Colombier s = seprint(s, se, "%s", dsnames[ep->dev->state]);
2825d9de2d3SDavid du Colombier s = seprint(s, se, " %s", ttname[ep->ttype]);
2835d9de2d3SDavid du Colombier assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR);
2845d9de2d3SDavid du Colombier s = seprint(s, se, " %s", usbmodename[ep->mode]);
2855d9de2d3SDavid du Colombier s = seprint(s, se, " speed %s", spname[d->speed]);
2865d9de2d3SDavid du Colombier s = seprint(s, se, " maxpkt %ld", ep->maxpkt);
2875d9de2d3SDavid du Colombier s = seprint(s, se, " pollival %ld", ep->pollival);
2885d9de2d3SDavid du Colombier s = seprint(s, se, " samplesz %ld", ep->samplesz);
2895d9de2d3SDavid du Colombier s = seprint(s, se, " hz %ld", ep->hz);
2905d9de2d3SDavid du Colombier s = seprint(s, se, " hub %d", ep->dev->hub);
2915d9de2d3SDavid du Colombier s = seprint(s, se, " port %d", ep->dev->port);
2925d9de2d3SDavid du Colombier if(ep->inuse)
2935d9de2d3SDavid du Colombier s = seprint(s, se, " busy");
2945d9de2d3SDavid du Colombier else
2955d9de2d3SDavid du Colombier s = seprint(s, se, " idle");
2965d9de2d3SDavid du Colombier if(all){
2975d9de2d3SDavid du Colombier s = seprint(s, se, " load %uld", ep->load);
2985d9de2d3SDavid du Colombier s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep);
2995d9de2d3SDavid du Colombier s = seprint(s, se, " idx %d", ep->idx);
3005d9de2d3SDavid du Colombier if(ep->name != nil)
3015d9de2d3SDavid du Colombier s = seprint(s, se, " name '%s'", ep->name);
3025d9de2d3SDavid du Colombier if(ep->tmout != 0)
3035d9de2d3SDavid du Colombier s = seprint(s, se, " tmout");
3045d9de2d3SDavid du Colombier if(ep == ep->ep0){
3055d9de2d3SDavid du Colombier s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno);
3065d9de2d3SDavid du Colombier s = seprint(s, se, " eps:");
3075d9de2d3SDavid du Colombier for(i = 0; i < nelem(d->eps); i++)
3085d9de2d3SDavid du Colombier if(d->eps[i] != nil)
3095d9de2d3SDavid du Colombier s = seprint(s, se, " ep%d.%d", di, i);
3105d9de2d3SDavid du Colombier }
3115d9de2d3SDavid du Colombier }
3125d9de2d3SDavid du Colombier if(ep->info != nil)
3135d9de2d3SDavid du Colombier s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type);
3145d9de2d3SDavid du Colombier else
3155d9de2d3SDavid du Colombier s = seprint(s, se, "\n");
3165d9de2d3SDavid du Colombier qunlock(ep);
3175d9de2d3SDavid du Colombier poperror();
3185d9de2d3SDavid du Colombier return s;
3195d9de2d3SDavid du Colombier }
3205d9de2d3SDavid du Colombier
3215d9de2d3SDavid du Colombier static Ep*
epalloc(Hci * hp)3225d9de2d3SDavid du Colombier epalloc(Hci *hp)
3235d9de2d3SDavid du Colombier {
3245d9de2d3SDavid du Colombier Ep *ep;
3255d9de2d3SDavid du Colombier int i;
3265d9de2d3SDavid du Colombier
3275d9de2d3SDavid du Colombier ep = smalloc(sizeof(Ep));
3285d9de2d3SDavid du Colombier ep->ref = 1;
3295d9de2d3SDavid du Colombier qlock(&epslck);
3305d9de2d3SDavid du Colombier for(i = 0; i < Neps; i++)
3315d9de2d3SDavid du Colombier if(eps[i] == nil)
3325d9de2d3SDavid du Colombier break;
3335d9de2d3SDavid du Colombier if(i == Neps){
3345d9de2d3SDavid du Colombier qunlock(&epslck);
3355d9de2d3SDavid du Colombier free(ep);
3365d9de2d3SDavid du Colombier print("usb: bug: too few endpoints.\n");
3375d9de2d3SDavid du Colombier return nil;
3385d9de2d3SDavid du Colombier }
3395d9de2d3SDavid du Colombier ep->idx = i;
3405d9de2d3SDavid du Colombier if(epmax <= i)
3415d9de2d3SDavid du Colombier epmax = i+1;
3425d9de2d3SDavid du Colombier eps[i] = ep;
3435d9de2d3SDavid du Colombier ep->hp = hp;
3445d9de2d3SDavid du Colombier ep->maxpkt = 8;
3455d9de2d3SDavid du Colombier ep->ntds = 1;
3465d9de2d3SDavid du Colombier ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */
3475d9de2d3SDavid du Colombier qunlock(&epslck);
3485d9de2d3SDavid du Colombier return ep;
3495d9de2d3SDavid du Colombier }
3505d9de2d3SDavid du Colombier
3515d9de2d3SDavid du Colombier static Ep*
getep(int i)3525d9de2d3SDavid du Colombier getep(int i)
3535d9de2d3SDavid du Colombier {
3545d9de2d3SDavid du Colombier Ep *ep;
3555d9de2d3SDavid du Colombier
3565d9de2d3SDavid du Colombier if(i < 0 || i >= epmax || eps[i] == nil)
3575d9de2d3SDavid du Colombier return nil;
3585d9de2d3SDavid du Colombier qlock(&epslck);
3595d9de2d3SDavid du Colombier ep = eps[i];
3605d9de2d3SDavid du Colombier if(ep != nil)
3615d9de2d3SDavid du Colombier incref(ep);
3625d9de2d3SDavid du Colombier qunlock(&epslck);
3635d9de2d3SDavid du Colombier return ep;
3645d9de2d3SDavid du Colombier }
3655d9de2d3SDavid du Colombier
3665d9de2d3SDavid du Colombier static void
putep(Ep * ep)3675d9de2d3SDavid du Colombier putep(Ep *ep)
3685d9de2d3SDavid du Colombier {
3695d9de2d3SDavid du Colombier Udev *d;
3705d9de2d3SDavid du Colombier
3715d9de2d3SDavid du Colombier if(ep != nil && decref(ep) == 0){
3725d9de2d3SDavid du Colombier d = ep->dev;
3735d9de2d3SDavid du Colombier deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep);
3745d9de2d3SDavid du Colombier qlock(&epslck);
3755d9de2d3SDavid du Colombier eps[ep->idx] = nil;
3765d9de2d3SDavid du Colombier if(ep->idx == epmax-1)
3775d9de2d3SDavid du Colombier epmax--;
3785d9de2d3SDavid du Colombier if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen)
3795d9de2d3SDavid du Colombier usbidgen--;
3805d9de2d3SDavid du Colombier qunlock(&epslck);
3815d9de2d3SDavid du Colombier if(d != nil){
3825d9de2d3SDavid du Colombier qlock(ep->ep0);
3835d9de2d3SDavid du Colombier d->eps[ep->nb] = nil;
3845d9de2d3SDavid du Colombier qunlock(ep->ep0);
3855d9de2d3SDavid du Colombier }
3865d9de2d3SDavid du Colombier if(ep->ep0 != ep){
3875d9de2d3SDavid du Colombier putep(ep->ep0);
3885d9de2d3SDavid du Colombier ep->ep0 = nil;
3895d9de2d3SDavid du Colombier }
3905d9de2d3SDavid du Colombier free(ep->info);
3915d9de2d3SDavid du Colombier free(ep->name);
3925d9de2d3SDavid du Colombier free(ep);
3935d9de2d3SDavid du Colombier }
3945d9de2d3SDavid du Colombier }
3955d9de2d3SDavid du Colombier
3965d9de2d3SDavid du Colombier static void
dumpeps(void)3975d9de2d3SDavid du Colombier dumpeps(void)
3985d9de2d3SDavid du Colombier {
3995d9de2d3SDavid du Colombier int i;
4005d9de2d3SDavid du Colombier static char buf[512];
401*b4d1cf41SDavid du Colombier char *s;
402*b4d1cf41SDavid du Colombier char *e;
403*b4d1cf41SDavid du Colombier Ep *ep;
4045d9de2d3SDavid du Colombier
4055d9de2d3SDavid du Colombier print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps);
4065d9de2d3SDavid du Colombier for(i = 0; i < epmax; i++){
4075d9de2d3SDavid du Colombier s = buf;
4085d9de2d3SDavid du Colombier e = buf+sizeof(buf);
4095d9de2d3SDavid du Colombier ep = getep(i);
4105d9de2d3SDavid du Colombier if(ep != nil){
4115d9de2d3SDavid du Colombier if(waserror()){
4125d9de2d3SDavid du Colombier putep(ep);
4135d9de2d3SDavid du Colombier nexterror();
4145d9de2d3SDavid du Colombier }
4155d9de2d3SDavid du Colombier s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb);
4165d9de2d3SDavid du Colombier seprintep(s, e, ep, 1);
4175d9de2d3SDavid du Colombier print("%s", buf);
4185d9de2d3SDavid du Colombier ep->hp->seprintep(buf, e, ep);
4195d9de2d3SDavid du Colombier print("%s", buf);
4205d9de2d3SDavid du Colombier poperror();
4215d9de2d3SDavid du Colombier putep(ep);
4225d9de2d3SDavid du Colombier }
4235d9de2d3SDavid du Colombier }
4245d9de2d3SDavid du Colombier print("usb dump hcis:\n");
4255d9de2d3SDavid du Colombier for(i = 0; i < Nhcis; i++)
4265d9de2d3SDavid du Colombier if(hcis[i] != nil)
4275d9de2d3SDavid du Colombier hcis[i]->dump(hcis[i]);
4285d9de2d3SDavid du Colombier }
4295d9de2d3SDavid du Colombier
4305d9de2d3SDavid du Colombier static int
newusbid(Hci *)4315d9de2d3SDavid du Colombier newusbid(Hci *)
4325d9de2d3SDavid du Colombier {
4335d9de2d3SDavid du Colombier int id;
4345d9de2d3SDavid du Colombier
4355d9de2d3SDavid du Colombier qlock(&epslck);
4365d9de2d3SDavid du Colombier id = ++usbidgen;
4375d9de2d3SDavid du Colombier if(id >= 0x7F)
4385d9de2d3SDavid du Colombier print("#u: too many device addresses; reuse them more\n");
4395d9de2d3SDavid du Colombier qunlock(&epslck);
4405d9de2d3SDavid du Colombier return id;
4415d9de2d3SDavid du Colombier }
4425d9de2d3SDavid du Colombier
4435d9de2d3SDavid du Colombier /*
4445d9de2d3SDavid du Colombier * Create endpoint 0 for a new device
4455d9de2d3SDavid du Colombier */
4465d9de2d3SDavid du Colombier static Ep*
newdev(Hci * hp,int ishub,int isroot)4475d9de2d3SDavid du Colombier newdev(Hci *hp, int ishub, int isroot)
4485d9de2d3SDavid du Colombier {
4495d9de2d3SDavid du Colombier Ep *ep;
4505d9de2d3SDavid du Colombier Udev *d;
4515d9de2d3SDavid du Colombier
4525d9de2d3SDavid du Colombier ep = epalloc(hp);
4535d9de2d3SDavid du Colombier d = ep->dev = smalloc(sizeof(Udev));
4545d9de2d3SDavid du Colombier d->nb = newusbid(hp);
4555d9de2d3SDavid du Colombier d->eps[0] = ep;
4565d9de2d3SDavid du Colombier ep->nb = 0;
4575d9de2d3SDavid du Colombier ep->toggle[0] = ep->toggle[1] = 0;
4585d9de2d3SDavid du Colombier d->ishub = ishub;
4595d9de2d3SDavid du Colombier d->isroot = isroot;
4605d9de2d3SDavid du Colombier if(hp->highspeed != 0)
4615d9de2d3SDavid du Colombier d->speed = Highspeed;
4625d9de2d3SDavid du Colombier else
4635d9de2d3SDavid du Colombier d->speed = Fullspeed;
4645d9de2d3SDavid du Colombier d->state = Dconfig; /* address not yet set */
4655d9de2d3SDavid du Colombier ep->dev = d;
4665d9de2d3SDavid du Colombier ep->ep0 = ep; /* no ref counted here */
4675d9de2d3SDavid du Colombier ep->ttype = Tctl;
4685d9de2d3SDavid du Colombier ep->tmout = Xfertmout;
4695d9de2d3SDavid du Colombier ep->mode = ORDWR;
4705d9de2d3SDavid du Colombier dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep);
4715d9de2d3SDavid du Colombier return ep;
4725d9de2d3SDavid du Colombier }
4735d9de2d3SDavid du Colombier
4745d9de2d3SDavid du Colombier /*
4755d9de2d3SDavid du Colombier * Create a new endpoint for the device
4765d9de2d3SDavid du Colombier * accessed via the given endpoint 0.
4775d9de2d3SDavid du Colombier */
4785d9de2d3SDavid du Colombier static Ep*
newdevep(Ep * ep,int i,int tt,int mode)4795d9de2d3SDavid du Colombier newdevep(Ep *ep, int i, int tt, int mode)
4805d9de2d3SDavid du Colombier {
4815d9de2d3SDavid du Colombier Ep *nep;
4825d9de2d3SDavid du Colombier Udev *d;
4835d9de2d3SDavid du Colombier
4845d9de2d3SDavid du Colombier d = ep->dev;
4855d9de2d3SDavid du Colombier if(d->eps[i] != nil)
4865d9de2d3SDavid du Colombier error("endpoint already in use");
4875d9de2d3SDavid du Colombier nep = epalloc(ep->hp);
4885d9de2d3SDavid du Colombier incref(ep);
4895d9de2d3SDavid du Colombier d->eps[i] = nep;
4905d9de2d3SDavid du Colombier nep->nb = i;
4915d9de2d3SDavid du Colombier nep->toggle[0] = nep->toggle[1] = 0;
4925d9de2d3SDavid du Colombier nep->ep0 = ep;
4935d9de2d3SDavid du Colombier nep->dev = ep->dev;
4945d9de2d3SDavid du Colombier nep->mode = mode;
4955d9de2d3SDavid du Colombier nep->ttype = tt;
4965d9de2d3SDavid du Colombier nep->debug = ep->debug;
4975d9de2d3SDavid du Colombier /* set defaults */
4985d9de2d3SDavid du Colombier switch(tt){
4995d9de2d3SDavid du Colombier case Tctl:
5005d9de2d3SDavid du Colombier nep->tmout = Xfertmout;
5015d9de2d3SDavid du Colombier break;
5025d9de2d3SDavid du Colombier case Tintr:
5035d9de2d3SDavid du Colombier nep->pollival = 10;
5045d9de2d3SDavid du Colombier break;
5055d9de2d3SDavid du Colombier case Tiso:
5065d9de2d3SDavid du Colombier nep->tmout = Xfertmout;
5075d9de2d3SDavid du Colombier nep->pollival = 10;
5085d9de2d3SDavid du Colombier nep->samplesz = 4;
5095d9de2d3SDavid du Colombier nep->hz = 44100;
5105d9de2d3SDavid du Colombier break;
5115d9de2d3SDavid du Colombier }
5125d9de2d3SDavid du Colombier deprint("newdevep ep%d.%d %#p\n", d->nb, nep->nb, nep);
5135d9de2d3SDavid du Colombier return ep;
5145d9de2d3SDavid du Colombier }
5155d9de2d3SDavid du Colombier
5165d9de2d3SDavid du Colombier static int
epdataperm(int mode)5175d9de2d3SDavid du Colombier epdataperm(int mode)
5185d9de2d3SDavid du Colombier {
519*b4d1cf41SDavid du Colombier
5205d9de2d3SDavid du Colombier switch(mode){
5215d9de2d3SDavid du Colombier case OREAD:
5225d9de2d3SDavid du Colombier return 0440|DMEXCL;
5235d9de2d3SDavid du Colombier break;
5245d9de2d3SDavid du Colombier case OWRITE:
5255d9de2d3SDavid du Colombier return 0220|DMEXCL;
5265d9de2d3SDavid du Colombier break;
5275d9de2d3SDavid du Colombier default:
5285d9de2d3SDavid du Colombier return 0660|DMEXCL;
5295d9de2d3SDavid du Colombier }
5305d9de2d3SDavid du Colombier }
5315d9de2d3SDavid du Colombier
5325d9de2d3SDavid du Colombier static int
usbgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)5335d9de2d3SDavid du Colombier usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
5345d9de2d3SDavid du Colombier {
5355d9de2d3SDavid du Colombier Qid q;
536*b4d1cf41SDavid du Colombier Dirtab *dir;
537*b4d1cf41SDavid du Colombier int perm;
538*b4d1cf41SDavid du Colombier char *se;
539*b4d1cf41SDavid du Colombier Ep *ep;
540*b4d1cf41SDavid du Colombier int nb;
541*b4d1cf41SDavid du Colombier int mode;
5425d9de2d3SDavid du Colombier
5435d9de2d3SDavid du Colombier if(0)ddprint("usbgen q %#x s %d...", QID(c->qid), s);
5445d9de2d3SDavid du Colombier if(s == DEVDOTDOT){
5455d9de2d3SDavid du Colombier if(QID(c->qid) <= Qusbdir){
5465d9de2d3SDavid du Colombier mkqid(&q, Qdir, 0, QTDIR);
5475d9de2d3SDavid du Colombier devdir(c, q, "#u", 0, eve, 0555, dp);
5485d9de2d3SDavid du Colombier }else{
5495d9de2d3SDavid du Colombier mkqid(&q, Qusbdir, 0, QTDIR);
5505d9de2d3SDavid du Colombier devdir(c, q, "usb", 0, eve, 0555, dp);
5515d9de2d3SDavid du Colombier }
5525d9de2d3SDavid du Colombier if(0)ddprint("ok\n");
5535d9de2d3SDavid du Colombier return 1;
5545d9de2d3SDavid du Colombier }
5555d9de2d3SDavid du Colombier
5565d9de2d3SDavid du Colombier switch(QID(c->qid)){
5575d9de2d3SDavid du Colombier case Qdir: /* list #u */
5585d9de2d3SDavid du Colombier if(s == 0){
5595d9de2d3SDavid du Colombier mkqid(&q, Qusbdir, 0, QTDIR);
5605d9de2d3SDavid du Colombier devdir(c, q, "usb", 0, eve, 0555, dp);
5615d9de2d3SDavid du Colombier if(0)ddprint("ok\n");
5625d9de2d3SDavid du Colombier return 1;
5635d9de2d3SDavid du Colombier }
5645d9de2d3SDavid du Colombier s--;
5655d9de2d3SDavid du Colombier if(s < 0 || s >= epmax)
5665d9de2d3SDavid du Colombier goto Fail;
5675d9de2d3SDavid du Colombier ep = getep(s);
5685d9de2d3SDavid du Colombier if(ep == nil || ep->name == nil){
5695d9de2d3SDavid du Colombier if(ep != nil)
5705d9de2d3SDavid du Colombier putep(ep);
5715d9de2d3SDavid du Colombier if(0)ddprint("skip\n");
5725d9de2d3SDavid du Colombier return 0;
5735d9de2d3SDavid du Colombier }
5745d9de2d3SDavid du Colombier if(waserror()){
5755d9de2d3SDavid du Colombier putep(ep);
5765d9de2d3SDavid du Colombier nexterror();
5775d9de2d3SDavid du Colombier }
5785d9de2d3SDavid du Colombier mkqid(&q, Qep0io+s*4, 0, QTFILE);
5795d9de2d3SDavid du Colombier devdir(c, q, ep->name, 0, eve, epdataperm(ep->mode), dp);
5805d9de2d3SDavid du Colombier putep(ep);
5815d9de2d3SDavid du Colombier poperror();
5825d9de2d3SDavid du Colombier if(0)ddprint("ok\n");
5835d9de2d3SDavid du Colombier return 1;
5845d9de2d3SDavid du Colombier
5855d9de2d3SDavid du Colombier case Qusbdir: /* list #u/usb */
5865d9de2d3SDavid du Colombier Usbdir:
5875d9de2d3SDavid du Colombier if(s < nelem(usbdir)){
5885d9de2d3SDavid du Colombier dir = &usbdir[s];
5895d9de2d3SDavid du Colombier mkqid(&q, dir->qid.path, 0, QTFILE);
5905d9de2d3SDavid du Colombier devdir(c, q, dir->name, dir->length, eve, dir->perm, dp);
5915d9de2d3SDavid du Colombier if(0)ddprint("ok\n");
5925d9de2d3SDavid du Colombier return 1;
5935d9de2d3SDavid du Colombier }
5945d9de2d3SDavid du Colombier s -= nelem(usbdir);
5955d9de2d3SDavid du Colombier if(s < 0 || s >= epmax)
5965d9de2d3SDavid du Colombier goto Fail;
5975d9de2d3SDavid du Colombier ep = getep(s);
5985d9de2d3SDavid du Colombier if(ep == nil){
5995d9de2d3SDavid du Colombier if(0)ddprint("skip\n");
6005d9de2d3SDavid du Colombier return 0;
6015d9de2d3SDavid du Colombier }
6025d9de2d3SDavid du Colombier if(waserror()){
6035d9de2d3SDavid du Colombier putep(ep);
6045d9de2d3SDavid du Colombier nexterror();
6055d9de2d3SDavid du Colombier }
6065d9de2d3SDavid du Colombier se = up->genbuf+sizeof(up->genbuf);
6075d9de2d3SDavid du Colombier seprint(up->genbuf, se, "ep%d.%d", ep->dev->nb, ep->nb);
6085d9de2d3SDavid du Colombier mkqid(&q, Qep0dir+4*s, 0, QTDIR);
6095d9de2d3SDavid du Colombier putep(ep);
6105d9de2d3SDavid du Colombier poperror();
6115d9de2d3SDavid du Colombier devdir(c, q, up->genbuf, 0, eve, 0755, dp);
6125d9de2d3SDavid du Colombier if(0)ddprint("ok\n");
6135d9de2d3SDavid du Colombier return 1;
6145d9de2d3SDavid du Colombier
6155d9de2d3SDavid du Colombier case Qctl:
6165d9de2d3SDavid du Colombier s = 0;
6175d9de2d3SDavid du Colombier goto Usbdir;
6185d9de2d3SDavid du Colombier
6195d9de2d3SDavid du Colombier default: /* list #u/usb/epN.M */
6205d9de2d3SDavid du Colombier nb = qid2epidx(QID(c->qid));
6215d9de2d3SDavid du Colombier ep = getep(nb);
6225d9de2d3SDavid du Colombier if(ep == nil)
6235d9de2d3SDavid du Colombier goto Fail;
6245d9de2d3SDavid du Colombier mode = ep->mode;
6255d9de2d3SDavid du Colombier putep(ep);
6265d9de2d3SDavid du Colombier if(isqtype(QID(c->qid), Qepdir)){
6275d9de2d3SDavid du Colombier Epdir:
6285d9de2d3SDavid du Colombier switch(s){
6295d9de2d3SDavid du Colombier case 0:
6305d9de2d3SDavid du Colombier mkqid(&q, Qep0io+nb*4, 0, QTFILE);
6315d9de2d3SDavid du Colombier perm = epdataperm(mode);
6325d9de2d3SDavid du Colombier devdir(c, q, "data", 0, eve, perm, dp);
6335d9de2d3SDavid du Colombier break;
6345d9de2d3SDavid du Colombier case 1:
6355d9de2d3SDavid du Colombier mkqid(&q, Qep0ctl+nb*4, 0, QTFILE);
6365d9de2d3SDavid du Colombier devdir(c, q, "ctl", 0, eve, 0664, dp);
6375d9de2d3SDavid du Colombier break;
6385d9de2d3SDavid du Colombier default:
6395d9de2d3SDavid du Colombier goto Fail;
6405d9de2d3SDavid du Colombier }
6415d9de2d3SDavid du Colombier }else if(isqtype(QID(c->qid), Qepctl)){
6425d9de2d3SDavid du Colombier s = 1;
6435d9de2d3SDavid du Colombier goto Epdir;
6445d9de2d3SDavid du Colombier }else{
6455d9de2d3SDavid du Colombier s = 0;
6465d9de2d3SDavid du Colombier goto Epdir;
6475d9de2d3SDavid du Colombier }
6485d9de2d3SDavid du Colombier if(0)ddprint("ok\n");
6495d9de2d3SDavid du Colombier return 1;
6505d9de2d3SDavid du Colombier }
6515d9de2d3SDavid du Colombier Fail:
6525d9de2d3SDavid du Colombier if(0)ddprint("fail\n");
6535d9de2d3SDavid du Colombier return -1;
6545d9de2d3SDavid du Colombier }
6555d9de2d3SDavid du Colombier
6565d9de2d3SDavid du Colombier static Hci*
hciprobe(int cardno,int ctlrno)6575d9de2d3SDavid du Colombier hciprobe(int cardno, int ctlrno)
6585d9de2d3SDavid du Colombier {
6595d9de2d3SDavid du Colombier Hci *hp;
6605d9de2d3SDavid du Colombier char *type;
6615d9de2d3SDavid du Colombier char name[64];
6625d9de2d3SDavid du Colombier static int epnb = 1; /* guess the endpoint nb. for the controller */
6635d9de2d3SDavid du Colombier
6645d9de2d3SDavid du Colombier ddprint("hciprobe %d %d\n", cardno, ctlrno);
6655d9de2d3SDavid du Colombier hp = smalloc(sizeof(Hci));
6665d9de2d3SDavid du Colombier hp->ctlrno = ctlrno;
6675d9de2d3SDavid du Colombier
6685d9de2d3SDavid du Colombier if(cardno < 0)
6695d9de2d3SDavid du Colombier for(cardno = 0; cardno < Nhcis; cardno++){
6705d9de2d3SDavid du Colombier if(hcitypes[cardno].type == nil)
6715d9de2d3SDavid du Colombier break;
6725d9de2d3SDavid du Colombier type = hp->type;
6735d9de2d3SDavid du Colombier if(type==nil || *type==0)
6745d9de2d3SDavid du Colombier type = "uhci";
6755d9de2d3SDavid du Colombier if(cistrcmp(hcitypes[cardno].type, type) == 0)
6765d9de2d3SDavid du Colombier break;
6775d9de2d3SDavid du Colombier }
6785d9de2d3SDavid du Colombier
6795d9de2d3SDavid du Colombier if(cardno >= Nhcis || hcitypes[cardno].type == nil){
6805d9de2d3SDavid du Colombier free(hp);
6815d9de2d3SDavid du Colombier return nil;
6825d9de2d3SDavid du Colombier }
6835d9de2d3SDavid du Colombier dprint("%s...", hcitypes[cardno].type);
6845d9de2d3SDavid du Colombier if(hcitypes[cardno].reset(hp) < 0){
6855d9de2d3SDavid du Colombier free(hp);
6865d9de2d3SDavid du Colombier return nil;
6875d9de2d3SDavid du Colombier }
6885d9de2d3SDavid du Colombier
6895d9de2d3SDavid du Colombier snprint(name, sizeof(name), "usb%s", hcitypes[cardno].type);
6905d9de2d3SDavid du Colombier intrenable(hp->irq, hp->interrupt, hp, UNKNOWN, name);
6915d9de2d3SDavid du Colombier
6925d9de2d3SDavid du Colombier print("#u/usb/ep%d.0: %s: port %#luX irq %d\n",
6935d9de2d3SDavid du Colombier epnb, hcitypes[cardno].type, hp->port, hp->irq);
6945d9de2d3SDavid du Colombier epnb++;
6955d9de2d3SDavid du Colombier
6965d9de2d3SDavid du Colombier return hp;
6975d9de2d3SDavid du Colombier }
6985d9de2d3SDavid du Colombier
6995d9de2d3SDavid du Colombier static void
usbreset(void)7005d9de2d3SDavid du Colombier usbreset(void)
7015d9de2d3SDavid du Colombier {
7025d9de2d3SDavid du Colombier int cardno, ctlrno;
7035d9de2d3SDavid du Colombier Hci *hp;
7045d9de2d3SDavid du Colombier
7055d9de2d3SDavid du Colombier dprint("usbreset\n");
7065d9de2d3SDavid du Colombier
7075d9de2d3SDavid du Colombier for(ctlrno = 0; ctlrno < Nhcis; ctlrno++)
7085d9de2d3SDavid du Colombier if((hp = hciprobe(-1, ctlrno)) != nil)
7095d9de2d3SDavid du Colombier hcis[ctlrno] = hp;
7105d9de2d3SDavid du Colombier cardno = ctlrno = 0;
7115d9de2d3SDavid du Colombier while(cardno < Nhcis && ctlrno < Nhcis && hcitypes[cardno].type != nil)
7125d9de2d3SDavid du Colombier if(hcis[ctlrno] != nil)
7135d9de2d3SDavid du Colombier ctlrno++;
7145d9de2d3SDavid du Colombier else{
7155d9de2d3SDavid du Colombier hp = hciprobe(cardno, ctlrno);
7165d9de2d3SDavid du Colombier if(hp == nil)
7175d9de2d3SDavid du Colombier cardno++;
7185d9de2d3SDavid du Colombier hcis[ctlrno++] = hp;
7195d9de2d3SDavid du Colombier }
7205d9de2d3SDavid du Colombier if(hcis[Nhcis-1] != nil)
7215d9de2d3SDavid du Colombier print("usbreset: bug: Nhcis too small\n");
7225d9de2d3SDavid du Colombier }
7235d9de2d3SDavid du Colombier
7245d9de2d3SDavid du Colombier static void
usbinit(void)7255d9de2d3SDavid du Colombier usbinit(void)
7265d9de2d3SDavid du Colombier {
7275d9de2d3SDavid du Colombier Hci *hp;
7285d9de2d3SDavid du Colombier int ctlrno;
7295d9de2d3SDavid du Colombier Ep *d;
7305d9de2d3SDavid du Colombier char info[40];
7315d9de2d3SDavid du Colombier
7325d9de2d3SDavid du Colombier dprint("usbinit\n");
7335d9de2d3SDavid du Colombier for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){
7345d9de2d3SDavid du Colombier hp = hcis[ctlrno];
7355d9de2d3SDavid du Colombier if(hp != nil){
7365d9de2d3SDavid du Colombier if(hp->init != nil)
7375d9de2d3SDavid du Colombier hp->init(hp);
7385d9de2d3SDavid du Colombier d = newdev(hp, 1, 1); /* new root hub */
7395d9de2d3SDavid du Colombier d->dev->state = Denabled; /* although addr == 0 */
7405d9de2d3SDavid du Colombier d->maxpkt = 64;
7415d9de2d3SDavid du Colombier snprint(info, sizeof(info), "ports %d", hp->nports);
7425d9de2d3SDavid du Colombier kstrdup(&d->info, info);
7435d9de2d3SDavid du Colombier }
7445d9de2d3SDavid du Colombier }
7455d9de2d3SDavid du Colombier }
7465d9de2d3SDavid du Colombier
7475d9de2d3SDavid du Colombier static Chan*
usbattach(char * spec)7485d9de2d3SDavid du Colombier usbattach(char *spec)
7495d9de2d3SDavid du Colombier {
7505d9de2d3SDavid du Colombier return devattach(L'u', spec);
7515d9de2d3SDavid du Colombier }
7525d9de2d3SDavid du Colombier
7535d9de2d3SDavid du Colombier static Walkqid*
usbwalk(Chan * c,Chan * nc,char ** name,int nname)7545d9de2d3SDavid du Colombier usbwalk(Chan *c, Chan *nc, char **name, int nname)
7555d9de2d3SDavid du Colombier {
7565d9de2d3SDavid du Colombier return devwalk(c, nc, name, nname, nil, 0, usbgen);
7575d9de2d3SDavid du Colombier }
7585d9de2d3SDavid du Colombier
7595d9de2d3SDavid du Colombier static int
usbstat(Chan * c,uchar * db,int n)7605d9de2d3SDavid du Colombier usbstat(Chan *c, uchar *db, int n)
7615d9de2d3SDavid du Colombier {
7625d9de2d3SDavid du Colombier return devstat(c, db, n, nil, 0, usbgen);
7635d9de2d3SDavid du Colombier }
7645d9de2d3SDavid du Colombier
7655d9de2d3SDavid du Colombier /*
7665d9de2d3SDavid du Colombier * µs for the given transfer, for bandwidth allocation.
7675d9de2d3SDavid du Colombier * This is a very rough worst case for what 5.11.3
7685d9de2d3SDavid du Colombier * of the usb 2.0 spec says.
7695d9de2d3SDavid du Colombier * Also, we are using maxpkt and not actual transfer sizes.
7705d9de2d3SDavid du Colombier * Only when we are sure we
7715d9de2d3SDavid du Colombier * are not exceeding b/w might we consider adjusting it.
7725d9de2d3SDavid du Colombier */
7735d9de2d3SDavid du Colombier static ulong
usbload(int speed,int maxpkt)7745d9de2d3SDavid du Colombier usbload(int speed, int maxpkt)
7755d9de2d3SDavid du Colombier {
7765d9de2d3SDavid du Colombier enum{ Hostns = 1000, Hubns = 333 };
777*b4d1cf41SDavid du Colombier ulong l;
778*b4d1cf41SDavid du Colombier ulong bs;
7795d9de2d3SDavid du Colombier
7805d9de2d3SDavid du Colombier l = 0;
7815d9de2d3SDavid du Colombier bs = 10UL * maxpkt;
7825d9de2d3SDavid du Colombier switch(speed){
7835d9de2d3SDavid du Colombier case Highspeed:
7845d9de2d3SDavid du Colombier l = 55*8*2 + 2 * (3 + bs) + Hostns;
7855d9de2d3SDavid du Colombier break;
7865d9de2d3SDavid du Colombier case Fullspeed:
7875d9de2d3SDavid du Colombier l = 9107 + 84 * (4 + bs) + Hostns;
7885d9de2d3SDavid du Colombier break;
7895d9de2d3SDavid du Colombier case Lowspeed:
7905d9de2d3SDavid du Colombier l = 64107 + 2 * Hubns + 667 * (3 + bs) + Hostns;
7915d9de2d3SDavid du Colombier break;
7925d9de2d3SDavid du Colombier default:
7935d9de2d3SDavid du Colombier print("usbload: bad speed %d\n", speed);
7945d9de2d3SDavid du Colombier /* let it run */
7955d9de2d3SDavid du Colombier }
7965d9de2d3SDavid du Colombier return l / 1000UL; /* in µs */
7975d9de2d3SDavid du Colombier }
7985d9de2d3SDavid du Colombier
7995d9de2d3SDavid du Colombier static Chan*
usbopen(Chan * c,int omode)8005d9de2d3SDavid du Colombier usbopen(Chan *c, int omode)
8015d9de2d3SDavid du Colombier {
802*b4d1cf41SDavid du Colombier int q;
8035d9de2d3SDavid du Colombier Ep *ep;
804*b4d1cf41SDavid du Colombier int mode;
8055d9de2d3SDavid du Colombier
8065d9de2d3SDavid du Colombier mode = openmode(omode);
8075d9de2d3SDavid du Colombier q = QID(c->qid);
8085d9de2d3SDavid du Colombier
8095d9de2d3SDavid du Colombier if(q >= Qep0dir && qid2epidx(q) < 0)
8105d9de2d3SDavid du Colombier error(Eio);
8115d9de2d3SDavid du Colombier if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
8125d9de2d3SDavid du Colombier return devopen(c, omode, nil, 0, usbgen);
8135d9de2d3SDavid du Colombier
8145d9de2d3SDavid du Colombier ep = getep(qid2epidx(q));
8155d9de2d3SDavid du Colombier if(ep == nil)
8165d9de2d3SDavid du Colombier error(Eio);
8175d9de2d3SDavid du Colombier deprint("usbopen q %#x fid %d omode %d\n", q, c->fid, mode);
8185d9de2d3SDavid du Colombier if(waserror()){
8195d9de2d3SDavid du Colombier putep(ep);
8205d9de2d3SDavid du Colombier nexterror();
8215d9de2d3SDavid du Colombier }
8225d9de2d3SDavid du Colombier qlock(ep);
8235d9de2d3SDavid du Colombier if(ep->inuse){
8245d9de2d3SDavid du Colombier qunlock(ep);
8255d9de2d3SDavid du Colombier error(Einuse);
8265d9de2d3SDavid du Colombier }
8275d9de2d3SDavid du Colombier ep->inuse = 1;
8285d9de2d3SDavid du Colombier qunlock(ep);
8295d9de2d3SDavid du Colombier if(waserror()){
8305d9de2d3SDavid du Colombier ep->inuse = 0;
8315d9de2d3SDavid du Colombier nexterror();
8325d9de2d3SDavid du Colombier }
8335d9de2d3SDavid du Colombier if(mode != OREAD && ep->mode == OREAD)
8345d9de2d3SDavid du Colombier error(Eperm);
8355d9de2d3SDavid du Colombier if(mode != OWRITE && ep->mode == OWRITE)
8365d9de2d3SDavid du Colombier error(Eperm);
8375d9de2d3SDavid du Colombier if(ep->ttype == Tnone)
8385d9de2d3SDavid du Colombier error(Enotconf);
8395d9de2d3SDavid du Colombier ep->clrhalt = 0;
8405d9de2d3SDavid du Colombier ep->rhrepl = -1;
8415d9de2d3SDavid du Colombier if(ep->load == 0)
8425d9de2d3SDavid du Colombier ep->load = usbload(ep->dev->speed, ep->maxpkt);
8435d9de2d3SDavid du Colombier ep->hp->epopen(ep);
8445d9de2d3SDavid du Colombier
8455d9de2d3SDavid du Colombier poperror(); /* ep->inuse */
8465d9de2d3SDavid du Colombier poperror(); /* don't putep(): ref kept for fid using the ep. */
8475d9de2d3SDavid du Colombier
8485d9de2d3SDavid du Colombier c->mode = mode;
8495d9de2d3SDavid du Colombier c->flag |= COPEN;
8505d9de2d3SDavid du Colombier c->offset = 0;
8515d9de2d3SDavid du Colombier c->aux = nil; /* paranoia */
8525d9de2d3SDavid du Colombier return c;
8535d9de2d3SDavid du Colombier }
8545d9de2d3SDavid du Colombier
8555d9de2d3SDavid du Colombier static void
epclose(Ep * ep)8565d9de2d3SDavid du Colombier epclose(Ep *ep)
8575d9de2d3SDavid du Colombier {
8585d9de2d3SDavid du Colombier qlock(ep);
8595d9de2d3SDavid du Colombier if(waserror()){
8605d9de2d3SDavid du Colombier qunlock(ep);
8615d9de2d3SDavid du Colombier nexterror();
8625d9de2d3SDavid du Colombier }
8635d9de2d3SDavid du Colombier if(ep->inuse){
8645d9de2d3SDavid du Colombier ep->hp->epclose(ep);
8655d9de2d3SDavid du Colombier ep->inuse = 0;
8665d9de2d3SDavid du Colombier }
8675d9de2d3SDavid du Colombier qunlock(ep);
8685d9de2d3SDavid du Colombier poperror();
8695d9de2d3SDavid du Colombier }
8705d9de2d3SDavid du Colombier
8715d9de2d3SDavid du Colombier static void
usbclose(Chan * c)8725d9de2d3SDavid du Colombier usbclose(Chan *c)
8735d9de2d3SDavid du Colombier {
8745d9de2d3SDavid du Colombier int q;
8755d9de2d3SDavid du Colombier Ep *ep;
8765d9de2d3SDavid du Colombier
8775d9de2d3SDavid du Colombier q = QID(c->qid);
8785d9de2d3SDavid du Colombier if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
8795d9de2d3SDavid du Colombier return;
8805d9de2d3SDavid du Colombier
8815d9de2d3SDavid du Colombier ep = getep(qid2epidx(q));
8825d9de2d3SDavid du Colombier if(ep == nil)
8835d9de2d3SDavid du Colombier return;
8845d9de2d3SDavid du Colombier deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref);
8855d9de2d3SDavid du Colombier if(waserror()){
8865d9de2d3SDavid du Colombier putep(ep);
8875d9de2d3SDavid du Colombier nexterror();
8885d9de2d3SDavid du Colombier }
8895d9de2d3SDavid du Colombier if(c->flag & COPEN){
8905d9de2d3SDavid du Colombier free(c->aux);
8915d9de2d3SDavid du Colombier c->aux = nil;
8925d9de2d3SDavid du Colombier epclose(ep);
8935d9de2d3SDavid du Colombier putep(ep); /* release ref kept since usbopen */
8945d9de2d3SDavid du Colombier c->flag &= ~COPEN;
8955d9de2d3SDavid du Colombier }
8965d9de2d3SDavid du Colombier poperror();
8975d9de2d3SDavid du Colombier putep(ep);
8985d9de2d3SDavid du Colombier }
8995d9de2d3SDavid du Colombier
9005d9de2d3SDavid du Colombier static long
ctlread(Chan * c,void * a,long n,vlong offset)9015d9de2d3SDavid du Colombier ctlread(Chan *c, void *a, long n, vlong offset)
9025d9de2d3SDavid du Colombier {
903*b4d1cf41SDavid du Colombier int q;
904*b4d1cf41SDavid du Colombier char *s;
905*b4d1cf41SDavid du Colombier char *us;
906*b4d1cf41SDavid du Colombier char *se;
9075d9de2d3SDavid du Colombier Ep *ep;
908*b4d1cf41SDavid du Colombier int i;
9095d9de2d3SDavid du Colombier
9105d9de2d3SDavid du Colombier q = QID(c->qid);
9115d9de2d3SDavid du Colombier us = s = smalloc(READSTR);
9125d9de2d3SDavid du Colombier se = s + READSTR;
9135d9de2d3SDavid du Colombier if(waserror()){
9145d9de2d3SDavid du Colombier free(us);
9155d9de2d3SDavid du Colombier nexterror();
9165d9de2d3SDavid du Colombier }
9175d9de2d3SDavid du Colombier if(q == Qctl)
9185d9de2d3SDavid du Colombier for(i = 0; i < epmax; i++){
9195d9de2d3SDavid du Colombier ep = getep(i);
9205d9de2d3SDavid du Colombier if(ep != nil){
9215d9de2d3SDavid du Colombier if(waserror()){
9225d9de2d3SDavid du Colombier putep(ep);
9235d9de2d3SDavid du Colombier nexterror();
9245d9de2d3SDavid du Colombier }
925*b4d1cf41SDavid du Colombier s = seprint(s, se, "ep%d.%d ", ep->dev->nb, ep->nb);
9265d9de2d3SDavid du Colombier s = seprintep(s, se, ep, 0);
9275d9de2d3SDavid du Colombier poperror();
9285d9de2d3SDavid du Colombier }
9295d9de2d3SDavid du Colombier putep(ep);
9305d9de2d3SDavid du Colombier }
9315d9de2d3SDavid du Colombier else{
9325d9de2d3SDavid du Colombier ep = getep(qid2epidx(q));
9335d9de2d3SDavid du Colombier if(ep == nil)
9345d9de2d3SDavid du Colombier error(Eio);
9355d9de2d3SDavid du Colombier if(waserror()){
9365d9de2d3SDavid du Colombier putep(ep);
9375d9de2d3SDavid du Colombier nexterror();
9385d9de2d3SDavid du Colombier }
9395d9de2d3SDavid du Colombier if(c->aux != nil){
940*b4d1cf41SDavid du Colombier /* After a new endpoint request we read
9415d9de2d3SDavid du Colombier * the new endpoint name back.
9425d9de2d3SDavid du Colombier */
9435d9de2d3SDavid du Colombier strecpy(s, se, c->aux);
9445d9de2d3SDavid du Colombier free(c->aux);
9455d9de2d3SDavid du Colombier c->aux = nil;
9465d9de2d3SDavid du Colombier }else
9475d9de2d3SDavid du Colombier seprintep(s, se, ep, 0);
9485d9de2d3SDavid du Colombier poperror();
9495d9de2d3SDavid du Colombier putep(ep);
9505d9de2d3SDavid du Colombier }
9515d9de2d3SDavid du Colombier n = readstr(offset, a, n, us);
9525d9de2d3SDavid du Colombier poperror();
9535d9de2d3SDavid du Colombier free(us);
9545d9de2d3SDavid du Colombier return n;
9555d9de2d3SDavid du Colombier }
9565d9de2d3SDavid du Colombier
9575d9de2d3SDavid du Colombier /*
9585d9de2d3SDavid du Colombier * Fake root hub emulation.
9595d9de2d3SDavid du Colombier */
9605d9de2d3SDavid du Colombier static long
rhubread(Ep * ep,void * a,long n)9615d9de2d3SDavid du Colombier rhubread(Ep *ep, void *a, long n)
9625d9de2d3SDavid du Colombier {
9635d9de2d3SDavid du Colombier char *b;
9645d9de2d3SDavid du Colombier
9655d9de2d3SDavid du Colombier if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2)
9665d9de2d3SDavid du Colombier return -1;
9675d9de2d3SDavid du Colombier if(ep->rhrepl < 0)
9685d9de2d3SDavid du Colombier return -1;
9695d9de2d3SDavid du Colombier
9705d9de2d3SDavid du Colombier b = a;
9715d9de2d3SDavid du Colombier memset(b, 0, n);
9725d9de2d3SDavid du Colombier PUT2(b, ep->rhrepl);
9735d9de2d3SDavid du Colombier ep->rhrepl = -1;
9745d9de2d3SDavid du Colombier return n;
9755d9de2d3SDavid du Colombier }
9765d9de2d3SDavid du Colombier
9775d9de2d3SDavid du Colombier static long
rhubwrite(Ep * ep,void * a,long n)9785d9de2d3SDavid du Colombier rhubwrite(Ep *ep, void *a, long n)
9795d9de2d3SDavid du Colombier {
9805d9de2d3SDavid du Colombier uchar *s;
981*b4d1cf41SDavid du Colombier int cmd;
982*b4d1cf41SDavid du Colombier int feature;
983*b4d1cf41SDavid du Colombier int port;
9845d9de2d3SDavid du Colombier Hci *hp;
9855d9de2d3SDavid du Colombier
9865d9de2d3SDavid du Colombier if(ep->dev == nil || ep->dev->isroot == 0 || ep->nb != 0)
9875d9de2d3SDavid du Colombier return -1;
9885d9de2d3SDavid du Colombier if(n != Rsetuplen)
9895d9de2d3SDavid du Colombier error("root hub is a toy hub");
9905d9de2d3SDavid du Colombier ep->rhrepl = -1;
9915d9de2d3SDavid du Colombier s = a;
9925d9de2d3SDavid du Colombier if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother))
9935d9de2d3SDavid du Colombier error("root hub is a toy hub");
9945d9de2d3SDavid du Colombier hp = ep->hp;
9955d9de2d3SDavid du Colombier cmd = s[Rreq];
9965d9de2d3SDavid du Colombier feature = GET2(s+Rvalue);
9975d9de2d3SDavid du Colombier port = GET2(s+Rindex);
9985d9de2d3SDavid du Colombier if(port < 1 || port > hp->nports)
9995d9de2d3SDavid du Colombier error("bad hub port number");
10005d9de2d3SDavid du Colombier switch(feature){
10015d9de2d3SDavid du Colombier case Rportenable:
10025d9de2d3SDavid du Colombier ep->rhrepl = hp->portenable(hp, port, cmd == Rsetfeature);
10035d9de2d3SDavid du Colombier break;
10045d9de2d3SDavid du Colombier case Rportreset:
10055d9de2d3SDavid du Colombier ep->rhrepl = hp->portreset(hp, port, cmd == Rsetfeature);
10065d9de2d3SDavid du Colombier break;
10075d9de2d3SDavid du Colombier case Rgetstatus:
10085d9de2d3SDavid du Colombier ep->rhrepl = hp->portstatus(hp, port);
10095d9de2d3SDavid du Colombier break;
10105d9de2d3SDavid du Colombier default:
10115d9de2d3SDavid du Colombier ep->rhrepl = 0;
10125d9de2d3SDavid du Colombier }
10135d9de2d3SDavid du Colombier return n;
10145d9de2d3SDavid du Colombier }
10155d9de2d3SDavid du Colombier
10165d9de2d3SDavid du Colombier static long
usbread(Chan * c,void * a,long n,vlong offset)10175d9de2d3SDavid du Colombier usbread(Chan *c, void *a, long n, vlong offset)
10185d9de2d3SDavid du Colombier {
1019*b4d1cf41SDavid du Colombier int q;
10205d9de2d3SDavid du Colombier Ep *ep;
1021*b4d1cf41SDavid du Colombier int nr;
10225d9de2d3SDavid du Colombier
10235d9de2d3SDavid du Colombier q = QID(c->qid);
10245d9de2d3SDavid du Colombier
10255d9de2d3SDavid du Colombier if(c->qid.type == QTDIR)
10265d9de2d3SDavid du Colombier return devdirread(c, a, n, nil, 0, usbgen);
10275d9de2d3SDavid du Colombier
10285d9de2d3SDavid du Colombier if(q == Qctl || isqtype(q, Qepctl))
10295d9de2d3SDavid du Colombier return ctlread(c, a, n, offset);
10305d9de2d3SDavid du Colombier
10315d9de2d3SDavid du Colombier ep = getep(qid2epidx(q));
10325d9de2d3SDavid du Colombier if(ep == nil)
10335d9de2d3SDavid du Colombier error(Eio);
10345d9de2d3SDavid du Colombier if(waserror()){
10355d9de2d3SDavid du Colombier putep(ep);
10365d9de2d3SDavid du Colombier nexterror();
10375d9de2d3SDavid du Colombier }
10385d9de2d3SDavid du Colombier if(ep->dev->state == Ddetach)
10395d9de2d3SDavid du Colombier error(Edetach);
10405d9de2d3SDavid du Colombier if(ep->mode == OWRITE || ep->inuse == 0)
10415d9de2d3SDavid du Colombier error(Ebadusefd);
10425d9de2d3SDavid du Colombier switch(ep->ttype){
10435d9de2d3SDavid du Colombier case Tnone:
10445d9de2d3SDavid du Colombier error("endpoint not configured");
10455d9de2d3SDavid du Colombier case Tctl:
10465d9de2d3SDavid du Colombier nr = rhubread(ep, a, n);
10475d9de2d3SDavid du Colombier if(nr >= 0){
10485d9de2d3SDavid du Colombier n = nr;
10495d9de2d3SDavid du Colombier break;
10505d9de2d3SDavid du Colombier }
1051*b4d1cf41SDavid du Colombier /* else fall */
10525d9de2d3SDavid du Colombier default:
1053*b4d1cf41SDavid du Colombier ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",q,c->fid,n,offset);
10545d9de2d3SDavid du Colombier n = ep->hp->epread(ep, a, n);
10555d9de2d3SDavid du Colombier break;
10565d9de2d3SDavid du Colombier }
10575d9de2d3SDavid du Colombier poperror();
10585d9de2d3SDavid du Colombier putep(ep);
10595d9de2d3SDavid du Colombier return n;
10605d9de2d3SDavid du Colombier }
10615d9de2d3SDavid du Colombier
10625d9de2d3SDavid du Colombier static long
pow2(int n)10635d9de2d3SDavid du Colombier pow2(int n)
10645d9de2d3SDavid du Colombier {
10655d9de2d3SDavid du Colombier return 1 << n;
10665d9de2d3SDavid du Colombier }
10675d9de2d3SDavid du Colombier
10685d9de2d3SDavid du Colombier static void
setmaxpkt(Ep * ep,char * s)10695d9de2d3SDavid du Colombier setmaxpkt(Ep *ep, char* s)
10705d9de2d3SDavid du Colombier {
10715d9de2d3SDavid du Colombier long spp; /* samples per packet */
10725d9de2d3SDavid du Colombier
10735d9de2d3SDavid du Colombier if(ep->dev->speed == Highspeed)
10745d9de2d3SDavid du Colombier spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000;
10755d9de2d3SDavid du Colombier else
10765d9de2d3SDavid du Colombier spp = (ep->hz * ep->pollival + 999) / 1000;
10775d9de2d3SDavid du Colombier ep->maxpkt = spp * ep->samplesz;
10785d9de2d3SDavid du Colombier deprint("usb: %s: setmaxpkt: hz %ld poll %ld"
10795d9de2d3SDavid du Colombier " ntds %d %s speed -> spp %ld maxpkt %ld\n", s,
10805d9de2d3SDavid du Colombier ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed],
10815d9de2d3SDavid du Colombier spp, ep->maxpkt);
10825d9de2d3SDavid du Colombier if(ep->maxpkt > 1024){
10835d9de2d3SDavid du Colombier print("usb: %s: maxpkt %ld > 1024. truncating\n", s, ep->maxpkt);
10845d9de2d3SDavid du Colombier ep->maxpkt = 1024;
10855d9de2d3SDavid du Colombier }
10865d9de2d3SDavid du Colombier }
10875d9de2d3SDavid du Colombier
10885d9de2d3SDavid du Colombier /*
1089*b4d1cf41SDavid du Colombier * Many endpoint ctls. simply update the portable representation
10905d9de2d3SDavid du Colombier * of the endpoint. The actual controller driver will look
10915d9de2d3SDavid du Colombier * at them to setup the endpoints as dictated.
10925d9de2d3SDavid du Colombier */
10935d9de2d3SDavid du Colombier static long
epctl(Ep * ep,Chan * c,void * a,long n)10945d9de2d3SDavid du Colombier epctl(Ep *ep, Chan *c, void *a, long n)
10955d9de2d3SDavid du Colombier {
10965d9de2d3SDavid du Colombier int i, l, mode, nb, tt;
10975d9de2d3SDavid du Colombier char *b, *s;
10985d9de2d3SDavid du Colombier Cmdbuf *cb;
10995d9de2d3SDavid du Colombier Cmdtab *ct;
11005d9de2d3SDavid du Colombier Ep *nep;
11015d9de2d3SDavid du Colombier Udev *d;
11025d9de2d3SDavid du Colombier static char *Info = "info ";
11035d9de2d3SDavid du Colombier
11045d9de2d3SDavid du Colombier d = ep->dev;
11055d9de2d3SDavid du Colombier
11065d9de2d3SDavid du Colombier cb = parsecmd(a, n);
11075d9de2d3SDavid du Colombier if(waserror()){
11085d9de2d3SDavid du Colombier free(cb);
11095d9de2d3SDavid du Colombier nexterror();
11105d9de2d3SDavid du Colombier }
11115d9de2d3SDavid du Colombier ct = lookupcmd(cb, epctls, nelem(epctls));
11125d9de2d3SDavid du Colombier if(ct == nil)
11135d9de2d3SDavid du Colombier error(Ebadctl);
11145d9de2d3SDavid du Colombier i = ct->index;
11155d9de2d3SDavid du Colombier if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset)
11165d9de2d3SDavid du Colombier if(ep != ep->ep0)
11175d9de2d3SDavid du Colombier error("allowed only on a setup endpoint");
11185d9de2d3SDavid du Colombier if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname)
11195d9de2d3SDavid du Colombier if(ep != ep->ep0 && ep->inuse != 0)
11205d9de2d3SDavid du Colombier error("must configure before using");
11215d9de2d3SDavid du Colombier switch(i){
11225d9de2d3SDavid du Colombier case CMnew:
11235d9de2d3SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
11245d9de2d3SDavid du Colombier nb = strtol(cb->f[1], nil, 0);
11255d9de2d3SDavid du Colombier if(nb < 0 || nb >= Ndeveps)
11265d9de2d3SDavid du Colombier error("bad endpoint number");
11275d9de2d3SDavid du Colombier tt = name2ttype(cb->f[2]);
11285d9de2d3SDavid du Colombier if(tt == Tnone)
11295d9de2d3SDavid du Colombier error("unknown endpoint type");
11305d9de2d3SDavid du Colombier mode = name2mode(cb->f[3]);
11315d9de2d3SDavid du Colombier if(mode < 0)
11325d9de2d3SDavid du Colombier error("unknown i/o mode");
11335d9de2d3SDavid du Colombier newdevep(ep, nb, tt, mode);
11345d9de2d3SDavid du Colombier break;
11355d9de2d3SDavid du Colombier case CMnewdev:
11365d9de2d3SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
11375d9de2d3SDavid du Colombier if(ep != ep->ep0 || d->ishub == 0)
11385d9de2d3SDavid du Colombier error("not a hub setup endpoint");
11395d9de2d3SDavid du Colombier l = name2speed(cb->f[1]);
11405d9de2d3SDavid du Colombier if(l == Nospeed)
11415d9de2d3SDavid du Colombier error("speed must be full|low|high");
11425d9de2d3SDavid du Colombier nep = newdev(ep->hp, 0, 0);
11435d9de2d3SDavid du Colombier nep->dev->speed = l;
11445d9de2d3SDavid du Colombier if(nep->dev->speed != Lowspeed)
11455d9de2d3SDavid du Colombier nep->maxpkt = 64; /* assume full speed */
11465d9de2d3SDavid du Colombier nep->dev->hub = d->nb;
11475d9de2d3SDavid du Colombier nep->dev->port = atoi(cb->f[2]);
1148*b4d1cf41SDavid du Colombier /* next read request will read
11495d9de2d3SDavid du Colombier * the name for the new endpoint
11505d9de2d3SDavid du Colombier */
11515d9de2d3SDavid du Colombier l = sizeof(up->genbuf);
11525d9de2d3SDavid du Colombier snprint(up->genbuf, l, "ep%d.%d", nep->dev->nb, nep->nb);
11535d9de2d3SDavid du Colombier kstrdup(&c->aux, up->genbuf);
11545d9de2d3SDavid du Colombier break;
11555d9de2d3SDavid du Colombier case CMhub:
11565d9de2d3SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
11575d9de2d3SDavid du Colombier d->ishub = 1;
11585d9de2d3SDavid du Colombier break;
11595d9de2d3SDavid du Colombier case CMspeed:
11605d9de2d3SDavid du Colombier l = name2speed(cb->f[1]);
11615d9de2d3SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l);
11625d9de2d3SDavid du Colombier if(l == Nospeed)
11635d9de2d3SDavid du Colombier error("speed must be full|low|high");
11645d9de2d3SDavid du Colombier qlock(ep->ep0);
11655d9de2d3SDavid du Colombier d->speed = l;
11665d9de2d3SDavid du Colombier qunlock(ep->ep0);
11675d9de2d3SDavid du Colombier break;
11685d9de2d3SDavid du Colombier case CMmaxpkt:
11695d9de2d3SDavid du Colombier l = strtoul(cb->f[1], nil, 0);
11705d9de2d3SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l);
11715d9de2d3SDavid du Colombier if(l < 1 || l > 1024)
11725d9de2d3SDavid du Colombier error("maxpkt not in [1:1024]");
11735d9de2d3SDavid du Colombier qlock(ep);
11745d9de2d3SDavid du Colombier ep->maxpkt = l;
11755d9de2d3SDavid du Colombier qunlock(ep);
11765d9de2d3SDavid du Colombier break;
11775d9de2d3SDavid du Colombier case CMntds:
11785d9de2d3SDavid du Colombier l = strtoul(cb->f[1], nil, 0);
11795d9de2d3SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l);
11805d9de2d3SDavid du Colombier if(l < 1 || l > 3)
11815d9de2d3SDavid du Colombier error("ntds not in [1:3]");
11825d9de2d3SDavid du Colombier qlock(ep);
11835d9de2d3SDavid du Colombier ep->ntds = l;
11845d9de2d3SDavid du Colombier qunlock(ep);
11855d9de2d3SDavid du Colombier break;
11865d9de2d3SDavid du Colombier case CMpollival:
11875d9de2d3SDavid du Colombier if(ep->ttype != Tintr && ep->ttype != Tiso)
11885d9de2d3SDavid du Colombier error("not an intr or iso endpoint");
11895d9de2d3SDavid du Colombier l = strtoul(cb->f[1], nil, 0);
11905d9de2d3SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l);
11915d9de2d3SDavid du Colombier if(ep->ttype == Tiso ||
11925d9de2d3SDavid du Colombier (ep->ttype == Tintr && ep->dev->speed == Highspeed)){
11935d9de2d3SDavid du Colombier if(l < 1 || l > 16)
11945d9de2d3SDavid du Colombier error("pollival power not in [1:16]");
11955d9de2d3SDavid du Colombier l = pow2(l-1);
11965d9de2d3SDavid du Colombier }else
11975d9de2d3SDavid du Colombier if(l < 1 || l > 255)
11985d9de2d3SDavid du Colombier error("pollival not in [1:255]");
11995d9de2d3SDavid du Colombier qlock(ep);
12005d9de2d3SDavid du Colombier ep->pollival = l;
12015d9de2d3SDavid du Colombier if(ep->ttype == Tiso)
12025d9de2d3SDavid du Colombier setmaxpkt(ep, "pollival");
12035d9de2d3SDavid du Colombier qunlock(ep);
12045d9de2d3SDavid du Colombier break;
12055d9de2d3SDavid du Colombier case CMsamplesz:
12065d9de2d3SDavid du Colombier if(ep->ttype != Tiso)
12075d9de2d3SDavid du Colombier error("not an iso endpoint");
12085d9de2d3SDavid du Colombier l = strtoul(cb->f[1], nil, 0);
12095d9de2d3SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l);
12105d9de2d3SDavid du Colombier if(l <= 0 || l > 8)
12115d9de2d3SDavid du Colombier error("samplesz not in [1:8]");
12125d9de2d3SDavid du Colombier qlock(ep);
12135d9de2d3SDavid du Colombier ep->samplesz = l;
12145d9de2d3SDavid du Colombier setmaxpkt(ep, "samplesz");
12155d9de2d3SDavid du Colombier qunlock(ep);
12165d9de2d3SDavid du Colombier break;
12175d9de2d3SDavid du Colombier case CMhz:
12185d9de2d3SDavid du Colombier if(ep->ttype != Tiso)
12195d9de2d3SDavid du Colombier error("not an iso endpoint");
12205d9de2d3SDavid du Colombier l = strtoul(cb->f[1], nil, 0);
12215d9de2d3SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l);
12225d9de2d3SDavid du Colombier if(l <= 0 || l > 100000)
12235d9de2d3SDavid du Colombier error("hz not in [1:100000]");
12245d9de2d3SDavid du Colombier qlock(ep);
12255d9de2d3SDavid du Colombier ep->hz = l;
12265d9de2d3SDavid du Colombier setmaxpkt(ep, "hz");
12275d9de2d3SDavid du Colombier qunlock(ep);
12285d9de2d3SDavid du Colombier break;
12295d9de2d3SDavid du Colombier case CMclrhalt:
12305d9de2d3SDavid du Colombier qlock(ep);
12315d9de2d3SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
12325d9de2d3SDavid du Colombier ep->clrhalt = 1;
12335d9de2d3SDavid du Colombier qunlock(ep);
12345d9de2d3SDavid du Colombier break;
12355d9de2d3SDavid du Colombier case CMinfo:
12365d9de2d3SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
12375d9de2d3SDavid du Colombier l = strlen(Info);
12385d9de2d3SDavid du Colombier s = a;
12395d9de2d3SDavid du Colombier if(n < l+2 || strncmp(Info, s, l) != 0)
12405d9de2d3SDavid du Colombier error(Ebadctl);
12415d9de2d3SDavid du Colombier if(n > 1024)
12425d9de2d3SDavid du Colombier n = 1024;
12435d9de2d3SDavid du Colombier b = smalloc(n);
12445d9de2d3SDavid du Colombier memmove(b, s+l, n-l);
12455d9de2d3SDavid du Colombier b[n-l] = 0;
12465d9de2d3SDavid du Colombier if(b[n-l-1] == '\n')
12475d9de2d3SDavid du Colombier b[n-l-1] = 0;
12485d9de2d3SDavid du Colombier qlock(ep);
12495d9de2d3SDavid du Colombier free(ep->info);
12505d9de2d3SDavid du Colombier ep->info = b;
12515d9de2d3SDavid du Colombier qunlock(ep);
12525d9de2d3SDavid du Colombier break;
12535d9de2d3SDavid du Colombier case CMaddress:
12545d9de2d3SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
12555d9de2d3SDavid du Colombier ep->dev->state = Denabled;
12565d9de2d3SDavid du Colombier break;
12575d9de2d3SDavid du Colombier case CMdetach:
12585d9de2d3SDavid du Colombier if(ep->dev->isroot != 0)
12595d9de2d3SDavid du Colombier error("can't detach a root hub");
12605d9de2d3SDavid du Colombier deprint("usb epctl %s ep%d.%d\n",
12615d9de2d3SDavid du Colombier cb->f[0], ep->dev->nb, ep->nb);
12625d9de2d3SDavid du Colombier ep->dev->state = Ddetach;
12635d9de2d3SDavid du Colombier /* Release file system ref. for its endpoints */
12645d9de2d3SDavid du Colombier for(i = 0; i < nelem(ep->dev->eps); i++)
12655d9de2d3SDavid du Colombier putep(ep->dev->eps[i]);
12665d9de2d3SDavid du Colombier break;
12675d9de2d3SDavid du Colombier case CMdebugep:
12685d9de2d3SDavid du Colombier if(strcmp(cb->f[1], "on") == 0)
12695d9de2d3SDavid du Colombier ep->debug = 1;
12705d9de2d3SDavid du Colombier else if(strcmp(cb->f[1], "off") == 0)
12715d9de2d3SDavid du Colombier ep->debug = 0;
12725d9de2d3SDavid du Colombier else
12735d9de2d3SDavid du Colombier ep->debug = strtoul(cb->f[1], nil, 0);
12745d9de2d3SDavid du Colombier print("usb: ep%d.%d debug %d\n",
12755d9de2d3SDavid du Colombier ep->dev->nb, ep->nb, ep->debug);
12765d9de2d3SDavid du Colombier break;
12775d9de2d3SDavid du Colombier case CMname:
12785d9de2d3SDavid du Colombier deprint("usb epctl %s %s\n", cb->f[0], cb->f[1]);
12795d9de2d3SDavid du Colombier validname(cb->f[1], 0);
12805d9de2d3SDavid du Colombier kstrdup(&ep->name, cb->f[1]);
12815d9de2d3SDavid du Colombier break;
12825d9de2d3SDavid du Colombier case CMtmout:
12835d9de2d3SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
12845d9de2d3SDavid du Colombier if(ep->ttype == Tiso || ep->ttype == Tctl)
12855d9de2d3SDavid du Colombier error("ctl ignored for this endpoint type");
12865d9de2d3SDavid du Colombier ep->tmout = strtoul(cb->f[1], nil, 0);
12875d9de2d3SDavid du Colombier if(ep->tmout != 0 && ep->tmout < Xfertmout)
12885d9de2d3SDavid du Colombier ep->tmout = Xfertmout;
12895d9de2d3SDavid du Colombier break;
12905d9de2d3SDavid du Colombier case CMpreset:
12915d9de2d3SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
12925d9de2d3SDavid du Colombier if(ep->ttype != Tctl)
12935d9de2d3SDavid du Colombier error("not a control endpoint");
12945d9de2d3SDavid du Colombier if(ep->dev->state != Denabled)
12955d9de2d3SDavid du Colombier error("forbidden on devices not enabled");
12965d9de2d3SDavid du Colombier ep->dev->state = Dreset;
12975d9de2d3SDavid du Colombier break;
12985d9de2d3SDavid du Colombier default:
12995d9de2d3SDavid du Colombier panic("usb: unknown epctl %d", ct->index);
13005d9de2d3SDavid du Colombier }
13015d9de2d3SDavid du Colombier free(cb);
13025d9de2d3SDavid du Colombier poperror();
13035d9de2d3SDavid du Colombier return n;
13045d9de2d3SDavid du Colombier }
13055d9de2d3SDavid du Colombier
13065d9de2d3SDavid du Colombier static long
usbctl(void * a,long n)13075d9de2d3SDavid du Colombier usbctl(void *a, long n)
13085d9de2d3SDavid du Colombier {
13095d9de2d3SDavid du Colombier Cmdtab *ct;
13105d9de2d3SDavid du Colombier Cmdbuf *cb;
13115d9de2d3SDavid du Colombier Ep *ep;
13125d9de2d3SDavid du Colombier int i;
13135d9de2d3SDavid du Colombier
13145d9de2d3SDavid du Colombier cb = parsecmd(a, n);
13155d9de2d3SDavid du Colombier if(waserror()){
13165d9de2d3SDavid du Colombier free(cb);
13175d9de2d3SDavid du Colombier nexterror();
13185d9de2d3SDavid du Colombier }
13195d9de2d3SDavid du Colombier ct = lookupcmd(cb, usbctls, nelem(usbctls));
13205d9de2d3SDavid du Colombier dprint("usb ctl %s\n", cb->f[0]);
13215d9de2d3SDavid du Colombier switch(ct->index){
13225d9de2d3SDavid du Colombier case CMdebug:
13235d9de2d3SDavid du Colombier if(strcmp(cb->f[1], "on") == 0)
13245d9de2d3SDavid du Colombier debug = 1;
13255d9de2d3SDavid du Colombier else if(strcmp(cb->f[1], "off") == 0)
13265d9de2d3SDavid du Colombier debug = 0;
13275d9de2d3SDavid du Colombier else
13285d9de2d3SDavid du Colombier debug = strtol(cb->f[1], nil, 0);
13295d9de2d3SDavid du Colombier print("usb: debug %d\n", debug);
13305d9de2d3SDavid du Colombier for(i = 0; i < epmax; i++)
13315d9de2d3SDavid du Colombier if((ep = getep(i)) != nil){
13325d9de2d3SDavid du Colombier ep->hp->debug(ep->hp, debug);
13335d9de2d3SDavid du Colombier putep(ep);
13345d9de2d3SDavid du Colombier }
13355d9de2d3SDavid du Colombier break;
13365d9de2d3SDavid du Colombier case CMdump:
13375d9de2d3SDavid du Colombier dumpeps();
13385d9de2d3SDavid du Colombier break;
13395d9de2d3SDavid du Colombier }
13405d9de2d3SDavid du Colombier free(cb);
13415d9de2d3SDavid du Colombier poperror();
13425d9de2d3SDavid du Colombier return n;
13435d9de2d3SDavid du Colombier }
13445d9de2d3SDavid du Colombier
13455d9de2d3SDavid du Colombier static long
ctlwrite(Chan * c,void * a,long n)13465d9de2d3SDavid du Colombier ctlwrite(Chan *c, void *a, long n)
13475d9de2d3SDavid du Colombier {
13485d9de2d3SDavid du Colombier int q;
13495d9de2d3SDavid du Colombier Ep *ep;
13505d9de2d3SDavid du Colombier
13515d9de2d3SDavid du Colombier q = QID(c->qid);
13525d9de2d3SDavid du Colombier if(q == Qctl)
13535d9de2d3SDavid du Colombier return usbctl(a, n);
13545d9de2d3SDavid du Colombier
13555d9de2d3SDavid du Colombier ep = getep(qid2epidx(q));
13565d9de2d3SDavid du Colombier if(ep == nil)
13575d9de2d3SDavid du Colombier error(Eio);
13585d9de2d3SDavid du Colombier if(waserror()){
13595d9de2d3SDavid du Colombier putep(ep);
13605d9de2d3SDavid du Colombier nexterror();
13615d9de2d3SDavid du Colombier }
13625d9de2d3SDavid du Colombier if(ep->dev->state == Ddetach)
13635d9de2d3SDavid du Colombier error(Edetach);
13645d9de2d3SDavid du Colombier if(isqtype(q, Qepctl) && c->aux != nil){
13655d9de2d3SDavid du Colombier /* Be sure we don't keep a cloned ep name */
13665d9de2d3SDavid du Colombier free(c->aux);
13675d9de2d3SDavid du Colombier c->aux = nil;
13685d9de2d3SDavid du Colombier error("read, not write, expected");
13695d9de2d3SDavid du Colombier }
13705d9de2d3SDavid du Colombier n = epctl(ep, c, a, n);
13715d9de2d3SDavid du Colombier putep(ep);
13725d9de2d3SDavid du Colombier poperror();
13735d9de2d3SDavid du Colombier return n;
13745d9de2d3SDavid du Colombier }
13755d9de2d3SDavid du Colombier
13765d9de2d3SDavid du Colombier static long
usbwrite(Chan * c,void * a,long n,vlong off)13775d9de2d3SDavid du Colombier usbwrite(Chan *c, void *a, long n, vlong off)
13785d9de2d3SDavid du Colombier {
13795d9de2d3SDavid du Colombier int nr, q;
13805d9de2d3SDavid du Colombier Ep *ep;
13815d9de2d3SDavid du Colombier
13825d9de2d3SDavid du Colombier if(c->qid.type == QTDIR)
13835d9de2d3SDavid du Colombier error(Eisdir);
13845d9de2d3SDavid du Colombier
13855d9de2d3SDavid du Colombier q = QID(c->qid);
13865d9de2d3SDavid du Colombier
13875d9de2d3SDavid du Colombier if(q == Qctl || isqtype(q, Qepctl))
13885d9de2d3SDavid du Colombier return ctlwrite(c, a, n);
13895d9de2d3SDavid du Colombier
13905d9de2d3SDavid du Colombier ep = getep(qid2epidx(q));
13915d9de2d3SDavid du Colombier if(ep == nil)
13925d9de2d3SDavid du Colombier error(Eio);
13935d9de2d3SDavid du Colombier if(waserror()){
13945d9de2d3SDavid du Colombier putep(ep);
13955d9de2d3SDavid du Colombier nexterror();
13965d9de2d3SDavid du Colombier }
13975d9de2d3SDavid du Colombier if(ep->dev->state == Ddetach)
13985d9de2d3SDavid du Colombier error(Edetach);
13995d9de2d3SDavid du Colombier if(ep->mode == OREAD || ep->inuse == 0)
14005d9de2d3SDavid du Colombier error(Ebadusefd);
14015d9de2d3SDavid du Colombier
14025d9de2d3SDavid du Colombier switch(ep->ttype){
14035d9de2d3SDavid du Colombier case Tnone:
14045d9de2d3SDavid du Colombier error("endpoint not configured");
14055d9de2d3SDavid du Colombier case Tctl:
14065d9de2d3SDavid du Colombier nr = rhubwrite(ep, a, n);
14075d9de2d3SDavid du Colombier if(nr >= 0){
14085d9de2d3SDavid du Colombier n = nr;
14095d9de2d3SDavid du Colombier break;
14105d9de2d3SDavid du Colombier }
1411*b4d1cf41SDavid du Colombier /* else fall */
14125d9de2d3SDavid du Colombier default:
1413*b4d1cf41SDavid du Colombier ddeprint("\nusbwrite q %#x fid %d cnt %ld off %lld\n",q, c->fid, n, off);
14145d9de2d3SDavid du Colombier ep->hp->epwrite(ep, a, n);
14155d9de2d3SDavid du Colombier }
14165d9de2d3SDavid du Colombier putep(ep);
14175d9de2d3SDavid du Colombier poperror();
14185d9de2d3SDavid du Colombier return n;
14195d9de2d3SDavid du Colombier }
14205d9de2d3SDavid du Colombier
14215d9de2d3SDavid du Colombier Block*
usbbread(Chan * c,long n,ulong offset)14225d9de2d3SDavid du Colombier usbbread(Chan *c, long n, ulong offset)
14235d9de2d3SDavid du Colombier {
14245d9de2d3SDavid du Colombier Block *bp;
14255d9de2d3SDavid du Colombier
14265d9de2d3SDavid du Colombier bp = allocb(n);
14275d9de2d3SDavid du Colombier if(bp == 0)
14285d9de2d3SDavid du Colombier error(Enomem);
14295d9de2d3SDavid du Colombier if(waserror()) {
14305d9de2d3SDavid du Colombier freeb(bp);
14315d9de2d3SDavid du Colombier nexterror();
14325d9de2d3SDavid du Colombier }
14335d9de2d3SDavid du Colombier bp->wp += usbread(c, bp->wp, n, offset);
14345d9de2d3SDavid du Colombier poperror();
14355d9de2d3SDavid du Colombier return bp;
14365d9de2d3SDavid du Colombier }
14375d9de2d3SDavid du Colombier
14385d9de2d3SDavid du Colombier long
usbbwrite(Chan * c,Block * bp,ulong offset)14395d9de2d3SDavid du Colombier usbbwrite(Chan *c, Block *bp, ulong offset)
14405d9de2d3SDavid du Colombier {
14415d9de2d3SDavid du Colombier long n;
14425d9de2d3SDavid du Colombier
14435d9de2d3SDavid du Colombier if(waserror()) {
14445d9de2d3SDavid du Colombier freeb(bp);
14455d9de2d3SDavid du Colombier nexterror();
14465d9de2d3SDavid du Colombier }
14475d9de2d3SDavid du Colombier n = usbwrite(c, bp->rp, BLEN(bp), offset);
14485d9de2d3SDavid du Colombier poperror();
14495d9de2d3SDavid du Colombier freeb(bp);
14505d9de2d3SDavid du Colombier
14515d9de2d3SDavid du Colombier return n;
14525d9de2d3SDavid du Colombier }
14535d9de2d3SDavid du Colombier
14545d9de2d3SDavid du Colombier void
usbshutdown(void)14555d9de2d3SDavid du Colombier usbshutdown(void)
14565d9de2d3SDavid du Colombier {
14575d9de2d3SDavid du Colombier Hci *hp;
14585d9de2d3SDavid du Colombier int i;
14595d9de2d3SDavid du Colombier
14605d9de2d3SDavid du Colombier for(i = 0; i < Nhcis; i++){
14615d9de2d3SDavid du Colombier hp = hcis[i];
14625d9de2d3SDavid du Colombier if(hp == nil)
14635d9de2d3SDavid du Colombier continue;
14645d9de2d3SDavid du Colombier if(hp->shutdown == nil)
14655d9de2d3SDavid du Colombier print("#u: no shutdown function for %s\n", hp->type);
14665d9de2d3SDavid du Colombier else
14675d9de2d3SDavid du Colombier hp->shutdown(hp);
14685d9de2d3SDavid du Colombier }
14695d9de2d3SDavid du Colombier }
14705d9de2d3SDavid du Colombier
14715d9de2d3SDavid du Colombier Dev usbdevtab = {
14725d9de2d3SDavid du Colombier L'u',
14735d9de2d3SDavid du Colombier "usb",
14745d9de2d3SDavid du Colombier
14755d9de2d3SDavid du Colombier usbreset,
14765d9de2d3SDavid du Colombier usbinit,
14775d9de2d3SDavid du Colombier usbshutdown,
14785d9de2d3SDavid du Colombier usbattach,
14795d9de2d3SDavid du Colombier usbwalk,
14805d9de2d3SDavid du Colombier usbstat,
14815d9de2d3SDavid du Colombier usbopen,
14825d9de2d3SDavid du Colombier devcreate,
14835d9de2d3SDavid du Colombier usbclose,
14845d9de2d3SDavid du Colombier usbread,
14855d9de2d3SDavid du Colombier usbbread,
14865d9de2d3SDavid du Colombier usbwrite,
14875d9de2d3SDavid du Colombier usbbwrite,
14885d9de2d3SDavid du Colombier devremove,
14895d9de2d3SDavid du Colombier devwstat,
14905d9de2d3SDavid du Colombier };
1491