1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <fcall.h>
5 #include "usb.h"
6 #include "usbfs.h"
7 #include "usbd.h"
8
9 static Channel *portc;
10 static int win;
11 static int verbose;
12
13 int mainstacksize = Stack;
14 static Hub *hubs;
15 static int nhubs;
16 static int mustdump;
17 static int pollms = Pollms;
18
19 static char *dsname[] = { "disabled", "attached", "configed" };
20
21 static int
hubfeature(Hub * h,int port,int f,int on)22 hubfeature(Hub *h, int port, int f, int on)
23 {
24 int cmd;
25
26 if(on)
27 cmd = Rsetfeature;
28 else
29 cmd = Rclearfeature;
30 return usbcmd(h->dev, Rh2d|Rclass|Rother, cmd, f, port, nil, 0);
31 }
32
33 /*
34 * This may be used to detect overcurrent on the hub
35 */
36 static void
checkhubstatus(Hub * h)37 checkhubstatus(Hub *h)
38 {
39 uchar buf[4];
40 int sts;
41
42 if(h->isroot) /* not for root hubs */
43 return;
44 if(usbcmd(h->dev, Rd2h|Rclass|Rdev, Rgetstatus, 0, 0, buf, 4) < 0){
45 dprint(2, "%s: get hub status: %r\n", h->dev->dir);
46 return;
47 }
48 sts = GET2(buf);
49 dprint(2, "hub %s: status %#ux\n", h->dev->dir, sts);
50 }
51
52 static int
confighub(Hub * h)53 confighub(Hub *h)
54 {
55 int type;
56 uchar buf[128]; /* room for extra descriptors */
57 int i;
58 Usbdev *d;
59 DHub *dd;
60 Port *pp;
61 int nr;
62 int nmap;
63 uchar *PortPwrCtrlMask;
64 int offset;
65 int mask;
66
67 d = h->dev->usb;
68 for(i = 0; i < nelem(d->ddesc); i++)
69 if(d->ddesc[i] == nil)
70 break;
71 else if(d->ddesc[i]->data.bDescriptorType == Dhub){
72 dd = (DHub*)&d->ddesc[i]->data;
73 nr = Dhublen;
74 goto Config;
75 }
76 type = Rd2h|Rclass|Rdev;
77 nr = usbcmd(h->dev, type, Rgetdesc, Dhub<<8|0, 0, buf, sizeof buf);
78 if(nr < Dhublen){
79 dprint(2, "%s: %s: getdesc hub: %r\n", argv0, h->dev->dir);
80 return -1;
81 }
82 dd = (DHub*)buf;
83 Config:
84 h->nport = dd->bNbrPorts;
85 nmap = 1 + h->nport/8;
86 if(nr < 7 + 2*nmap){
87 fprint(2, "%s: %s: descr. too small\n", argv0, h->dev->dir);
88 return -1;
89 }
90 h->port = emallocz((h->nport+1)*sizeof(Port), 1);
91 h->pwrms = dd->bPwrOn2PwrGood*2;
92 if(h->pwrms < Powerdelay)
93 h->pwrms = Powerdelay;
94 h->maxcurrent = dd->bHubContrCurrent;
95 h->pwrmode = dd->wHubCharacteristics[0] & 3;
96 h->compound = (dd->wHubCharacteristics[0] & (1<<2))!=0;
97 h->leds = (dd->wHubCharacteristics[0] & (1<<7)) != 0;
98 PortPwrCtrlMask = dd->DeviceRemovable + nmap;
99 for(i = 1; i <= h->nport; i++){
100 pp = &h->port[i];
101 offset = i/8;
102 mask = 1<<(i%8);
103 pp->removable = (dd->DeviceRemovable[offset] & mask) != 0;
104 pp->pwrctl = (PortPwrCtrlMask[offset] & mask) != 0;
105 }
106 return 0;
107 }
108
109 static void
configroothub(Hub * h)110 configroothub(Hub *h)
111 {
112 Dev *d;
113 char buf[128];
114 char *p;
115 int nr;
116
117 d = h->dev;
118 h->nport = 2;
119 h->maxpkt = 8;
120 seek(d->cfd, 0, 0);
121 nr = read(d->cfd, buf, sizeof(buf)-1);
122 if(nr < 0)
123 goto Done;
124 buf[nr] = 0;
125
126 p = strstr(buf, "ports ");
127 if(p == nil)
128 fprint(2, "%s: %s: no port information\n", argv0, d->dir);
129 else
130 h->nport = atoi(p+6);
131 p = strstr(buf, "maxpkt ");
132 if(p == nil)
133 fprint(2, "%s: %s: no maxpkt information\n", argv0, d->dir);
134 else
135 h->maxpkt = atoi(p+7);
136 Done:
137 h->port = emallocz((h->nport+1)*sizeof(Port), 1);
138 dprint(2, "%s: %s: ports %d maxpkt %d\n", argv0, d->dir, h->nport, h->maxpkt);
139 }
140
141 Hub*
newhub(char * fn,Dev * d)142 newhub(char *fn, Dev *d)
143 {
144 Hub *h;
145 int i;
146 Usbdev *ud;
147
148 h = emallocz(sizeof(Hub), 1);
149 h->isroot = (d == nil);
150 if(h->isroot){
151 h->dev = opendev(fn);
152 if(h->dev == nil){
153 fprint(2, "%s: opendev: %s: %r", argv0, fn);
154 goto Fail;
155 }
156 if(opendevdata(h->dev, ORDWR) < 0){
157 fprint(2, "%s: opendevdata: %s: %r\n", argv0, fn);
158 goto Fail;
159 }
160 configroothub(h); /* never fails */
161 }else{
162 h->dev = d;
163 if(confighub(h) < 0){
164 fprint(2, "%s: %s: config: %r\n", argv0, fn);
165 goto Fail;
166 }
167 }
168 if(h->dev == nil){
169 fprint(2, "%s: opendev: %s: %r\n", argv0, fn);
170 goto Fail;
171 }
172 devctl(h->dev, "hub");
173 ud = h->dev->usb;
174 if(h->isroot)
175 devctl(h->dev, "info roothub csp %#08ux ports %d",
176 0x000009, h->nport);
177 else{
178 devctl(h->dev, "info hub csp %#08ulx ports %d %q %q",
179 ud->csp, h->nport, ud->vendor, ud->product);
180 for(i = 1; i <= h->nport; i++)
181 if(hubfeature(h, i, Fportpower, 1) < 0)
182 fprint(2, "%s: %s: power: %r\n", argv0, fn);
183 sleep(h->pwrms);
184 for(i = 1; i <= h->nport; i++)
185 if(h->leds != 0)
186 hubfeature(h, i, Fportindicator, 1);
187 }
188 h->next = hubs;
189 hubs = h;
190 nhubs++;
191 dprint(2, "%s: hub %#p allocated:", argv0, h);
192 dprint(2, " ports %d pwrms %d max curr %d pwrm %d cmp %d leds %d\n",
193 h->nport, h->pwrms, h->maxcurrent,
194 h->pwrmode, h->compound, h->leds);
195 incref(h->dev);
196 return h;
197 Fail:
198 if(d != nil)
199 devctl(d, "detach");
200 free(h->port);
201 free(h);
202 dprint(2, "%s: hub %#p failed to start:", argv0, h);
203 return nil;
204 }
205
206 static void portdetach(Hub *h, int p);
207
208 /*
209 * If during enumeration we get an I/O error the hub is gone or
210 * in pretty bad shape. Because of retries of failed usb commands
211 * (and the sleeps they include) it can take a while to detach all
212 * ports for the hub. This detaches all ports and makes the hub void.
213 * The parent hub will detect a detach (probably right now) and
214 * close it later.
215 */
216 static void
hubfail(Hub * h)217 hubfail(Hub *h)
218 {
219 int i;
220
221 for(i = 1; i <= h->nport; i++)
222 portdetach(h, i);
223 h->failed = 1;
224 }
225
226 static void
closehub(Hub * h)227 closehub(Hub *h)
228 {
229 Hub **hl;
230
231 dprint(2, "%s: closing hub %#p\n", argv0, h);
232 for(hl = &hubs; *hl != nil; hl = &(*hl)->next)
233 if(*hl == h)
234 break;
235 if(*hl == nil)
236 sysfatal("closehub: no hub");
237 *hl = h->next;
238 nhubs--;
239 hubfail(h); /* detach all ports */
240 free(h->port);
241 assert(h->dev != nil);
242 devctl(h->dev, "detach");
243 closedev(h->dev);
244 free(h);
245 }
246
247 static int
portstatus(Hub * h,int p)248 portstatus(Hub *h, int p)
249 {
250 Dev *d;
251 uchar buf[4];
252 int t;
253 int sts;
254 int dbg;
255
256 dbg = usbdebug;
257 if(dbg != 0 && dbg < 4)
258 usbdebug = 1; /* do not be too chatty */
259 d = h->dev;
260 t = Rd2h|Rclass|Rother;
261 if(usbcmd(d, t, Rgetstatus, 0, p, buf, sizeof(buf)) < 0)
262 sts = -1;
263 else
264 sts = GET2(buf);
265 usbdebug = dbg;
266 return sts;
267 }
268
269 static char*
stsstr(int sts)270 stsstr(int sts)
271 {
272 static char s[80];
273 char *e;
274
275 e = s;
276 if(sts&PSsuspend)
277 *e++ = 'z';
278 if(sts&PSreset)
279 *e++ = 'r';
280 if(sts&PSslow)
281 *e++ = 'l';
282 if(sts&PShigh)
283 *e++ = 'h';
284 if(sts&PSchange)
285 *e++ = 'c';
286 if(sts&PSenable)
287 *e++ = 'e';
288 if(sts&PSstatuschg)
289 *e++ = 's';
290 if(sts&PSpresent)
291 *e++ = 'p';
292 if(e == s)
293 *e++ = '-';
294 *e = 0;
295 return s;
296 }
297
298 static int
getmaxpkt(Dev * d,int islow)299 getmaxpkt(Dev *d, int islow)
300 {
301 uchar buf[64]; /* More room to try to get device-specific descriptors */
302 DDev *dd;
303
304 dd = (DDev*)buf;
305 if(islow)
306 dd->bMaxPacketSize0 = 8;
307 else
308 dd->bMaxPacketSize0 = 64;
309 if(usbcmd(d, Rd2h|Rstd|Rdev, Rgetdesc, Ddev<<8|0, 0, buf, sizeof(buf)) < 0)
310 return -1;
311 return dd->bMaxPacketSize0;
312 }
313
314 /*
315 * BUG: does not consider max. power avail.
316 */
317 static Dev*
portattach(Hub * h,int p,int sts)318 portattach(Hub *h, int p, int sts)
319 {
320 Dev *d;
321 Port *pp;
322 Dev *nd;
323 char fname[80];
324 char buf[40];
325 char *sp;
326 int mp;
327 int nr;
328
329 d = h->dev;
330 pp = &h->port[p];
331 nd = nil;
332 pp->state = Pattached;
333 dprint(2, "%s: %s: port %d attach sts %#ux\n", argv0, d->dir, p, sts);
334 sleep(Connectdelay);
335 if(hubfeature(h, p, Fportenable, 1) < 0)
336 dprint(2, "%s: %s: port %d: enable: %r\n", argv0, d->dir, p);
337 sleep(Enabledelay);
338 if(hubfeature(h, p, Fportreset, 1) < 0){
339 dprint(2, "%s: %s: port %d: reset: %r\n", argv0, d->dir, p);
340 goto Fail;
341 }
342 sleep(Resetdelay);
343 sts = portstatus(h, p);
344 if(sts < 0)
345 goto Fail;
346 if((sts & PSenable) == 0){
347 dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p);
348 hubfeature(h, p, Fportenable, 1);
349 sts = portstatus(h, p);
350 if((sts & PSenable) == 0)
351 goto Fail;
352 }
353 sp = "full";
354 if(sts & PSslow)
355 sp = "low";
356 if(sts & PShigh)
357 sp = "high";
358 dprint(2, "%s: %s: port %d: attached status %#ux\n", argv0, d->dir, p, sts);
359
360 if(devctl(d, "newdev %s %d", sp, p) < 0){
361 fprint(2, "%s: %s: port %d: newdev: %r\n", argv0, d->dir, p);
362 goto Fail;
363 }
364 seek(d->cfd, 0, 0);
365 nr = read(d->cfd, buf, sizeof(buf)-1);
366 if(nr == 0){
367 fprint(2, "%s: %s: port %d: newdev: eof\n", argv0, d->dir, p);
368 goto Fail;
369 }
370 if(nr < 0){
371 fprint(2, "%s: %s: port %d: newdev: %r\n", argv0, d->dir, p);
372 goto Fail;
373 }
374 buf[nr] = 0;
375 snprint(fname, sizeof(fname), "/dev/usb/%s", buf);
376 nd = opendev(fname);
377 if(nd == nil){
378 fprint(2, "%s: %s: port %d: opendev: %r\n", argv0, d->dir, p);
379 goto Fail;
380 }
381 if(usbdebug > 2)
382 devctl(nd, "debug 1");
383 if(opendevdata(nd, ORDWR) < 0){
384 fprint(2, "%s: %s: opendevdata: %r\n", argv0, nd->dir);
385 goto Fail;
386 }
387 if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetaddress, nd->id, 0, nil, 0) < 0){
388 dprint(2, "%s: %s: port %d: setaddress: %r\n", argv0, d->dir, p);
389 goto Fail;
390 }
391 if(devctl(nd, "address") < 0){
392 dprint(2, "%s: %s: port %d: set address: %r\n", argv0, d->dir, p);
393 goto Fail;
394 }
395
396 mp=getmaxpkt(nd, strcmp(sp, "low") == 0);
397 if(mp < 0){
398 dprint(2, "%s: %s: port %d: getmaxpkt: %r\n", argv0, d->dir, p);
399 goto Fail;
400 }else{
401 dprint(2, "%s; %s: port %d: maxpkt %d\n", argv0, d->dir, p, mp);
402 devctl(nd, "maxpkt %d", mp);
403 }
404 if((sts & PSslow) != 0 && strcmp(sp, "full") == 0)
405 dprint(2, "%s: %s: port %d: %s is full speed when port is low\n",
406 argv0, d->dir, p, nd->dir);
407 if(configdev(nd) < 0){
408 dprint(2, "%s: %s: port %d: configdev: %r\n", argv0, d->dir, p);
409 goto Fail;
410 }
411 /*
412 * We always set conf #1. BUG.
413 */
414 if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0){
415 dprint(2, "%s: %s: port %d: setconf: %r\n", argv0, d->dir, p);
416 unstall(nd, nd, Eout);
417 if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0)
418 goto Fail;
419 }
420 dprint(2, "%s: %U", argv0, nd);
421 pp->state = Pconfiged;
422 dprint(2, "%s: %s: port %d: configed: %s\n",
423 argv0, d->dir, p, nd->dir);
424 return pp->dev = nd;
425 Fail:
426 pp->state = Pdisabled;
427 pp->sts = 0;
428 if(pp->hub != nil)
429 pp->hub = nil; /* hub closed by enumhub */
430 hubfeature(h, p, Fportenable, 0);
431 if(nd != nil)
432 devctl(nd, "detach");
433 closedev(nd);
434 return nil;
435 }
436
437 static void
portdetach(Hub * h,int p)438 portdetach(Hub *h, int p)
439 {
440 Dev *d;
441 Port *pp;
442 extern void usbfsgone(char*);
443 d = h->dev;
444 pp = &h->port[p];
445
446 /*
447 * Clear present, so that we detect an attach on reconnects.
448 */
449 pp->sts &= ~(PSpresent|PSenable);
450
451 if(pp->state == Pdisabled)
452 return;
453 pp->state = Pdisabled;
454 dprint(2, "%s: %s: port %d: detached\n", argv0, d->dir, p);
455
456 if(pp->hub != nil){
457 closehub(pp->hub);
458 pp->hub = nil;
459 }
460 if(pp->devmaskp != nil)
461 putdevnb(pp->devmaskp, pp->devnb);
462 pp->devmaskp = nil;
463 if(pp->dev != nil){
464 devctl(pp->dev, "detach");
465 usbfsgone(pp->dev->dir);
466 closedev(pp->dev);
467 pp->dev = nil;
468 }
469 }
470
471 /*
472 * The next two functions are included to
473 * perform a port reset asked for by someone (usually a driver).
474 * This must be done while no other device is in using the
475 * configuration address and with care to keep the old address.
476 * To keep drivers decoupled from usbd they write the reset request
477 * to the #u/usb/epN.0/ctl file and then exit.
478 * This is unfortunate because usbd must now poll twice as much.
479 *
480 * An alternative to this reset process would be for the driver to detach
481 * the device. The next function could see that, issue a port reset, and
482 * then restart the driver once to see if it's a temporary error.
483 *
484 * The real fix would be to use interrupt endpoints for non-root hubs
485 * (would probably make some hubs fail) and add an events file to
486 * the kernel to report events to usbd. This is a severe change not
487 * yet implemented.
488 */
489 static int
portresetwanted(Hub * h,int p)490 portresetwanted(Hub *h, int p)
491 {
492 char buf[5];
493 Port *pp;
494 Dev *nd;
495
496 pp = &h->port[p];
497 nd = pp->dev;
498 if(nd != nil && nd->cfd >= 0 && pread(nd->cfd, buf, 5, 0LL) == 5)
499 return strncmp(buf, "reset", 5) == 0;
500 else
501 return 0;
502 }
503
504 static void
portreset(Hub * h,int p)505 portreset(Hub *h, int p)
506 {
507 int sts;
508 Dev *d, *nd;
509 Port *pp;
510
511 d = h->dev;
512 pp = &h->port[p];
513 nd = pp->dev;
514 dprint(2, "%s: %s: port %d: resetting\n", argv0, d->dir, p);
515 if(hubfeature(h, p, Fportreset, 1) < 0){
516 dprint(2, "%s: %s: port %d: reset: %r\n", argv0, d->dir, p);
517 goto Fail;
518 }
519 sleep(Resetdelay);
520 sts = portstatus(h, p);
521 if(sts < 0)
522 goto Fail;
523 if((sts & PSenable) == 0){
524 dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p);
525 hubfeature(h, p, Fportenable, 1);
526 sts = portstatus(h, p);
527 if((sts & PSenable) == 0)
528 goto Fail;
529 }
530 nd = pp->dev;
531 opendevdata(nd, ORDWR);
532 if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetaddress, nd->id, 0, nil, 0) < 0){
533 dprint(2, "%s: %s: port %d: setaddress: %r\n", argv0, d->dir, p);
534 goto Fail;
535 }
536 if(devctl(nd, "address") < 0){
537 dprint(2, "%s: %s: port %d: set address: %r\n", argv0, d->dir, p);
538 goto Fail;
539 }
540 if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0){
541 dprint(2, "%s: %s: port %d: setconf: %r\n", argv0, d->dir, p);
542 unstall(nd, nd, Eout);
543 if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0)
544 goto Fail;
545 }
546 if(nd->dfd >= 0)
547 close(nd->dfd);
548 return;
549 Fail:
550 pp->state = Pdisabled;
551 pp->sts = 0;
552 if(pp->hub != nil)
553 pp->hub = nil; /* hub closed by enumhub */
554 hubfeature(h, p, Fportenable, 0);
555 if(nd != nil)
556 devctl(nd, "detach");
557 closedev(nd);
558 }
559
560 static int
portgone(Port * pp,int sts)561 portgone(Port *pp, int sts)
562 {
563 if(sts < 0)
564 return 1;
565 /*
566 * If it was enabled and it's not now then it may be reconnect.
567 * We pretend it's gone and later we'll see it as attached.
568 */
569 if((pp->sts & PSenable) != 0 && (sts & PSenable) == 0)
570 return 1;
571 return (pp->sts & PSpresent) != 0 && (sts & PSpresent) == 0;
572 }
573
574 static int
enumhub(Hub * h,int p)575 enumhub(Hub *h, int p)
576 {
577 int sts;
578 Dev *d;
579 Port *pp;
580 int onhubs;
581
582 if(h->failed)
583 return 0;
584 d = h->dev;
585 if(usbdebug > 3)
586 fprint(2, "%s: %s: port %d enumhub\n", argv0, d->dir, p);
587
588 sts = portstatus(h, p);
589 if(sts < 0){
590 hubfail(h); /* avoid delays on detachment */
591 return -1;
592 }
593 pp = &h->port[p];
594 onhubs = nhubs;
595 if((sts & PSsuspend) != 0){
596 if(hubfeature(h, p, Fportenable, 1) < 0)
597 dprint(2, "%s: %s: port %d: enable: %r\n", argv0, d->dir, p);
598 sleep(Enabledelay);
599 sts = portstatus(h, p);
600 fprint(2, "%s: %s: port %d: resumed (sts %#ux)\n", argv0, d->dir, p, sts);
601 }
602 if((pp->sts & PSpresent) == 0 && (sts & PSpresent) != 0){
603 if(portattach(h, p, sts) != nil)
604 if(startdev(pp) < 0)
605 portdetach(h, p);
606 }else if(portgone(pp, sts))
607 portdetach(h, p);
608 else if(portresetwanted(h, p))
609 portreset(h, p);
610 else if(pp->sts != sts){
611 dprint(2, "%s: %s port %d: sts %s %#x ->",
612 argv0, d->dir, p, stsstr(pp->sts), pp->sts);
613 dprint(2, " %s %#x\n",stsstr(sts), sts);
614 }
615 pp->sts = sts;
616 if(onhubs != nhubs)
617 return -1;
618 return 0;
619 }
620
621 static void
dump(void)622 dump(void)
623 {
624 Hub *h;
625 int i;
626
627 mustdump = 0;
628 for(h = hubs; h != nil; h = h->next)
629 for(i = 1; i <= h->nport; i++)
630 fprint(2, "%s: hub %#p %s port %d: %U",
631 argv0, h, h->dev->dir, i, h->port[i].dev);
632 usbfsdirdump();
633
634 }
635
636 static void
work(void * a)637 work(void *a)
638 {
639 Channel *portc;
640 char *fn;
641 Hub *h;
642 int i;
643
644 portc = a;
645 threadsetname("work");
646 hubs = nil;
647 /*
648 * Receive requests for root hubs
649 */
650 while((fn = recvp(portc)) != nil){
651 dprint(2, "%s: %s starting\n", argv0, fn);
652 h = newhub(fn, nil);
653 if(h == nil)
654 fprint(2, "%s: %s: newhub failed: %r\n", argv0, fn);
655 free(fn);
656 }
657 /*
658 * Enumerate (and acknowledge after first enumeration).
659 * Do NOT perform enumeration concurrently for the same
660 * controller. new devices attached respond to a default
661 * address (0) after reset, thus enumeration has to work
662 * one device at a time at least before addresses have been
663 * assigned.
664 * Do not use hub interrupt endpoint because we
665 * have to poll the root hub(s) in any case.
666 */
667 for(;;){
668 Again:
669 for(h = hubs; h != nil; h = h->next)
670 for(i = 1; i <= h->nport; i++)
671 if(enumhub(h, i) < 0){
672 /* changes in hub list; repeat */
673 goto Again;
674 }
675 if(portc != nil){
676 sendp(portc, nil);
677 portc = nil;
678 }
679 sleep(pollms);
680 if(mustdump)
681 dump();
682 }
683 }
684
685 static int
cfswalk(Usbfs *,Fid *,char *)686 cfswalk(Usbfs*, Fid *, char *)
687 {
688 werrstr(Enotfound);
689 return -1;
690 }
691
692 static int
cfsopen(Usbfs *,Fid *,int)693 cfsopen(Usbfs*, Fid *, int)
694 {
695 return 0;
696 }
697
698 static long
cfsread(Usbfs *,Fid *,void *,long,vlong)699 cfsread(Usbfs*, Fid *, void *, long , vlong )
700 {
701 return 0;
702 }
703
704 static void
setdrvargs(char * name,char * args)705 setdrvargs(char *name, char *args)
706 {
707 Devtab *dt;
708 extern Devtab devtab[];
709
710 for(dt = devtab; dt->name != nil; dt++)
711 if(strstr(dt->name, name) != nil)
712 dt->args = estrdup(args);
713 }
714
715 static void
setdrvauto(char * name,int on)716 setdrvauto(char *name, int on)
717 {
718 Devtab *dt;
719 extern Devtab devtab[];
720
721 for(dt = devtab; dt->name != nil; dt++)
722 if(strstr(dt->name, name) != nil)
723 dt->noauto = !on;
724 }
725
726 static long
cfswrite(Usbfs *,Fid *,void * data,long cnt,vlong)727 cfswrite(Usbfs*, Fid *, void *data, long cnt, vlong )
728 {
729 char *cmd, *arg;
730 char buf[80];
731 char *toks[4];
732
733 if(cnt > sizeof(buf))
734 cnt = sizeof(buf) - 1;
735 strncpy(buf, data, cnt);
736 buf[cnt] = 0;
737 if(cnt > 0 && buf[cnt-1] == '\n')
738 buf[cnt-1] = 0;
739 if(strncmp(buf, "dump", 4) == 0){
740 mustdump = 1;
741 return cnt;
742 }
743 if(strncmp(buf, "reset", 5) == 0){
744 werrstr("reset not implemented");
745 return -1;
746 }
747 if(strncmp(buf, "exit", 4) == 0){
748 threadexitsall(nil);
749 return cnt;
750 }
751 if(tokenize(buf, toks, nelem(toks)) != 2){
752 werrstr("usage: auto|debug|diskargs|fsdebug|kbargs|noauto n");
753 return -1;
754 }
755 cmd = toks[0];
756 arg = toks[1];
757 if(strcmp(cmd, "auto") == 0)
758 setdrvauto(arg, 1);
759 else if(strcmp(cmd, "debug") == 0)
760 usbdebug = atoi(arg);
761 else if(strcmp(cmd, "diskargs") == 0)
762 setdrvargs("disk", arg);
763 else if(strcmp(cmd, "etherargs") == 0)
764 setdrvargs("ether", arg);
765 else if(strcmp(cmd, "fsdebug") == 0)
766 usbfsdebug = atoi(arg);
767 else if(strcmp(cmd, "kbargs") == 0)
768 setdrvargs("kb", arg);
769 else if(strcmp(cmd, "noauto") == 0)
770 setdrvauto(arg, 0);
771 else{
772 werrstr("unknown ctl '%s'", buf);
773 return -1;
774 }
775 fprint(2, "%s: debug %d fsdebug %d\n", argv0, usbdebug, usbfsdebug);
776 return cnt;
777 }
778
779 static int
cfsstat(Usbfs * fs,Qid qid,Dir * d)780 cfsstat(Usbfs* fs, Qid qid, Dir *d)
781 {
782 d->qid = qid;
783 d->qid.path |= fs->qid;
784 d->qid.type = 0;
785 d->qid.vers = 0;
786 d->name = "usbdctl";
787 d->length = 0;
788 d->mode = 0664;
789 return 0;
790 }
791
792 static Usbfs ctlfs =
793 {
794 .walk = cfswalk,
795 .open = cfsopen,
796 .read = cfsread,
797 .write = cfswrite,
798 .stat = cfsstat
799 };
800
801 static void
getenvint(char * env,int * lp)802 getenvint(char *env, int *lp)
803 {
804 char *s;
805
806 s = getenv(env);
807 if (s != nil)
808 *lp = atoi(s);
809 free(s);
810 }
811
812 static void
getenvdrvargs(char * env,char * argname)813 getenvdrvargs(char *env, char *argname)
814 {
815 char *s;
816
817 s = getenv(env);
818 if(s != nil)
819 setdrvargs(argname, s);
820 free(s);
821 }
822
823 static void
args(void)824 args(void)
825 {
826 getenvint("usbdebug", &usbdebug);
827 getenvint("usbfsdebug", &usbfsdebug);
828 getenvdrvargs("kbargs", "kb");
829 getenvdrvargs("diskargs", "disk");
830 getenvdrvargs("etherargs", "ether");
831 }
832
833 static void
usage(void)834 usage(void)
835 {
836 fprint(2, "usage: %s [-Dd] [-s srv] [-m mnt] [dev...]\n", argv0);
837 threadexitsall("usage");
838 }
839
840 extern void usbfsexits(int);
841
842 void
threadmain(int argc,char ** argv)843 threadmain(int argc, char **argv)
844 {
845 int fd, i, nd;
846 char *err, *mnt, *srv;
847 Dir *d;
848
849 srv = "usb";
850 mnt = "/dev";
851 ARGBEGIN{
852 case 'D':
853 usbfsdebug++;
854 break;
855 case 'd':
856 usbdebug++;
857 break;
858 case 's':
859 srv = EARGF(usage());
860 break;
861 case 'i':
862 pollms = atoi(EARGF(usage()));
863 break;
864 case 'm':
865 mnt = EARGF(usage());
866 break;
867 default:
868 usage();
869 }ARGEND;
870 if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0)
871 sysfatal("#u: %r");
872
873 args();
874
875 fmtinstall('U', Ufmt);
876 quotefmtinstall();
877 rfork(RFNOTEG);
878 portc = chancreate(sizeof(char *), 0);
879 if(portc == nil)
880 sysfatal("chancreate");
881 proccreate(work, portc, Stack);
882 if(argc == 0){
883 fd = open("/dev/usb", OREAD);
884 if(fd < 0)
885 sysfatal("/dev/usb: %r");
886 nd = dirreadall(fd, &d);
887 close(fd);
888 if(nd < 2)
889 sysfatal("/dev/usb: no hubs");
890 for(i = 0; i < nd; i++)
891 if(strcmp(d[i].name, "ctl") != 0)
892 sendp(portc, smprint("/dev/usb/%s", d[i].name));
893 free(d);
894 }else
895 for(i = 0; i < argc; i++)
896 sendp(portc, strdup(argv[i]));
897 sendp(portc, nil);
898 err = recvp(portc);
899 chanfree(portc);
900 usbfsexits(0);
901 usbfsinit(srv, mnt, &usbdirfs, MAFTER);
902 snprint(ctlfs.name, sizeof(ctlfs.name), "usbdctl");
903 usbfsadd(&ctlfs);
904 threadexits(err);
905 }
906