ref: 62f311310263bafb7af9aee1783bf33ceb12c00e
parent: 20a1d2268d69a0bbe66d0f21999649133878377c
author: cinap_lenrek <[email protected]>
date: Sun Jul 30 12:47:03 EDT 2023
nusb/kb: fix portreset feature and recovery portreset feature cannot work with xhci, as we cannot keep the address. if nusb/kb encounters a transaction error, request a port reset and exit immediately. then usbd will detach the device from the port and and try a re-attach of the device when still present, which automatically triggers a port reset.
--- a/sys/src/cmd/nusb/kb/kb.c
+++ b/sys/src/cmd/nusb/kb/kb.c
@@ -442,35 +442,6 @@
}
static void
-hdrecover(Hiddev *f)
-{
- char err[ERRMAX];
- static QLock l;
- int i;
-
- if(canqlock(&l)){
- close(f->dev->dfd);
- devctl(f->dev, "reset");
- for(i=0; i<4; i++){
- sleep(500);
- if(opendevdata(f->dev, ORDWR) >= 0)
- goto Resetdone;
- }
- threadexitsall(err);
- } else {
- /* wait for reset to complete */
- qlock(&l);
- }
-Resetdone:
- if(setproto(f, f->ep->ep->iface) < 0){
- rerrstr(err, sizeof(err));
- qunlock(&l);
- hdfatal(f, err);
- }
- qunlock(&l);
-}
-
-static void
putscan(Hiddev *f, uchar sc, uchar up)
{
uchar s[2] = {SCesc1, 0};
@@ -724,7 +695,7 @@
{
char err[ERRMAX], mbuf[80];
ushort lastk[Nkey], uks[Nkey], dks[Nkey], nuks, ndks;
- int i, c, nerrs, bpress, lastb, nlastk, stopped;
+ int i, c, bpress, lastb, nlastk, stopped;
int abs, x, y, z, b;
Hidreport p;
Hidslot lasts[nelem(p.s)], *s, *l;
@@ -735,7 +706,7 @@
memset(&p, 0, sizeof(p));
memset(lasts, 0, sizeof(lasts));
- lastb = nlastk = nerrs = 0;
+ lastb = nlastk = 0;
for(;;){
if(f->ep == nil)
@@ -750,14 +721,14 @@
rerrstr(err, sizeof(err));
else
strcpy(err, "zero read");
+
fprint(2, "%s: hid: %s: read: %s\n", argv0, f->ep->dir, err);
- if(++nerrs <= 3){
- hdrecover(f);
- continue;
- }
+
+ /* reset the whole device */
+ devctl(f->dev, "reset");
+
hdfatal(f, err);
}
- nerrs = 0;
p.o = 0;
p.e = p.p + c;
--- a/sys/src/cmd/nusb/usbd/hub.c
+++ b/sys/src/cmd/nusb/usbd/hub.c
@@ -504,24 +504,6 @@
}
}
-/*
- * 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)
{
@@ -537,57 +519,6 @@
return 0;
}
-static void
-portreset(Hub *h, int p)
-{
- u32int sts;
- Dev *d, *nd;
- Port *pp;
-
- d = h->dev;
- pp = &h->port[p];
- dprint(2, "%s: %s: port %d: resetting\n", argv0, d->dir, p);
- if(portfeature(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);
- goto Fail;
- }
- if((nd = pp->dev) == nil)
- return;
- opendevdata(nd, ORDWR);
- 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;
- }
- 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;
- }
- if(nd->dfd >= 0){
- close(nd->dfd);
- nd->dfd = -1;
- }
- return;
-Fail:
- pp->sts = 0;
- portdetach(h, p);
- if(!d->isusb3)
- portfeature(h, p, Fportenable, 0);
-}
-
static int
portgone(Port *pp, u32int sts)
{
@@ -638,9 +569,13 @@
portdetach(h, p);
}else if(portgone(pp, sts)){
portdetach(h, p);
- }else if(portresetwanted(h, p))
- portreset(h, p);
- else if(pp->sts != sts){
+ }else if(portresetwanted(h, p)){
+ portdetach(h, p);
+ if(!d->isusb3)
+ portfeature(h, p, Fportenable, 0);
+ /* pretend it is gone, causing a re-attach and port reset */
+ sts = 0;
+ } 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);