shithub: riscv

Download patch

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);