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 } 906