xref: /plan9/sys/src/cmd/usb/usbd/usbd.c (revision f1a26d48345bb11f3a3fddaf7f7d35e9d4d8a55b)
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