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