1ade43d10SDavid du Colombier /*
284860c5dSDavid du Colombier * USB device driver framework.
3906943f9SDavid du Colombier *
4906943f9SDavid du Colombier * This is in charge of providing access to actual HCIs
5906943f9SDavid du Colombier * and providing I/O to the various endpoints of devices.
6906943f9SDavid du Colombier * A separate user program (usbd) is in charge of
7906943f9SDavid du Colombier * enumerating the bus, setting up endpoints and
8906943f9SDavid du Colombier * starting devices (also user programs).
9906943f9SDavid du Colombier *
10906943f9SDavid du Colombier * The interface provided is a violation of the standard:
11906943f9SDavid du Colombier * you're welcome.
12906943f9SDavid du Colombier *
13906943f9SDavid du Colombier * The interface consists of a root directory with several files
14906943f9SDavid du Colombier * plus a directory (epN.M) with two files per endpoint.
15906943f9SDavid du Colombier * A device is represented by its first endpoint, which
16906943f9SDavid du Colombier * is a control endpoint automatically allocated for each device.
17906943f9SDavid du Colombier * Device control endpoints may be used to create new endpoints.
18906943f9SDavid du Colombier * Devices corresponding to hubs may also allocate new devices,
19906943f9SDavid du Colombier * perhaps also hubs. Initially, a hub device is allocated for
20906943f9SDavid du Colombier * each controller present, to represent its root hub. Those can
21906943f9SDavid du Colombier * never be removed.
22906943f9SDavid du Colombier *
23906943f9SDavid du Colombier * All endpoints refer to the first endpoint (epN.0) of the device,
24906943f9SDavid du Colombier * which keeps per-device information, and also to the HCI used
25906943f9SDavid du Colombier * to reach them. Although all endpoints cache that information.
26906943f9SDavid du Colombier *
27906943f9SDavid du Colombier * epN.M/data files permit I/O and are considered DMEXCL.
28906943f9SDavid du Colombier * epN.M/ctl files provide status info and accept control requests.
29906943f9SDavid du Colombier *
30906943f9SDavid du Colombier * Endpoints may be given file names to be listed also at #u,
31906943f9SDavid du Colombier * for those drivers that have nothing to do after configuring the
32906943f9SDavid du Colombier * device and its endpoints.
33906943f9SDavid du Colombier *
34906943f9SDavid du Colombier * Drivers for different controllers are kept at usb[oue]hci.c
35906943f9SDavid du Colombier * It's likely we could factor out much from controllers into
36906943f9SDavid du Colombier * a generic controller driver, the problem is that details
37906943f9SDavid du Colombier * regarding how to handle toggles, tokens, Tds, etc. will
38906943f9SDavid du Colombier * get in the way. Thus, code is probably easier the way it is.
39ade43d10SDavid du Colombier */
40906943f9SDavid du Colombier
419a747e4fSDavid du Colombier #include "u.h"
429a747e4fSDavid du Colombier #include "../port/lib.h"
439a747e4fSDavid du Colombier #include "mem.h"
449a747e4fSDavid du Colombier #include "dat.h"
459a747e4fSDavid du Colombier #include "fns.h"
469a747e4fSDavid du Colombier #include "io.h"
479a747e4fSDavid du Colombier #include "../port/error.h"
4884860c5dSDavid du Colombier #include "../port/usb.h"
499a747e4fSDavid du Colombier
50906943f9SDavid du Colombier typedef struct Hcitype Hcitype;
519a747e4fSDavid du Colombier
529a747e4fSDavid du Colombier enum
539a747e4fSDavid du Colombier {
54906943f9SDavid du Colombier /* Qid numbers */
55906943f9SDavid du Colombier Qdir = 0, /* #u */
56906943f9SDavid du Colombier Qusbdir, /* #u/usb */
57906943f9SDavid du Colombier Qctl, /* #u/usb/ctl - control requests */
58906943f9SDavid du Colombier
59906943f9SDavid du Colombier Qep0dir, /* #u/usb/ep0.0 - endpoint 0 dir */
60906943f9SDavid du Colombier Qep0io, /* #u/usb/ep0.0/data - endpoint 0 I/O */
61906943f9SDavid du Colombier Qep0ctl, /* #u/usb/ep0.0/ctl - endpoint 0 ctl. */
62906943f9SDavid du Colombier Qep0dummy, /* give 4 qids to each endpoint */
63906943f9SDavid du Colombier
64906943f9SDavid du Colombier Qepdir = 0, /* (qid-qep0dir)&3 is one of these */
65906943f9SDavid du Colombier Qepio, /* to identify which file for the endpoint */
66906943f9SDavid du Colombier Qepctl,
67906943f9SDavid du Colombier
68906943f9SDavid du Colombier /* ... */
69906943f9SDavid du Colombier
70906943f9SDavid du Colombier /* Usb ctls. */
71906943f9SDavid du Colombier CMdebug = 0, /* debug on|off */
72906943f9SDavid du Colombier CMdump, /* dump (data structures for debug) */
73906943f9SDavid du Colombier
74906943f9SDavid du Colombier /* Ep. ctls */
75906943f9SDavid du Colombier CMnew = 0, /* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */
76906943f9SDavid du Colombier CMnewdev, /* newdev full|low|high portnb (allocate new devices) */
77906943f9SDavid du Colombier CMhub, /* hub (set the device as a hub) */
78906943f9SDavid du Colombier CMspeed, /* speed full|low|high|no */
79906943f9SDavid du Colombier CMmaxpkt, /* maxpkt size */
80906943f9SDavid du Colombier CMntds, /* ntds nb (max nb. of tds per µframe) */
81906943f9SDavid du Colombier CMclrhalt, /* clrhalt (halt was cleared on endpoint) */
82906943f9SDavid du Colombier CMpollival, /* pollival interval (interrupt/iso) */
83906943f9SDavid du Colombier CMhz, /* hz n (samples/sec; iso) */
84906943f9SDavid du Colombier CMsamplesz, /* samplesz n (sample size; iso) */
85906943f9SDavid du Colombier CMinfo, /* info infostr (ke.ep info for humans) */
86906943f9SDavid du Colombier CMdetach, /* detach (abort I/O forever on this ep). */
87906943f9SDavid du Colombier CMaddress, /* address (address is assigned) */
88906943f9SDavid du Colombier CMdebugep, /* debug n (set/clear debug for this ep) */
89906943f9SDavid du Colombier CMname, /* name str (show up as #u/name as well) */
90d37e33ffSDavid du Colombier CMtmout, /* timeout n (activate timeouts for ep) */
91a23bc242SDavid du Colombier CMpreset, /* reset the port */
92906943f9SDavid du Colombier
93906943f9SDavid du Colombier /* Hub feature selectors */
94906943f9SDavid du Colombier Rportenable = 1,
95906943f9SDavid du Colombier Rportreset = 4,
96906943f9SDavid du Colombier
979a747e4fSDavid du Colombier };
989a747e4fSDavid du Colombier
99906943f9SDavid du Colombier struct Hcitype
1009a747e4fSDavid du Colombier {
1019a747e4fSDavid du Colombier char* type;
102906943f9SDavid du Colombier int (*reset)(Hci*);
103906943f9SDavid du Colombier };
104906943f9SDavid du Colombier
105906943f9SDavid du Colombier #define QID(q) ((int)(q).path)
106906943f9SDavid du Colombier
107906943f9SDavid du Colombier static Cmdtab usbctls[] =
108906943f9SDavid du Colombier {
109906943f9SDavid du Colombier {CMdebug, "debug", 2},
110906943f9SDavid du Colombier {CMdump, "dump", 1},
111906943f9SDavid du Colombier };
112906943f9SDavid du Colombier
113906943f9SDavid du Colombier static Cmdtab epctls[] =
114906943f9SDavid du Colombier {
115906943f9SDavid du Colombier {CMnew, "new", 4},
116906943f9SDavid du Colombier {CMnewdev, "newdev", 3},
117906943f9SDavid du Colombier {CMhub, "hub", 1},
118906943f9SDavid du Colombier {CMspeed, "speed", 2},
119906943f9SDavid du Colombier {CMmaxpkt, "maxpkt", 2},
120906943f9SDavid du Colombier {CMntds, "ntds", 2},
121906943f9SDavid du Colombier {CMpollival, "pollival", 2},
122906943f9SDavid du Colombier {CMsamplesz, "samplesz", 2},
123906943f9SDavid du Colombier {CMhz, "hz", 2},
124906943f9SDavid du Colombier {CMinfo, "info", 0},
125906943f9SDavid du Colombier {CMdetach, "detach", 1},
126906943f9SDavid du Colombier {CMaddress, "address", 1},
127906943f9SDavid du Colombier {CMdebugep, "debug", 2},
128906943f9SDavid du Colombier {CMclrhalt, "clrhalt", 1},
129906943f9SDavid du Colombier {CMname, "name", 2},
130d37e33ffSDavid du Colombier {CMtmout, "timeout", 2},
131a23bc242SDavid du Colombier {CMpreset, "reset", 1},
132906943f9SDavid du Colombier };
133906943f9SDavid du Colombier
134906943f9SDavid du Colombier static Dirtab usbdir[] =
135906943f9SDavid du Colombier {
136906943f9SDavid du Colombier "ctl", {Qctl}, 0, 0666,
137906943f9SDavid du Colombier };
138906943f9SDavid du Colombier
139906943f9SDavid du Colombier char *usbmodename[] =
140906943f9SDavid du Colombier {
141906943f9SDavid du Colombier [OREAD] "r",
142906943f9SDavid du Colombier [OWRITE] "w",
143906943f9SDavid du Colombier [ORDWR] "rw",
144906943f9SDavid du Colombier };
145906943f9SDavid du Colombier
146906943f9SDavid du Colombier static char *ttname[] =
147906943f9SDavid du Colombier {
148906943f9SDavid du Colombier [Tnone] "none",
149906943f9SDavid du Colombier [Tctl] "control",
150906943f9SDavid du Colombier [Tiso] "iso",
151906943f9SDavid du Colombier [Tintr] "interrupt",
152906943f9SDavid du Colombier [Tbulk] "bulk",
153906943f9SDavid du Colombier };
154906943f9SDavid du Colombier
155906943f9SDavid du Colombier static char *spname[] =
156906943f9SDavid du Colombier {
157906943f9SDavid du Colombier [Fullspeed] "full",
158906943f9SDavid du Colombier [Lowspeed] "low",
159906943f9SDavid du Colombier [Highspeed] "high",
160906943f9SDavid du Colombier [Nospeed] "no",
161906943f9SDavid du Colombier };
162906943f9SDavid du Colombier
163906943f9SDavid du Colombier static int debug;
164906943f9SDavid du Colombier static Hcitype hcitypes[Nhcis];
165906943f9SDavid du Colombier static Hci* hcis[Nhcis];
166906943f9SDavid du Colombier static QLock epslck; /* add, del, lookup endpoints */
167906943f9SDavid du Colombier static Ep* eps[Neps]; /* all endpoints known */
168906943f9SDavid du Colombier static int epmax; /* 1 + last endpoint index used */
169906943f9SDavid du Colombier static int usbidgen; /* device address generator */
170906943f9SDavid du Colombier
171906943f9SDavid du Colombier /*
172906943f9SDavid du Colombier * Is there something like this in a library? should it be?
173906943f9SDavid du Colombier */
174906943f9SDavid du Colombier char*
seprintdata(char * s,char * se,uchar * d,int n)175906943f9SDavid du Colombier seprintdata(char *s, char *se, uchar *d, int n)
176906943f9SDavid du Colombier {
17784860c5dSDavid du Colombier int i, l;
178906943f9SDavid du Colombier
179906943f9SDavid du Colombier s = seprint(s, se, " %#p[%d]: ", d, n);
180906943f9SDavid du Colombier l = n;
181906943f9SDavid du Colombier if(l > 10)
182906943f9SDavid du Colombier l = 10;
183906943f9SDavid du Colombier for(i=0; i<l; i++)
184906943f9SDavid du Colombier s = seprint(s, se, " %2.2ux", d[i]);
185906943f9SDavid du Colombier if(l < n)
186906943f9SDavid du Colombier s = seprint(s, se, "...");
187906943f9SDavid du Colombier return s;
188906943f9SDavid du Colombier }
189906943f9SDavid du Colombier
190906943f9SDavid du Colombier static int
name2speed(char * name)191906943f9SDavid du Colombier name2speed(char *name)
192906943f9SDavid du Colombier {
193906943f9SDavid du Colombier int i;
194906943f9SDavid du Colombier
195906943f9SDavid du Colombier for(i = 0; i < nelem(spname); i++)
196906943f9SDavid du Colombier if(strcmp(name, spname[i]) == 0)
197906943f9SDavid du Colombier return i;
198906943f9SDavid du Colombier return Nospeed;
199906943f9SDavid du Colombier }
200906943f9SDavid du Colombier
201906943f9SDavid du Colombier static int
name2ttype(char * name)202906943f9SDavid du Colombier name2ttype(char *name)
203906943f9SDavid du Colombier {
204906943f9SDavid du Colombier int i;
205906943f9SDavid du Colombier
206906943f9SDavid du Colombier for(i = 0; i < nelem(ttname); i++)
207906943f9SDavid du Colombier if(strcmp(name, ttname[i]) == 0)
208906943f9SDavid du Colombier return i;
209906943f9SDavid du Colombier /* may be a std. USB ep. type */
210906943f9SDavid du Colombier i = strtol(name, nil, 0);
211906943f9SDavid du Colombier switch(i+1){
212906943f9SDavid du Colombier case Tctl:
213906943f9SDavid du Colombier case Tiso:
214906943f9SDavid du Colombier case Tbulk:
215906943f9SDavid du Colombier case Tintr:
216906943f9SDavid du Colombier return i+1;
217906943f9SDavid du Colombier default:
218906943f9SDavid du Colombier return Tnone;
219906943f9SDavid du Colombier }
220906943f9SDavid du Colombier }
221906943f9SDavid du Colombier
222906943f9SDavid du Colombier static int
name2mode(char * mode)223906943f9SDavid du Colombier name2mode(char *mode)
224906943f9SDavid du Colombier {
225906943f9SDavid du Colombier int i;
226906943f9SDavid du Colombier
227906943f9SDavid du Colombier for(i = 0; i < nelem(usbmodename); i++)
228906943f9SDavid du Colombier if(strcmp(mode, usbmodename[i]) == 0)
229906943f9SDavid du Colombier return i;
230906943f9SDavid du Colombier return -1;
231906943f9SDavid du Colombier }
232906943f9SDavid du Colombier
233906943f9SDavid du Colombier static int
qid2epidx(int q)234906943f9SDavid du Colombier qid2epidx(int q)
235906943f9SDavid du Colombier {
236906943f9SDavid du Colombier q = (q-Qep0dir)/4;
237906943f9SDavid du Colombier if(q < 0 || q >= epmax || eps[q] == nil)
238906943f9SDavid du Colombier return -1;
239906943f9SDavid du Colombier return q;
240906943f9SDavid du Colombier }
241906943f9SDavid du Colombier
242906943f9SDavid du Colombier static int
isqtype(int q,int type)243906943f9SDavid du Colombier isqtype(int q, int type)
244906943f9SDavid du Colombier {
245906943f9SDavid du Colombier if(q < Qep0dir)
246906943f9SDavid du Colombier return 0;
247906943f9SDavid du Colombier q -= Qep0dir;
248906943f9SDavid du Colombier return (q & 3) == type;
249906943f9SDavid du Colombier }
2509a747e4fSDavid du Colombier
2519a747e4fSDavid du Colombier void
addhcitype(char * t,int (* r)(Hci *))252906943f9SDavid du Colombier addhcitype(char* t, int (*r)(Hci*))
2539a747e4fSDavid du Colombier {
2549a747e4fSDavid du Colombier static int ntype;
2559a747e4fSDavid du Colombier
256906943f9SDavid du Colombier if(ntype == Nhcis)
2579a747e4fSDavid du Colombier panic("too many USB host interface types");
258906943f9SDavid du Colombier hcitypes[ntype].type = t;
259906943f9SDavid du Colombier hcitypes[ntype].reset = r;
2609a747e4fSDavid du Colombier ntype++;
2619a747e4fSDavid du Colombier }
2629a747e4fSDavid du Colombier
263906943f9SDavid du Colombier static char*
seprintep(char * s,char * se,Ep * ep,int all)264906943f9SDavid du Colombier seprintep(char *s, char *se, Ep *ep, int all)
2659a747e4fSDavid du Colombier {
266a23bc242SDavid du Colombier static char* dsnames[] = { "config", "enabled", "detached", "reset" };
2679a747e4fSDavid du Colombier Udev *d;
2689a747e4fSDavid du Colombier int i;
269906943f9SDavid du Colombier int di;
2709a747e4fSDavid du Colombier
271906943f9SDavid du Colombier d = ep->dev;
272906943f9SDavid du Colombier
273906943f9SDavid du Colombier qlock(ep);
2749a747e4fSDavid du Colombier if(waserror()){
275906943f9SDavid du Colombier qunlock(ep);
2769a747e4fSDavid du Colombier nexterror();
2779a747e4fSDavid du Colombier }
278906943f9SDavid du Colombier di = ep->dev->nb;
279906943f9SDavid du Colombier if(all)
280906943f9SDavid du Colombier s = seprint(s, se, "dev %d ep %d ", di, ep->nb);
281906943f9SDavid du Colombier s = seprint(s, se, "%s", dsnames[ep->dev->state]);
282906943f9SDavid du Colombier s = seprint(s, se, " %s", ttname[ep->ttype]);
283906943f9SDavid du Colombier assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR);
284906943f9SDavid du Colombier s = seprint(s, se, " %s", usbmodename[ep->mode]);
285906943f9SDavid du Colombier s = seprint(s, se, " speed %s", spname[d->speed]);
286906943f9SDavid du Colombier s = seprint(s, se, " maxpkt %ld", ep->maxpkt);
287906943f9SDavid du Colombier s = seprint(s, se, " pollival %ld", ep->pollival);
288906943f9SDavid du Colombier s = seprint(s, se, " samplesz %ld", ep->samplesz);
289906943f9SDavid du Colombier s = seprint(s, se, " hz %ld", ep->hz);
290906943f9SDavid du Colombier s = seprint(s, se, " hub %d", ep->dev->hub);
291906943f9SDavid du Colombier s = seprint(s, se, " port %d", ep->dev->port);
292906943f9SDavid du Colombier if(ep->inuse)
293906943f9SDavid du Colombier s = seprint(s, se, " busy");
294906943f9SDavid du Colombier else
295906943f9SDavid du Colombier s = seprint(s, se, " idle");
296906943f9SDavid du Colombier if(all){
297906943f9SDavid du Colombier s = seprint(s, se, " load %uld", ep->load);
298906943f9SDavid du Colombier s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep);
299906943f9SDavid du Colombier s = seprint(s, se, " idx %d", ep->idx);
300906943f9SDavid du Colombier if(ep->name != nil)
301906943f9SDavid du Colombier s = seprint(s, se, " name '%s'", ep->name);
302d37e33ffSDavid du Colombier if(ep->tmout != 0)
303d37e33ffSDavid du Colombier s = seprint(s, se, " tmout");
304906943f9SDavid du Colombier if(ep == ep->ep0){
305906943f9SDavid du Colombier s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno);
306906943f9SDavid du Colombier s = seprint(s, se, " eps:");
307906943f9SDavid du Colombier for(i = 0; i < nelem(d->eps); i++)
308906943f9SDavid du Colombier if(d->eps[i] != nil)
309906943f9SDavid du Colombier s = seprint(s, se, " ep%d.%d", di, i);
3109a747e4fSDavid du Colombier }
311906943f9SDavid du Colombier }
312906943f9SDavid du Colombier if(ep->info != nil)
313c8cbc0e9SDavid du Colombier s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type);
314906943f9SDavid du Colombier else
315906943f9SDavid du Colombier s = seprint(s, se, "\n");
316906943f9SDavid du Colombier qunlock(ep);
3179a747e4fSDavid du Colombier poperror();
318906943f9SDavid du Colombier return s;
319906943f9SDavid du Colombier }
320906943f9SDavid du Colombier
321906943f9SDavid du Colombier static Ep*
epalloc(Hci * hp)322906943f9SDavid du Colombier epalloc(Hci *hp)
323906943f9SDavid du Colombier {
324906943f9SDavid du Colombier Ep *ep;
325906943f9SDavid du Colombier int i;
326906943f9SDavid du Colombier
327305b51b8SDavid du Colombier ep = smalloc(sizeof(Ep));
328906943f9SDavid du Colombier ep->ref = 1;
329906943f9SDavid du Colombier qlock(&epslck);
330906943f9SDavid du Colombier for(i = 0; i < Neps; i++)
331906943f9SDavid du Colombier if(eps[i] == nil)
332906943f9SDavid du Colombier break;
333906943f9SDavid du Colombier if(i == Neps){
334906943f9SDavid du Colombier qunlock(&epslck);
335906943f9SDavid du Colombier free(ep);
336*81fda2e3SDavid du Colombier panic("usb: epalloc: too few endpoints (%d)", Neps);
337906943f9SDavid du Colombier }
338906943f9SDavid du Colombier ep->idx = i;
339906943f9SDavid du Colombier if(epmax <= i)
340906943f9SDavid du Colombier epmax = i+1;
341906943f9SDavid du Colombier eps[i] = ep;
342906943f9SDavid du Colombier ep->hp = hp;
343906943f9SDavid du Colombier ep->maxpkt = 8;
344906943f9SDavid du Colombier ep->ntds = 1;
345906943f9SDavid du Colombier ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */
346906943f9SDavid du Colombier qunlock(&epslck);
347906943f9SDavid du Colombier return ep;
348906943f9SDavid du Colombier }
349906943f9SDavid du Colombier
350906943f9SDavid du Colombier static Ep*
getep(int i)351906943f9SDavid du Colombier getep(int i)
352906943f9SDavid du Colombier {
353906943f9SDavid du Colombier Ep *ep;
354906943f9SDavid du Colombier
355906943f9SDavid du Colombier if(i < 0 || i >= epmax || eps[i] == nil)
356906943f9SDavid du Colombier return nil;
357906943f9SDavid du Colombier qlock(&epslck);
358906943f9SDavid du Colombier ep = eps[i];
359906943f9SDavid du Colombier if(ep != nil)
360906943f9SDavid du Colombier incref(ep);
361906943f9SDavid du Colombier qunlock(&epslck);
362906943f9SDavid du Colombier return ep;
3639a747e4fSDavid du Colombier }
3649a747e4fSDavid du Colombier
3659a747e4fSDavid du Colombier static void
putep(Ep * ep)366906943f9SDavid du Colombier putep(Ep *ep)
367906943f9SDavid du Colombier {
368906943f9SDavid du Colombier Udev *d;
369906943f9SDavid du Colombier
370906943f9SDavid du Colombier if(ep != nil && decref(ep) == 0){
371906943f9SDavid du Colombier d = ep->dev;
372906943f9SDavid du Colombier deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep);
373906943f9SDavid du Colombier qlock(&epslck);
374906943f9SDavid du Colombier eps[ep->idx] = nil;
375906943f9SDavid du Colombier if(ep->idx == epmax-1)
376906943f9SDavid du Colombier epmax--;
377906943f9SDavid du Colombier if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen)
378906943f9SDavid du Colombier usbidgen--;
379906943f9SDavid du Colombier qunlock(&epslck);
380906943f9SDavid du Colombier if(d != nil){
381906943f9SDavid du Colombier qlock(ep->ep0);
382906943f9SDavid du Colombier d->eps[ep->nb] = nil;
383906943f9SDavid du Colombier qunlock(ep->ep0);
384906943f9SDavid du Colombier }
385906943f9SDavid du Colombier if(ep->ep0 != ep){
386906943f9SDavid du Colombier putep(ep->ep0);
387906943f9SDavid du Colombier ep->ep0 = nil;
388906943f9SDavid du Colombier }
389906943f9SDavid du Colombier free(ep->info);
390906943f9SDavid du Colombier free(ep->name);
391906943f9SDavid du Colombier free(ep);
392906943f9SDavid du Colombier }
393906943f9SDavid du Colombier }
394906943f9SDavid du Colombier
395906943f9SDavid du Colombier static void
dumpeps(void)396906943f9SDavid du Colombier dumpeps(void)
3979a747e4fSDavid du Colombier {
3989a747e4fSDavid du Colombier int i;
399906943f9SDavid du Colombier static char buf[512];
400906943f9SDavid du Colombier char *s;
401906943f9SDavid du Colombier char *e;
402906943f9SDavid du Colombier Ep *ep;
4039a747e4fSDavid du Colombier
404906943f9SDavid du Colombier print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps);
405906943f9SDavid du Colombier for(i = 0; i < epmax; i++){
406906943f9SDavid du Colombier s = buf;
407906943f9SDavid du Colombier e = buf+sizeof(buf);
408906943f9SDavid du Colombier ep = getep(i);
409906943f9SDavid du Colombier if(ep != nil){
410906943f9SDavid du Colombier if(waserror()){
411906943f9SDavid du Colombier putep(ep);
412906943f9SDavid du Colombier nexterror();
4139a747e4fSDavid du Colombier }
414906943f9SDavid du Colombier s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb);
415906943f9SDavid du Colombier seprintep(s, e, ep, 1);
416906943f9SDavid du Colombier print("%s", buf);
417906943f9SDavid du Colombier ep->hp->seprintep(buf, e, ep);
418906943f9SDavid du Colombier print("%s", buf);
419906943f9SDavid du Colombier poperror();
420906943f9SDavid du Colombier putep(ep);
421906943f9SDavid du Colombier }
422906943f9SDavid du Colombier }
423906943f9SDavid du Colombier print("usb dump hcis:\n");
424906943f9SDavid du Colombier for(i = 0; i < Nhcis; i++)
425906943f9SDavid du Colombier if(hcis[i] != nil)
426906943f9SDavid du Colombier hcis[i]->dump(hcis[i]);
427906943f9SDavid du Colombier }
428906943f9SDavid du Colombier
429906943f9SDavid du Colombier static int
newusbid(Hci *)430906943f9SDavid du Colombier newusbid(Hci *)
431906943f9SDavid du Colombier {
432906943f9SDavid du Colombier int id;
433906943f9SDavid du Colombier
434906943f9SDavid du Colombier qlock(&epslck);
435906943f9SDavid du Colombier id = ++usbidgen;
436906943f9SDavid du Colombier if(id >= 0x7F)
437906943f9SDavid du Colombier print("#u: too many device addresses; reuse them more\n");
438906943f9SDavid du Colombier qunlock(&epslck);
439906943f9SDavid du Colombier return id;
440906943f9SDavid du Colombier }
441906943f9SDavid du Colombier
442906943f9SDavid du Colombier /*
443906943f9SDavid du Colombier * Create endpoint 0 for a new device
444906943f9SDavid du Colombier */
445906943f9SDavid du Colombier static Ep*
newdev(Hci * hp,int ishub,int isroot)446906943f9SDavid du Colombier newdev(Hci *hp, int ishub, int isroot)
447906943f9SDavid du Colombier {
448906943f9SDavid du Colombier Ep *ep;
449906943f9SDavid du Colombier Udev *d;
450906943f9SDavid du Colombier
451906943f9SDavid du Colombier ep = epalloc(hp);
452305b51b8SDavid du Colombier d = ep->dev = smalloc(sizeof(Udev));
453906943f9SDavid du Colombier d->nb = newusbid(hp);
454906943f9SDavid du Colombier d->eps[0] = ep;
455906943f9SDavid du Colombier ep->nb = 0;
456906943f9SDavid du Colombier ep->toggle[0] = ep->toggle[1] = 0;
457906943f9SDavid du Colombier d->ishub = ishub;
458906943f9SDavid du Colombier d->isroot = isroot;
459906943f9SDavid du Colombier if(hp->highspeed != 0)
460906943f9SDavid du Colombier d->speed = Highspeed;
461906943f9SDavid du Colombier else
462906943f9SDavid du Colombier d->speed = Fullspeed;
463906943f9SDavid du Colombier d->state = Dconfig; /* address not yet set */
464906943f9SDavid du Colombier ep->dev = d;
465906943f9SDavid du Colombier ep->ep0 = ep; /* no ref counted here */
466906943f9SDavid du Colombier ep->ttype = Tctl;
467d37e33ffSDavid du Colombier ep->tmout = Xfertmout;
468906943f9SDavid du Colombier ep->mode = ORDWR;
469906943f9SDavid du Colombier dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep);
470906943f9SDavid du Colombier return ep;
471906943f9SDavid du Colombier }
472906943f9SDavid du Colombier
473906943f9SDavid du Colombier /*
474906943f9SDavid du Colombier * Create a new endpoint for the device
475906943f9SDavid du Colombier * accessed via the given endpoint 0.
476906943f9SDavid du Colombier */
477906943f9SDavid du Colombier static Ep*
newdevep(Ep * ep,int i,int tt,int mode)478906943f9SDavid du Colombier newdevep(Ep *ep, int i, int tt, int mode)
479906943f9SDavid du Colombier {
480906943f9SDavid du Colombier Ep *nep;
481906943f9SDavid du Colombier Udev *d;
482906943f9SDavid du Colombier
483906943f9SDavid du Colombier d = ep->dev;
484906943f9SDavid du Colombier if(d->eps[i] != nil)
485906943f9SDavid du Colombier error("endpoint already in use");
486906943f9SDavid du Colombier nep = epalloc(ep->hp);
487906943f9SDavid du Colombier incref(ep);
488906943f9SDavid du Colombier d->eps[i] = nep;
489906943f9SDavid du Colombier nep->nb = i;
490906943f9SDavid du Colombier nep->toggle[0] = nep->toggle[1] = 0;
491906943f9SDavid du Colombier nep->ep0 = ep;
492906943f9SDavid du Colombier nep->dev = ep->dev;
493906943f9SDavid du Colombier nep->mode = mode;
494906943f9SDavid du Colombier nep->ttype = tt;
495906943f9SDavid du Colombier nep->debug = ep->debug;
496d37e33ffSDavid du Colombier /* set defaults */
497d37e33ffSDavid du Colombier switch(tt){
498d37e33ffSDavid du Colombier case Tctl:
499d37e33ffSDavid du Colombier nep->tmout = Xfertmout;
500d37e33ffSDavid du Colombier break;
501d37e33ffSDavid du Colombier case Tintr:
502906943f9SDavid du Colombier nep->pollival = 10;
503d37e33ffSDavid du Colombier break;
504d37e33ffSDavid du Colombier case Tiso:
505d37e33ffSDavid du Colombier nep->tmout = Xfertmout;
506d37e33ffSDavid du Colombier nep->pollival = 10;
507906943f9SDavid du Colombier nep->samplesz = 4;
508906943f9SDavid du Colombier nep->hz = 44100;
509d37e33ffSDavid du Colombier break;
510906943f9SDavid du Colombier }
511906943f9SDavid du Colombier deprint("newdevep ep%d.%d %#p\n", d->nb, nep->nb, nep);
512906943f9SDavid du Colombier return ep;
513906943f9SDavid du Colombier }
514906943f9SDavid du Colombier
515906943f9SDavid du Colombier static int
epdataperm(int mode)516906943f9SDavid du Colombier epdataperm(int mode)
517906943f9SDavid du Colombier {
518906943f9SDavid du Colombier
519906943f9SDavid du Colombier switch(mode){
520906943f9SDavid du Colombier case OREAD:
521906943f9SDavid du Colombier return 0440|DMEXCL;
522906943f9SDavid du Colombier break;
523906943f9SDavid du Colombier case OWRITE:
524906943f9SDavid du Colombier return 0220|DMEXCL;
525906943f9SDavid du Colombier break;
526906943f9SDavid du Colombier default:
527906943f9SDavid du Colombier return 0660|DMEXCL;
5289a747e4fSDavid du Colombier }
5299a747e4fSDavid du Colombier }
5309a747e4fSDavid du Colombier
5319a747e4fSDavid du Colombier static int
usbgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)5329a747e4fSDavid du Colombier usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
5339a747e4fSDavid du Colombier {
5349a747e4fSDavid du Colombier Qid q;
535906943f9SDavid du Colombier Dirtab *dir;
536906943f9SDavid du Colombier int perm;
537906943f9SDavid du Colombier char *se;
538906943f9SDavid du Colombier Ep *ep;
539906943f9SDavid du Colombier int nb;
540906943f9SDavid du Colombier int mode;
5419a747e4fSDavid du Colombier
542906943f9SDavid du Colombier if(0)ddprint("usbgen q %#x s %d...", QID(c->qid), s);
5439a747e4fSDavid du Colombier if(s == DEVDOTDOT){
544906943f9SDavid du Colombier if(QID(c->qid) <= Qusbdir){
545906943f9SDavid du Colombier mkqid(&q, Qdir, 0, QTDIR);
546906943f9SDavid du Colombier devdir(c, q, "#u", 0, eve, 0555, dp);
547906943f9SDavid du Colombier }else{
548906943f9SDavid du Colombier mkqid(&q, Qusbdir, 0, QTDIR);
549906943f9SDavid du Colombier devdir(c, q, "usb", 0, eve, 0555, dp);
550906943f9SDavid du Colombier }
551906943f9SDavid du Colombier if(0)ddprint("ok\n");
5529a747e4fSDavid du Colombier return 1;
5539a747e4fSDavid du Colombier }
5549a747e4fSDavid du Colombier
555906943f9SDavid du Colombier switch(QID(c->qid)){
556906943f9SDavid du Colombier case Qdir: /* list #u */
557906943f9SDavid du Colombier if(s == 0){
558906943f9SDavid du Colombier mkqid(&q, Qusbdir, 0, QTDIR);
559906943f9SDavid du Colombier devdir(c, q, "usb", 0, eve, 0555, dp);
560906943f9SDavid du Colombier if(0)ddprint("ok\n");
5619a747e4fSDavid du Colombier return 1;
5629a747e4fSDavid du Colombier }
563906943f9SDavid du Colombier s--;
564906943f9SDavid du Colombier if(s < 0 || s >= epmax)
565906943f9SDavid du Colombier goto Fail;
566906943f9SDavid du Colombier ep = getep(s);
567906943f9SDavid du Colombier if(ep == nil || ep->name == nil){
568906943f9SDavid du Colombier if(ep != nil)
569906943f9SDavid du Colombier putep(ep);
570906943f9SDavid du Colombier if(0)ddprint("skip\n");
5719a747e4fSDavid du Colombier return 0;
572906943f9SDavid du Colombier }
573906943f9SDavid du Colombier if(waserror()){
574906943f9SDavid du Colombier putep(ep);
575906943f9SDavid du Colombier nexterror();
576906943f9SDavid du Colombier }
577906943f9SDavid du Colombier mkqid(&q, Qep0io+s*4, 0, QTFILE);
578906943f9SDavid du Colombier devdir(c, q, ep->name, 0, eve, epdataperm(ep->mode), dp);
579906943f9SDavid du Colombier putep(ep);
580906943f9SDavid du Colombier poperror();
581906943f9SDavid du Colombier if(0)ddprint("ok\n");
5829a747e4fSDavid du Colombier return 1;
5839a747e4fSDavid du Colombier
584906943f9SDavid du Colombier case Qusbdir: /* list #u/usb */
585906943f9SDavid du Colombier Usbdir:
586906943f9SDavid du Colombier if(s < nelem(usbdir)){
587906943f9SDavid du Colombier dir = &usbdir[s];
588906943f9SDavid du Colombier mkqid(&q, dir->qid.path, 0, QTFILE);
589906943f9SDavid du Colombier devdir(c, q, dir->name, dir->length, eve, dir->perm, dp);
590906943f9SDavid du Colombier if(0)ddprint("ok\n");
5919a747e4fSDavid du Colombier return 1;
5929a747e4fSDavid du Colombier }
593906943f9SDavid du Colombier s -= nelem(usbdir);
594906943f9SDavid du Colombier if(s < 0 || s >= epmax)
595906943f9SDavid du Colombier goto Fail;
596906943f9SDavid du Colombier ep = getep(s);
597906943f9SDavid du Colombier if(ep == nil){
598906943f9SDavid du Colombier if(0)ddprint("skip\n");
5999a747e4fSDavid du Colombier return 0;
600906943f9SDavid du Colombier }
601906943f9SDavid du Colombier if(waserror()){
602906943f9SDavid du Colombier putep(ep);
603906943f9SDavid du Colombier nexterror();
604906943f9SDavid du Colombier }
605906943f9SDavid du Colombier se = up->genbuf+sizeof(up->genbuf);
606906943f9SDavid du Colombier seprint(up->genbuf, se, "ep%d.%d", ep->dev->nb, ep->nb);
607906943f9SDavid du Colombier mkqid(&q, Qep0dir+4*s, 0, QTDIR);
608906943f9SDavid du Colombier putep(ep);
609906943f9SDavid du Colombier poperror();
610906943f9SDavid du Colombier devdir(c, q, up->genbuf, 0, eve, 0755, dp);
611906943f9SDavid du Colombier if(0)ddprint("ok\n");
612906943f9SDavid du Colombier return 1;
613906943f9SDavid du Colombier
614906943f9SDavid du Colombier case Qctl:
615906943f9SDavid du Colombier s = 0;
616906943f9SDavid du Colombier goto Usbdir;
617906943f9SDavid du Colombier
618906943f9SDavid du Colombier default: /* list #u/usb/epN.M */
619906943f9SDavid du Colombier nb = qid2epidx(QID(c->qid));
620906943f9SDavid du Colombier ep = getep(nb);
621906943f9SDavid du Colombier if(ep == nil)
622906943f9SDavid du Colombier goto Fail;
623906943f9SDavid du Colombier mode = ep->mode;
624906943f9SDavid du Colombier putep(ep);
625906943f9SDavid du Colombier if(isqtype(QID(c->qid), Qepdir)){
626906943f9SDavid du Colombier Epdir:
627906943f9SDavid du Colombier switch(s){
628906943f9SDavid du Colombier case 0:
629906943f9SDavid du Colombier mkqid(&q, Qep0io+nb*4, 0, QTFILE);
630906943f9SDavid du Colombier perm = epdataperm(mode);
631906943f9SDavid du Colombier devdir(c, q, "data", 0, eve, perm, dp);
6329a747e4fSDavid du Colombier break;
633906943f9SDavid du Colombier case 1:
634906943f9SDavid du Colombier mkqid(&q, Qep0ctl+nb*4, 0, QTFILE);
635906943f9SDavid du Colombier devdir(c, q, "ctl", 0, eve, 0664, dp);
6369a747e4fSDavid du Colombier break;
6379a747e4fSDavid du Colombier default:
638906943f9SDavid du Colombier goto Fail;
6399a747e4fSDavid du Colombier }
640906943f9SDavid du Colombier }else if(isqtype(QID(c->qid), Qepctl)){
641906943f9SDavid du Colombier s = 1;
642906943f9SDavid du Colombier goto Epdir;
643906943f9SDavid du Colombier }else{
644906943f9SDavid du Colombier s = 0;
645906943f9SDavid du Colombier goto Epdir;
646906943f9SDavid du Colombier }
647906943f9SDavid du Colombier if(0)ddprint("ok\n");
6489a747e4fSDavid du Colombier return 1;
6499a747e4fSDavid du Colombier }
650906943f9SDavid du Colombier Fail:
651906943f9SDavid du Colombier if(0)ddprint("fail\n");
652906943f9SDavid du Colombier return -1;
653906943f9SDavid du Colombier }
654906943f9SDavid du Colombier
655906943f9SDavid du Colombier static Hci*
hciprobe(int cardno,int ctlrno)656906943f9SDavid du Colombier hciprobe(int cardno, int ctlrno)
6579a747e4fSDavid du Colombier {
658906943f9SDavid du Colombier Hci *hp;
659906943f9SDavid du Colombier char *type;
660e4ac449cSDavid du Colombier char name[64];
661906943f9SDavid du Colombier static int epnb = 1; /* guess the endpoint nb. for the controller */
6629a747e4fSDavid du Colombier
663906943f9SDavid du Colombier ddprint("hciprobe %d %d\n", cardno, ctlrno);
664305b51b8SDavid du Colombier hp = smalloc(sizeof(Hci));
665906943f9SDavid du Colombier hp->ctlrno = ctlrno;
666906943f9SDavid du Colombier hp->tbdf = BUSUNKNOWN;
6679a747e4fSDavid du Colombier
6689a747e4fSDavid du Colombier if(cardno < 0){
669906943f9SDavid du Colombier if(isaconfig("usb", ctlrno, hp) == 0){
670906943f9SDavid du Colombier free(hp);
6719a747e4fSDavid du Colombier return nil;
6729a747e4fSDavid du Colombier }
673906943f9SDavid du Colombier for(cardno = 0; cardno < Nhcis; cardno++){
674906943f9SDavid du Colombier if(hcitypes[cardno].type == nil)
675906943f9SDavid du Colombier break;
676906943f9SDavid du Colombier type = hp->type;
6779a747e4fSDavid du Colombier if(type==nil || *type==0)
6789a747e4fSDavid du Colombier type = "uhci";
679906943f9SDavid du Colombier if(cistrcmp(hcitypes[cardno].type, type) == 0)
6809a747e4fSDavid du Colombier break;
6819a747e4fSDavid du Colombier }
6829a747e4fSDavid du Colombier }
6839a747e4fSDavid du Colombier
684c8cbc0e9SDavid du Colombier if(cardno >= Nhcis || hcitypes[cardno].type == nil){
685c8cbc0e9SDavid du Colombier free(hp);
686c8cbc0e9SDavid du Colombier return nil;
687c8cbc0e9SDavid du Colombier }
688c8cbc0e9SDavid du Colombier dprint("%s...", hcitypes[cardno].type);
689c8cbc0e9SDavid du Colombier if(hcitypes[cardno].reset(hp) < 0){
690906943f9SDavid du Colombier free(hp);
6919a747e4fSDavid du Colombier return nil;
6929a747e4fSDavid du Colombier }
6939a747e4fSDavid du Colombier
6949a747e4fSDavid du Colombier /*
6959a747e4fSDavid du Colombier * IRQ2 doesn't really exist, it's used to gang the interrupt
6969a747e4fSDavid du Colombier * controllers together. A device set to IRQ2 will appear on
6979a747e4fSDavid du Colombier * the second interrupt controller as IRQ9.
6989a747e4fSDavid du Colombier */
699906943f9SDavid du Colombier if(hp->irq == 2)
700906943f9SDavid du Colombier hp->irq = 9;
701906943f9SDavid du Colombier snprint(name, sizeof(name), "usb%s", hcitypes[cardno].type);
702906943f9SDavid du Colombier intrenable(hp->irq, hp->interrupt, hp, hp->tbdf, name);
7039a747e4fSDavid du Colombier
704e4ac449cSDavid du Colombier /*
705e4ac449cSDavid du Colombier * modern machines have too many usb controllers to list on
706e4ac449cSDavid du Colombier * the console.
707e4ac449cSDavid du Colombier */
708a23bc242SDavid du Colombier dprint("#u/usb/ep%d.0: %s: port 0x%luX irq %d\n",
709a23bc242SDavid du Colombier epnb, hcitypes[cardno].type, hp->port, hp->irq);
710e4ac449cSDavid du Colombier epnb++;
711906943f9SDavid du Colombier return hp;
7129a747e4fSDavid du Colombier }
7139a747e4fSDavid du Colombier
7149a747e4fSDavid du Colombier static void
usbreset(void)7159a747e4fSDavid du Colombier usbreset(void)
7169a747e4fSDavid du Colombier {
7179a747e4fSDavid du Colombier int cardno, ctlrno;
718906943f9SDavid du Colombier Hci *hp;
7199a747e4fSDavid du Colombier
720ea58ad6fSDavid du Colombier if(getconf("*nousbprobe"))
721ea58ad6fSDavid du Colombier return;
722906943f9SDavid du Colombier dprint("usbreset\n");
723ea58ad6fSDavid du Colombier
724906943f9SDavid du Colombier for(ctlrno = 0; ctlrno < Nhcis; ctlrno++)
725906943f9SDavid du Colombier if((hp = hciprobe(-1, ctlrno)) != nil)
726906943f9SDavid du Colombier hcis[ctlrno] = hp;
7279a747e4fSDavid du Colombier cardno = ctlrno = 0;
728906943f9SDavid du Colombier while(cardno < Nhcis && ctlrno < Nhcis && hcitypes[cardno].type != nil)
729906943f9SDavid du Colombier if(hcis[ctlrno] != nil)
7309a747e4fSDavid du Colombier ctlrno++;
731906943f9SDavid du Colombier else{
732906943f9SDavid du Colombier hp = hciprobe(cardno, ctlrno);
733906943f9SDavid du Colombier if(hp == nil)
7349a747e4fSDavid du Colombier cardno++;
735906943f9SDavid du Colombier hcis[ctlrno++] = hp;
7369a747e4fSDavid du Colombier }
737906943f9SDavid du Colombier if(hcis[Nhcis-1] != nil)
73884860c5dSDavid du Colombier print("usbreset: bug: Nhcis (%d) too small\n", Nhcis);
7399a747e4fSDavid du Colombier }
7409a747e4fSDavid du Colombier
741906943f9SDavid du Colombier static void
usbinit(void)7429a747e4fSDavid du Colombier usbinit(void)
7439a747e4fSDavid du Colombier {
744906943f9SDavid du Colombier Hci *hp;
7459a747e4fSDavid du Colombier int ctlrno;
746906943f9SDavid du Colombier Ep *d;
747906943f9SDavid du Colombier char info[40];
7489a747e4fSDavid du Colombier
749906943f9SDavid du Colombier dprint("usbinit\n");
750906943f9SDavid du Colombier for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){
751906943f9SDavid du Colombier hp = hcis[ctlrno];
752906943f9SDavid du Colombier if(hp != nil){
753906943f9SDavid du Colombier if(hp->init != nil)
754906943f9SDavid du Colombier hp->init(hp);
755906943f9SDavid du Colombier d = newdev(hp, 1, 1); /* new root hub */
756906943f9SDavid du Colombier d->dev->state = Denabled; /* although addr == 0 */
757906943f9SDavid du Colombier d->maxpkt = 64;
758906943f9SDavid du Colombier snprint(info, sizeof(info), "ports %d", hp->nports);
759906943f9SDavid du Colombier kstrdup(&d->info, info);
760906943f9SDavid du Colombier }
7619a747e4fSDavid du Colombier }
7629a747e4fSDavid du Colombier }
7639a747e4fSDavid du Colombier
764906943f9SDavid du Colombier static Chan*
usbattach(char * spec)7659a747e4fSDavid du Colombier usbattach(char *spec)
7669a747e4fSDavid du Colombier {
767906943f9SDavid du Colombier return devattach(L'u', spec);
7689a747e4fSDavid du Colombier }
7699a747e4fSDavid du Colombier
7709a747e4fSDavid du Colombier static Walkqid*
usbwalk(Chan * c,Chan * nc,char ** name,int nname)7719a747e4fSDavid du Colombier usbwalk(Chan *c, Chan *nc, char **name, int nname)
7729a747e4fSDavid du Colombier {
7739a747e4fSDavid du Colombier return devwalk(c, nc, name, nname, nil, 0, usbgen);
7749a747e4fSDavid du Colombier }
7759a747e4fSDavid du Colombier
7769a747e4fSDavid du Colombier static int
usbstat(Chan * c,uchar * db,int n)7779a747e4fSDavid du Colombier usbstat(Chan *c, uchar *db, int n)
7789a747e4fSDavid du Colombier {
7799a747e4fSDavid du Colombier return devstat(c, db, n, nil, 0, usbgen);
7809a747e4fSDavid du Colombier }
7819a747e4fSDavid du Colombier
782906943f9SDavid du Colombier /*
783906943f9SDavid du Colombier * µs for the given transfer, for bandwidth allocation.
784906943f9SDavid du Colombier * This is a very rough worst case for what 5.11.3
785906943f9SDavid du Colombier * of the usb 2.0 spec says.
786906943f9SDavid du Colombier * Also, we are using maxpkt and not actual transfer sizes.
787906943f9SDavid du Colombier * Only when we are sure we
788906943f9SDavid du Colombier * are not exceeding b/w might we consider adjusting it.
789906943f9SDavid du Colombier */
790906943f9SDavid du Colombier static ulong
usbload(int speed,int maxpkt)791906943f9SDavid du Colombier usbload(int speed, int maxpkt)
792906943f9SDavid du Colombier {
793906943f9SDavid du Colombier enum{ Hostns = 1000, Hubns = 333 };
794906943f9SDavid du Colombier ulong l;
795906943f9SDavid du Colombier ulong bs;
796906943f9SDavid du Colombier
797906943f9SDavid du Colombier l = 0;
798906943f9SDavid du Colombier bs = 10UL * maxpkt;
799906943f9SDavid du Colombier switch(speed){
800906943f9SDavid du Colombier case Highspeed:
801906943f9SDavid du Colombier l = 55*8*2 + 2 * (3 + bs) + Hostns;
802906943f9SDavid du Colombier break;
803906943f9SDavid du Colombier case Fullspeed:
804906943f9SDavid du Colombier l = 9107 + 84 * (4 + bs) + Hostns;
805906943f9SDavid du Colombier break;
806906943f9SDavid du Colombier case Lowspeed:
807906943f9SDavid du Colombier l = 64107 + 2 * Hubns + 667 * (3 + bs) + Hostns;
808906943f9SDavid du Colombier break;
809906943f9SDavid du Colombier default:
810906943f9SDavid du Colombier print("usbload: bad speed %d\n", speed);
811906943f9SDavid du Colombier /* let it run */
812906943f9SDavid du Colombier }
813906943f9SDavid du Colombier return l / 1000UL; /* in µs */
814906943f9SDavid du Colombier }
815906943f9SDavid du Colombier
816906943f9SDavid du Colombier static Chan*
usbopen(Chan * c,int omode)8179a747e4fSDavid du Colombier usbopen(Chan *c, int omode)
8189a747e4fSDavid du Colombier {
819906943f9SDavid du Colombier int q;
820906943f9SDavid du Colombier Ep *ep;
821906943f9SDavid du Colombier int mode;
8229a747e4fSDavid du Colombier
823906943f9SDavid du Colombier mode = openmode(omode);
824906943f9SDavid du Colombier q = QID(c->qid);
825906943f9SDavid du Colombier
826906943f9SDavid du Colombier if(q >= Qep0dir && qid2epidx(q) < 0)
827906943f9SDavid du Colombier error(Eio);
828906943f9SDavid du Colombier if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
8299a747e4fSDavid du Colombier return devopen(c, omode, nil, 0, usbgen);
8309a747e4fSDavid du Colombier
831906943f9SDavid du Colombier ep = getep(qid2epidx(q));
832906943f9SDavid du Colombier if(ep == nil)
833906943f9SDavid du Colombier error(Eio);
834906943f9SDavid du Colombier deprint("usbopen q %#x fid %d omode %d\n", q, c->fid, mode);
8359a747e4fSDavid du Colombier if(waserror()){
836906943f9SDavid du Colombier putep(ep);
8379a747e4fSDavid du Colombier nexterror();
8389a747e4fSDavid du Colombier }
839906943f9SDavid du Colombier qlock(ep);
840906943f9SDavid du Colombier if(ep->inuse){
841906943f9SDavid du Colombier qunlock(ep);
8429a747e4fSDavid du Colombier error(Einuse);
843906943f9SDavid du Colombier }
844906943f9SDavid du Colombier ep->inuse = 1;
845906943f9SDavid du Colombier qunlock(ep);
846906943f9SDavid du Colombier if(waserror()){
847906943f9SDavid du Colombier ep->inuse = 0;
848906943f9SDavid du Colombier nexterror();
849906943f9SDavid du Colombier }
850906943f9SDavid du Colombier if(mode != OREAD && ep->mode == OREAD)
851906943f9SDavid du Colombier error(Eperm);
852906943f9SDavid du Colombier if(mode != OWRITE && ep->mode == OWRITE)
853906943f9SDavid du Colombier error(Eperm);
854906943f9SDavid du Colombier if(ep->ttype == Tnone)
855906943f9SDavid du Colombier error(Enotconf);
856906943f9SDavid du Colombier ep->clrhalt = 0;
857906943f9SDavid du Colombier ep->rhrepl = -1;
858906943f9SDavid du Colombier if(ep->load == 0)
859906943f9SDavid du Colombier ep->load = usbload(ep->dev->speed, ep->maxpkt);
860906943f9SDavid du Colombier ep->hp->epopen(ep);
8619a747e4fSDavid du Colombier
862906943f9SDavid du Colombier poperror(); /* ep->inuse */
863906943f9SDavid du Colombier poperror(); /* don't putep(): ref kept for fid using the ep. */
864ade43d10SDavid du Colombier
865906943f9SDavid du Colombier c->mode = mode;
8669a747e4fSDavid du Colombier c->flag |= COPEN;
8679a747e4fSDavid du Colombier c->offset = 0;
868906943f9SDavid du Colombier c->aux = nil; /* paranoia */
8699a747e4fSDavid du Colombier return c;
8709a747e4fSDavid du Colombier }
8719a747e4fSDavid du Colombier
872906943f9SDavid du Colombier static void
epclose(Ep * ep)873906943f9SDavid du Colombier epclose(Ep *ep)
8749a747e4fSDavid du Colombier {
875906943f9SDavid du Colombier qlock(ep);
8769a747e4fSDavid du Colombier if(waserror()){
877906943f9SDavid du Colombier qunlock(ep);
8789a747e4fSDavid du Colombier nexterror();
8799a747e4fSDavid du Colombier }
880906943f9SDavid du Colombier if(ep->inuse){
881906943f9SDavid du Colombier ep->hp->epclose(ep);
882906943f9SDavid du Colombier ep->inuse = 0;
883906943f9SDavid du Colombier }
884906943f9SDavid du Colombier qunlock(ep);
885906943f9SDavid du Colombier poperror();
886906943f9SDavid du Colombier }
887906943f9SDavid du Colombier
888906943f9SDavid du Colombier static void
usbclose(Chan * c)889906943f9SDavid du Colombier usbclose(Chan *c)
890906943f9SDavid du Colombier {
891906943f9SDavid du Colombier int q;
892906943f9SDavid du Colombier Ep *ep;
893906943f9SDavid du Colombier
894906943f9SDavid du Colombier q = QID(c->qid);
895906943f9SDavid du Colombier if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
896906943f9SDavid du Colombier return;
897906943f9SDavid du Colombier
898906943f9SDavid du Colombier ep = getep(qid2epidx(q));
899906943f9SDavid du Colombier if(ep == nil)
900906943f9SDavid du Colombier return;
901906943f9SDavid du Colombier deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref);
902906943f9SDavid du Colombier if(waserror()){
903906943f9SDavid du Colombier putep(ep);
904906943f9SDavid du Colombier nexterror();
905906943f9SDavid du Colombier }
9069a747e4fSDavid du Colombier if(c->flag & COPEN){
907906943f9SDavid du Colombier free(c->aux);
908906943f9SDavid du Colombier c->aux = nil;
909906943f9SDavid du Colombier epclose(ep);
910906943f9SDavid du Colombier putep(ep); /* release ref kept since usbopen */
911906943f9SDavid du Colombier c->flag &= ~COPEN;
9129a747e4fSDavid du Colombier }
9139a747e4fSDavid du Colombier poperror();
914906943f9SDavid du Colombier putep(ep);
9159a747e4fSDavid du Colombier }
9169a747e4fSDavid du Colombier
917906943f9SDavid du Colombier static long
ctlread(Chan * c,void * a,long n,vlong offset)918906943f9SDavid du Colombier ctlread(Chan *c, void *a, long n, vlong offset)
9199a747e4fSDavid du Colombier {
920906943f9SDavid du Colombier int q;
921906943f9SDavid du Colombier char *s;
922906943f9SDavid du Colombier char *us;
923906943f9SDavid du Colombier char *se;
924906943f9SDavid du Colombier Ep *ep;
925906943f9SDavid du Colombier int i;
9269a747e4fSDavid du Colombier
927906943f9SDavid du Colombier q = QID(c->qid);
928906943f9SDavid du Colombier us = s = smalloc(READSTR);
929906943f9SDavid du Colombier se = s + READSTR;
930906943f9SDavid du Colombier if(waserror()){
931906943f9SDavid du Colombier free(us);
932906943f9SDavid du Colombier nexterror();
9339a747e4fSDavid du Colombier }
934906943f9SDavid du Colombier if(q == Qctl)
935906943f9SDavid du Colombier for(i = 0; i < epmax; i++){
936906943f9SDavid du Colombier ep = getep(i);
937906943f9SDavid du Colombier if(ep != nil){
938906943f9SDavid du Colombier if(waserror()){
939906943f9SDavid du Colombier putep(ep);
940906943f9SDavid du Colombier nexterror();
941906943f9SDavid du Colombier }
942906943f9SDavid du Colombier s = seprint(s, se, "ep%d.%d ", ep->dev->nb, ep->nb);
943906943f9SDavid du Colombier s = seprintep(s, se, ep, 0);
944906943f9SDavid du Colombier poperror();
945906943f9SDavid du Colombier }
946906943f9SDavid du Colombier putep(ep);
947906943f9SDavid du Colombier }
948906943f9SDavid du Colombier else{
949906943f9SDavid du Colombier ep = getep(qid2epidx(q));
950906943f9SDavid du Colombier if(ep == nil)
951906943f9SDavid du Colombier error(Eio);
952906943f9SDavid du Colombier if(waserror()){
953906943f9SDavid du Colombier putep(ep);
954906943f9SDavid du Colombier nexterror();
955906943f9SDavid du Colombier }
956906943f9SDavid du Colombier if(c->aux != nil){
957906943f9SDavid du Colombier /* After a new endpoint request we read
958906943f9SDavid du Colombier * the new endpoint name back.
959906943f9SDavid du Colombier */
960906943f9SDavid du Colombier strecpy(s, se, c->aux);
961906943f9SDavid du Colombier free(c->aux);
962906943f9SDavid du Colombier c->aux = nil;
963906943f9SDavid du Colombier }else
964906943f9SDavid du Colombier seprintep(s, se, ep, 0);
965906943f9SDavid du Colombier poperror();
966906943f9SDavid du Colombier putep(ep);
967906943f9SDavid du Colombier }
968906943f9SDavid du Colombier n = readstr(offset, a, n, us);
969906943f9SDavid du Colombier poperror();
970906943f9SDavid du Colombier free(us);
971906943f9SDavid du Colombier return n;
9729a747e4fSDavid du Colombier }
9739a747e4fSDavid du Colombier
974906943f9SDavid du Colombier /*
975906943f9SDavid du Colombier * Fake root hub emulation.
976906943f9SDavid du Colombier */
977906943f9SDavid du Colombier static long
rhubread(Ep * ep,void * a,long n)978906943f9SDavid du Colombier rhubread(Ep *ep, void *a, long n)
979906943f9SDavid du Colombier {
980906943f9SDavid du Colombier char *b;
981906943f9SDavid du Colombier
982906943f9SDavid du Colombier if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2)
983906943f9SDavid du Colombier return -1;
984906943f9SDavid du Colombier if(ep->rhrepl < 0)
985906943f9SDavid du Colombier return -1;
986906943f9SDavid du Colombier
987906943f9SDavid du Colombier b = a;
988906943f9SDavid du Colombier memset(b, 0, n);
989906943f9SDavid du Colombier PUT2(b, ep->rhrepl);
990906943f9SDavid du Colombier ep->rhrepl = -1;
991906943f9SDavid du Colombier return n;
992906943f9SDavid du Colombier }
993906943f9SDavid du Colombier
994906943f9SDavid du Colombier static long
rhubwrite(Ep * ep,void * a,long n)995906943f9SDavid du Colombier rhubwrite(Ep *ep, void *a, long n)
996906943f9SDavid du Colombier {
997906943f9SDavid du Colombier uchar *s;
998906943f9SDavid du Colombier int cmd;
999906943f9SDavid du Colombier int feature;
1000906943f9SDavid du Colombier int port;
1001906943f9SDavid du Colombier Hci *hp;
1002906943f9SDavid du Colombier
1003906943f9SDavid du Colombier if(ep->dev == nil || ep->dev->isroot == 0 || ep->nb != 0)
1004906943f9SDavid du Colombier return -1;
1005906943f9SDavid du Colombier if(n != Rsetuplen)
1006906943f9SDavid du Colombier error("root hub is a toy hub");
1007906943f9SDavid du Colombier ep->rhrepl = -1;
1008906943f9SDavid du Colombier s = a;
1009906943f9SDavid du Colombier if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother))
1010906943f9SDavid du Colombier error("root hub is a toy hub");
1011906943f9SDavid du Colombier hp = ep->hp;
1012906943f9SDavid du Colombier cmd = s[Rreq];
1013906943f9SDavid du Colombier feature = GET2(s+Rvalue);
1014906943f9SDavid du Colombier port = GET2(s+Rindex);
1015906943f9SDavid du Colombier if(port < 1 || port > hp->nports)
1016906943f9SDavid du Colombier error("bad hub port number");
1017906943f9SDavid du Colombier switch(feature){
1018906943f9SDavid du Colombier case Rportenable:
1019906943f9SDavid du Colombier ep->rhrepl = hp->portenable(hp, port, cmd == Rsetfeature);
1020906943f9SDavid du Colombier break;
1021906943f9SDavid du Colombier case Rportreset:
1022906943f9SDavid du Colombier ep->rhrepl = hp->portreset(hp, port, cmd == Rsetfeature);
1023906943f9SDavid du Colombier break;
1024906943f9SDavid du Colombier case Rgetstatus:
1025906943f9SDavid du Colombier ep->rhrepl = hp->portstatus(hp, port);
1026906943f9SDavid du Colombier break;
1027906943f9SDavid du Colombier default:
1028906943f9SDavid du Colombier ep->rhrepl = 0;
1029906943f9SDavid du Colombier }
1030906943f9SDavid du Colombier return n;
1031906943f9SDavid du Colombier }
1032906943f9SDavid du Colombier
1033906943f9SDavid du Colombier static long
usbread(Chan * c,void * a,long n,vlong offset)10349a747e4fSDavid du Colombier usbread(Chan *c, void *a, long n, vlong offset)
10359a747e4fSDavid du Colombier {
1036906943f9SDavid du Colombier int q;
1037906943f9SDavid du Colombier Ep *ep;
1038906943f9SDavid du Colombier int nr;
1039906943f9SDavid du Colombier
1040906943f9SDavid du Colombier q = QID(c->qid);
10419a747e4fSDavid du Colombier
10429a747e4fSDavid du Colombier if(c->qid.type == QTDIR)
10439a747e4fSDavid du Colombier return devdirread(c, a, n, nil, 0, usbgen);
10449a747e4fSDavid du Colombier
1045906943f9SDavid du Colombier if(q == Qctl || isqtype(q, Qepctl))
1046906943f9SDavid du Colombier return ctlread(c, a, n, offset);
10479a747e4fSDavid du Colombier
1048906943f9SDavid du Colombier ep = getep(qid2epidx(q));
1049906943f9SDavid du Colombier if(ep == nil)
10509a747e4fSDavid du Colombier error(Eio);
10519a747e4fSDavid du Colombier if(waserror()){
1052906943f9SDavid du Colombier putep(ep);
10539a747e4fSDavid du Colombier nexterror();
10549a747e4fSDavid du Colombier }
1055906943f9SDavid du Colombier if(ep->dev->state == Ddetach)
1056906943f9SDavid du Colombier error(Edetach);
1057906943f9SDavid du Colombier if(ep->mode == OWRITE || ep->inuse == 0)
1058906943f9SDavid du Colombier error(Ebadusefd);
1059906943f9SDavid du Colombier switch(ep->ttype){
1060906943f9SDavid du Colombier case Tnone:
1061906943f9SDavid du Colombier error("endpoint not configured");
1062906943f9SDavid du Colombier case Tctl:
1063906943f9SDavid du Colombier nr = rhubread(ep, a, n);
1064906943f9SDavid du Colombier if(nr >= 0){
1065906943f9SDavid du Colombier n = nr;
106602c76561SDavid du Colombier break;
10679a747e4fSDavid du Colombier }
1068906943f9SDavid du Colombier /* else fall */
1069906943f9SDavid du Colombier default:
1070906943f9SDavid du Colombier ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",q,c->fid,n,offset);
1071906943f9SDavid du Colombier n = ep->hp->epread(ep, a, n);
1072906943f9SDavid du Colombier break;
10739a747e4fSDavid du Colombier }
10749a747e4fSDavid du Colombier poperror();
1075906943f9SDavid du Colombier putep(ep);
10769a747e4fSDavid du Colombier return n;
10779a747e4fSDavid du Colombier }
10789a747e4fSDavid du Colombier
1079906943f9SDavid du Colombier static long
pow2(int n)1080906943f9SDavid du Colombier pow2(int n)
10819a747e4fSDavid du Colombier {
108284860c5dSDavid du Colombier return 1 << n;
1083906943f9SDavid du Colombier }
1084906943f9SDavid du Colombier
1085906943f9SDavid du Colombier static void
setmaxpkt(Ep * ep,char * s)1086906943f9SDavid du Colombier setmaxpkt(Ep *ep, char* s)
1087906943f9SDavid du Colombier {
1088906943f9SDavid du Colombier long spp; /* samples per packet */
1089906943f9SDavid du Colombier
1090906943f9SDavid du Colombier if(ep->dev->speed == Highspeed)
1091906943f9SDavid du Colombier spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000;
1092906943f9SDavid du Colombier else
1093906943f9SDavid du Colombier spp = (ep->hz * ep->pollival + 999) / 1000;
1094906943f9SDavid du Colombier ep->maxpkt = spp * ep->samplesz;
1095906943f9SDavid du Colombier deprint("usb: %s: setmaxpkt: hz %ld poll %ld"
1096906943f9SDavid du Colombier " ntds %d %s speed -> spp %ld maxpkt %ld\n", s,
1097906943f9SDavid du Colombier ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed],
1098906943f9SDavid du Colombier spp, ep->maxpkt);
1099906943f9SDavid du Colombier if(ep->maxpkt > 1024){
1100906943f9SDavid du Colombier print("usb: %s: maxpkt %ld > 1024. truncating\n", s, ep->maxpkt);
1101906943f9SDavid du Colombier ep->maxpkt = 1024;
1102906943f9SDavid du Colombier }
1103906943f9SDavid du Colombier }
1104906943f9SDavid du Colombier
1105906943f9SDavid du Colombier /*
1106906943f9SDavid du Colombier * Many endpoint ctls. simply update the portable representation
1107906943f9SDavid du Colombier * of the endpoint. The actual controller driver will look
1108906943f9SDavid du Colombier * at them to setup the endpoints as dictated.
1109906943f9SDavid du Colombier */
1110906943f9SDavid du Colombier static long
epctl(Ep * ep,Chan * c,void * a,long n)1111906943f9SDavid du Colombier epctl(Ep *ep, Chan *c, void *a, long n)
1112906943f9SDavid du Colombier {
111384860c5dSDavid du Colombier int i, l, mode, nb, tt;
111484860c5dSDavid du Colombier char *b, *s;
111584860c5dSDavid du Colombier Cmdbuf *cb;
111684860c5dSDavid du Colombier Cmdtab *ct;
1117906943f9SDavid du Colombier Ep *nep;
11189a747e4fSDavid du Colombier Udev *d;
111984860c5dSDavid du Colombier static char *Info = "info ";
1120906943f9SDavid du Colombier
1121906943f9SDavid du Colombier d = ep->dev;
1122906943f9SDavid du Colombier
1123906943f9SDavid du Colombier cb = parsecmd(a, n);
1124906943f9SDavid du Colombier if(waserror()){
1125906943f9SDavid du Colombier free(cb);
1126906943f9SDavid du Colombier nexterror();
1127906943f9SDavid du Colombier }
1128906943f9SDavid du Colombier ct = lookupcmd(cb, epctls, nelem(epctls));
1129906943f9SDavid du Colombier if(ct == nil)
1130906943f9SDavid du Colombier error(Ebadctl);
1131906943f9SDavid du Colombier i = ct->index;
1132a23bc242SDavid du Colombier if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset)
1133906943f9SDavid du Colombier if(ep != ep->ep0)
1134906943f9SDavid du Colombier error("allowed only on a setup endpoint");
1135906943f9SDavid du Colombier if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname)
1136906943f9SDavid du Colombier if(ep != ep->ep0 && ep->inuse != 0)
1137906943f9SDavid du Colombier error("must configure before using");
1138906943f9SDavid du Colombier switch(i){
1139906943f9SDavid du Colombier case CMnew:
1140906943f9SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
1141906943f9SDavid du Colombier nb = strtol(cb->f[1], nil, 0);
1142906943f9SDavid du Colombier if(nb < 0 || nb >= Ndeveps)
1143906943f9SDavid du Colombier error("bad endpoint number");
1144906943f9SDavid du Colombier tt = name2ttype(cb->f[2]);
1145906943f9SDavid du Colombier if(tt == Tnone)
1146906943f9SDavid du Colombier error("unknown endpoint type");
1147906943f9SDavid du Colombier mode = name2mode(cb->f[3]);
1148906943f9SDavid du Colombier if(mode < 0)
1149906943f9SDavid du Colombier error("unknown i/o mode");
1150906943f9SDavid du Colombier newdevep(ep, nb, tt, mode);
1151906943f9SDavid du Colombier break;
1152906943f9SDavid du Colombier case CMnewdev:
1153906943f9SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
1154906943f9SDavid du Colombier if(ep != ep->ep0 || d->ishub == 0)
1155906943f9SDavid du Colombier error("not a hub setup endpoint");
1156906943f9SDavid du Colombier l = name2speed(cb->f[1]);
1157906943f9SDavid du Colombier if(l == Nospeed)
1158906943f9SDavid du Colombier error("speed must be full|low|high");
1159906943f9SDavid du Colombier nep = newdev(ep->hp, 0, 0);
1160906943f9SDavid du Colombier nep->dev->speed = l;
1161906943f9SDavid du Colombier if(nep->dev->speed != Lowspeed)
1162906943f9SDavid du Colombier nep->maxpkt = 64; /* assume full speed */
1163906943f9SDavid du Colombier nep->dev->hub = d->nb;
1164906943f9SDavid du Colombier nep->dev->port = atoi(cb->f[2]);
1165906943f9SDavid du Colombier /* next read request will read
1166906943f9SDavid du Colombier * the name for the new endpoint
1167906943f9SDavid du Colombier */
1168906943f9SDavid du Colombier l = sizeof(up->genbuf);
1169906943f9SDavid du Colombier snprint(up->genbuf, l, "ep%d.%d", nep->dev->nb, nep->nb);
1170906943f9SDavid du Colombier kstrdup(&c->aux, up->genbuf);
1171906943f9SDavid du Colombier break;
1172906943f9SDavid du Colombier case CMhub:
1173906943f9SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
1174906943f9SDavid du Colombier d->ishub = 1;
1175906943f9SDavid du Colombier break;
1176906943f9SDavid du Colombier case CMspeed:
1177906943f9SDavid du Colombier l = name2speed(cb->f[1]);
1178906943f9SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l);
1179906943f9SDavid du Colombier if(l == Nospeed)
1180906943f9SDavid du Colombier error("speed must be full|low|high");
1181906943f9SDavid du Colombier qlock(ep->ep0);
1182906943f9SDavid du Colombier d->speed = l;
1183906943f9SDavid du Colombier qunlock(ep->ep0);
1184906943f9SDavid du Colombier break;
1185906943f9SDavid du Colombier case CMmaxpkt:
1186906943f9SDavid du Colombier l = strtoul(cb->f[1], nil, 0);
1187906943f9SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l);
1188906943f9SDavid du Colombier if(l < 1 || l > 1024)
1189906943f9SDavid du Colombier error("maxpkt not in [1:1024]");
1190906943f9SDavid du Colombier qlock(ep);
1191906943f9SDavid du Colombier ep->maxpkt = l;
1192906943f9SDavid du Colombier qunlock(ep);
1193906943f9SDavid du Colombier break;
1194906943f9SDavid du Colombier case CMntds:
1195906943f9SDavid du Colombier l = strtoul(cb->f[1], nil, 0);
1196906943f9SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l);
1197906943f9SDavid du Colombier if(l < 1 || l > 3)
1198906943f9SDavid du Colombier error("ntds not in [1:3]");
1199906943f9SDavid du Colombier qlock(ep);
1200906943f9SDavid du Colombier ep->ntds = l;
1201906943f9SDavid du Colombier qunlock(ep);
1202906943f9SDavid du Colombier break;
1203906943f9SDavid du Colombier case CMpollival:
1204906943f9SDavid du Colombier if(ep->ttype != Tintr && ep->ttype != Tiso)
1205906943f9SDavid du Colombier error("not an intr or iso endpoint");
1206906943f9SDavid du Colombier l = strtoul(cb->f[1], nil, 0);
1207906943f9SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l);
1208906943f9SDavid du Colombier if(ep->ttype == Tiso ||
1209906943f9SDavid du Colombier (ep->ttype == Tintr && ep->dev->speed == Highspeed)){
1210906943f9SDavid du Colombier if(l < 1 || l > 16)
1211906943f9SDavid du Colombier error("pollival power not in [1:16]");
1212906943f9SDavid du Colombier l = pow2(l-1);
1213906943f9SDavid du Colombier }else
1214906943f9SDavid du Colombier if(l < 1 || l > 255)
1215906943f9SDavid du Colombier error("pollival not in [1:255]");
1216906943f9SDavid du Colombier qlock(ep);
1217906943f9SDavid du Colombier ep->pollival = l;
1218906943f9SDavid du Colombier if(ep->ttype == Tiso)
1219906943f9SDavid du Colombier setmaxpkt(ep, "pollival");
1220906943f9SDavid du Colombier qunlock(ep);
1221906943f9SDavid du Colombier break;
1222906943f9SDavid du Colombier case CMsamplesz:
1223906943f9SDavid du Colombier if(ep->ttype != Tiso)
1224906943f9SDavid du Colombier error("not an iso endpoint");
1225906943f9SDavid du Colombier l = strtoul(cb->f[1], nil, 0);
1226906943f9SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l);
1227906943f9SDavid du Colombier if(l <= 0 || l > 8)
1228906943f9SDavid du Colombier error("samplesz not in [1:8]");
1229906943f9SDavid du Colombier qlock(ep);
1230906943f9SDavid du Colombier ep->samplesz = l;
1231906943f9SDavid du Colombier setmaxpkt(ep, "samplesz");
1232906943f9SDavid du Colombier qunlock(ep);
1233906943f9SDavid du Colombier break;
1234906943f9SDavid du Colombier case CMhz:
1235906943f9SDavid du Colombier if(ep->ttype != Tiso)
1236906943f9SDavid du Colombier error("not an iso endpoint");
1237906943f9SDavid du Colombier l = strtoul(cb->f[1], nil, 0);
1238906943f9SDavid du Colombier deprint("usb epctl %s %d\n", cb->f[0], l);
1239906943f9SDavid du Colombier if(l <= 0 || l > 100000)
1240906943f9SDavid du Colombier error("hz not in [1:100000]");
1241906943f9SDavid du Colombier qlock(ep);
1242906943f9SDavid du Colombier ep->hz = l;
1243906943f9SDavid du Colombier setmaxpkt(ep, "hz");
1244906943f9SDavid du Colombier qunlock(ep);
1245906943f9SDavid du Colombier break;
1246906943f9SDavid du Colombier case CMclrhalt:
1247906943f9SDavid du Colombier qlock(ep);
1248906943f9SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
1249906943f9SDavid du Colombier ep->clrhalt = 1;
1250906943f9SDavid du Colombier qunlock(ep);
1251906943f9SDavid du Colombier break;
1252906943f9SDavid du Colombier case CMinfo:
1253906943f9SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
1254906943f9SDavid du Colombier l = strlen(Info);
1255906943f9SDavid du Colombier s = a;
1256906943f9SDavid du Colombier if(n < l+2 || strncmp(Info, s, l) != 0)
1257906943f9SDavid du Colombier error(Ebadctl);
1258906943f9SDavid du Colombier if(n > 1024)
1259906943f9SDavid du Colombier n = 1024;
1260906943f9SDavid du Colombier b = smalloc(n);
1261906943f9SDavid du Colombier memmove(b, s+l, n-l);
1262906943f9SDavid du Colombier b[n-l] = 0;
1263906943f9SDavid du Colombier if(b[n-l-1] == '\n')
1264906943f9SDavid du Colombier b[n-l-1] = 0;
1265906943f9SDavid du Colombier qlock(ep);
1266906943f9SDavid du Colombier free(ep->info);
1267906943f9SDavid du Colombier ep->info = b;
1268906943f9SDavid du Colombier qunlock(ep);
1269906943f9SDavid du Colombier break;
1270906943f9SDavid du Colombier case CMaddress:
1271906943f9SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
1272906943f9SDavid du Colombier ep->dev->state = Denabled;
1273906943f9SDavid du Colombier break;
1274906943f9SDavid du Colombier case CMdetach:
1275906943f9SDavid du Colombier if(ep->dev->isroot != 0)
1276906943f9SDavid du Colombier error("can't detach a root hub");
1277906943f9SDavid du Colombier deprint("usb epctl %s ep%d.%d\n",
1278906943f9SDavid du Colombier cb->f[0], ep->dev->nb, ep->nb);
1279906943f9SDavid du Colombier ep->dev->state = Ddetach;
1280906943f9SDavid du Colombier /* Release file system ref. for its endpoints */
1281906943f9SDavid du Colombier for(i = 0; i < nelem(ep->dev->eps); i++)
1282906943f9SDavid du Colombier putep(ep->dev->eps[i]);
1283906943f9SDavid du Colombier break;
1284906943f9SDavid du Colombier case CMdebugep:
1285906943f9SDavid du Colombier if(strcmp(cb->f[1], "on") == 0)
1286906943f9SDavid du Colombier ep->debug = 1;
1287906943f9SDavid du Colombier else if(strcmp(cb->f[1], "off") == 0)
1288906943f9SDavid du Colombier ep->debug = 0;
1289906943f9SDavid du Colombier else
1290906943f9SDavid du Colombier ep->debug = strtoul(cb->f[1], nil, 0);
1291906943f9SDavid du Colombier print("usb: ep%d.%d debug %d\n",
1292906943f9SDavid du Colombier ep->dev->nb, ep->nb, ep->debug);
1293906943f9SDavid du Colombier break;
1294906943f9SDavid du Colombier case CMname:
1295906943f9SDavid du Colombier deprint("usb epctl %s %s\n", cb->f[0], cb->f[1]);
1296906943f9SDavid du Colombier validname(cb->f[1], 0);
1297906943f9SDavid du Colombier kstrdup(&ep->name, cb->f[1]);
1298906943f9SDavid du Colombier break;
1299d37e33ffSDavid du Colombier case CMtmout:
1300d37e33ffSDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
1301d37e33ffSDavid du Colombier if(ep->ttype == Tiso || ep->ttype == Tctl)
1302d37e33ffSDavid du Colombier error("ctl ignored for this endpoint type");
1303d37e33ffSDavid du Colombier ep->tmout = strtoul(cb->f[1], nil, 0);
1304d37e33ffSDavid du Colombier if(ep->tmout != 0 && ep->tmout < Xfertmout)
1305d37e33ffSDavid du Colombier ep->tmout = Xfertmout;
1306d37e33ffSDavid du Colombier break;
1307a23bc242SDavid du Colombier case CMpreset:
1308a23bc242SDavid du Colombier deprint("usb epctl %s\n", cb->f[0]);
1309a23bc242SDavid du Colombier if(ep->ttype != Tctl)
1310a23bc242SDavid du Colombier error("not a control endpoint");
1311a23bc242SDavid du Colombier if(ep->dev->state != Denabled)
1312a23bc242SDavid du Colombier error("forbidden on devices not enabled");
1313a23bc242SDavid du Colombier ep->dev->state = Dreset;
1314a23bc242SDavid du Colombier break;
1315906943f9SDavid du Colombier default:
1316c9b6d007SDavid du Colombier panic("usb: unknown epctl %d", ct->index);
1317906943f9SDavid du Colombier }
1318906943f9SDavid du Colombier free(cb);
1319906943f9SDavid du Colombier poperror();
1320906943f9SDavid du Colombier return n;
1321906943f9SDavid du Colombier }
1322906943f9SDavid du Colombier
1323906943f9SDavid du Colombier static long
usbctl(void * a,long n)1324906943f9SDavid du Colombier usbctl(void *a, long n)
1325906943f9SDavid du Colombier {
1326906943f9SDavid du Colombier Cmdtab *ct;
1327906943f9SDavid du Colombier Cmdbuf *cb;
1328906943f9SDavid du Colombier Ep *ep;
1329906943f9SDavid du Colombier int i;
1330906943f9SDavid du Colombier
1331906943f9SDavid du Colombier cb = parsecmd(a, n);
1332906943f9SDavid du Colombier if(waserror()){
1333906943f9SDavid du Colombier free(cb);
1334906943f9SDavid du Colombier nexterror();
1335906943f9SDavid du Colombier }
1336906943f9SDavid du Colombier ct = lookupcmd(cb, usbctls, nelem(usbctls));
1337906943f9SDavid du Colombier dprint("usb ctl %s\n", cb->f[0]);
1338906943f9SDavid du Colombier switch(ct->index){
1339906943f9SDavid du Colombier case CMdebug:
1340906943f9SDavid du Colombier if(strcmp(cb->f[1], "on") == 0)
1341906943f9SDavid du Colombier debug = 1;
1342906943f9SDavid du Colombier else if(strcmp(cb->f[1], "off") == 0)
1343906943f9SDavid du Colombier debug = 0;
1344906943f9SDavid du Colombier else
1345906943f9SDavid du Colombier debug = strtol(cb->f[1], nil, 0);
1346906943f9SDavid du Colombier print("usb: debug %d\n", debug);
1347906943f9SDavid du Colombier for(i = 0; i < epmax; i++)
1348906943f9SDavid du Colombier if((ep = getep(i)) != nil){
1349906943f9SDavid du Colombier ep->hp->debug(ep->hp, debug);
1350906943f9SDavid du Colombier putep(ep);
1351906943f9SDavid du Colombier }
1352906943f9SDavid du Colombier break;
1353906943f9SDavid du Colombier case CMdump:
1354906943f9SDavid du Colombier dumpeps();
1355906943f9SDavid du Colombier break;
1356906943f9SDavid du Colombier }
1357906943f9SDavid du Colombier free(cb);
1358906943f9SDavid du Colombier poperror();
1359906943f9SDavid du Colombier return n;
1360906943f9SDavid du Colombier }
1361906943f9SDavid du Colombier
1362906943f9SDavid du Colombier static long
ctlwrite(Chan * c,void * a,long n)1363906943f9SDavid du Colombier ctlwrite(Chan *c, void *a, long n)
1364906943f9SDavid du Colombier {
1365906943f9SDavid du Colombier int q;
1366906943f9SDavid du Colombier Ep *ep;
1367906943f9SDavid du Colombier
1368906943f9SDavid du Colombier q = QID(c->qid);
1369906943f9SDavid du Colombier if(q == Qctl)
1370906943f9SDavid du Colombier return usbctl(a, n);
1371906943f9SDavid du Colombier
1372906943f9SDavid du Colombier ep = getep(qid2epidx(q));
1373906943f9SDavid du Colombier if(ep == nil)
1374906943f9SDavid du Colombier error(Eio);
1375906943f9SDavid du Colombier if(waserror()){
1376906943f9SDavid du Colombier putep(ep);
1377906943f9SDavid du Colombier nexterror();
1378906943f9SDavid du Colombier }
1379906943f9SDavid du Colombier if(ep->dev->state == Ddetach)
1380906943f9SDavid du Colombier error(Edetach);
1381906943f9SDavid du Colombier if(isqtype(q, Qepctl) && c->aux != nil){
1382906943f9SDavid du Colombier /* Be sure we don't keep a cloned ep name */
1383906943f9SDavid du Colombier free(c->aux);
1384906943f9SDavid du Colombier c->aux = nil;
1385906943f9SDavid du Colombier error("read, not write, expected");
1386906943f9SDavid du Colombier }
1387906943f9SDavid du Colombier n = epctl(ep, c, a, n);
1388906943f9SDavid du Colombier putep(ep);
1389906943f9SDavid du Colombier poperror();
1390906943f9SDavid du Colombier return n;
1391906943f9SDavid du Colombier }
1392906943f9SDavid du Colombier
1393906943f9SDavid du Colombier static long
usbwrite(Chan * c,void * a,long n,vlong off)1394906943f9SDavid du Colombier usbwrite(Chan *c, void *a, long n, vlong off)
1395906943f9SDavid du Colombier {
139684860c5dSDavid du Colombier int nr, q;
1397906943f9SDavid du Colombier Ep *ep;
13989a747e4fSDavid du Colombier
13999a747e4fSDavid du Colombier if(c->qid.type == QTDIR)
140045d5944cSDavid du Colombier error(Eisdir);
14019a747e4fSDavid du Colombier
1402906943f9SDavid du Colombier q = QID(c->qid);
14039a747e4fSDavid du Colombier
1404906943f9SDavid du Colombier if(q == Qctl || isqtype(q, Qepctl))
1405906943f9SDavid du Colombier return ctlwrite(c, a, n);
14069a747e4fSDavid du Colombier
1407906943f9SDavid du Colombier ep = getep(qid2epidx(q));
1408906943f9SDavid du Colombier if(ep == nil)
14099a747e4fSDavid du Colombier error(Eio);
1410906943f9SDavid du Colombier if(waserror()){
1411906943f9SDavid du Colombier putep(ep);
1412906943f9SDavid du Colombier nexterror();
1413906943f9SDavid du Colombier }
1414906943f9SDavid du Colombier if(ep->dev->state == Ddetach)
1415906943f9SDavid du Colombier error(Edetach);
1416906943f9SDavid du Colombier if(ep->mode == OREAD || ep->inuse == 0)
1417906943f9SDavid du Colombier error(Ebadusefd);
14189a747e4fSDavid du Colombier
1419906943f9SDavid du Colombier switch(ep->ttype){
1420906943f9SDavid du Colombier case Tnone:
1421906943f9SDavid du Colombier error("endpoint not configured");
1422906943f9SDavid du Colombier case Tctl:
1423906943f9SDavid du Colombier nr = rhubwrite(ep, a, n);
1424906943f9SDavid du Colombier if(nr >= 0){
1425906943f9SDavid du Colombier n = nr;
14269a747e4fSDavid du Colombier break;
14279a747e4fSDavid du Colombier }
1428906943f9SDavid du Colombier /* else fall */
1429906943f9SDavid du Colombier default:
1430906943f9SDavid du Colombier ddeprint("\nusbwrite q %#x fid %d cnt %ld off %lld\n",q, c->fid, n, off);
1431906943f9SDavid du Colombier ep->hp->epwrite(ep, a, n);
1432906943f9SDavid du Colombier }
1433906943f9SDavid du Colombier putep(ep);
1434906943f9SDavid du Colombier poperror();
14359a747e4fSDavid du Colombier return n;
14369a747e4fSDavid du Colombier }
14379a747e4fSDavid du Colombier
1438c8cbc0e9SDavid du Colombier void
usbshutdown(void)1439c8cbc0e9SDavid du Colombier usbshutdown(void)
1440c8cbc0e9SDavid du Colombier {
1441c8cbc0e9SDavid du Colombier Hci *hp;
1442c8cbc0e9SDavid du Colombier int i;
1443c8cbc0e9SDavid du Colombier
1444c8cbc0e9SDavid du Colombier for(i = 0; i < Nhcis; i++){
1445c8cbc0e9SDavid du Colombier hp = hcis[i];
1446c8cbc0e9SDavid du Colombier if(hp == nil)
1447c8cbc0e9SDavid du Colombier continue;
1448c8cbc0e9SDavid du Colombier if(hp->shutdown == nil)
1449c8cbc0e9SDavid du Colombier print("#u: no shutdown function for %s\n", hp->type);
1450c8cbc0e9SDavid du Colombier else
1451c8cbc0e9SDavid du Colombier hp->shutdown(hp);
1452c8cbc0e9SDavid du Colombier }
1453c8cbc0e9SDavid du Colombier }
1454c8cbc0e9SDavid du Colombier
14559a747e4fSDavid du Colombier Dev usbdevtab = {
1456906943f9SDavid du Colombier L'u',
14579a747e4fSDavid du Colombier "usb",
14589a747e4fSDavid du Colombier
14599a747e4fSDavid du Colombier usbreset,
14609a747e4fSDavid du Colombier usbinit,
1461c8cbc0e9SDavid du Colombier usbshutdown,
14629a747e4fSDavid du Colombier usbattach,
14639a747e4fSDavid du Colombier usbwalk,
14649a747e4fSDavid du Colombier usbstat,
14659a747e4fSDavid du Colombier usbopen,
14669a747e4fSDavid du Colombier devcreate,
14679a747e4fSDavid du Colombier usbclose,
14689a747e4fSDavid du Colombier usbread,
14699a747e4fSDavid du Colombier devbread,
14709a747e4fSDavid du Colombier usbwrite,
14719a747e4fSDavid du Colombier devbwrite,
14729a747e4fSDavid du Colombier devremove,
14739a747e4fSDavid du Colombier devwstat,
14749a747e4fSDavid du Colombier };
1475