Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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
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*
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
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
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
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*
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
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*
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
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
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
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
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
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
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
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
686
cfswalk(Usbfs*, Fid *, char *)
687
{
688
	werrstr(Enotfound);
689
	return -1;
690
}
691
 
692
static int
693
cfsopen(Usbfs*, Fid *, int)
694
{
695
	return 0;
696
}
697
 
698
static long
699
cfsread(Usbfs*, Fid *, void *, long , vlong )
700
{
701
	return 0;
702
}
703
 
704
static void
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
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
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
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
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
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
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
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
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
}