ref: 5cc0a1f76db0a944552975ab61cde17118b7aa62
parent: 2f12ad38ca1ed1fae0c56c04909ce9c0e76a2070
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Fri Dec 6 11:10:31 EST 2024
nusb/usbd: impelement warm-reset for usb3 ports, attempt port reset on failed hubs, enable port-power for rootports
--- a/sys/src/cmd/nusb/usbd/dat.h
+++ b/sys/src/cmd/nusb/usbd/dat.h
@@ -30,6 +30,8 @@
Fcportovercurrent= 19,
Fcportreset = 20,
Fportindicator = 22,
+ Fbhportreset = 28,
+
/* Port status and status change bits
* Constants at /sys/src/9/pc/usb.h starting with HP-
@@ -55,7 +57,7 @@
/* Delays, timeouts (ms) */
Resetdelay = 20, /* how much to wait after a reset */
- Enabledelay = 20, /* how much to wait after an enable */
+ Resumedelay = 20, /* how much to wait after a resume */
Powerdelay = 100, /* after powering up ports */
Pollms = 250, /* port poll interval */
@@ -75,7 +77,7 @@
{
uchar pwrmode;
uchar compound;
- uchar pwrms; /* time to wait in ms */
+ int pwrms; /* time to wait in ms */
uchar maxcurrent; /* after powering port*/
uchar ttt; /* tt think-time */
uchar mtt; /* muti tt enabled */
--- a/sys/src/cmd/nusb/usbd/hub.c
+++ b/sys/src/cmd/nusb/usbd/hub.c
@@ -58,8 +58,6 @@
}
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;
@@ -75,11 +73,6 @@
}
h->mtt = h->dev->usb->ver == 0x0200 && Proto(h->dev->usb->csp) == 2;
- if(usbcmd(h->dev, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0){
- fprint(2, "%s: %s: hub setconf #1: %r\n", argv0, h->dev->dir);
- return -1;
- }
-
if(h->mtt){ /* try enable multi TT */
if(usbcmd(h->dev, Rh2d|Rstd|Riface, Rsetiface, 1, 0, nil, 0) < 0){
fprint(2, "%s: %s: setifcace (mtt): %r\n", argv0, h->dev->dir);
@@ -103,8 +96,6 @@
}
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;
@@ -118,17 +109,6 @@
}
h->mtt = 0;
- /*
- * SetConfiguration(0) appears to be neccessary for self powered
- * usb3 hub after upstream disconnect with the power remaining.
- */
- if(usbcmd(h->dev, Rh2d|Rstd|Rdev, Rsetconf, 0, 0, nil, 0) < 0)
- fprint(2, "%s: %s: hub setconf #0: %r\n", argv0, h->dev->dir);
-
- if(usbcmd(h->dev, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0){
- fprint(2, "%s: %s: hub setconf #1: %r\n", argv0, h->dev->dir);
- return -1;
- }
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;
@@ -182,6 +162,7 @@
d = h->dev;
h->nport = 2;
h->maxpkt = 8;
+ h->pwrmode = 1; /* fake */
seek(d->cfd, 0, 0);
nr = read(d->cfd, buf, sizeof(buf)-1);
if(nr < 0)
@@ -244,15 +225,12 @@
fprint(2, "%s: %s: opendevdata: %r\n", argv0, fn);
goto Fail;
}
- if(d != nil){
+ for(i = 1; i <= h->nport; i++)
+ portfeature(h, i, Fportpower, 1);
+ sleep(Powerdelay + h->pwrms);
+ if(h->leds){
for(i = 1; i <= h->nport; i++)
- if(portfeature(h, i, Fportpower, 1) < 0)
- fprint(2, "%s: %s: power: %r\n", argv0, fn);
- sleep(h->pwrms);
- if(h->leds){
- for(i = 1; i <= h->nport; i++)
- portfeature(h, i, Fportindicator, 1);
- }
+ portfeature(h, i, Fportindicator, 1);
}
h->next = hubs;
hubs = h;
@@ -284,6 +262,7 @@
{
int i;
+ dprint(2, "%s: %s: hub failed %#p\n", argv0, h->dev->dir, h);
for(i = 1; i <= h->nport; i++)
portdetach(h, i);
h->failed = 1;
@@ -311,23 +290,24 @@
static u32int
portstatus(Hub *h, int p)
{
- Dev *d;
uchar buf[4];
- u32int sts;
- int t;
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 = GET4(buf);
+ if(usbcmd(h->dev, Rd2h|Rclass|Rother, Rgetstatus, 0, p, buf, sizeof(buf)) < 0){
+ usbdebug = dbg;
+ fprint(2, "%s: %s: port %d: get status: %r\n", argv0, h->dev->dir, p);
+
+ /* try to reset the hubs upstream port */
+ devctl(h->dev, "reset");
+
+ hubfail(h);
+ return -1;
+ }
usbdebug = dbg;
- return sts;
+ return GET4(buf);
}
static char*
@@ -345,7 +325,65 @@
*e++ = 'o';
if(sts&PSreset)
*e++ = 'r';
- if(!isusb3){
+ if(isusb3){
+ if(sts != 0)
+ switch((sts >> 5) & 0xF){
+ case 0x00:
+ *e++ = 'U';
+ *e++ = '0';
+ break;
+ case 0x01:
+ *e++ = 'U';
+ *e++ = '1';
+ break;
+ case 0x02:
+ *e++ = 'U';
+ *e++ = '2';
+ break;
+ case 0x03:
+ *e++ = 'U';
+ *e++ = '3';
+ break;
+ case 0x04:
+ /* SS.Disabled */
+ *e++ = 'S';
+ *e++ = 'S';
+ *e++ = 'D';
+ break;
+ case 0x05:
+ /* Rx.Detect */
+ *e++ = 'R';
+ *e++ = 'x';
+ *e++ = 'D';
+ break;
+ case 0x06:
+ /* SS.Inactive */
+ *e++ = 'S';
+ *e++ = 'S';
+ *e++ = 'I';
+ break;
+ case 0x07:
+ /* Polling State */
+ *e++ = 'P';
+ break;
+ case 0x08:
+ /* Recovery */
+ *e++ = 'R';
+ break;
+ case 0x09:
+ /* Hot Reset */
+ *e++ = 'H';
+ break;
+ case 0x0A:
+ /* Compliance */
+ *e++ = 'C';
+ break;
+ case 0x0B:
+ /* Loopback */
+ *e++ = 'L';
+ break;
+ }
+ } else {
if(sts&PSslow)
*e++ = 'l';
if(sts&PShigh)
@@ -397,7 +435,6 @@
pp = &h->port[p];
if(pp->state != Pdisabled)
return -1;
-
/*
* prevent repeated attaches in short succession as it is a indication
* for a reset loop or a very flanky device.
@@ -410,31 +447,16 @@
argv0, d->dir, p);
return -1;
}
-
- dprint(2, "%s: %s: port %d attach sts %#ux\n", argv0, d->dir, p, sts);
if(h->dev->isusb3){
- sleep(Enabledelay);
- sts = portstatus(h, p);
- if(sts == -1 || (sts & PSenable) == 0){
- dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p);
- return -1;
- }
sp = "super";
} else {
- sleep(Enabledelay);
if(portfeature(h, p, Fportreset, 1) < 0){
- dprint(2, "%s: %s: port %d: reset: %r\n", argv0, d->dir, p);
+ fprint(2, "%s: %s: port %d: set reset: %r\n", argv0, d->dir, p);
return -1;
}
sleep(Resetdelay);
- sts = portstatus(h, p);
- if(sts == -1 || (sts & PSenable) == 0){
- dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p);
- portfeature(h, p, Fportenable, 1);
- sts = portstatus(h, p);
- if(sts == -1 || (sts & PSenable) == 0)
- return -1;
- }
+ if((sts = portstatus(h, p)) == -1)
+ return -1;
sp = "full";
if(sts & PSslow)
sp = "low";
@@ -441,10 +463,14 @@
if(sts & PShigh)
sp = "high";
}
+ if((sts & PSenable) == 0){
+ dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p);
+ return -1;
+ }
+ dprint(2, "%s: %s: port %d: attached status %s %#ux, speed %s\n", argv0, d->dir, p,
+ stsstr(sts, h->dev->isusb3), sts, sp);
pp->sts = sts;
pp->state = Pattached;
- 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);
return -1;
@@ -475,9 +501,10 @@
fprint(2, "%s: %s: opendevdata: %r\n", argv0, nd->dir);
return -1;
}
- sts = portstatus(h, p);
- if(sts == -1 || (sts & PSenable) == 0)
+ if((sts = portstatus(h, p)) == -1)
return -1;
+ if((sts & PSenable) == 0)
+ return -1;
sleep(i*50);
}
/*
@@ -515,20 +542,6 @@
assignhname(nd);
/*
- * Hubs are handled directly by this process avoiding
- * concurrent operation so that at most one device
- * has the config address in use.
- */
- if(nd->usb->class == Clhub){
- pp->hub = newhub(nd->dir, nd);
- if(pp->hub == nil)
- return -1;
-
- pp->state = Pconfigured;
- return 0;
- }
-
- /*
* We always set conf #1. BUG.
*/
if(usbcmd(nd, Rh2d|Rstd|Rdev, Rsetconf, 1, 0, nil, 0) < 0){
@@ -540,6 +553,18 @@
pp->state = Pconfigured;
dprint(2, "%s: %s: port %d: configured: %s\n", argv0, d->dir, p, nd->dir);
+ /*
+ * Hubs are handled directly by this process avoiding
+ * concurrent operation so that at most one device
+ * has the config address in use.
+ */
+ if(nd->usb->class == Clhub){
+ pp->hub = newhub(nd->dir, nd);
+ if(pp->hub == nil)
+ return -1;
+ return 0;
+ }
+
/* close control endpoint so driver can open it */
close(nd->dfd);
nd->dfd = -1;
@@ -588,20 +613,21 @@
{
dprint(2, "%s: %s: port %d: failed: %s\n", argv0, h->dev->dir, p, what);
portdetach(h, p);
- if(!h->dev->isusb3)
- portfeature(h, p, Fportenable, 0);
+ if(h->dev->isusb3){
+ if(portfeature(h, p, Fbhportreset, 1) < 0)
+ fprint(2, "%s: %s: port %d: set warm reset: %r\n", argv0, h->dev->dir, p);
+ } else {
+ if(portfeature(h, p, Fportenable, 0) < 0)
+ fprint(2, "%s: %s: port %d: clear enable: %r\n", argv0, h->dev->dir, p);
+ }
}
static int
-portresetwanted(Hub *h, int p)
+portresetwanted(Dev *d)
{
char buf[5];
- Port *pp;
- Dev *nd;
- pp = &h->port[p];
- nd = pp->dev;
- if(nd != nil && nd->cfd >= 0 && pread(nd->cfd, buf, 5, 0LL) == 5)
+ if(d != nil && d->cfd >= 0 && pread(d->cfd, buf, 5, 0LL) == 5)
return memcmp(buf, "reset", 5) == 0;
else
return 0;
@@ -608,20 +634,6 @@
}
static int
-portgone(Port *pp, u32int sts)
-{
- if(sts == -1)
- return 1;
- /*
- * If it was enabled and it's not now then it may be reconnect.
- * We pretend it's gone and later we'll see it as attached.
- */
- if((pp->sts & PSenable) != 0 && (sts & PSenable) == 0)
- return 1;
- return (pp->sts & PSpresent) != 0 && (sts & PSpresent) == 0;
-}
-
-static int
enumhub(Hub *h, int p)
{
Dev *d;
@@ -634,49 +646,43 @@
d = h->dev;
if(usbdebug > 3)
fprint(2, "%s: %s: port %d enumhub\n", argv0, d->dir, p);
-
- pp = &h->port[p];
- if(portresetwanted(h, p)){
- pp->sts = 0; /* pretend its gone to force a new attach */
- portfail(h, p, "reset");
- }
-
- sts = portstatus(h, p);
- if(sts == -1){
- hubfail(h); /* avoid delays on detachment */
+ if((sts = portstatus(h, p)) == -1)
return -1;
+ if((sts & PSsuspend) != 0 && !d->isusb3){
+ if(portfeature(h, p, Fportsuspend, 0) < 0)
+ fprint(2, "%s: %s: port %d: clear suspend: %r\n", argv0, d->dir, p);
+ sleep(Resumedelay);
+ if((sts = portstatus(h, p)) != -1)
+ return -1;
+ dprint(2, "%s: %s: port %d: unsuspended sts: %s %#ux\n", argv0, d->dir, p,
+ stsstr(sts, d->isusb3), sts);
}
- if(!h->dev->isusb3){
- if((sts & PSsuspend) != 0){
- if(portfeature(h, p, Fportsuspend, 0) < 0)
- dprint(2, "%s: %s: port %d: unsuspend: %r\n", argv0, d->dir, p);
- sleep(Enabledelay);
- sts = portstatus(h, p);
- if(sts == -1){
- hubfail(h);
- return -1;
- }
- fprint(2, "%s: %s: port %d: unsuspended sts: %s %#ux\n", argv0, d->dir, p,
- stsstr(sts, h->dev->isusb3), sts);
- }
- }
-
onhubs = nhubs;
-
- if(portgone(pp, sts)){
+ pp = &h->port[p];
+ if((sts & PSpresent) == 0 && (pp->sts & PSpresent) != 0){
pp->sts = sts;
portdetach(h, p);
- } else if((pp->sts & PSpresent) == 0 && (sts & PSpresent) != 0){
+ } else if((sts & PSenable) == 0 && (pp->sts & PSenable) != 0){
+ pp->sts = 0;
+ portfail(h, p, "reconnect");
+ } else if((sts & PSenable) != 0 && portresetwanted(pp->dev)){
+ pp->sts = 0;
+ portfail(h, p, "reset");
+ } else if((sts & PSpresent) != 0 && (pp->sts & PSpresent) == 0){
pp->sts = sts;
- if(portattach(h, p, sts) < 0)
+ if(portattach(h, p, sts) < 0){
+ if(h->failed)
+ return -1;
+ if(pp->state != Pdisabled)
+ pp->sts = 0; /* force re-attach */
portfail(h, p, "attach");
- } else if(pp->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);
+ }
+ } else if(sts != pp->sts){
+ dprint(2, "%s: %s port %d: sts %s %#ux ->", argv0, d->dir, p,
+ stsstr(pp->sts, d->isusb3), pp->sts);
+ dprint(2, " %s %#ux\n", stsstr(sts, d->isusb3), sts);
pp->sts = sts;
}
-
return onhubs != nhubs;
}
--- a/sys/src/cmd/nusb/usbd/usbd.c
+++ b/sys/src/cmd/nusb/usbd/usbd.c
@@ -569,11 +569,11 @@
nd = dirreadall(fd, &d);
close(fd);
for(i = 0; i < nd; i++){
- if(strcmp(d[i].name, "ctl") != 0){
- fn = smprint("/dev/usb/%s", d[i].name);
- newhub(fn, nil);
- free(fn);
- }
+ if(strcmp(d[i].name, "ctl") == 0)
+ continue;
+ fn = smprint("/dev/usb/%s", d[i].name);
+ newhub(fn, nil);
+ free(fn);
}
free(d);
}else {
--
⑨