static int nhubs;
static int mustdump;
static int pollms = Pollms;
-static Lock masklck;
static char *dsname[] = { "disabled", "attached", "configed" };
-int
-getdevnb(uvlong *maskp)
-{
- int i;
-
- lock(&masklck);
- for(i = 0; i < 8 * sizeof *maskp; i++)
- if((*maskp & (1ULL<<i)) == 0){
- *maskp |= 1ULL<<i;
- unlock(&masklck);
- return i;
- }
- unlock(&masklck);
- return -1;
-}
-
-void
-putdevnb(uvlong *maskp, int id)
-{
- lock(&masklck);
- if(id >= 0)
- *maskp &= ~(1ULL<<id);
- unlock(&masklck);
-}
-
static int
hubfeature(Hub *h, int port, int f, int on)
{
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)
+static int
+configusb2hub(Hub *h, DHub *dd, int nr)
{
- uchar buf[4];
- int sts;
+ uchar *PortPwrCtrlMask;
+ int i, offset, mask, nmap;
+ Port *pp;
- 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;
+ 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;
}
- sts = GET2(buf);
- dprint(2, "hub %s: status %#ux\n", h->dev->dir, sts);
+ return 0;
}
static int
-confighub(Hub *h)
+configusb3hub(Hub *h, DSSHub *dd, int nr)
{
- int type;
- uchar buf[128]; /* room for extra descriptors */
- int i;
- Usbdev *d;
- DHub *dd;
+ int i, offset, mask, nmap;
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){
+ if(nr < 10 + nmap){
fprint(2, "%s: %s: descr. too small\n", argv0, h->dev->dir);
return -1;
}
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;
+ }
+ if(usbcmd(h->dev, Rh2d|Rclass|Rdev, Rsethubdepth, h->dev->depth, 0, nil, 0) < 0){
+ fprint(2, "%s: %s: sethubdepth: %r\n", argv0, h->dev->dir);
+ return -1;
}
return 0;
}
+static int
+confighub(Hub *h)
+{
+ uchar buf[128]; /* room for extra descriptors */
+ int i, dt, dl, nr;
+ Usbdev *d;
+ void *dd;
+
+ d = h->dev->usb;
+ if(h->dev->isusb3){
+ dt = Dsshub;
+ dl = Dsshublen;
+ } else {
+ dt = Dhub;
+ dl = Dhublen;
+ }
+ for(i = 0; i < nelem(d->ddesc); i++){
+ if(d->ddesc[i] == nil)
+ break;
+ if(d->ddesc[i]->data.bDescriptorType == dt){
+ dd = &d->ddesc[i]->data;
+ nr = d->ddesc[i]->data.bLength;
+ goto Config;
+ }
+ }
+ nr = usbcmd(h->dev, Rd2h|Rclass|Rdev, Rgetdesc, dt<<8|0, 0, buf, sizeof buf);
+ if(nr < 0){
+ fprint(2, "%s: %s: getdesc hub: %r\n", argv0, h->dev->dir);
+ return -1;
+ }
+ dd = buf;
+Config:
+ if(nr < dl){
+ fprint(2, "%s: %s: hub descriptor too small (%d < %d)\n", argv0, h->dev->dir, nr, dl);
+ return -1;
+ }
+ if(h->dev->isusb3)
+ return configusb3hub(h, dd, nr);
+ else
+ return configusb2hub(h, dd, nr);
+}
+
static void
configroothub(Hub *h)
{
- Dev *d;
- char buf[128];
+ char buf[1024];
char *p;
int nr;
+ Dev *d;
d = h->dev;
h->nport = 2;
if(nr < 0)
goto Done;
buf[nr] = 0;
-
+ d->isusb3 = strstr(buf, "speed super") != nil;
p = strstr(buf, "ports ");
if(p == nil)
fprint(2, "%s: %s: no port information\n", argv0, d->dir);
fprint(2, "%s: opendev: %s: %r", argv0, fn);
goto Fail;
}
+ h->dev->depth = -1;
+ configroothub(h); /* never fails */
if(opendevdata(h->dev, ORDWR) < 0){
fprint(2, "%s: opendevdata: %s: %r\n", argv0, fn);
+ closedev(h->dev);
goto Fail;
}
- configroothub(h); /* never fails */
}else{
h->dev = d;
if(confighub(h) < 0){
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)
free(h);
}
-static int
+static u32int
portstatus(Hub *h, int p)
{
Dev *d;
uchar buf[4];
+ u32int sts;
int t;
- int sts;
int dbg;
dbg = usbdebug;
if(usbcmd(d, t, Rgetstatus, 0, p, buf, sizeof(buf)) < 0)
sts = -1;
else
- sts = GET2(buf);
+ sts = GET4(buf);
usbdebug = dbg;
return sts;
}
static char*
-stsstr(int sts)
+stsstr(int sts, int isusb3)
{
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(sts&PSenable)
+ *e++ = 'e';
+ if(sts&PSovercurrent)
+ *e++ = 'o';
+ if(sts&PSreset)
+ *e++ = 'r';
+ if(!isusb3){
+ if(sts&PSslow)
+ *e++ = 'l';
+ if(sts&PShigh)
+ *e++ = 'h';
+ if(sts&PSchange)
+ *e++ = 'c';
+ if(sts&PSstatuschg)
+ *e++ = 's';
+ if(sts&PSsuspend)
+ *e++ = 'z';
+ }
if(e == s)
*e++ = '-';
*e = 0;
uchar buf[64]; /* More room to try to get device-specific descriptors */
DDev *dd;
+ if(d->isusb3)
+ return 512;
dd = (DDev*)buf;
if(islow)
dd->bMaxPacketSize0 = 8;
* BUG: does not consider max. power avail.
*/
static Dev*
-portattach(Hub *h, int p, int sts)
+portattach(Hub *h, int p, u32int sts)
{
Dev *d;
Port *pp;
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);
+ if(h->dev->isusb3){
+ sleep(Connectdelay);
sts = portstatus(h, p);
- if((sts & PSenable) == 0)
+ if(sts == -1)
+ goto Fail;
+ if((sts & PSenable) == 0){
+ dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p);
+ goto Fail;
+ }
+ sp = "super";
+ } else {
+ 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 == -1)
+ 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";
}
- 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);
+ dprint(2, "%s: %s: port %d: attached status %#ux, speed %s\n", argv0, d->dir, p, sts, sp);
if(devctl(d, "newdev %s %d", sp, p) < 0){
fprint(2, "%s: %s: port %d: newdev: %r\n", argv0, d->dir, p);
fprint(2, "%s: %s: port %d: opendev: %r\n", argv0, d->dir, p);
goto Fail;
}
+ nd->depth = h->dev->depth+1;
+ nd->isusb3 = h->dev->isusb3;
if(usbdebug > 2)
devctl(nd, "debug 1");
if(opendevdata(nd, ORDWR) < 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);
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;
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);
+ dprint(2, "%s: %s: port %d: configured: %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(!h->dev->isusb3)
+ hubfeature(h, p, Fportenable, 0);
if(nd != nil)
devctl(nd, "detach");
closedev(nd);
closehub(pp->hub);
pp->hub = nil;
}
- if(pp->devmaskp != nil)
- putdevnb(pp->devmaskp, pp->devnb);
- pp->devmaskp = nil;
if(pp->dev != nil){
+ detachdev(pp);
+
devctl(pp->dev, "detach");
closedev(pp->dev);
pp->dev = nil;
static void
portreset(Hub *h, int p)
{
- int sts;
+ u32int sts;
Dev *d, *nd;
Port *pp;
}
sleep(Resetdelay);
sts = portstatus(h, p);
- if(sts < 0)
+ if(sts == -1)
goto Fail;
if((sts & PSenable) == 0){
dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p);
+ if(h->dev->isusb3)
+ goto Fail;
hubfeature(h, p, Fportenable, 1);
sts = portstatus(h, p);
if((sts & PSenable) == 0)
if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0)
goto Fail;
}
- if(nd->dfd >= 0)
+ if(nd->dfd >= 0){
close(nd->dfd);
+ nd->dfd = -1;
+ }
return;
Fail:
pp->state = Pdisabled;
pp->sts = 0;
if(pp->hub != nil)
pp->hub = nil; /* hub closed by enumhub */
- hubfeature(h, p, Fportenable, 0);
+ if(!h->dev->isusb3)
+ hubfeature(h, p, Fportenable, 0);
if(nd != nil)
devctl(nd, "detach");
closedev(nd);
}
static int
-portgone(Port *pp, int sts)
+portgone(Port *pp, u32int sts)
{
- if(sts < 0)
+ if(sts == -1)
return 1;
/*
* If it was enabled and it's not now then it may be reconnect.
static int
enumhub(Hub *h, int p)
{
- int sts;
+ u32int sts;
Dev *d;
Port *pp;
int onhubs;
fprint(2, "%s: %s: port %d enumhub\n", argv0, d->dir, p);
sts = portstatus(h, p);
- if(sts < 0){
+ if(sts == -1){
hubfail(h); /* avoid delays on detachment */
return -1;
}
pp = &h->port[p];
onhubs = nhubs;
- if((sts & PSsuspend) != 0){
- if(hubfeature(h, p, Fportenable, 1) < 0)
- dprint(2, "%s: %s: port %d: enable: %r\n", argv0, d->dir, p);
- sleep(Enabledelay);
- sts = portstatus(h, p);
- fprint(2, "%s: %s: port %d: resumed (sts %#ux)\n", argv0, d->dir, p, sts);
+ if(!h->dev->isusb3){
+ if((sts & PSsuspend) != 0){
+ if(hubfeature(h, p, Fportenable, 1) < 0)
+ dprint(2, "%s: %s: port %d: enable: %r\n", argv0, d->dir, p);
+ sleep(Enabledelay);
+ sts = portstatus(h, p);
+ fprint(2, "%s: %s: port %d: resumed (sts %#ux)\n", argv0, d->dir, p, sts);
+ }
}
if((pp->sts & PSpresent) == 0 && (sts & PSpresent) != 0){
if(portattach(h, p, sts) != nil)
- if(startdev(pp) < 0)
+ if(attachdev(pp) < 0)
portdetach(h, p);
- }else if(portgone(pp, sts))
+ }else if(portgone(pp, sts)){
portdetach(h, p);
- else if(portresetwanted(h, p))
+ }else if(portresetwanted(h, p))
portreset(h, p);
else if(pp->sts != sts){
- dprint(2, "%s: %s port %d: sts %s %#x ->",
- argv0, d->dir, p, stsstr(pp->sts), pp->sts);
- dprint(2, " %s %#x\n",stsstr(sts), sts);
+ dprint(2, "%s: %s port %d: sts %s %#ux ->",
+ argv0, d->dir, p, stsstr(pp->sts, h->dev->isusb3), pp->sts);
+ dprint(2, " %s %#ux\n",stsstr(sts, h->dev->isusb3), sts);
}
pp->sts = sts;
if(onhubs != nhubs)
mustdump = 0;
for(h = hubs; h != nil; h = h->next)
for(i = 1; i <= h->nport; i++)
- fprint(2, "%s: hub %#p %s port %d: %U",
+ fprint(2, "%s: hub %#p %s port %d: %U\n",
argv0, h, h->dev->dir, i, h->port[i].dev);
}
fprint(2, "%s: %s: newhub failed: %r\n", argv0, fn);
free(fn);
}
+
+ if(hubs == nil)
+ return;
+
/*
* Enumerate (and acknowledge after first enumeration).
* Do NOT perform enumeration concurrently for the same
goto Again;
}
qunlock(&hublock);
+ checkidle();
sleep(pollms);
if(mustdump)
dump();