mirror of
git://git.9front.org/plan9front/plan9front
synced 2025-01-12 11:10:06 +00:00
nusb/usbd: impelement warm-reset for usb3 ports, attempt port reset on failed hubs, enable port-power for rootports
This commit is contained in:
parent
2f12ad38ca
commit
5cc0a1f76d
3 changed files with 147 additions and 139 deletions
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue