nusb/usbd: impelement warm-reset for usb3 ports, attempt port reset on failed hubs, enable port-power for rootports

This commit is contained in:
cinap_lenrek 2024-12-06 16:10:31 +00:00
parent 2f12ad38ca
commit 5cc0a1f76d
3 changed files with 147 additions and 139 deletions

View file

@ -30,6 +30,8 @@ enum
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 @@ enum
/* 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 @@ struct Hub
{
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 */

View file

@ -58,8 +58,6 @@ configusb2hub(Hub *h, DHub *dd, int nr)
}
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 @@ configusb2hub(Hub *h, DHub *dd, int nr)
}
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 @@ configusb3hub(Hub *h, DSSHub *dd, int)
}
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 @@ configusb3hub(Hub *h, DSSHub *dd, int)
}
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 @@ configroothub(Hub *h)
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 @@ newhub(char *fn, Dev *d)
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 @@ hubfail(Hub *h)
{
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 @@ closehub(Hub *h)
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 @@ stsstr(int sts, int isusb3)
*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 @@ portattach(Hub *h, int p, u32int sts)
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,41 +447,30 @@ portattach(Hub *h, int p, u32int sts)
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";
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,8 +501,9 @@ portattach(Hub *h, int p, u32int sts)
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);
}
@ -514,20 +541,6 @@ portattach(Hub *h, int p, u32int sts)
/* assign stable name based on device descriptor */
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.
*/
@ -540,6 +553,18 @@ portattach(Hub *h, int p, u32int sts)
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,39 +613,26 @@ portfail(Hub *h, int p, char *what)
{
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;
}
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)
{
@ -634,49 +646,43 @@ enumhub(Hub *h, int p)
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;
}

View file

@ -569,11 +569,11 @@ main(int argc, char **argv)
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 {