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