shithub: front

Download patch

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 {
--