/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h>#include <libc.h>#include <thread.h>#include <fcall.h>#include "usb.h"#include "usbfs.h"#include "usbd.h" static Channel *portc;static int win;static int verbose; int mainstacksize = Stack;static Hub *hubs;static int nhubs;static int mustdump;static int pollms = Pollms; static char *dsname[] = { "disabled", "attached", "configed" }; static inthubfeature(Hub *h, int port, int f, int on){ int cmd; if(on) cmd = Rsetfeature; else cmd = Rclearfeature; return usbcmd(h->dev, Rh2d|Rclass|Rother, cmd, f, port, nil, 0);} /* * This may be used to detect overcurrent on the hub */static voidcheckhubstatus(Hub *h){ uchar buf[4]; int sts; if(h->isroot) /* not for root hubs */ return; if(usbcmd(h->dev, Rd2h|Rclass|Rdev, Rgetstatus, 0, 0, buf, 4) < 0){ dprint(2, "%s: get hub status: %r\n", h->dev->dir); return; } sts = GET2(buf); dprint(2, "hub %s: status %#ux\n", h->dev->dir, sts);} static intconfighub(Hub *h){ int type; uchar buf[128]; /* room for extra descriptors */ int i; Usbdev *d; DHub *dd; Port *pp; int nr; int nmap; uchar *PortPwrCtrlMask; int offset; int mask; d = h->dev->usb; for(i = 0; i < nelem(d->ddesc); i++) if(d->ddesc[i] == nil) break; else if(d->ddesc[i]->data.bDescriptorType == Dhub){ dd = (DHub*)&d->ddesc[i]->data; nr = Dhublen; goto Config; } type = Rd2h|Rclass|Rdev; nr = usbcmd(h->dev, type, Rgetdesc, Dhub<<8|0, 0, buf, sizeof buf); if(nr < Dhublen){ dprint(2, "%s: %s: getdesc hub: %r\n", argv0, h->dev->dir); return -1; } dd = (DHub*)buf;Config: h->nport = dd->bNbrPorts; nmap = 1 + h->nport/8; if(nr < 7 + 2*nmap){ fprint(2, "%s: %s: descr. too small\n", argv0, h->dev->dir); return -1; } h->port = emallocz((h->nport+1)*sizeof(Port), 1); h->pwrms = dd->bPwrOn2PwrGood*2; if(h->pwrms < Powerdelay) h->pwrms = Powerdelay; h->maxcurrent = dd->bHubContrCurrent; h->pwrmode = dd->wHubCharacteristics[0] & 3; h->compound = (dd->wHubCharacteristics[0] & (1<<2))!=0; h->leds = (dd->wHubCharacteristics[0] & (1<<7)) != 0; PortPwrCtrlMask = dd->DeviceRemovable + nmap; for(i = 1; i <= h->nport; i++){ pp = &h->port[i]; offset = i/8; mask = 1<<(i%8); pp->removable = (dd->DeviceRemovable[offset] & mask) != 0; pp->pwrctl = (PortPwrCtrlMask[offset] & mask) != 0; } return 0;} static voidconfigroothub(Hub *h){ Dev *d; char buf[128]; char *p; int nr; d = h->dev; h->nport = 2; h->maxpkt = 8; seek(d->cfd, 0, 0); nr = read(d->cfd, buf, sizeof(buf)-1); if(nr < 0) goto Done; buf[nr] = 0; p = strstr(buf, "ports "); if(p == nil) fprint(2, "%s: %s: no port information\n", argv0, d->dir); else h->nport = atoi(p+6); p = strstr(buf, "maxpkt "); if(p == nil) fprint(2, "%s: %s: no maxpkt information\n", argv0, d->dir); else h->maxpkt = atoi(p+7);Done: h->port = emallocz((h->nport+1)*sizeof(Port), 1); dprint(2, "%s: %s: ports %d maxpkt %d\n", argv0, d->dir, h->nport, h->maxpkt);} Hub*newhub(char *fn, Dev *d){ Hub *h; int i; Usbdev *ud; h = emallocz(sizeof(Hub), 1); h->isroot = (d == nil); if(h->isroot){ h->dev = opendev(fn); if(h->dev == nil){ fprint(2, "%s: opendev: %s: %r", argv0, fn); goto Fail; } if(opendevdata(h->dev, ORDWR) < 0){ fprint(2, "%s: opendevdata: %s: %r\n", argv0, fn); goto Fail; } configroothub(h); /* never fails */ }else{ h->dev = d; if(confighub(h) < 0){ fprint(2, "%s: %s: config: %r\n", argv0, fn); goto Fail; } } if(h->dev == nil){ fprint(2, "%s: opendev: %s: %r\n", argv0, fn); goto Fail; } devctl(h->dev, "hub"); ud = h->dev->usb; if(h->isroot) devctl(h->dev, "info roothub csp %#08ux ports %d", 0x000009, h->nport); else{ devctl(h->dev, "info hub csp %#08ulx ports %d %q %q", ud->csp, h->nport, ud->vendor, ud->product); for(i = 1; i <= h->nport; i++) if(hubfeature(h, i, Fportpower, 1) < 0) fprint(2, "%s: %s: power: %r\n", argv0, fn); sleep(h->pwrms); for(i = 1; i <= h->nport; i++) if(h->leds != 0) hubfeature(h, i, Fportindicator, 1); } h->next = hubs; hubs = h; nhubs++; dprint(2, "%s: hub %#p allocated:", argv0, h); dprint(2, " ports %d pwrms %d max curr %d pwrm %d cmp %d leds %d\n", h->nport, h->pwrms, h->maxcurrent, h->pwrmode, h->compound, h->leds); incref(h->dev); return h;Fail: if(d != nil) devctl(d, "detach"); free(h->port); free(h); dprint(2, "%s: hub %#p failed to start:", argv0, h); return nil;} static void portdetach(Hub *h, int p); /* * If during enumeration we get an I/O error the hub is gone or * in pretty bad shape. Because of retries of failed usb commands * (and the sleeps they include) it can take a while to detach all * ports for the hub. This detaches all ports and makes the hub void. * The parent hub will detect a detach (probably right now) and * close it later. */static voidhubfail(Hub *h){ int i; for(i = 1; i <= h->nport; i++) portdetach(h, i); h->failed = 1;} static voidclosehub(Hub *h){ Hub **hl; dprint(2, "%s: closing hub %#p\n", argv0, h); for(hl = &hubs; *hl != nil; hl = &(*hl)->next) if(*hl == h) break; if(*hl == nil) sysfatal("closehub: no hub"); *hl = h->next; nhubs--; hubfail(h); /* detach all ports */ free(h->port); assert(h->dev != nil); devctl(h->dev, "detach"); closedev(h->dev); free(h);} static intportstatus(Hub *h, int p){ Dev *d; uchar buf[4]; int t; int sts; int dbg; dbg = usbdebug; if(dbg != 0 && dbg < 4) usbdebug = 1; /* do not be too chatty */ d = h->dev; t = Rd2h|Rclass|Rother; if(usbcmd(d, t, Rgetstatus, 0, p, buf, sizeof(buf)) < 0) sts = -1; else sts = GET2(buf); usbdebug = dbg; return sts;} static char*stsstr(int sts){ static char s[80]; char *e; e = s; if(sts&PSsuspend) *e++ = 'z'; if(sts&PSreset) *e++ = 'r'; if(sts&PSslow) *e++ = 'l'; if(sts&PShigh) *e++ = 'h'; if(sts&PSchange) *e++ = 'c'; if(sts&PSenable) *e++ = 'e'; if(sts&PSstatuschg) *e++ = 's'; if(sts&PSpresent) *e++ = 'p'; if(e == s) *e++ = '-'; *e = 0; return s;} static intgetmaxpkt(Dev *d, int islow){ uchar buf[64]; /* More room to try to get device-specific descriptors */ DDev *dd; dd = (DDev*)buf; if(islow) dd->bMaxPacketSize0 = 8; else dd->bMaxPacketSize0 = 64; if(usbcmd(d, Rd2h|Rstd|Rdev, Rgetdesc, Ddev<<8|0, 0, buf, sizeof(buf)) < 0) return -1; return dd->bMaxPacketSize0;} /* * BUG: does not consider max. power avail. */static Dev*portattach(Hub *h, int p, int sts){ Dev *d; Port *pp; Dev *nd; char fname[80]; char buf[40]; char *sp; int mp; int nr; d = h->dev; pp = &h->port[p]; nd = nil; pp->state = Pattached; dprint(2, "%s: %s: port %d attach sts %#ux\n", argv0, d->dir, p, sts); sleep(Connectdelay); if(hubfeature(h, p, Fportenable, 1) < 0) dprint(2, "%s: %s: port %d: enable: %r\n", argv0, d->dir, p); sleep(Enabledelay); if(hubfeature(h, p, Fportreset, 1) < 0){ dprint(2, "%s: %s: port %d: reset: %r\n", argv0, d->dir, p); goto Fail; } sleep(Resetdelay); sts = portstatus(h, p); if(sts < 0) goto Fail; if((sts & PSenable) == 0){ dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p); hubfeature(h, p, Fportenable, 1); sts = portstatus(h, p); if((sts & PSenable) == 0) goto Fail; } sp = "full"; if(sts & PSslow) sp = "low"; if(sts & PShigh) sp = "high"; dprint(2, "%s: %s: port %d: attached status %#ux\n", argv0, d->dir, p, sts); if(devctl(d, "newdev %s %d", sp, p) < 0){ fprint(2, "%s: %s: port %d: newdev: %r\n", argv0, d->dir, p); goto Fail; } seek(d->cfd, 0, 0); nr = read(d->cfd, buf, sizeof(buf)-1); if(nr == 0){ fprint(2, "%s: %s: port %d: newdev: eof\n", argv0, d->dir, p); goto Fail; } if(nr < 0){ fprint(2, "%s: %s: port %d: newdev: %r\n", argv0, d->dir, p); goto Fail; } buf[nr] = 0; snprint(fname, sizeof(fname), "/dev/usb/%s", buf); nd = opendev(fname); if(nd == nil){ fprint(2, "%s: %s: port %d: opendev: %r\n", argv0, d->dir, p); goto Fail; } if(usbdebug > 2) devctl(nd, "debug 1"); if(opendevdata(nd, ORDWR) < 0){ fprint(2, "%s: %s: opendevdata: %r\n", argv0, nd->dir); goto Fail; } if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetaddress, nd->id, 0, nil, 0) < 0){ dprint(2, "%s: %s: port %d: setaddress: %r\n", argv0, d->dir, p); goto Fail; } if(devctl(nd, "address") < 0){ dprint(2, "%s: %s: port %d: set address: %r\n", argv0, d->dir, p); goto Fail; } mp=getmaxpkt(nd, strcmp(sp, "low") == 0); if(mp < 0){ dprint(2, "%s: %s: port %d: getmaxpkt: %r\n", argv0, d->dir, p); goto Fail; }else{ dprint(2, "%s; %s: port %d: maxpkt %d\n", argv0, d->dir, p, mp); devctl(nd, "maxpkt %d", mp); } if((sts & PSslow) != 0 && strcmp(sp, "full") == 0) dprint(2, "%s: %s: port %d: %s is full speed when port is low\n", argv0, d->dir, p, nd->dir); if(configdev(nd) < 0){ dprint(2, "%s: %s: port %d: configdev: %r\n", argv0, d->dir, p); goto Fail; } /* * We always set conf #1. BUG. */ if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0){ dprint(2, "%s: %s: port %d: setconf: %r\n", argv0, d->dir, p); unstall(nd, nd, Eout); if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0) goto Fail; } dprint(2, "%s: %U", argv0, nd); pp->state = Pconfiged; dprint(2, "%s: %s: port %d: configed: %s\n", argv0, d->dir, p, nd->dir); return pp->dev = nd;Fail: pp->state = Pdisabled; pp->sts = 0; if(pp->hub != nil) pp->hub = nil; /* hub closed by enumhub */ hubfeature(h, p, Fportenable, 0); if(nd != nil) devctl(nd, "detach"); closedev(nd); return nil;} static voidportdetach(Hub *h, int p){ Dev *d; Port *pp; extern void usbfsgone(char*); d = h->dev; pp = &h->port[p]; /* * Clear present, so that we detect an attach on reconnects. */ pp->sts &= ~(PSpresent|PSenable); if(pp->state == Pdisabled) return; pp->state = Pdisabled; dprint(2, "%s: %s: port %d: detached\n", argv0, d->dir, p); if(pp->hub != nil){ closehub(pp->hub); pp->hub = nil; } if(pp->devmaskp != nil) putdevnb(pp->devmaskp, pp->devnb); pp->devmaskp = nil; if(pp->dev != nil){ devctl(pp->dev, "detach"); usbfsgone(pp->dev->dir); closedev(pp->dev); pp->dev = nil; }} /* * The next two functions are included to * perform a port reset asked for by someone (usually a driver). * This must be done while no other device is in using the * configuration address and with care to keep the old address. * To keep drivers decoupled from usbd they write the reset request * to the #u/usb/epN.0/ctl file and then exit. * This is unfortunate because usbd must now poll twice as much. * * An alternative to this reset process would be for the driver to detach * the device. The next function could see that, issue a port reset, and * then restart the driver once to see if it's a temporary error. * * The real fix would be to use interrupt endpoints for non-root hubs * (would probably make some hubs fail) and add an events file to * the kernel to report events to usbd. This is a severe change not * yet implemented. */static intportresetwanted(Hub *h, int p){Lines 1–500 of 914. Load more
/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ #include <u.h> #include <libc.h> #include <thread.h> #include <fcall.h> #include "usb.h" #include "usbfs.h" #include "usbd.h" static Channel *portc; static int win; static int verbose; int mainstacksize = Stack; static Hub *hubs; static int nhubs; static int mustdump; static int pollms = Pollms; static char *dsname[] = { "disabled", "attached", "configed" }; static int hubfeature(Hub *h, int port, int f, int on) { int cmd; if(on) cmd = Rsetfeature; else cmd = Rclearfeature; return usbcmd(h->dev, Rh2d|Rclass|Rother, cmd, f, port, nil, 0); } /* * This may be used to detect overcurrent on the hub */ static void checkhubstatus(Hub *h) { uchar buf[4]; int sts; if(h->isroot) /* not for root hubs */ return; if(usbcmd(h->dev, Rd2h|Rclass|Rdev, Rgetstatus, 0, 0, buf, 4) < 0){ dprint(2, "%s: get hub status: %r\n", h->dev->dir); return; } sts = GET2(buf); dprint(2, "hub %s: status %#ux\n", h->dev->dir, sts); } static int confighub(Hub *h) { int type; uchar buf[128]; /* room for extra descriptors */ int i; Usbdev *d; DHub *dd; Port *pp; int nr; int nmap; uchar *PortPwrCtrlMask; int offset; int mask; d = h->dev->usb; for(i = 0; i < nelem(d->ddesc); i++) if(d->ddesc[i] == nil) break; else if(d->ddesc[i]->data.bDescriptorType == Dhub){ dd = (DHub*)&d->ddesc[i]->data; nr = Dhublen; goto Config; } type = Rd2h|Rclass|Rdev; nr = usbcmd(h->dev, type, Rgetdesc, Dhub<<8|0, 0, buf, sizeof buf); if(nr < Dhublen){ dprint(2, "%s: %s: getdesc hub: %r\n", argv0, h->dev->dir); return -1; } dd = (DHub*)buf; Config: h->nport = dd->bNbrPorts; nmap = 1 + h->nport/8; if(nr < 7 + 2*nmap){ fprint(2, "%s: %s: descr. too small\n", argv0, h->dev->dir); return -1; } h->port = emallocz((h->nport+1)*sizeof(Port), 1); h->pwrms = dd->bPwrOn2PwrGood*2; if(h->pwrms < Powerdelay) h->pwrms = Powerdelay; h->maxcurrent = dd->bHubContrCurrent; h->pwrmode = dd->wHubCharacteristics[0] & 3; h->compound = (dd->wHubCharacteristics[0] & (1<<2))!=0; h->leds = (dd->wHubCharacteristics[0] & (1<<7)) != 0; PortPwrCtrlMask = dd->DeviceRemovable + nmap; for(i = 1; i <= h->nport; i++){ pp = &h->port[i]; offset = i/8; mask = 1<<(i%8); pp->removable = (dd->DeviceRemovable[offset] & mask) != 0; pp->pwrctl = (PortPwrCtrlMask[offset] & mask) != 0; } return 0; } static void configroothub(Hub *h) { Dev *d; char buf[128]; char *p; int nr; d = h->dev; h->nport = 2; h->maxpkt = 8; seek(d->cfd, 0, 0); nr = read(d->cfd, buf, sizeof(buf)-1); if(nr < 0) goto Done; buf[nr] = 0; p = strstr(buf, "ports "); if(p == nil) fprint(2, "%s: %s: no port information\n", argv0, d->dir); else h->nport = atoi(p+6); p = strstr(buf, "maxpkt "); if(p == nil) fprint(2, "%s: %s: no maxpkt information\n", argv0, d->dir); else h->maxpkt = atoi(p+7); Done: h->port = emallocz((h->nport+1)*sizeof(Port), 1); dprint(2, "%s: %s: ports %d maxpkt %d\n", argv0, d->dir, h->nport, h->maxpkt); } Hub* newhub(char *fn, Dev *d) { Hub *h; int i; Usbdev *ud; h = emallocz(sizeof(Hub), 1); h->isroot = (d == nil); if(h->isroot){ h->dev = opendev(fn); if(h->dev == nil){ fprint(2, "%s: opendev: %s: %r", argv0, fn); goto Fail; } if(opendevdata(h->dev, ORDWR) < 0){ fprint(2, "%s: opendevdata: %s: %r\n", argv0, fn); goto Fail; } configroothub(h); /* never fails */ }else{ h->dev = d; if(confighub(h) < 0){ fprint(2, "%s: %s: config: %r\n", argv0, fn); goto Fail; } } if(h->dev == nil){ fprint(2, "%s: opendev: %s: %r\n", argv0, fn); goto Fail; } devctl(h->dev, "hub"); ud = h->dev->usb; if(h->isroot) devctl(h->dev, "info roothub csp %#08ux ports %d", 0x000009, h->nport); else{ devctl(h->dev, "info hub csp %#08ulx ports %d %q %q", ud->csp, h->nport, ud->vendor, ud->product); for(i = 1; i <= h->nport; i++) if(hubfeature(h, i, Fportpower, 1) < 0) fprint(2, "%s: %s: power: %r\n", argv0, fn); sleep(h->pwrms); for(i = 1; i <= h->nport; i++) if(h->leds != 0) hubfeature(h, i, Fportindicator, 1); } h->next = hubs; hubs = h; nhubs++; dprint(2, "%s: hub %#p allocated:", argv0, h); dprint(2, " ports %d pwrms %d max curr %d pwrm %d cmp %d leds %d\n", h->nport, h->pwrms, h->maxcurrent, h->pwrmode, h->compound, h->leds); incref(h->dev); return h; Fail: if(d != nil) devctl(d, "detach"); free(h->port); free(h); dprint(2, "%s: hub %#p failed to start:", argv0, h); return nil; } static void portdetach(Hub *h, int p); /* * If during enumeration we get an I/O error the hub is gone or * in pretty bad shape. Because of retries of failed usb commands * (and the sleeps they include) it can take a while to detach all * ports for the hub. This detaches all ports and makes the hub void. * The parent hub will detect a detach (probably right now) and * close it later. */ static void hubfail(Hub *h) { int i; for(i = 1; i <= h->nport; i++) portdetach(h, i); h->failed = 1; } static void closehub(Hub *h) { Hub **hl; dprint(2, "%s: closing hub %#p\n", argv0, h); for(hl = &hubs; *hl != nil; hl = &(*hl)->next) if(*hl == h) break; if(*hl == nil) sysfatal("closehub: no hub"); *hl = h->next; nhubs--; hubfail(h); /* detach all ports */ free(h->port); assert(h->dev != nil); devctl(h->dev, "detach"); closedev(h->dev); free(h); } static int portstatus(Hub *h, int p) { Dev *d; uchar buf[4]; int t; int sts; int dbg; dbg = usbdebug; if(dbg != 0 && dbg < 4) usbdebug = 1; /* do not be too chatty */ d = h->dev; t = Rd2h|Rclass|Rother; if(usbcmd(d, t, Rgetstatus, 0, p, buf, sizeof(buf)) < 0) sts = -1; else sts = GET2(buf); usbdebug = dbg; return sts; } static char* stsstr(int sts) { static char s[80]; char *e; e = s; if(sts&PSsuspend) *e++ = 'z'; if(sts&PSreset) *e++ = 'r'; if(sts&PSslow) *e++ = 'l'; if(sts&PShigh) *e++ = 'h'; if(sts&PSchange) *e++ = 'c'; if(sts&PSenable) *e++ = 'e'; if(sts&PSstatuschg) *e++ = 's'; if(sts&PSpresent) *e++ = 'p'; if(e == s) *e++ = '-'; *e = 0; return s; } static int getmaxpkt(Dev *d, int islow) { uchar buf[64]; /* More room to try to get device-specific descriptors */ DDev *dd; dd = (DDev*)buf; if(islow) dd->bMaxPacketSize0 = 8; else dd->bMaxPacketSize0 = 64; if(usbcmd(d, Rd2h|Rstd|Rdev, Rgetdesc, Ddev<<8|0, 0, buf, sizeof(buf)) < 0) return -1; return dd->bMaxPacketSize0; } /* * BUG: does not consider max. power avail. */ static Dev* portattach(Hub *h, int p, int sts) { Dev *d; Port *pp; Dev *nd; char fname[80]; char buf[40]; char *sp; int mp; int nr; d = h->dev; pp = &h->port[p]; nd = nil; pp->state = Pattached; dprint(2, "%s: %s: port %d attach sts %#ux\n", argv0, d->dir, p, sts); sleep(Connectdelay); if(hubfeature(h, p, Fportenable, 1) < 0) dprint(2, "%s: %s: port %d: enable: %r\n", argv0, d->dir, p); sleep(Enabledelay); if(hubfeature(h, p, Fportreset, 1) < 0){ dprint(2, "%s: %s: port %d: reset: %r\n", argv0, d->dir, p); goto Fail; } sleep(Resetdelay); sts = portstatus(h, p); if(sts < 0) goto Fail; if((sts & PSenable) == 0){ dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p); hubfeature(h, p, Fportenable, 1); sts = portstatus(h, p); if((sts & PSenable) == 0) goto Fail; } sp = "full"; if(sts & PSslow) sp = "low"; if(sts & PShigh) sp = "high"; dprint(2, "%s: %s: port %d: attached status %#ux\n", argv0, d->dir, p, sts); if(devctl(d, "newdev %s %d", sp, p) < 0){ fprint(2, "%s: %s: port %d: newdev: %r\n", argv0, d->dir, p); goto Fail; } seek(d->cfd, 0, 0); nr = read(d->cfd, buf, sizeof(buf)-1); if(nr == 0){ fprint(2, "%s: %s: port %d: newdev: eof\n", argv0, d->dir, p); goto Fail; } if(nr < 0){ fprint(2, "%s: %s: port %d: newdev: %r\n", argv0, d->dir, p); goto Fail; } buf[nr] = 0; snprint(fname, sizeof(fname), "/dev/usb/%s", buf); nd = opendev(fname); if(nd == nil){ fprint(2, "%s: %s: port %d: opendev: %r\n", argv0, d->dir, p); goto Fail; } if(usbdebug > 2) devctl(nd, "debug 1"); if(opendevdata(nd, ORDWR) < 0){ fprint(2, "%s: %s: opendevdata: %r\n", argv0, nd->dir); goto Fail; } if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetaddress, nd->id, 0, nil, 0) < 0){ dprint(2, "%s: %s: port %d: setaddress: %r\n", argv0, d->dir, p); goto Fail; } if(devctl(nd, "address") < 0){ dprint(2, "%s: %s: port %d: set address: %r\n", argv0, d->dir, p); goto Fail; } mp=getmaxpkt(nd, strcmp(sp, "low") == 0); if(mp < 0){ dprint(2, "%s: %s: port %d: getmaxpkt: %r\n", argv0, d->dir, p); goto Fail; }else{ dprint(2, "%s; %s: port %d: maxpkt %d\n", argv0, d->dir, p, mp); devctl(nd, "maxpkt %d", mp); } if((sts & PSslow) != 0 && strcmp(sp, "full") == 0) dprint(2, "%s: %s: port %d: %s is full speed when port is low\n", argv0, d->dir, p, nd->dir); if(configdev(nd) < 0){ dprint(2, "%s: %s: port %d: configdev: %r\n", argv0, d->dir, p); goto Fail; } /* * We always set conf #1. BUG. */ if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0){ dprint(2, "%s: %s: port %d: setconf: %r\n", argv0, d->dir, p); unstall(nd, nd, Eout); if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0) goto Fail; } dprint(2, "%s: %U", argv0, nd); pp->state = Pconfiged; dprint(2, "%s: %s: port %d: configed: %s\n", argv0, d->dir, p, nd->dir); return pp->dev = nd; Fail: pp->state = Pdisabled; pp->sts = 0; if(pp->hub != nil) pp->hub = nil; /* hub closed by enumhub */ hubfeature(h, p, Fportenable, 0); if(nd != nil) devctl(nd, "detach"); closedev(nd); return nil; } static void portdetach(Hub *h, int p) { Dev *d; Port *pp; extern void usbfsgone(char*); d = h->dev; pp = &h->port[p]; /* * Clear present, so that we detect an attach on reconnects. */ pp->sts &= ~(PSpresent|PSenable); if(pp->state == Pdisabled) return; pp->state = Pdisabled; dprint(2, "%s: %s: port %d: detached\n", argv0, d->dir, p); if(pp->hub != nil){ closehub(pp->hub); pp->hub = nil; } if(pp->devmaskp != nil) putdevnb(pp->devmaskp, pp->devnb); pp->devmaskp = nil; if(pp->dev != nil){ devctl(pp->dev, "detach"); usbfsgone(pp->dev->dir); closedev(pp->dev); pp->dev = nil; } } /* * The next two functions are included to * perform a port reset asked for by someone (usually a driver). * This must be done while no other device is in using the * configuration address and with care to keep the old address. * To keep drivers decoupled from usbd they write the reset request * to the #u/usb/epN.0/ctl file and then exit. * This is unfortunate because usbd must now poll twice as much. * * An alternative to this reset process would be for the driver to detach * the device. The next function could see that, issue a port reset, and * then restart the driver once to see if it's a temporary error. * * The real fix would be to use interrupt endpoints for non-root hubs * (would probably make some hubs fail) and add an events file to * the kernel to report events to usbd. This is a severe change not * yet implemented. */ static int portresetwanted(Hub *h, int p) {