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"
48*5c47fe09SDavid du Colombier #include "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) */
76*5c47fe09SDavid du Colombier CMnewdev, /* newdev full|low|high|super 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 {
157*5c47fe09SDavid du Colombier [Superspeed] "super",
1585d9de2d3SDavid du Colombier [Fullspeed] "full",
1595d9de2d3SDavid du Colombier [Lowspeed] "low",
1605d9de2d3SDavid du Colombier [Highspeed] "high",
1615d9de2d3SDavid du Colombier [Nospeed] "no",
1625d9de2d3SDavid du Colombier };
1635d9de2d3SDavid du Colombier
1645d9de2d3SDavid du Colombier static int debug;
1655d9de2d3SDavid du Colombier static Hcitype hcitypes[Nhcis];
1665d9de2d3SDavid du Colombier static Hci* hcis[Nhcis];
1675d9de2d3SDavid du Colombier static QLock epslck; /* add, del, lookup endpoints */
1685d9de2d3SDavid du Colombier static Ep* eps[Neps]; /* all endpoints known */
1695d9de2d3SDavid du Colombier static int epmax; /* 1 + last endpoint index used */
1705d9de2d3SDavid du Colombier static int usbidgen; /* device address generator */
1715d9de2d3SDavid du Colombier
1725d9de2d3SDavid du Colombier /*
1735d9de2d3SDavid du Colombier * Is there something like this in a library? should it be?
1745d9de2d3SDavid du Colombier */
1755d9de2d3SDavid du Colombier char*
seprintdata(char * s,char * se,uchar * d,int n)1765d9de2d3SDavid du Colombier seprintdata(char *s, char *se, uchar *d, int n)
1775d9de2d3SDavid du Colombier {
1785d9de2d3SDavid du Colombier int i, l;
1795d9de2d3SDavid du Colombier
1805d9de2d3SDavid du Colombier s = seprint(s, se, " %#p[%d]: ", d, n);
1815d9de2d3SDavid du Colombier l = n;
1825d9de2d3SDavid du Colombier if(l > 10)
1835d9de2d3SDavid du Colombier l = 10;
1845d9de2d3SDavid du Colombier for(i=0; i<l; i++)
1855d9de2d3SDavid du Colombier s = seprint(s, se, " %2.2ux", d[i]);
1865d9de2d3SDavid du Colombier if(l < n)
1875d9de2d3SDavid du Colombier s = seprint(s, se, "...");
1885d9de2d3SDavid du Colombier return s;
1895d9de2d3SDavid du Colombier }
1905d9de2d3SDavid du Colombier
1915d9de2d3SDavid du Colombier static int
name2speed(char * name)1925d9de2d3SDavid du Colombier name2speed(char *name)
1935d9de2d3SDavid du Colombier {
1945d9de2d3SDavid du Colombier int i;
1955d9de2d3SDavid du Colombier
1965d9de2d3SDavid du Colombier for(i = 0; i < nelem(spname); i++)
1975d9de2d3SDavid du Colombier if(strcmp(name, spname[i]) == 0)
1985d9de2d3SDavid du Colombier return i;
1995d9de2d3SDavid du Colombier return Nospeed;
2005d9de2d3SDavid du Colombier }
2015d9de2d3SDavid du Colombier
2025d9de2d3SDavid du Colombier static int
name2ttype(char * name)2035d9de2d3SDavid du Colombier name2ttype(char *name)
2045d9de2d3SDavid du Colombier {
2055d9de2d3SDavid du Colombier int i;
2065d9de2d3SDavid du Colombier
2075d9de2d3SDavid du Colombier for(i = 0; i < nelem(ttname); i++)
2085d9de2d3SDavid du Colombier if(strcmp(name, ttname[i]) == 0)
2095d9de2d3SDavid du Colombier return i;
2105d9de2d3SDavid du Colombier /* may be a std. USB ep. type */
2115d9de2d3SDavid du Colombier i = strtol(name, nil, 0);
2125d9de2d3SDavid du Colombier switch(i+1){
2135d9de2d3SDavid du Colombier case Tctl:
2145d9de2d3SDavid du Colombier case Tiso:
2155d9de2d3SDavid du Colombier case Tbulk:
2165d9de2d3SDavid du Colombier case Tintr:
2175d9de2d3SDavid du Colombier return i+1;
2185d9de2d3SDavid du Colombier default:
2195d9de2d3SDavid du Colombier return Tnone;
2205d9de2d3SDavid du Colombier }
2215d9de2d3SDavid du Colombier }
2225d9de2d3SDavid du Colombier
2235d9de2d3SDavid du Colombier static int
name2mode(char * mode)2245d9de2d3SDavid du Colombier name2mode(char *mode)
2255d9de2d3SDavid du Colombier {
2265d9de2d3SDavid du Colombier int i;
2275d9de2d3SDavid du Colombier
2285d9de2d3SDavid du Colombier for(i = 0; i < nelem(usbmodename); i++)
2295d9de2d3SDavid du Colombier if(strcmp(mode, usbmodename[i]) == 0)
2305d9de2d3SDavid du Colombier return i;
2315d9de2d3SDavid du Colombier return -1;
2325d9de2d3SDavid du Colombier }
2335d9de2d3SDavid du Colombier
2345d9de2d3SDavid du Colombier static int
qid2epidx(int q)2355d9de2d3SDavid du Colombier qid2epidx(int q)
2365d9de2d3SDavid du Colombier {
2375d9de2d3SDavid du Colombier q = (q-Qep0dir)/4;
2385d9de2d3SDavid du Colombier if(q < 0 || q >= epmax || eps[q] == nil)
2395d9de2d3SDavid du Colombier return -1;
2405d9de2d3SDavid du Colombier return q;
2415d9de2d3SDavid du Colombier }
2425d9de2d3SDavid du Colombier
2435d9de2d3SDavid du Colombier static int
isqtype(int q,int type)2445d9de2d3SDavid du Colombier isqtype(int q, int type)
2455d9de2d3SDavid du Colombier {
2465d9de2d3SDavid du Colombier if(q < Qep0dir)
2475d9de2d3SDavid du Colombier return 0;
2485d9de2d3SDavid du Colombier q -= Qep0dir;
2495d9de2d3SDavid du Colombier return (q & 3) == type;
2505d9de2d3SDavid du Colombier }
2515d9de2d3SDavid du Colombier
2525d9de2d3SDavid du Colombier void
addhcitype(char * t,int (* r)(Hci *))2535d9de2d3SDavid du Colombier addhcitype(char* t, int (*r)(Hci*))
2545d9de2d3SDavid du Colombier {
2555d9de2d3SDavid du Colombier static int ntype;
2565d9de2d3SDavid du Colombier
2575d9de2d3SDavid du Colombier if(ntype == Nhcis)
2585d9de2d3SDavid du Colombier panic("too many USB host interface types");
2595d9de2d3SDavid du Colombier hcitypes[ntype].type = t;
2605d9de2d3SDavid du Colombier hcitypes[ntype].reset = r;
2615d9de2d3SDavid du Colombier ntype++;
2625d9de2d3SDavid du Colombier }
2635d9de2d3SDavid du Colombier
2645d9de2d3SDavid du Colombier static char*
seprintep(char * s,char * se,Ep * ep,int all)2655d9de2d3SDavid du Colombier seprintep(char *s, char *se, Ep *ep, int all)
2665d9de2d3SDavid du Colombier {
2675d9de2d3SDavid du Colombier static char* dsnames[] = { "config", "enabled", "detached", "reset" };
268b4d1cf41SDavid du Colombier Udev *d;
269b4d1cf41SDavid du Colombier int i;
270b4d1cf41SDavid du Colombier int di;
2715d9de2d3SDavid du Colombier
2725d9de2d3SDavid du Colombier d = ep->dev;
2735d9de2d3SDavid du Colombier
2745d9de2d3SDavid du Colombier qlock(ep);
2755d9de2d3SDavid du Colombier if(waserror()){
2765d9de2d3SDavid du Colombier qunlock(ep);
2775d9de2d3SDavid du Colombier nexterror();
2785d9de2d3SDavid du Colombier }
2795d9de2d3SDavid du Colombier di = ep->dev->nb;
2805d9de2d3SDavid du Colombier if(all)
2815d9de2d3SDavid du Colombier s = seprint(s, se, "dev %d ep %d ", di, ep->nb);
2825d9de2d3SDavid du Colombier s = seprint(s, se, "%s", dsnames[ep->dev->state]);
2835d9de2d3SDavid du Colombier s = seprint(s, se, " %s", ttname[ep->ttype]);
2845d9de2d3SDavid du Colombier assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR);
2855d9de2d3SDavid du Colombier s = seprint(s, se, " %s", usbmodename[ep->mode]);
2865d9de2d3SDavid du Colombier s = seprint(s, se, " speed %s", spname[d->speed]);
2875d9de2d3SDavid du Colombier s = seprint(s, se, " maxpkt %ld", ep->maxpkt);
2885d9de2d3SDavid du Colombier s = seprint(s, se, " pollival %ld", ep->pollival);
2895d9de2d3SDavid du Colombier s = seprint(s, se, " samplesz %ld", ep->samplesz);
2905d9de2d3SDavid du Colombier s = seprint(s, se, " hz %ld", ep->hz);
2915d9de2d3SDavid du Colombier s = seprint(s, se, " hub %d", ep->dev->hub);
2925d9de2d3SDavid du Colombier s = seprint(s, se, " port %d", ep->dev->port);
293*5c47fe09SDavid du Colombier s = seprint(s, se, " rootport %d", ep->dev->rootport);
294*5c47fe09SDavid du Colombier s = seprint(s, se, " addr %d", ep->dev->addr);
2955d9de2d3SDavid du Colombier if(ep->inuse)
2965d9de2d3SDavid du Colombier s = seprint(s, se, " busy");
2975d9de2d3SDavid du Colombier else
2985d9de2d3SDavid du Colombier s = seprint(s, se, " idle");
2995d9de2d3SDavid du Colombier if(all){
3005d9de2d3SDavid du Colombier s = seprint(s, se, " load %uld", ep->load);
3015d9de2d3SDavid du Colombier s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep);
3025d9de2d3SDavid du Colombier s = seprint(s, se, " idx %d", ep->idx);
3035d9de2d3SDavid du Colombier if(ep->name != nil)
3045d9de2d3SDavid du Colombier s = seprint(s, se, " name '%s'", ep->name);
3055d9de2d3SDavid du Colombier if(ep->tmout != 0)
3065d9de2d3SDavid du Colombier s = seprint(s, se, " tmout");
3075d9de2d3SDavid du Colombier if(ep == ep->ep0){
3085d9de2d3SDavid du Colombier s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno);
3095d9de2d3SDavid du Colombier s = seprint(s, se, " eps:");
3105d9de2d3SDavid du Colombier for(i = 0; i < nelem(d->eps); i++)
3115d9de2d3SDavid du Colombier if(d->eps[i] != nil)
3125d9de2d3SDavid du Colombier s = seprint(s, se, " ep%d.%d", di, i);
3135d9de2d3SDavid du Colombier }
3145d9de2d3SDavid du Colombier }
3155d9de2d3SDavid du Colombier if(ep->info != nil)
3165d9de2d3SDavid du Colombier s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type);
3175d9de2d3SDavid du Colombier else
3185d9de2d3SDavid du Colombier s = seprint(s, se, "\n");
3195d9de2d3SDavid du Colombier qunlock(ep);
3205d9de2d3SDavid du Colombier poperror();
3215d9de2d3SDavid du Colombier return s;
3225d9de2d3SDavid du Colombier }
3235d9de2d3SDavid du Colombier
3245d9de2d3SDavid du Colombier static Ep*
epalloc(Hci * hp)3255d9de2d3SDavid du Colombier epalloc(Hci *hp)
3265d9de2d3SDavid du Colombier {
3275d9de2d3SDavid du Colombier Ep *ep;
3285d9de2d3SDavid du Colombier int i;
3295d9de2d3SDavid du Colombier
3305d9de2d3SDavid du Colombier ep = smalloc(sizeof(Ep));
3315d9de2d3SDavid du Colombier ep->ref = 1;
3325d9de2d3SDavid du Colombier qlock(&epslck);
3335d9de2d3SDavid du Colombier for(i = 0; i < Neps; i++)
3345d9de2d3SDavid du Colombier if(eps[i] == nil)
3355d9de2d3SDavid du Colombier break;
3365d9de2d3SDavid du Colombier if(i == Neps){
3375d9de2d3SDavid du Colombier qunlock(&epslck);
3385d9de2d3SDavid du Colombier free(ep);
3395d9de2d3SDavid du Colombier print("usb: bug: too few endpoints.\n");
3405d9de2d3SDavid du Colombier return nil;
3415d9de2d3SDavid du Colombier }
3425d9de2d3SDavid du Colombier ep->idx = i;
3435d9de2d3SDavid du Colombier if(epmax <= i)
3445d9de2d3SDavid du Colombier epmax = i+1;
3455d9de2d3SDavid du Colombier eps[i] = ep;
3465d9de2d3SDavid du Colombier ep->hp = hp;
3475d9de2d3SDavid du Colombier ep->maxpkt = 8;
3485d9de2d3SDavid du Colombier ep->ntds = 1;
3495d9de2d3SDavid du Colombier ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */
3505d9de2d3SDavid du Colombier qunlock(&epslck);
3515d9de2d3SDavid du Colombier return ep;
3525d9de2d3SDavid du Colombier }
3535d9de2d3SDavid du Colombier
3545d9de2d3SDavid du Colombier static Ep*
getep(int i)3555d9de2d3SDavid du Colombier getep(int i)
3565d9de2d3SDavid du Colombier {
3575d9de2d3SDavid du Colombier Ep *ep;
3585d9de2d3SDavid du Colombier
3595d9de2d3SDavid du Colombier if(i < 0 || i >= epmax || eps[i] == nil)
3605d9de2d3SDavid du Colombier return nil;
3615d9de2d3SDavid du Colombier qlock(&epslck);
3625d9de2d3SDavid du Colombier ep = eps[i];
3635d9de2d3SDavid du Colombier if(ep != nil)
3645d9de2d3SDavid du Colombier incref(ep);
3655d9de2d3SDavid du Colombier qunlock(&epslck);
3665d9de2d3SDavid du Colombier return ep;
3675d9de2d3SDavid du Colombier }
3685d9de2d3SDavid du Colombier
3695d9de2d3SDavid du Colombier static void
putep(Ep * ep)3705d9de2d3SDavid du Colombier putep(Ep *ep)
3715d9de2d3SDavid du Colombier {
3725d9de2d3SDavid du Colombier Udev *d;
3735d9de2d3SDavid du Colombier
3745d9de2d3SDavid du Colombier if(ep != nil && decref(ep) == 0){
3755d9de2d3SDavid du Colombier d = ep->dev;
3765d9de2d3SDavid du Colombier deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep);
3775d9de2d3SDavid du Colombier qlock(&epslck);
3785d9de2d3SDavid du Colombier eps[ep->idx] = nil;
3795d9de2d3SDavid du Colombier if(ep->idx == epmax-1)
3805d9de2d3SDavid du Colombier epmax--;
3815d9de2d3SDavid du Colombier if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen)
3825d9de2d3SDavid du Colombier usbidgen--;
3835d9de2d3SDavid du Colombier qunlock(&epslck);
3845d9de2d3SDavid du Colombier if(d != nil){
3855d9de2d3SDavid du Colombier qlock(ep->ep0);
3865d9de2d3SDavid du Colombier d->eps[ep->nb] = nil;
3875d9de2d3SDavid du Colombier qunlock(ep->ep0);
3885d9de2d3SDavid du Colombier }
3895d9de2d3SDavid du Colombier if(ep->ep0 != ep){
3905d9de2d3SDavid du Colombier putep(ep->ep0);
3915d9de2d3SDavid du Colombier ep->ep0 = nil;
3925d9de2d3SDavid du Colombier }
3935d9de2d3SDavid du Colombier free(ep->info);
3945d9de2d3SDavid du Colombier free(ep->name);
3955d9de2d3SDavid du Colombier free(ep);
3965d9de2d3SDavid du Colombier }
3975d9de2d3SDavid du Colombier }
3985d9de2d3SDavid du Colombier
3995d9de2d3SDavid du Colombier static void
dumpeps(void)4005d9de2d3SDavid du Colombier dumpeps(void)
4015d9de2d3SDavid du Colombier {
4025d9de2d3SDavid du Colombier int i;
4035d9de2d3SDavid du Colombier static char buf[512];
404b4d1cf41SDavid du Colombier char *s;
405b4d1cf41SDavid du Colombier char *e;
406b4d1cf41SDavid du Colombier Ep *ep;
4075d9de2d3SDavid du Colombier
4085d9de2d3SDavid du Colombier print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps);
4095d9de2d3SDavid du Colombier for(i = 0; i < epmax; i++){
4105d9de2d3SDavid du Colombier s = buf;
4115d9de2d3SDavid du Colombier e = buf+sizeof(buf);
4125d9de2d3SDavid du Colombier ep = getep(i);
4135d9de2d3SDavid du Colombier if(ep != nil){
4145d9de2d3SDavid du Colombier if(waserror()){
4155d9de2d3SDavid du Colombier putep(ep);
4165d9de2d3SDavid du Colombier nexterror();
4175d9de2d3SDavid du Colombier }
4185d9de2d3SDavid du Colombier s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb);
4195d9de2d3SDavid du Colombier seprintep(s, e, ep, 1);
4205d9de2d3SDavid du Colombier print("%s", buf);
4215d9de2d3SDavid du Colombier ep->hp->seprintep(buf, e, ep);
4225d9de2d3SDavid du Colombier print("%s", buf);
4235d9de2d3SDavid du Colombier poperror();
4245d9de2d3SDavid du Colombier putep(ep);
4255d9de2d3SDavid du Colombier }
4265d9de2d3SDavid du Colombier }
4275d9de2d3SDavid du Colombier print("usb dump hcis:\n");
4285d9de2d3SDavid du Colombier for(i = 0; i < Nhcis; i++)
4295d9de2d3SDavid du Colombier if(hcis[i] != nil)
4305d9de2d3SDavid du Colombier hcis[i]->dump(hcis[i]);
4315d9de2d3SDavid du Colombier }
4325d9de2d3SDavid du Colombier
4335d9de2d3SDavid du Colombier static int
newusbid(Hci *)4345d9de2d3SDavid du Colombier newusbid(Hci *)
4355d9de2d3SDavid du Colombier {
4365d9de2d3SDavid du Colombier int id;
4375d9de2d3SDavid du Colombier
4385d9de2d3SDavid du Colombier qlock(&epslck);
4395d9de2d3SDavid du Colombier id = ++usbidgen;
4405d9de2d3SDavid du Colombier if(id >= 0x7F)
4415d9de2d3SDavid du Colombier print("#u: too many device addresses; reuse them more\n");
4425d9de2d3SDavid du Colombier qunlock(&epslck);
4435d9de2d3SDavid du Colombier return id;
4445d9de2d3SDavid du Colombier }
4455d9de2d3SDavid du Colombier
4465d9de2d3SDavid du Colombier /*
4475d9de2d3SDavid du Colombier * Create endpoint 0 for a new device
4485d9de2d3SDavid du Colombier */
4495d9de2d3SDavid du Colombier static Ep*
newdev(Hci * hp,int ishub,int isroot)4505d9de2d3SDavid du Colombier newdev(Hci *hp, int ishub, int isroot)
4515d9de2d3SDavid du Colombier {
4525d9de2d3SDavid du Colombier Ep *ep;
4535d9de2d3SDavid du Colombier Udev *d;
4545d9de2d3SDavid du Colombier
4555d9de2d3SDavid du Colombier ep = epalloc(hp);
4565d9de2d3SDavid du Colombier d = ep->dev = smalloc(sizeof(Udev));
4575d9de2d3SDavid du Colombier d->nb = newusbid(hp);
458*5c47fe09SDavid du Colombier d->addr = 0;
4595d9de2d3SDavid du Colombier d->eps[0] = ep;
4605d9de2d3SDavid du Colombier ep->nb = 0;
4615d9de2d3SDavid du Colombier ep->toggle[0] = ep->toggle[1] = 0;
4625d9de2d3SDavid du Colombier d->ishub = ishub;
4635d9de2d3SDavid du Colombier d->isroot = isroot;
464*5c47fe09SDavid du Colombier d->rootport = 0;
465*5c47fe09SDavid du Colombier d->routestr = 0;
466*5c47fe09SDavid du Colombier d->depth = -1;
4675d9de2d3SDavid du Colombier d->speed = Fullspeed;
4685d9de2d3SDavid du Colombier d->state = Dconfig; /* address not yet set */
4695d9de2d3SDavid du Colombier ep->dev = d;
4705d9de2d3SDavid du Colombier ep->ep0 = ep; /* no ref counted here */
4715d9de2d3SDavid du Colombier ep->ttype = Tctl;
4725d9de2d3SDavid du Colombier ep->tmout = Xfertmout;
4735d9de2d3SDavid du Colombier ep->mode = ORDWR;
4745d9de2d3SDavid du Colombier dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep);
4755d9de2d3SDavid du Colombier return ep;
4765d9de2d3SDavid du Colombier }
4775d9de2d3SDavid du Colombier
4785d9de2d3SDavid du Colombier /*
4795d9de2d3SDavid du Colombier * Create a new endpoint for the device
4805d9de2d3SDavid du Colombier * accessed via the given endpoint 0.
4815d9de2d3SDavid du Colombier */
4825d9de2d3SDavid du Colombier static Ep*
newdevep(Ep * ep,int i,int tt,int mode)4835d9de2d3SDavid du Colombier newdevep(Ep *ep, int i, int tt, int mode)
4845d9de2d3SDavid du Colombier {
4855d9de2d3SDavid du Colombier Ep *nep;
4865d9de2d3SDavid du Colombier Udev *d;
4875d9de2d3SDavid du Colombier
4885d9de2d3SDavid du Colombier d = ep->dev;
4895d9de2d3SDavid du Colombier if(d->eps[i] != nil)
4905d9de2d3SDavid du Colombier error("endpoint already in use");
4915d9de2d3SDavid du Colombier nep = epalloc(ep->hp);
4925d9de2d3SDavid du Colombier incref(ep);
4935d9de2d3SDavid du Colombier d->eps[i] = nep;
4945d9de2d3SDavid du Colombier nep->nb = i;
4955d9de2d3SDavid du Colombier nep->toggle[0] = nep->toggle[1] = 0;
4965d9de2d3SDavid du Colombier nep->ep0 = ep;
4975d9de2d3SDavid du Colombier nep->dev = ep->dev;
4985d9de2d3SDavid du Colombier nep->mode = mode;
4995d9de2d3SDavid du Colombier nep->ttype = tt;
5005d9de2d3SDavid du Colombier nep->debug = ep->debug;
5015d9de2d3SDavid du Colombier /* set defaults */
5025d9de2d3SDavid du Colombier switch(tt){
5035d9de2d3SDavid du Colombier case Tctl:
5045d9de2d3SDavid du Colombier nep->tmout = Xfertmout;
5055d9de2d3SDavid du Colombier break;
5065d9de2d3SDavid du Colombier case Tintr:
5075d9de2d3SDavid du Colombier nep->pollival = 10;
5085d9de2d3SDavid du Colombier break;
5095d9de2d3SDavid du Colombier case Tiso:
5105d9de2d3SDavid du Colombier nep->tmout = Xfertmout;
5115d9de2d3SDavid du Colombier nep->pollival = 10;
5125d9de2d3SDavid du Colombier nep->samplesz = 4;
5135d9de2d3SDavid du Colombier nep->hz = 44100;
5145d9de2d3SDavid du Colombier break;
5155d9de2d3SDavid du Colombier }
5165d9de2d3SDavid du Colombier deprint("newdevep ep%d.%d %#p\n", d->nb, nep->nb, nep);
5175d9de2d3SDavid du Colombier return ep;
5185d9de2d3SDavid du Colombier }
5195d9de2d3SDavid du Colombier
5205d9de2d3SDavid du Colombier static int
epdataperm(int mode)5215d9de2d3SDavid du Colombier epdataperm(int mode)
5225d9de2d3SDavid du Colombier {
523b4d1cf41SDavid du Colombier
5245d9de2d3SDavid du Colombier switch(mode){
5255d9de2d3SDavid du Colombier case OREAD:
5265d9de2d3SDavid du Colombier return 0440|DMEXCL;
5275d9de2d3SDavid du Colombier break;
5285d9de2d3SDavid du Colombier case OWRITE:
5295d9de2d3SDavid du Colombier return 0220|DMEXCL;
5305d9de2d3SDavid du Colombier break;
5315d9de2d3SDavid du Colombier default:
5325d9de2d3SDavid du Colombier return 0660|DMEXCL;
5335d9de2d3SDavid du Colombier }
5345d9de2d3SDavid du Colombier }
5355d9de2d3SDavid du Colombier
5365d9de2d3SDavid du Colombier static int
usbgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)5375d9de2d3SDavid du Colombier usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
5385d9de2d3SDavid du Colombier {
5395d9de2d3SDavid du Colombier Qid q;
540b4d1cf41SDavid du Colombier Dirtab *dir;
541b4d1cf41SDavid du Colombier int perm;
542b4d1cf41SDavid du Colombier char *se;
543b4d1cf41SDavid du Colombier Ep *ep;
544b4d1cf41SDavid du Colombier int nb;
545b4d1cf41SDavid du Colombier int mode;
5465d9de2d3SDavid du Colombier
5475d9de2d3SDavid du Colombier if(0)ddprint("usbgen q %#x s %d...", QID(c->qid), s);
5485d9de2d3SDavid du Colombier if(s == DEVDOTDOT){
5495d9de2d3SDavid du Colombier if(QID(c->qid) <= Qusbdir){
5505d9de2d3SDavid du Colombier mkqid(&q, Qdir, 0, QTDIR);
5515d9de2d3SDavid du Colombier devdir(c, q, "#u", 0, eve, 0555, dp);
5525d9de2d3SDavid du Colombier }else{
5535d9de2d3SDavid du Colombier mkqid(&q, Qusbdir, 0, QTDIR);
5545d9de2d3SDavid du Colombier devdir(c, q, "usb", 0, eve, 0555, dp);
5555d9de2d3SDavid du Colombier }
5565d9de2d3SDavid du Colombier if(0)ddprint("ok\n");
5575d9de2d3SDavid du Colombier return 1;
5585d9de2d3SDavid du Colombier }
5595d9de2d3SDavid du Colombier
5605d9de2d3SDavid du Colombier switch(QID(c->qid)){
5615d9de2d3SDavid du Colombier case Qdir: /* list #u */
5625d9de2d3SDavid du Colombier if(s == 0){
5635d9de2d3SDavid du Colombier mkqid(&q, Qusbdir, 0, QTDIR);
5645d9de2d3SDavid du Colombier devdir(c, q, "usb", 0, eve, 0555, dp);
5655d9de2d3SDavid du Colombier if(0)ddprint("ok\n");
5665d9de2d3SDavid du Colombier return 1;
5675d9de2d3SDavid du Colombier }
5685d9de2d3SDavid du Colombier s--;
5695d9de2d3SDavid du Colombier if(s < 0 || s >= epmax)
5705d9de2d3SDavid du Colombier goto Fail;
5715d9de2d3SDavid du Colombier ep = getep(s);
5725d9de2d3SDavid du Colombier if(ep == nil || ep->name == nil){
5735d9de2d3SDavid du Colombier if(ep != nil)
5745d9de2d3SDavid du Colombier putep(ep);
5755d9de2d3SDavid du Colombier if(0)ddprint("skip\n");
5765d9de2d3SDavid du Colombier return 0;
5775d9de2d3SDavid du Colombier }
5785d9de2d3SDavid du Colombier if(waserror()){
5795d9de2d3SDavid du Colombier putep(ep);
5805d9de2d3SDavid du Colombier nexterror();
5815d9de2d3SDavid du Colombier }
5825d9de2d3SDavid du Colombier mkqid(&q, Qep0io+s*4, 0, QTFILE);
5835d9de2d3SDavid du Colombier devdir(c, q, ep->name, 0, eve, epdataperm(ep->mode), dp);
5845d9de2d3SDavid du Colombier putep(ep);
5855d9de2d3SDavid du Colombier poperror();
5865d9de2d3SDavid du Colombier if(0)ddprint("ok\n");
5875d9de2d3SDavid du Colombier return 1;
5885d9de2d3SDavid du Colombier
5895d9de2d3SDavid du Colombier case Qusbdir: /* list #u/usb */
5905d9de2d3SDavid du Colombier Usbdir:
5915d9de2d3SDavid du Colombier if(s < nelem(usbdir)){
5925d9de2d3SDavid du Colombier dir = &usbdir[s];
5935d9de2d3SDavid du Colombier mkqid(&q, dir->qid.path, 0, QTFILE);
5945d9de2d3SDavid du Colombier devdir(c, q, dir->name, dir->length, eve, dir->perm, dp);
5955d9de2d3SDavid du Colombier if(0)ddprint("ok\n");
5965d9de2d3SDavid du Colombier return 1;
5975d9de2d3SDavid du Colombier }
5985d9de2d3SDavid du Colombier s -= nelem(usbdir);
5995d9de2d3SDavid du Colombier if(s < 0 || s >= epmax)
6005d9de2d3SDavid du Colombier goto Fail;
6015d9de2d3SDavid du Colombier ep = getep(s);
6025d9de2d3SDavid du Colombier if(ep == nil){
6035d9de2d3SDavid du Colombier if(0)ddprint("skip\n");
6045d9de2d3SDavid du Colombier return 0;
6055d9de2d3SDavid du Colombier }
6065d9de2d3SDavid du Colombier if(waserror()){
6075d9de2d3SDavid du Colombier putep(ep);
6085d9de2d3SDavid du Colombier nexterror();
6095d9de2d3SDavid du Colombier }
6105d9de2d3SDavid du Colombier se = up->genbuf+sizeof(up->genbuf);
6115d9de2d3SDavid du Colombier seprint(up->genbuf, se, "ep%d.%d", ep->dev->nb, ep->nb);
6125d9de2d3SDavid du Colombier mkqid(&q, Qep0dir+4*s, 0, QTDIR);
6135d9de2d3SDavid du Colombier putep(ep);
6145d9de2d3SDavid du Colombier poperror();
6155d9de2d3SDavid du Colombier devdir(c, q, up->genbuf, 0, eve, 0755, dp);
6165d9de2d3SDavid du Colombier if(0)ddprint("ok\n");
6175d9de2d3SDavid du Colombier return 1;
6185d9de2d3SDavid du Colombier
6195d9de2d3SDavid du Colombier case Qctl:
6205d9de2d3SDavid du Colombier s = 0;
6215d9de2d3SDavid du Colombier goto Usbdir;
6225d9de2d3SDavid du Colombier
6235d9de2d3SDavid du Colombier default: /* list #u/usb/epN.M */
6245d9de2d3SDavid du Colombier nb = qid2epidx(QID(c->qid));
6255d9de2d3SDavid du Colombier ep = getep(nb);
6265d9de2d3SDavid du Colombier if(ep == nil)
6275d9de2d3SDavid du Colombier goto Fail;
6285d9de2d3SDavid du Colombier mode = ep->mode;
6295d9de2d3SDavid du Colombier putep(ep);
6305d9de2d3SDavid du Colombier if(isqtype(QID(c->qid), Qepdir)){
6315d9de2d3SDavid du Colombier Epdir:
6325d9de2d3SDavid du Colombier switch(s){
6335d9de2d3SDavid du Colombier case 0:
6345d9de2d3SDavid du Colombier mkqid(&q, Qep0io+nb*4, 0, QTFILE);
6355d9de2d3SDavid du Colombier perm = epdataperm(mode);
6365d9de2d3SDavid du Colombier devdir(c, q, "data", 0, eve, perm, dp);
6375d9de2d3SDavid du Colombier break;
6385d9de2d3SDavid du Colombier case 1:
6395d9de2d3SDavid du Colombier mkqid(&q, Qep0ctl+nb*4, 0, QTFILE);
6405d9de2d3SDavid du Colombier devdir(c, q, "ctl", 0, eve, 0664, dp);
6415d9de2d3SDavid du Colombier break;
6425d9de2d3SDavid du Colombier default:
6435d9de2d3SDavid du Colombier goto Fail;
6445d9de2d3SDavid du Colombier }
6455d9de2d3SDavid du Colombier }else if(isqtype(QID(c->qid), Qepctl)){
6465d9de2d3SDavid du Colombier s = 1;
6475d9de2d3SDavid du Colombier goto Epdir;
6485d9de2d3SDavid du Colombier }else{
6495d9de2d3SDavid du Colombier s = 0;
6505d9de2d3SDavid du Colombier goto Epdir;
6515d9de2d3SDavid du Colombier }
6525d9de2d3SDavid du Colombier if(0)ddprint("ok\n");
6535d9de2d3SDavid du Colombier return 1;
6545d9de2d3SDavid du Colombier }
6555d9de2d3SDavid du Colombier Fail:
6565d9de2d3SDavid du Colombier if(0)ddprint("fail\n");
6575d9de2d3SDavid du Colombier return -1;
6585d9de2d3SDavid du Colombier }
6595d9de2d3SDavid du Colombier
6605d9de2d3SDavid du Colombier static Hci*
hciprobe(int cardno,int ctlrno)6615d9de2d3SDavid du Colombier hciprobe(int cardno, int ctlrno)
6625d9de2d3SDavid du Colombier {
6635d9de2d3SDavid du Colombier Hci *hp;
6645d9de2d3SDavid du Colombier char *type;
6655d9de2d3SDavid du Colombier char name[64];
6665d9de2d3SDavid du Colombier static int epnb = 1; /* guess the endpoint nb. for the controller */
6675d9de2d3SDavid du Colombier
6685d9de2d3SDavid du Colombier ddprint("hciprobe %d %d\n", cardno, ctlrno);
6695d9de2d3SDavid du Colombier hp = smalloc(sizeof(Hci));
6705d9de2d3SDavid du Colombier hp->ctlrno = ctlrno;
6715d9de2d3SDavid du Colombier
6725d9de2d3SDavid du Colombier if(cardno < 0)
6735d9de2d3SDavid du Colombier for(cardno = 0; cardno < Nhcis; cardno++){
6745d9de2d3SDavid du Colombier if(hcitypes[cardno].type == nil)
6755d9de2d3SDavid du Colombier break;
6765d9de2d3SDavid du Colombier type = hp->type;
6775d9de2d3SDavid du Colombier if(type==nil || *type==0)
6785d9de2d3SDavid du Colombier type = "uhci";
6795d9de2d3SDavid du Colombier if(cistrcmp(hcitypes[cardno].type, type) == 0)
6805d9de2d3SDavid du Colombier break;
6815d9de2d3SDavid du Colombier }
6825d9de2d3SDavid du Colombier
6835d9de2d3SDavid du Colombier if(cardno >= Nhcis || hcitypes[cardno].type == nil){
6845d9de2d3SDavid du Colombier free(hp);
6855d9de2d3SDavid du Colombier return nil;
6865d9de2d3SDavid du Colombier }
6875d9de2d3SDavid du Colombier dprint("%s...", hcitypes[cardno].type);
6885d9de2d3SDavid du Colombier if(hcitypes[cardno].reset(hp) < 0){
6895d9de2d3SDavid du Colombier free(hp);
6905d9de2d3SDavid du Colombier return nil;
6915d9de2d3SDavid du Colombier }
6925d9de2d3SDavid du Colombier
6935d9de2d3SDavid du Colombier snprint(name, sizeof(name), "usb%s", hcitypes[cardno].type);
6945d9de2d3SDavid du Colombier intrenable(hp->irq, hp->interrupt, hp, UNKNOWN, name);
6955d9de2d3SDavid du Colombier
6965d9de2d3SDavid du Colombier print("#u/usb/ep%d.0: %s: port %#luX irq %d\n",
6975d9de2d3SDavid du Colombier epnb, hcitypes[cardno].type, hp->port, hp->irq);
6985d9de2d3SDavid du Colombier epnb++;
6995d9de2d3SDavid du Colombier
7005d9de2d3SDavid du Colombier return hp;
7015d9de2d3SDavid du Colombier }
7025d9de2d3SDavid du Colombier
7035d9de2d3SDavid du Colombier static void
usbreset(void)7045d9de2d3SDavid du Colombier usbreset(void)
7055d9de2d3SDavid du Colombier {
7065d9de2d3SDavid du Colombier int cardno, ctlrno;
7075d9de2d3SDavid du Colombier Hci *hp;
7085d9de2d3SDavid du Colombier
7095d9de2d3SDavid du Colombier dprint("usbreset\n");
7105d9de2d3SDavid du Colombier
7115d9de2d3SDavid du Colombier for(ctlrno = 0; ctlrno < Nhcis; ctlrno++)
7125d9de2d3SDavid du Colombier if((hp = hciprobe(-1, ctlrno)) != nil)
7135d9de2d3SDavid du Colombier hcis[ctlrno] = hp;
7145d9de2d3SDavid du Colombier cardno = ctlrno = 0;
7155d9de2d3SDavid du Colombier while(cardno < Nhcis && ctlrno < Nhcis && hcitypes[cardno].type != nil)
7165d9de2d3SDavid du Colombier if(hcis[ctlrno] != nil)
7175d9de2d3SDavid du Colombier ctlrno++;
7185d9de2d3SDavid du Colombier else{
7195d9de2d3SDavid du Colombier hp = hciprobe(cardno, ctlrno);
7205d9de2d3SDavid du Colombier if(hp == nil)
7215d9de2d3SDavid du Colombier cardno++;
7225d9de2d3SDavid du Colombier hcis[ctlrno++] = hp;
7235d9de2d3SDavid du Colombier }
7245d9de2d3SDavid du Colombier if(hcis[Nhcis-1] != nil)
7255d9de2d3SDavid du Colombier print("usbreset: bug: Nhcis too small\n");
7265d9de2d3SDavid du Colombier }
7275d9de2d3SDavid du Colombier
728*5c47fe09SDavid du Colombier static int
numbits(uint n)729*5c47fe09SDavid du Colombier numbits(uint n)
730*5c47fe09SDavid du Colombier {
731*5c47fe09SDavid du Colombier int c = 0;
732*5c47fe09SDavid du Colombier while(n != 0){
733*5c47fe09SDavid du Colombier c++;
734*5c47fe09SDavid du Colombier n = (n-1) & n;
735*5c47fe09SDavid du Colombier }
736*5c47fe09SDavid du Colombier return c;
737*5c47fe09SDavid du Colombier }
738*5c47fe09SDavid du Colombier
7395d9de2d3SDavid du Colombier static void
usbinit(void)7405d9de2d3SDavid du Colombier usbinit(void)
7415d9de2d3SDavid du Colombier {
7425d9de2d3SDavid du Colombier Hci *hp;
7435d9de2d3SDavid du Colombier int ctlrno;
7445d9de2d3SDavid du Colombier Ep *d;
7455d9de2d3SDavid du Colombier char info[40];
7465d9de2d3SDavid du Colombier
7475d9de2d3SDavid du Colombier dprint("usbinit\n");
7485d9de2d3SDavid du Colombier for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){
7495d9de2d3SDavid du Colombier hp = hcis[ctlrno];
7505d9de2d3SDavid du Colombier if(hp != nil){
751*5c47fe09SDavid du Colombier int n;
752*5c47fe09SDavid du Colombier
753*5c47fe09SDavid du Colombier if(hp->init != nil){
754*5c47fe09SDavid du Colombier if(waserror()){
755*5c47fe09SDavid du Colombier print("usbinit: %s: %s\n", hp->type, up->errstr);
756*5c47fe09SDavid du Colombier continue;
757*5c47fe09SDavid du Colombier }
7585d9de2d3SDavid du Colombier hp->init(hp);
759*5c47fe09SDavid du Colombier poperror();
760*5c47fe09SDavid du Colombier }
761*5c47fe09SDavid du Colombier
762*5c47fe09SDavid du Colombier hp->superspeed &= (1<<hp->nports)-1;
763*5c47fe09SDavid du Colombier n = hp->nports - numbits(hp->superspeed);
764*5c47fe09SDavid du Colombier if(n > 0){
765*5c47fe09SDavid du Colombier d = newdev(hp, 1, 1); /* new LS/FS/HS root hub */
7665d9de2d3SDavid du Colombier d->maxpkt = 64;
767*5c47fe09SDavid du Colombier if(hp->highspeed != 0)
768*5c47fe09SDavid du Colombier d->dev->speed = Highspeed;
769*5c47fe09SDavid du Colombier d->dev->state = Denabled; /* although addr == 0 */
770*5c47fe09SDavid du Colombier snprint(info, sizeof(info), "roothub ports %d", n);
7715d9de2d3SDavid du Colombier kstrdup(&d->info, info);
7725d9de2d3SDavid du Colombier }
773*5c47fe09SDavid du Colombier n = numbits(hp->superspeed);
774*5c47fe09SDavid du Colombier if(n > 0){
775*5c47fe09SDavid du Colombier d = newdev(hp, 1, 1); /* new SS root hub */
776*5c47fe09SDavid du Colombier d->maxpkt = 512;
777*5c47fe09SDavid du Colombier d->dev->speed = Superspeed;
778*5c47fe09SDavid du Colombier d->dev->state = Denabled; /* although addr == 0 */
779*5c47fe09SDavid du Colombier snprint(info, sizeof(info), "roothub ports %d", n);
780*5c47fe09SDavid du Colombier kstrdup(&d->info, info);
781*5c47fe09SDavid du Colombier }
782*5c47fe09SDavid du Colombier }
7835d9de2d3SDavid du Colombier }
7845d9de2d3SDavid du Colombier }
7855d9de2d3SDavid du Colombier
7865d9de2d3SDavid du Colombier static Chan*
usbattach(char * spec)7875d9de2d3SDavid du Colombier usbattach(char *spec)
7885d9de2d3SDavid du Colombier {
7895d9de2d3SDavid du Colombier return devattach(L'u', spec);
7905d9de2d3SDavid du Colombier }
7915d9de2d3SDavid du Colombier
7925d9de2d3SDavid du Colombier static Walkqid*
usbwalk(Chan * c,Chan * nc,char ** name,int nname)7935d9de2d3SDavid du Colombier usbwalk(Chan *c, Chan *nc, char **name, int nname)
7945d9de2d3SDavid du Colombier {
7955d9de2d3SDavid du Colombier return devwalk(c, nc, name, nname, nil, 0, usbgen);
7965d9de2d3SDavid du Colombier }
7975d9de2d3SDavid du Colombier
7985d9de2d3SDavid du Colombier static int
usbstat(Chan * c,uchar * db,int n)7995d9de2d3SDavid du Colombier usbstat(Chan *c, uchar *db, int n)
8005d9de2d3SDavid du Colombier {
8015d9de2d3SDavid du Colombier return devstat(c, db, n, nil, 0, usbgen);
8025d9de2d3SDavid du Colombier }
8035d9de2d3SDavid du Colombier
8045d9de2d3SDavid du Colombier /*
8055d9de2d3SDavid du Colombier * µs for the given transfer, for bandwidth allocation.
8065d9de2d3SDavid du Colombier * This is a very rough worst case for what 5.11.3
8075d9de2d3SDavid du Colombier * of the usb 2.0 spec says.
8085d9de2d3SDavid du Colombier * Also, we are using maxpkt and not actual transfer sizes.
8095d9de2d3SDavid du Colombier * Only when we are sure we
8105d9de2d3SDavid du Colombier * are not exceeding b/w might we consider adjusting it.
8115d9de2d3SDavid du Colombier */
8125d9de2d3SDavid du Colombier static ulong
usbload(int speed,int maxpkt)8135d9de2d3SDavid du Colombier usbload(int speed, int maxpkt)
8145d9de2d3SDavid du Colombier {
8155d9de2d3SDavid du Colombier enum{ Hostns = 1000, Hubns = 333 };
816b4d1cf41SDavid du Colombier ulong l;
817b4d1cf41SDavid du Colombier ulong bs;
8185d9de2d3SDavid du Colombier
8195d9de2d3SDavid du Colombier l = 0;
8205d9de2d3SDavid du Colombier bs = 10UL * maxpkt;
8215d9de2d3SDavid du Colombier switch(speed){
8225d9de2d3SDavid du Colombier case Highspeed:
8235d9de2d3SDavid du Colombier l = 55*8*2 + 2 * (3 + bs) + Hostns;
8245d9de2d3SDavid du Colombier break;
8255d9de2d3SDavid du Colombier case Fullspeed:
8265d9de2d3SDavid du Colombier l = 9107 + 84 * (4 + bs) + Hostns;
8275d9de2d3SDavid du Colombier break;
8285d9de2d3SDavid du Colombier case Lowspeed:
8295d9de2d3SDavid du Colombier l = 64107 + 2 * Hubns + 667 * (3 + bs) + Hostns;
8305d9de2d3SDavid du Colombier break;
8315d9de2d3SDavid du Colombier default:
8325d9de2d3SDavid du Colombier print("usbload: bad speed %d\n", speed);
8335d9de2d3SDavid du Colombier /* let it run */
8345d9de2d3SDavid du Colombier }
8355d9de2d3SDavid du Colombier return l / 1000UL; /* in µs */
8365d9de2d3SDavid du Colombier }
8375d9de2d3SDavid du Colombier
8385d9de2d3SDavid du Colombier static Chan*
usbopen(Chan * c,int omode)8395d9de2d3SDavid du Colombier usbopen(Chan *c, int omode)
8405d9de2d3SDavid du Colombier {
841b4d1cf41SDavid du Colombier int q;
8425d9de2d3SDavid du Colombier Ep *ep;
843b4d1cf41SDavid du Colombier int mode;
8445d9de2d3SDavid du Colombier
8455d9de2d3SDavid du Colombier mode = openmode(omode);
8465d9de2d3SDavid du Colombier q = QID(c->qid);
8475d9de2d3SDavid du Colombier
8485d9de2d3SDavid du Colombier if(q >= Qep0dir && qid2epidx(q) < 0)
8495d9de2d3SDavid du Colombier error(Eio);
8505d9de2d3SDavid du Colombier if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
8515d9de2d3SDavid du Colombier return devopen(c, omode, nil, 0, usbgen);
8525d9de2d3SDavid du Colombier
8535d9de2d3SDavid du Colombier ep = getep(qid2epidx(q));
8545d9de2d3SDavid du Colombier if(ep == nil)
8555d9de2d3SDavid du Colombier error(Eio);
8565d9de2d3SDavid du Colombier deprint("usbopen q %#x fid %d omode %d\n", q, c->fid, mode);
8575d9de2d3SDavid du Colombier if(waserror()){
8585d9de2d3SDavid du Colombier putep(ep);
8595d9de2d3SDavid du Colombier nexterror();
8605d9de2d3SDavid du Colombier }
8615d9de2d3SDavid du Colombier qlock(ep);
8625d9de2d3SDavid du Colombier if(ep->inuse){
8635d9de2d3SDavid du Colombier qunlock(ep);
8645d9de2d3SDavid du Colombier error(Einuse);
8655d9de2d3SDavid du Colombier }
8665d9de2d3SDavid du Colombier ep->inuse = 1;
8675d9de2d3SDavid du Colombier qunlock(ep);
8685d9de2d3SDavid du Colombier if(waserror()){
8695d9de2d3SDavid du Colombier ep->inuse = 0;
8705d9de2d3SDavid du Colombier nexterror();
8715d9de2d3SDavid du Colombier }
8725d9de2d3SDavid du Colombier if(mode != OREAD && ep->mode == OREAD)
8735d9de2d3SDavid du Colombier error(Eperm);
8745d9de2d3SDavid du Colombier if(mode != OWRITE && ep->mode == OWRITE)
8755d9de2d3SDavid du Colombier error(Eperm);
8765d9de2d3SDavid du Colombier if(ep->ttype == Tnone)
8775d9de2d3SDavid du Colombier error(Enotconf);
8785d9de2d3SDavid du Colombier ep->clrhalt = 0;
8795d9de2d3SDavid du Colombier ep->rhrepl = -1;
880*5c47fe09SDavid du Colombier if(ep->load == 0 && ep->dev->speed != Superspeed)
8815d9de2d3SDavid du Colombier ep->load = usbload(ep->dev->speed, ep->maxpkt);
8825d9de2d3SDavid du Colombier ep->hp->epopen(ep);
8835d9de2d3SDavid du Colombier
8845d9de2d3SDavid du Colombier poperror(); /* ep->inuse */
8855d9de2d3SDavid du Colombier poperror(); /* don't putep(): ref kept for fid using the ep. */
8865d9de2d3SDavid du Colombier
8875d9de2d3SDavid du Colombier c->mode = mode;
8885d9de2d3SDavid du Colombier c->flag |= COPEN;
8895d9de2d3SDavid du Colombier c->offset = 0;
8905d9de2d3SDavid du Colombier c->aux = nil; /* paranoia */
8915d9de2d3SDavid du Colombier return c;
8925d9de2d3SDavid du Colombier }
8935d9de2d3SDavid du Colombier
8945d9de2d3SDavid du Colombier static void
epclose(Ep * ep)8955d9de2d3SDavid du Colombier epclose(Ep *ep)
8965d9de2d3SDavid du Colombier {
8975d9de2d3SDavid du Colombier qlock(ep);
8985d9de2d3SDavid du Colombier if(waserror()){
8995d9de2d3SDavid du Colombier qunlock(ep);
9005d9de2d3SDavid du Colombier nexterror();
9015d9de2d3SDavid du Colombier }
9025d9de2d3SDavid du Colombier if(ep->inuse){
9035d9de2d3SDavid du Colombier ep->hp->epclose(ep);
9045d9de2d3SDavid du Colombier ep->inuse = 0;
9055d9de2d3SDavid du Colombier }
9065d9de2d3SDavid du Colombier qunlock(ep);
9075d9de2d3SDavid du Colombier poperror();
9085d9de2d3SDavid du Colombier }
9095d9de2d3SDavid du Colombier
9105d9de2d3SDavid du Colombier static void
usbclose(Chan * c)9115d9de2d3SDavid du Colombier usbclose(Chan *c)
9125d9de2d3SDavid du Colombier {
9135d9de2d3SDavid du Colombier int q;
9145d9de2d3SDavid du Colombier Ep *ep;
9155d9de2d3SDavid du Colombier
9165d9de2d3SDavid du Colombier q = QID(c->qid);
9175d9de2d3SDavid du Colombier if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
9185d9de2d3SDavid du Colombier return;
9195d9de2d3SDavid du Colombier
9205d9de2d3SDavid du Colombier ep = getep(qid2epidx(q));
9215d9de2d3SDavid du Colombier if(ep == nil)
9225d9de2d3SDavid du Colombier return;
9235d9de2d3SDavid du Colombier deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref);
9245d9de2d3SDavid du Colombier if(waserror()){
9255d9de2d3SDavid du Colombier putep(ep);
9265d9de2d3SDavid du Colombier nexterror();
9275d9de2d3SDavid du Colombier }
9285d9de2d3SDavid du Colombier if(c->flag & COPEN){
9295d9de2d3SDavid du Colombier free(c->aux);
9305d9de2d3SDavid du Colombier c->aux = nil;
9315d9de2d3SDavid du Colombier epclose(ep);
9325d9de2d3SDavid du Colombier putep(ep); /* release ref kept since usbopen */
9335d9de2d3SDavid du Colombier c->flag &= ~COPEN;
9345d9de2d3SDavid du Colombier }
9355d9de2d3SDavid du Colombier poperror();
9365d9de2d3SDavid du Colombier putep(ep);
9375d9de2d3SDavid du Colombier }
9385d9de2d3SDavid du Colombier
9395d9de2d3SDavid du Colombier static long
ctlread(Chan * c,void * a,long n,vlong offset)9405d9de2d3SDavid du Colombier ctlread(Chan *c, void *a, long n, vlong offset)
9415d9de2d3SDavid du Colombier {
942b4d1cf41SDavid du Colombier int q;
943b4d1cf41SDavid du Colombier char *s;
944b4d1cf41SDavid du Colombier char *us;
945b4d1cf41SDavid du Colombier char *se;
9465d9de2d3SDavid du Colombier Ep *ep;
947b4d1cf41SDavid du Colombier int i;
9485d9de2d3SDavid du Colombier
9495d9de2d3SDavid du Colombier q = QID(c->qid);
9505d9de2d3SDavid du Colombier us = s = smalloc(READSTR);
9515d9de2d3SDavid du Colombier se = s + READSTR;
9525d9de2d3SDavid du Colombier if(waserror()){
9535d9de2d3SDavid du Colombier free(us);
9545d9de2d3SDavid du Colombier nexterror();
9555d9de2d3SDavid du Colombier }
9565d9de2d3SDavid du Colombier if(q == Qctl)
9575d9de2d3SDavid du Colombier for(i = 0; i < epmax; i++){
9585d9de2d3SDavid du Colombier ep = getep(i);
9595d9de2d3SDavid du Colombier if(ep != nil){
9605d9de2d3SDavid du Colombier if(waserror()){
9615d9de2d3SDavid du Colombier putep(ep);
9625d9de2d3SDavid du Colombier nexterror();
9635d9de2d3SDavid du Colombier }
964b4d1cf41SDavid du Colombier s = seprint(s, se, "ep%d.%d ", ep->dev->nb, ep->nb);
9655d9de2d3SDavid du Colombier s = seprintep(s, se, ep, 0);
9665d9de2d3SDavid du Colombier poperror();
9675d9de2d3SDavid du Colombier }
9685d9de2d3SDavid du Colombier putep(ep);
9695d9de2d3SDavid du Colombier }
9705d9de2d3SDavid du Colombier else{
9715d9de2d3SDavid du Colombier ep = getep(qid2epidx(q));
9725d9de2d3SDavid du Colombier if(ep == nil)
9735d9de2d3SDavid du Colombier error(Eio);
9745d9de2d3SDavid du Colombier if(waserror()){
9755d9de2d3SDavid du Colombier putep(ep);
9765d9de2d3SDavid du Colombier nexterror();
9775d9de2d3SDavid du Colombier }
9785d9de2d3SDavid du Colombier if(c->aux != nil){
979b4d1cf41SDavid du Colombier /* After a new endpoint request we read
9805d9de2d3SDavid du Colombier * the new endpoint name back.
9815d9de2d3SDavid du Colombier */
9825d9de2d3SDavid du Colombier strecpy(s, se, c->aux);
9835d9de2d3SDavid du Colombier free(c->aux);
9845d9de2d3SDavid du Colombier c->aux = nil;
9855d9de2d3SDavid du Colombier }else
9865d9de2d3SDavid du Colombier seprintep(s, se, ep, 0);
9875d9de2d3SDavid du Colombier poperror();
9885d9de2d3SDavid du Colombier putep(ep);
9895d9de2d3SDavid du Colombier }
9905d9de2d3SDavid du Colombier n = readstr(offset, a, n, us);
9915d9de2d3SDavid du Colombier poperror();
9925d9de2d3SDavid du Colombier free(us);
9935d9de2d3SDavid du Colombier return n;
9945d9de2d3SDavid du Colombier }
9955d9de2d3SDavid du Colombier
9965d9de2d3SDavid du Colombier /*
9975d9de2d3SDavid du Colombier * Fake root hub emulation.
9985d9de2d3SDavid du Colombier */
9995d9de2d3SDavid du Colombier static long
rhubread(Ep * ep,void * a,long n)10005d9de2d3SDavid du Colombier rhubread(Ep *ep, void *a, long n)
10015d9de2d3SDavid du Colombier {
1002*5c47fe09SDavid du Colombier uchar b[8];
10035d9de2d3SDavid du Colombier
1004*5c47fe09SDavid du Colombier if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2 || ep->rhrepl == -1)
10055d9de2d3SDavid du Colombier return -1;
10065d9de2d3SDavid du Colombier
1007*5c47fe09SDavid du Colombier b[0] = ep->rhrepl;
1008*5c47fe09SDavid du Colombier b[1] = ep->rhrepl>>8;
1009*5c47fe09SDavid du Colombier b[2] = ep->rhrepl>>16;
1010*5c47fe09SDavid du Colombier b[3] = ep->rhrepl>>24;
1011*5c47fe09SDavid du Colombier b[4] = ep->rhrepl>>32;
1012*5c47fe09SDavid du Colombier b[5] = ep->rhrepl>>40;
1013*5c47fe09SDavid du Colombier b[6] = ep->rhrepl>>48;
1014*5c47fe09SDavid du Colombier b[7] = ep->rhrepl>>56;
1015*5c47fe09SDavid du Colombier
10165d9de2d3SDavid du Colombier ep->rhrepl = -1;
1017*5c47fe09SDavid du Colombier
1018*5c47fe09SDavid du Colombier if(n > sizeof(b))
1019*5c47fe09SDavid du Colombier n = sizeof(b);
1020*5c47fe09SDavid du Colombier memmove(a, b, n);
1021*5c47fe09SDavid du Colombier
10225d9de2d3SDavid du Colombier return n;
10235d9de2d3SDavid du Colombier }
10245d9de2d3SDavid du Colombier
1025*5c47fe09SDavid du Colombier static int
rootport(Ep * ep,int port)1026*5c47fe09SDavid du Colombier rootport(Ep *ep, int port)
1027*5c47fe09SDavid du Colombier {
1028*5c47fe09SDavid du Colombier Hci *hp;
1029*5c47fe09SDavid du Colombier Udev *hub;
1030*5c47fe09SDavid du Colombier uint mask;
1031*5c47fe09SDavid du Colombier int rootport;
1032*5c47fe09SDavid du Colombier
1033*5c47fe09SDavid du Colombier hp = ep->hp;
1034*5c47fe09SDavid du Colombier hub = ep->dev;
1035*5c47fe09SDavid du Colombier if(!hub->isroot)
1036*5c47fe09SDavid du Colombier return hub->rootport;
1037*5c47fe09SDavid du Colombier
1038*5c47fe09SDavid du Colombier mask = hp->superspeed;
1039*5c47fe09SDavid du Colombier if(hub->speed != Superspeed)
1040*5c47fe09SDavid du Colombier mask = (1<<hp->nports)-1 & ~mask;
1041*5c47fe09SDavid du Colombier
1042*5c47fe09SDavid du Colombier for(rootport = 1; mask != 0; rootport++){
1043*5c47fe09SDavid du Colombier if(mask & 1){
1044*5c47fe09SDavid du Colombier if(--port == 0)
1045*5c47fe09SDavid du Colombier return rootport;
1046*5c47fe09SDavid du Colombier }
1047*5c47fe09SDavid du Colombier mask >>= 1;
1048*5c47fe09SDavid du Colombier }
1049*5c47fe09SDavid du Colombier
1050*5c47fe09SDavid du Colombier return 0;
1051*5c47fe09SDavid du Colombier }
1052*5c47fe09SDavid du Colombier
10535d9de2d3SDavid du Colombier static long
rhubwrite(Ep * ep,void * a,long n)10545d9de2d3SDavid du Colombier rhubwrite(Ep *ep, void *a, long n)
10555d9de2d3SDavid du Colombier {
10565d9de2d3SDavid du Colombier uchar *s;
1057b4d1cf41SDavid du Colombier int cmd;
1058b4d1cf41SDavid du Colombier int feature;
1059b4d1cf41SDavid du Colombier int port;
10605d9de2d3SDavid du Colombier Hci *hp;
10615d9de2d3SDavid du Colombier
10625d9de2d3SDavid du Colombier if(ep->dev == nil || ep->dev->isroot == 0 || ep->nb != 0)
10635d9de2d3SDavid du Colombier return -1;
10645d9de2d3SDavid du Colombier if(n != Rsetuplen)
10655d9de2d3SDavid du Colombier error("root hub is a toy hub");
10665d9de2d3SDavid du Colombier ep->rhrepl = -1;
10675d9de2d3SDavid du Colombier s = a;
10685d9de2d3SDavid du Colombier if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother))
10695d9de2d3SDavid du Colombier error("root hub is a toy hub");
10705d9de2d3SDavid du Colombier hp = ep->hp;
10715d9de2d3SDavid du Colombier cmd = s[Rreq];
10725d9de2d3SDavid du Colombier feature = GET2(s+Rvalue);
1073*5c47fe09SDavid du Colombier port = rootport(ep, GET2(s+Rindex));
1074*5c47fe09SDavid du Colombier if(port == 0)
10755d9de2d3SDavid du Colombier error("bad hub port number");
10765d9de2d3SDavid du Colombier switch(feature){
10775d9de2d3SDavid du Colombier case Rportenable:
10785d9de2d3SDavid du Colombier ep->rhrepl = hp->portenable(hp, port, cmd == Rsetfeature);
10795d9de2d3SDavid du Colombier break;
10805d9de2d3SDavid du Colombier case Rportreset:
10815d9de2d3SDavid du Colombier ep->rhrepl = hp->portreset(hp, port, cmd == Rsetfeature);
10825d9de2d3SDavid du Colombier break;
10835d9de2d3SDavid du Colombier case Rgetstatus:
10845d9de2d3SDavid du Colombier ep->rhrepl = hp->portstatus(hp, port);
10855d9de2d3SDavid du Colombier break;
10865d9de2d3SDavid du Colombier default:
10875d9de2d3SDavid du Colombier ep->rhrepl = 0;
10885d9de2d3SDavid du Colombier }
10895d9de2d3SDavid du Colombier return n;
10905d9de2d3SDavid du Colombier }
10915d9de2d3SDavid du Colombier
10925d9de2d3SDavid du Colombier static long
usbread(Chan * c,void * a,long n,vlong offset)10935d9de2d3SDavid du Colombier usbread(Chan *c, void *a, long n, vlong offset)
10945d9de2d3SDavid du Colombier {
1095b4d1cf41SDavid du Colombier int q;
10965d9de2d3SDavid du Colombier Ep *ep;
1097b4d1cf41SDavid du Colombier int nr;
10985d9de2d3SDavid du Colombier
10995d9de2d3SDavid du Colombier q = QID(c->qid);
11005d9de2d3SDavid du Colombier
11015d9de2d3SDavid du Colombier if(c->qid.type == QTDIR)
11025d9de2d3SDavid du Colombier return devdirread(c, a, n, nil, 0, usbgen);
11035d9de2d3SDavid du Colombier
11045d9de2d3SDavid du Colombier if(q == Qctl || isqtype(q, Qepctl))
11055d9de2d3SDavid du Colombier return ctlread(c, a, n, offset);
11065d9de2d3SDavid du Colombier
11075d9de2d3SDavid du Colombier ep = getep(qid2epidx(q));
11085d9de2d3SDavid du Colombier if(ep == nil)
11095d9de2d3SDavid du Colombier error(Eio);
11105d9de2d3SDavid du Colombier if(waserror()){
11115d9de2d3SDavid du Colombier putep(ep);
11125d9de2d3SDavid du Colombier nexterror();
11135d9de2d3SDavid du Colombier }
11145d9de2d3SDavid du Colombier if(ep->dev->state == Ddetach)
11155d9de2d3SDavid du Colombier error(Edetach);
11165d9de2d3SDavid du Colombier if(ep->mode == OWRITE || ep->inuse == 0)
11175d9de2d3SDavid du Colombier error(Ebadusefd);
11185d9de2d3SDavid du Colombier switch(ep->ttype){
11195d9de2d3SDavid du Colombier case Tnone:
11205d9de2d3SDavid du Colombier error("endpoint not configured");
11215d9de2d3SDavid du Colombier case Tctl:
11225d9de2d3SDavid du Colombier nr = rhubread(ep, a, n);
11235d9de2d3SDavid du Colombier if(nr >= 0){
11245d9de2d3SDavid du Colombier n = nr;
11255d9de2d3SDavid du Colombier break;
11265d9de2d3SDavid du Colombier }
1127b4d1cf41SDavid du Colombier /* else fall */
11285d9de2d3SDavid du Colombier default:
1129b4d1cf41SDavid du Colombier ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",q,c->fid,n,offset);
11305d9de2d3SDavid du Colombier n = ep->hp->epread(ep, a, n);
11315d9de2d3SDavid du Colombier break;
11325d9de2d3SDavid du Colombier }
11335d9de2d3SDavid du Colombier poperror();
11345d9de2d3SDavid du Colombier putep(ep);
11355d9de2d3SDavid du Colombier return n;
11365d9de2d3SDavid du Colombier }
11375d9de2d3SDavid du Colombier
11385d9de2d3SDavid du Colombier static long
pow2(int n)11395d9de2d3SDavid du Colombier pow2(int n)
11405d9de2d3SDavid du Colombier {
11415d9de2d3SDavid du Colombier return 1 << n;
11425d9de2d3SDavid du Colombier }
11435d9de2d3SDavid du Colombier
11445d9de2d3SDavid du Colombier static void
setmaxpkt(Ep * ep,char * s)11455d9de2d3SDavid du Colombier setmaxpkt(Ep *ep, char* s)
11465d9de2d3SDavid du Colombier {
1147*5c47fe09SDavid du Colombier long spp, max; /* samples per packet */
11485d9de2d3SDavid du Colombier
1149*5c47fe09SDavid du Colombier if(ep->dev->speed == Fullspeed)
11505d9de2d3SDavid du Colombier spp = (ep->hz * ep->pollival + 999) / 1000;
1151*5c47fe09SDavid du Colombier else
1152*5c47fe09SDavid du Colombier spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000;
11535d9de2d3SDavid du Colombier ep->maxpkt = spp * ep->samplesz;
11545d9de2d3SDavid du Colombier deprint("usb: %s: setmaxpkt: hz %ld poll %ld"
11555d9de2d3SDavid du Colombier " ntds %d %s speed -> spp %ld maxpkt %ld\n", s,
11565d9de2d3SDavid du Colombier ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed],
11575d9de2d3SDavid du Colombier spp, ep->maxpkt);
1158*5c47fe09SDavid du Colombier switch(ep->dev->speed){
1159*5c47fe09SDavid du Colombier case Fullspeed:
1160*5c47fe09SDavid du Colombier max = 1024;
1161*5c47fe09SDavid du Colombier break;
1162*5c47fe09SDavid du Colombier case Highspeed:
1163*5c47fe09SDavid du Colombier max = 3*1024;
1164*5c47fe09SDavid du Colombier break;
1165*5c47fe09SDavid du Colombier case Superspeed:
1166*5c47fe09SDavid du Colombier max = 48*1024;
1167*5c47fe09SDavid du Colombier break;
1168*5c47fe09SDavid du Colombier default:
1169*5c47fe09SDavid du Colombier return;
1170*5c47fe09SDavid du Colombier }
1171*5c47fe09SDavid du Colombier if(ep->maxpkt*ep->ntds > max){
1172*5c47fe09SDavid du Colombier print("usb: %s: maxpkt %ld > %ld for %s, truncating\n",
1173*5c47fe09SDavid du Colombier s, ep->maxpkt*ep->ntds, max, spname[ep->dev->speed]);
1174*5c47fe09SDavid du Colombier ep->maxpkt = max/ep->ntds;
11755d9de2d3SDavid du Colombier }
11765d9de2d3SDavid du Colombier }
11775d9de2d3SDavid du Colombier
11785d9de2d3SDavid du Colombier /*
1179b4d1cf41SDavid du Colombier * Many endpoint ctls. simply update the portable representation
11805d9de2d3SDavid du Colombier * of the endpoint. The actual controller driver will look
11815d9de2d3SDavid du Colombier * at them to setup the endpoints as dictated.
11825d9de2d3SDavid du Colombier */
11835d9de2d3SDavid du Colombier static long
epctl(Ep * ep,Chan * c,void * a,long n)11845d9de2d3SDavid du Colombier epctl(Ep *ep, Chan *c, void *a, long n)
11855d9de2d3SDavid du Colombier {
11865d9de2d3SDavid du Colombier int i, l, mode, nb, tt;
11875d9de2d3SDavid du Colombier char *b, *s;
11885d9de2d3SDavid du Colombier Cmdbuf *cb;
11895d9de2d3SDavid du Colombier Cmdtab *ct;
11905d9de2d3SDavid du Colombier Ep *nep;
11915d9de2d3SDavid du Colombier Udev *d;
11925d9de2d3SDavid du Colombier static char *Info = "info ";
11935d9de2d3SDavid du Colombier
11945d9de2d3SDavid du Colombier d = ep->dev;
11955d9de2d3SDavid du Colombier
11965d9de2d3SDavid du Colombier cb = parsecmd(a, n);
11975d9de2d3SDavid du Colombier if(waserror()){
11985d9de2d3SDavid du Colombier free(cb);
11995d9de2d3SDavid du Colombier nexterror();
12005d9de2d3SDavid du Colombier }
12015d9de2d3SDavid du Colombier ct = lookupcmd(cb, epctls, nelem(epctls));
12025d9de2d3SDavid du Colombier if(ct == nil)
12035d9de2d3SDavid du Colombier error(Ebadctl);
12045d9de2d3SDavid du Colombier i = ct->index;
12055d9de2d3SDavid du Colombier if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset)
12065d9de2d3SDavid du Colombier if(ep != ep->ep0)
12075d9de2d3SDavid du Colombier error("allowed only on a setup endpoint");
12085d9de2d3SDavid du Colombier if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname)
12095d9de2d3SDavid du Colombier if(ep != ep->ep0 && ep->inuse != 0)
12105d9de2d3SDavid du Colombier error("must configure before using");
12115d9de2d3SDavid du Colombier switch(i){
12125d9de2d3SDavid du Colombier case CMnew:
12135d9de2d3SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
12145d9de2d3SDavid du Colombier nb = strtol(cb->f[1], nil, 0);
12155d9de2d3SDavid du Colombier if(nb < 0 || nb >= Ndeveps)
12165d9de2d3SDavid du Colombier error("bad endpoint number");
12175d9de2d3SDavid du Colombier tt = name2ttype(cb->f[2]);
12185d9de2d3SDavid du Colombier if(tt == Tnone)
12195d9de2d3SDavid du Colombier error("unknown endpoint type");
12205d9de2d3SDavid du Colombier mode = name2mode(cb->f[3]);
12215d9de2d3SDavid du Colombier if(mode < 0)
12225d9de2d3SDavid du Colombier error("unknown i/o mode");
12235d9de2d3SDavid du Colombier newdevep(ep, nb, tt, mode);
12245d9de2d3SDavid du Colombier break;
12255d9de2d3SDavid du Colombier case CMnewdev:
12265d9de2d3SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
12275d9de2d3SDavid du Colombier if(ep != ep->ep0 || d->ishub == 0)
12285d9de2d3SDavid du Colombier error("not a hub setup endpoint");
12295d9de2d3SDavid du Colombier l = name2speed(cb->f[1]);
12305d9de2d3SDavid du Colombier if(l == Nospeed)
1231*5c47fe09SDavid du Colombier error("speed must be full|low|high|super");
1232*5c47fe09SDavid du Colombier if(l != d->speed && (l == Superspeed || d->speed == Superspeed))
1233*5c47fe09SDavid du Colombier error("wrong speed for superspeed hub/device");
12345d9de2d3SDavid du Colombier nep = newdev(ep->hp, 0, 0);
12355d9de2d3SDavid du Colombier nep->dev->speed = l;
1236*5c47fe09SDavid du Colombier if(l == Superspeed)
1237*5c47fe09SDavid du Colombier nep->maxpkt = 512;
1238*5c47fe09SDavid du Colombier else if(l != Lowspeed)
12395d9de2d3SDavid du Colombier nep->maxpkt = 64; /* assume full speed */
1240*5c47fe09SDavid du Colombier nep->dev->hub = d->addr;
12415d9de2d3SDavid du Colombier nep->dev->port = atoi(cb->f[2]);
1242*5c47fe09SDavid du Colombier nep->dev->depth = d->depth+1;
1243*5c47fe09SDavid du Colombier nep->dev->rootport = rootport(ep, nep->dev->port);
1244*5c47fe09SDavid du Colombier nep->dev->routestr = d->routestr | (((nep->dev->port&15) << 4*nep->dev->depth) >> 4);
1245b4d1cf41SDavid du Colombier /* next read request will read
12465d9de2d3SDavid du Colombier * the name for the new endpoint
12475d9de2d3SDavid du Colombier */
12485d9de2d3SDavid du Colombier l = sizeof(up->genbuf);
12495d9de2d3SDavid du Colombier snprint(up->genbuf, l, "ep%d.%d", nep->dev->nb, nep->nb);
12505d9de2d3SDavid du Colombier kstrdup(&c->aux, up->genbuf);
12515d9de2d3SDavid du Colombier break;
12525d9de2d3SDavid du Colombier case CMhub:
12535d9de2d3SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
12545d9de2d3SDavid du Colombier d->ishub = 1;
12555d9de2d3SDavid du Colombier break;
12565d9de2d3SDavid du Colombier case CMspeed:
12575d9de2d3SDavid du Colombier l = name2speed(cb->f[1]);
12585d9de2d3SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l);
12595d9de2d3SDavid du Colombier if(l == Nospeed)
1260*5c47fe09SDavid du Colombier error("speed must be full|low|high|super");
1261*5c47fe09SDavid du Colombier if(l != d->speed && (l == Superspeed || d->speed == Superspeed))
1262*5c47fe09SDavid du Colombier error("cannot change speed on superspeed device");
12635d9de2d3SDavid du Colombier qlock(ep->ep0);
12645d9de2d3SDavid du Colombier d->speed = l;
12655d9de2d3SDavid du Colombier qunlock(ep->ep0);
12665d9de2d3SDavid du Colombier break;
12675d9de2d3SDavid du Colombier case CMmaxpkt:
12685d9de2d3SDavid du Colombier l = strtoul(cb->f[1], nil, 0);
12695d9de2d3SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l);
12705d9de2d3SDavid du Colombier if(l < 1 || l > 1024)
12715d9de2d3SDavid du Colombier error("maxpkt not in [1:1024]");
12725d9de2d3SDavid du Colombier qlock(ep);
12735d9de2d3SDavid du Colombier ep->maxpkt = l;
12745d9de2d3SDavid du Colombier qunlock(ep);
12755d9de2d3SDavid du Colombier break;
12765d9de2d3SDavid du Colombier case CMntds:
12775d9de2d3SDavid du Colombier l = strtoul(cb->f[1], nil, 0);
12785d9de2d3SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l);
12795d9de2d3SDavid du Colombier if(l < 1 || l > 3)
12805d9de2d3SDavid du Colombier error("ntds not in [1:3]");
12815d9de2d3SDavid du Colombier qlock(ep);
12825d9de2d3SDavid du Colombier ep->ntds = l;
12835d9de2d3SDavid du Colombier qunlock(ep);
12845d9de2d3SDavid du Colombier break;
12855d9de2d3SDavid du Colombier case CMpollival:
12865d9de2d3SDavid du Colombier if(ep->ttype != Tintr && ep->ttype != Tiso)
12875d9de2d3SDavid du Colombier error("not an intr or iso endpoint");
12885d9de2d3SDavid du Colombier l = strtoul(cb->f[1], nil, 0);
12895d9de2d3SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l);
12905d9de2d3SDavid du Colombier if(ep->ttype == Tiso ||
12915d9de2d3SDavid du Colombier (ep->ttype == Tintr && ep->dev->speed == Highspeed)){
12925d9de2d3SDavid du Colombier if(l < 1 || l > 16)
12935d9de2d3SDavid du Colombier error("pollival power not in [1:16]");
12945d9de2d3SDavid du Colombier l = pow2(l-1);
12955d9de2d3SDavid du Colombier }else
12965d9de2d3SDavid du Colombier if(l < 1 || l > 255)
12975d9de2d3SDavid du Colombier error("pollival not in [1:255]");
12985d9de2d3SDavid du Colombier qlock(ep);
12995d9de2d3SDavid du Colombier ep->pollival = l;
13005d9de2d3SDavid du Colombier if(ep->ttype == Tiso)
13015d9de2d3SDavid du Colombier setmaxpkt(ep, "pollival");
13025d9de2d3SDavid du Colombier qunlock(ep);
13035d9de2d3SDavid du Colombier break;
13045d9de2d3SDavid du Colombier case CMsamplesz:
13055d9de2d3SDavid du Colombier if(ep->ttype != Tiso)
13065d9de2d3SDavid du Colombier error("not an iso endpoint");
13075d9de2d3SDavid du Colombier l = strtoul(cb->f[1], nil, 0);
13085d9de2d3SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l);
13095d9de2d3SDavid du Colombier if(l <= 0 || l > 8)
13105d9de2d3SDavid du Colombier error("samplesz not in [1:8]");
13115d9de2d3SDavid du Colombier qlock(ep);
13125d9de2d3SDavid du Colombier ep->samplesz = l;
13135d9de2d3SDavid du Colombier setmaxpkt(ep, "samplesz");
13145d9de2d3SDavid du Colombier qunlock(ep);
13155d9de2d3SDavid du Colombier break;
13165d9de2d3SDavid du Colombier case CMhz:
13175d9de2d3SDavid du Colombier if(ep->ttype != Tiso)
13185d9de2d3SDavid du Colombier error("not an iso endpoint");
13195d9de2d3SDavid du Colombier l = strtoul(cb->f[1], nil, 0);
13205d9de2d3SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l);
13215d9de2d3SDavid du Colombier if(l <= 0 || l > 100000)
13225d9de2d3SDavid du Colombier error("hz not in [1:100000]");
13235d9de2d3SDavid du Colombier qlock(ep);
13245d9de2d3SDavid du Colombier ep->hz = l;
13255d9de2d3SDavid du Colombier setmaxpkt(ep, "hz");
13265d9de2d3SDavid du Colombier qunlock(ep);
13275d9de2d3SDavid du Colombier break;
13285d9de2d3SDavid du Colombier case CMclrhalt:
13295d9de2d3SDavid du Colombier qlock(ep);
13305d9de2d3SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
13315d9de2d3SDavid du Colombier ep->clrhalt = 1;
13325d9de2d3SDavid du Colombier qunlock(ep);
13335d9de2d3SDavid du Colombier break;
13345d9de2d3SDavid du Colombier case CMinfo:
13355d9de2d3SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
13365d9de2d3SDavid du Colombier l = strlen(Info);
13375d9de2d3SDavid du Colombier s = a;
13385d9de2d3SDavid du Colombier if(n < l+2 || strncmp(Info, s, l) != 0)
13395d9de2d3SDavid du Colombier error(Ebadctl);
13405d9de2d3SDavid du Colombier if(n > 1024)
13415d9de2d3SDavid du Colombier n = 1024;
13425d9de2d3SDavid du Colombier b = smalloc(n);
13435d9de2d3SDavid du Colombier memmove(b, s+l, n-l);
13445d9de2d3SDavid du Colombier b[n-l] = 0;
13455d9de2d3SDavid du Colombier if(b[n-l-1] == '\n')
13465d9de2d3SDavid du Colombier b[n-l-1] = 0;
13475d9de2d3SDavid du Colombier qlock(ep);
13485d9de2d3SDavid du Colombier free(ep->info);
13495d9de2d3SDavid du Colombier ep->info = b;
13505d9de2d3SDavid du Colombier qunlock(ep);
13515d9de2d3SDavid du Colombier break;
13525d9de2d3SDavid du Colombier case CMaddress:
13535d9de2d3SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
1354*5c47fe09SDavid du Colombier if(ep->dev->addr == 0)
1355*5c47fe09SDavid du Colombier ep->dev->addr = ep->dev->nb;
13565d9de2d3SDavid du Colombier ep->dev->state = Denabled;
13575d9de2d3SDavid du Colombier break;
13585d9de2d3SDavid du Colombier case CMdetach:
13595d9de2d3SDavid du Colombier if(ep->dev->isroot != 0)
13605d9de2d3SDavid du Colombier error("can't detach a root hub");
13615d9de2d3SDavid du Colombier deprint("usb epctl %s ep%d.%d\n",
13625d9de2d3SDavid du Colombier cb->f[0], ep->dev->nb, ep->nb);
13635d9de2d3SDavid du Colombier ep->dev->state = Ddetach;
13645d9de2d3SDavid du Colombier /* Release file system ref. for its endpoints */
13655d9de2d3SDavid du Colombier for(i = 0; i < nelem(ep->dev->eps); i++)
13665d9de2d3SDavid du Colombier putep(ep->dev->eps[i]);
13675d9de2d3SDavid du Colombier break;
13685d9de2d3SDavid du Colombier case CMdebugep:
13695d9de2d3SDavid du Colombier if(strcmp(cb->f[1], "on") == 0)
13705d9de2d3SDavid du Colombier ep->debug = 1;
13715d9de2d3SDavid du Colombier else if(strcmp(cb->f[1], "off") == 0)
13725d9de2d3SDavid du Colombier ep->debug = 0;
13735d9de2d3SDavid du Colombier else
13745d9de2d3SDavid du Colombier ep->debug = strtoul(cb->f[1], nil, 0);
13755d9de2d3SDavid du Colombier print("usb: ep%d.%d debug %d\n",
13765d9de2d3SDavid du Colombier ep->dev->nb, ep->nb, ep->debug);
13775d9de2d3SDavid du Colombier break;
13785d9de2d3SDavid du Colombier case CMname:
13795d9de2d3SDavid du Colombier deprint("usb epctl %s %s\n", cb->f[0], cb->f[1]);
13805d9de2d3SDavid du Colombier validname(cb->f[1], 0);
13815d9de2d3SDavid du Colombier kstrdup(&ep->name, cb->f[1]);
13825d9de2d3SDavid du Colombier break;
13835d9de2d3SDavid du Colombier case CMtmout:
13845d9de2d3SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
13855d9de2d3SDavid du Colombier if(ep->ttype == Tiso || ep->ttype == Tctl)
13865d9de2d3SDavid du Colombier error("ctl ignored for this endpoint type");
13875d9de2d3SDavid du Colombier ep->tmout = strtoul(cb->f[1], nil, 0);
13885d9de2d3SDavid du Colombier if(ep->tmout != 0 && ep->tmout < Xfertmout)
13895d9de2d3SDavid du Colombier ep->tmout = Xfertmout;
13905d9de2d3SDavid du Colombier break;
13915d9de2d3SDavid du Colombier case CMpreset:
13925d9de2d3SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
13935d9de2d3SDavid du Colombier if(ep->ttype != Tctl)
13945d9de2d3SDavid du Colombier error("not a control endpoint");
13955d9de2d3SDavid du Colombier if(ep->dev->state != Denabled)
13965d9de2d3SDavid du Colombier error("forbidden on devices not enabled");
13975d9de2d3SDavid du Colombier ep->dev->state = Dreset;
13985d9de2d3SDavid du Colombier break;
13995d9de2d3SDavid du Colombier default:
14005d9de2d3SDavid du Colombier panic("usb: unknown epctl %d", ct->index);
14015d9de2d3SDavid du Colombier }
14025d9de2d3SDavid du Colombier free(cb);
14035d9de2d3SDavid du Colombier poperror();
14045d9de2d3SDavid du Colombier return n;
14055d9de2d3SDavid du Colombier }
14065d9de2d3SDavid du Colombier
14075d9de2d3SDavid du Colombier static long
usbctl(void * a,long n)14085d9de2d3SDavid du Colombier usbctl(void *a, long n)
14095d9de2d3SDavid du Colombier {
14105d9de2d3SDavid du Colombier Cmdtab *ct;
14115d9de2d3SDavid du Colombier Cmdbuf *cb;
14125d9de2d3SDavid du Colombier Ep *ep;
14135d9de2d3SDavid du Colombier int i;
14145d9de2d3SDavid du Colombier
14155d9de2d3SDavid du Colombier cb = parsecmd(a, n);
14165d9de2d3SDavid du Colombier if(waserror()){
14175d9de2d3SDavid du Colombier free(cb);
14185d9de2d3SDavid du Colombier nexterror();
14195d9de2d3SDavid du Colombier }
14205d9de2d3SDavid du Colombier ct = lookupcmd(cb, usbctls, nelem(usbctls));
14215d9de2d3SDavid du Colombier dprint("usb ctl %s\n", cb->f[0]);
14225d9de2d3SDavid du Colombier switch(ct->index){
14235d9de2d3SDavid du Colombier case CMdebug:
14245d9de2d3SDavid du Colombier if(strcmp(cb->f[1], "on") == 0)
14255d9de2d3SDavid du Colombier debug = 1;
14265d9de2d3SDavid du Colombier else if(strcmp(cb->f[1], "off") == 0)
14275d9de2d3SDavid du Colombier debug = 0;
14285d9de2d3SDavid du Colombier else
14295d9de2d3SDavid du Colombier debug = strtol(cb->f[1], nil, 0);
14305d9de2d3SDavid du Colombier print("usb: debug %d\n", debug);
14315d9de2d3SDavid du Colombier for(i = 0; i < epmax; i++)
14325d9de2d3SDavid du Colombier if((ep = getep(i)) != nil){
14335d9de2d3SDavid du Colombier ep->hp->debug(ep->hp, debug);
14345d9de2d3SDavid du Colombier putep(ep);
14355d9de2d3SDavid du Colombier }
14365d9de2d3SDavid du Colombier break;
14375d9de2d3SDavid du Colombier case CMdump:
14385d9de2d3SDavid du Colombier dumpeps();
14395d9de2d3SDavid du Colombier break;
14405d9de2d3SDavid du Colombier }
14415d9de2d3SDavid du Colombier free(cb);
14425d9de2d3SDavid du Colombier poperror();
14435d9de2d3SDavid du Colombier return n;
14445d9de2d3SDavid du Colombier }
14455d9de2d3SDavid du Colombier
14465d9de2d3SDavid du Colombier static long
ctlwrite(Chan * c,void * a,long n)14475d9de2d3SDavid du Colombier ctlwrite(Chan *c, void *a, long n)
14485d9de2d3SDavid du Colombier {
14495d9de2d3SDavid du Colombier int q;
14505d9de2d3SDavid du Colombier Ep *ep;
14515d9de2d3SDavid du Colombier
14525d9de2d3SDavid du Colombier q = QID(c->qid);
14535d9de2d3SDavid du Colombier if(q == Qctl)
14545d9de2d3SDavid du Colombier return usbctl(a, n);
14555d9de2d3SDavid du Colombier
14565d9de2d3SDavid du Colombier ep = getep(qid2epidx(q));
14575d9de2d3SDavid du Colombier if(ep == nil)
14585d9de2d3SDavid du Colombier error(Eio);
14595d9de2d3SDavid du Colombier if(waserror()){
14605d9de2d3SDavid du Colombier putep(ep);
14615d9de2d3SDavid du Colombier nexterror();
14625d9de2d3SDavid du Colombier }
14635d9de2d3SDavid du Colombier if(ep->dev->state == Ddetach)
14645d9de2d3SDavid du Colombier error(Edetach);
14655d9de2d3SDavid du Colombier if(isqtype(q, Qepctl) && c->aux != nil){
14665d9de2d3SDavid du Colombier /* Be sure we don't keep a cloned ep name */
14675d9de2d3SDavid du Colombier free(c->aux);
14685d9de2d3SDavid du Colombier c->aux = nil;
14695d9de2d3SDavid du Colombier error("read, not write, expected");
14705d9de2d3SDavid du Colombier }
14715d9de2d3SDavid du Colombier n = epctl(ep, c, a, n);
14725d9de2d3SDavid du Colombier putep(ep);
14735d9de2d3SDavid du Colombier poperror();
14745d9de2d3SDavid du Colombier return n;
14755d9de2d3SDavid du Colombier }
14765d9de2d3SDavid du Colombier
14775d9de2d3SDavid du Colombier static long
usbwrite(Chan * c,void * a,long n,vlong off)14785d9de2d3SDavid du Colombier usbwrite(Chan *c, void *a, long n, vlong off)
14795d9de2d3SDavid du Colombier {
14805d9de2d3SDavid du Colombier int nr, q;
14815d9de2d3SDavid du Colombier Ep *ep;
14825d9de2d3SDavid du Colombier
14835d9de2d3SDavid du Colombier if(c->qid.type == QTDIR)
14845d9de2d3SDavid du Colombier error(Eisdir);
14855d9de2d3SDavid du Colombier
14865d9de2d3SDavid du Colombier q = QID(c->qid);
14875d9de2d3SDavid du Colombier
14885d9de2d3SDavid du Colombier if(q == Qctl || isqtype(q, Qepctl))
14895d9de2d3SDavid du Colombier return ctlwrite(c, a, n);
14905d9de2d3SDavid du Colombier
14915d9de2d3SDavid du Colombier ep = getep(qid2epidx(q));
14925d9de2d3SDavid du Colombier if(ep == nil)
14935d9de2d3SDavid du Colombier error(Eio);
14945d9de2d3SDavid du Colombier if(waserror()){
14955d9de2d3SDavid du Colombier putep(ep);
14965d9de2d3SDavid du Colombier nexterror();
14975d9de2d3SDavid du Colombier }
14985d9de2d3SDavid du Colombier if(ep->dev->state == Ddetach)
14995d9de2d3SDavid du Colombier error(Edetach);
15005d9de2d3SDavid du Colombier if(ep->mode == OREAD || ep->inuse == 0)
15015d9de2d3SDavid du Colombier error(Ebadusefd);
15025d9de2d3SDavid du Colombier
15035d9de2d3SDavid du Colombier switch(ep->ttype){
15045d9de2d3SDavid du Colombier case Tnone:
15055d9de2d3SDavid du Colombier error("endpoint not configured");
15065d9de2d3SDavid du Colombier case Tctl:
15075d9de2d3SDavid du Colombier nr = rhubwrite(ep, a, n);
15085d9de2d3SDavid du Colombier if(nr >= 0){
15095d9de2d3SDavid du Colombier n = nr;
15105d9de2d3SDavid du Colombier break;
15115d9de2d3SDavid du Colombier }
1512b4d1cf41SDavid du Colombier /* else fall */
15135d9de2d3SDavid du Colombier default:
1514b4d1cf41SDavid du Colombier ddeprint("\nusbwrite q %#x fid %d cnt %ld off %lld\n",q, c->fid, n, off);
15155d9de2d3SDavid du Colombier ep->hp->epwrite(ep, a, n);
15165d9de2d3SDavid du Colombier }
15175d9de2d3SDavid du Colombier putep(ep);
15185d9de2d3SDavid du Colombier poperror();
15195d9de2d3SDavid du Colombier return n;
15205d9de2d3SDavid du Colombier }
15215d9de2d3SDavid du Colombier
15225d9de2d3SDavid du Colombier Block*
usbbread(Chan * c,long n,ulong offset)15235d9de2d3SDavid du Colombier usbbread(Chan *c, long n, ulong offset)
15245d9de2d3SDavid du Colombier {
15255d9de2d3SDavid du Colombier Block *bp;
15265d9de2d3SDavid du Colombier
15275d9de2d3SDavid du Colombier bp = allocb(n);
15285d9de2d3SDavid du Colombier if(bp == 0)
15295d9de2d3SDavid du Colombier error(Enomem);
15305d9de2d3SDavid du Colombier if(waserror()) {
15315d9de2d3SDavid du Colombier freeb(bp);
15325d9de2d3SDavid du Colombier nexterror();
15335d9de2d3SDavid du Colombier }
15345d9de2d3SDavid du Colombier bp->wp += usbread(c, bp->wp, n, offset);
15355d9de2d3SDavid du Colombier poperror();
15365d9de2d3SDavid du Colombier return bp;
15375d9de2d3SDavid du Colombier }
15385d9de2d3SDavid du Colombier
15395d9de2d3SDavid du Colombier long
usbbwrite(Chan * c,Block * bp,ulong offset)15405d9de2d3SDavid du Colombier usbbwrite(Chan *c, Block *bp, ulong offset)
15415d9de2d3SDavid du Colombier {
15425d9de2d3SDavid du Colombier long n;
15435d9de2d3SDavid du Colombier
15445d9de2d3SDavid du Colombier if(waserror()) {
15455d9de2d3SDavid du Colombier freeb(bp);
15465d9de2d3SDavid du Colombier nexterror();
15475d9de2d3SDavid du Colombier }
15485d9de2d3SDavid du Colombier n = usbwrite(c, bp->rp, BLEN(bp), offset);
15495d9de2d3SDavid du Colombier poperror();
15505d9de2d3SDavid du Colombier freeb(bp);
15515d9de2d3SDavid du Colombier
15525d9de2d3SDavid du Colombier return n;
15535d9de2d3SDavid du Colombier }
15545d9de2d3SDavid du Colombier
15555d9de2d3SDavid du Colombier void
usbshutdown(void)15565d9de2d3SDavid du Colombier usbshutdown(void)
15575d9de2d3SDavid du Colombier {
15585d9de2d3SDavid du Colombier Hci *hp;
15595d9de2d3SDavid du Colombier int i;
15605d9de2d3SDavid du Colombier
15615d9de2d3SDavid du Colombier for(i = 0; i < Nhcis; i++){
15625d9de2d3SDavid du Colombier hp = hcis[i];
15635d9de2d3SDavid du Colombier if(hp == nil)
15645d9de2d3SDavid du Colombier continue;
15655d9de2d3SDavid du Colombier if(hp->shutdown == nil)
15665d9de2d3SDavid du Colombier print("#u: no shutdown function for %s\n", hp->type);
15675d9de2d3SDavid du Colombier else
15685d9de2d3SDavid du Colombier hp->shutdown(hp);
15695d9de2d3SDavid du Colombier }
15705d9de2d3SDavid du Colombier }
15715d9de2d3SDavid du Colombier
15725d9de2d3SDavid du Colombier Dev usbdevtab = {
15735d9de2d3SDavid du Colombier L'u',
15745d9de2d3SDavid du Colombier "usb",
15755d9de2d3SDavid du Colombier
15765d9de2d3SDavid du Colombier usbreset,
15775d9de2d3SDavid du Colombier usbinit,
15785d9de2d3SDavid du Colombier usbshutdown,
15795d9de2d3SDavid du Colombier usbattach,
15805d9de2d3SDavid du Colombier usbwalk,
15815d9de2d3SDavid du Colombier usbstat,
15825d9de2d3SDavid du Colombier usbopen,
15835d9de2d3SDavid du Colombier devcreate,
15845d9de2d3SDavid du Colombier usbclose,
15855d9de2d3SDavid du Colombier usbread,
15865d9de2d3SDavid du Colombier usbbread,
15875d9de2d3SDavid du Colombier usbwrite,
15885d9de2d3SDavid du Colombier usbbwrite,
15895d9de2d3SDavid du Colombier devremove,
15905d9de2d3SDavid du Colombier devwstat,
15915d9de2d3SDavid du Colombier };
1592