Browse Source

Plan 9 from Bell Labs 2009-09-26

David du Colombier 14 years ago
parent
commit
23c35d943e

+ 14 - 7
sys/man/1/history

@@ -4,7 +4,9 @@ history \- print file names from the dump
 .SH SYNOPSIS
 .B history
 [
-.B -bDfuv
+.B -Dabcemnw
+] [
+.B -fuv
 ] [
 .B -d
 .I dumpfilesystem
@@ -52,13 +54,18 @@ The
 .B -D
 option causes
 .IR diff (1)
-.B -n
 to be run for each adjacent pair of dump files.
-Adding
-.B -b
-runs
-.IR diff
-.BR -nb .
+The options
+.B -abcemnw
+are passed through to
+.IR diff;
+the little-used
+.I diff option
+.B -f
+is replaced by the functionality described below,
+and the
+.B -r
+option is disallowed.
 .PP
 The
 .B -u

+ 1 - 1
sys/src/9/boot/boot.c

@@ -49,7 +49,7 @@ boot(int argc, char *argv[])
 #ifdef DEBUG
 	print("argc=%d\n", argc);
 	for(fd = 0; fd < argc; fd++)
-		print("%lux %s ", argv[fd], argv[fd]);
+		print("%#p %s ", argv[fd], argv[fd]);
 	print("\n");
 #endif DEBUG
 

+ 14 - 11
sys/src/9/boot/bootip.c

@@ -16,11 +16,9 @@ static void netenv(char*, uchar*);
 void
 configip(int bargc, char **bargv, int needfs)
 {
-	int argc, pid;
-	char **argv, *p;
 	Waitmsg *w;
-	char **arg;
-	char buf[32];
+	int argc, pid;
+	char **arg, **argv, buf[32], *p;
 
 	fmtinstall('I', eipfmt);
 	fmtinstall('M', eipfmt);
@@ -28,7 +26,7 @@ configip(int bargc, char **bargv, int needfs)
 
 	arg = malloc((bargc+1) * sizeof(char*));
 	if(arg == nil)
-		fatal("malloc");
+		fatal("%r");
 	memmove(arg, bargv, bargc * sizeof(char*));
 	arg[bargc] = 0;
 
@@ -51,17 +49,22 @@ configip(int bargc, char **bargv, int needfs)
 	} ARGEND;
 
 	/* bind in an ip interface */
-	bind("#I", mpoint, MAFTER);
-	bind("#l0", mpoint, MAFTER);
-	bind("#l1", mpoint, MAFTER);
-	bind("#l2", mpoint, MAFTER);
-	bind("#l3", mpoint, MAFTER);
+	if(bind("#I", mpoint, MAFTER) < 0)
+		fatal("bind #I: %r\n");
+	if(access("#l0", 0) == 0 && bind("#l0", mpoint, MAFTER) < 0)
+		print("bind #l0: %r\n");
+	if(access("#l1", 0) == 0 && bind("#l1", mpoint, MAFTER) < 0)
+		print("bind #l1: %r\n");
+	if(access("#l2", 0) == 0 && bind("#l2", mpoint, MAFTER) < 0)
+		print("bind #l2: %r\n");
+	if(access("#l3", 0) == 0 && bind("#l3", mpoint, MAFTER) < 0)
+		print("bind #l3: %r\n");
 	werrstr("");
 
 	/* let ipconfig configure the ip interface */
 	switch(pid = fork()){
 	case -1:
-		fatal("configuring ip");
+		fatal("configuring ip: %r");
 	case 0:
 		exec("/boot/ipconfig", arg);
 		fatal("execing /ipconfig");

+ 14 - 19
sys/src/9/pc/devusb.c

@@ -71,7 +71,6 @@ enum
 	/* Usb ctls. */
 	CMdebug = 0,		/* debug on|off */
 	CMdump,			/* dump (data structures for debug) */
-	CMreset,		/* reset the bus; start over */
 
 	/* Ep. ctls */
 	CMnew = 0,		/* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */
@@ -90,6 +89,7 @@ enum
 	CMdebugep,		/* debug n (set/clear debug for this ep) */
 	CMname,			/* name str (show up as #u/name as well) */
 	CMtmout,		/* timeout n (activate timeouts for ep) */
+	CMpreset,		/* reset the port */
 
 	/* Hub feature selectors */
 	Rportenable	= 1,
@@ -113,7 +113,6 @@ static Cmdtab usbctls[] =
 {
 	{CMdebug,	"debug",	2},
 	{CMdump,	"dump",		1},
-	{CMreset,	"reset",	1},
 };
 
 static Cmdtab epctls[] =
@@ -134,6 +133,7 @@ static Cmdtab epctls[] =
 	{CMclrhalt,	"clrhalt",	1},
 	{CMname,	"name",		2},
 	{CMtmout,	"timeout",	2},
+	{CMpreset,	"reset",	1},
 };
 
 static Dirtab usbdir[] =
@@ -269,7 +269,7 @@ addhcitype(char* t, int (*r)(Hci*))
 static char*
 seprintep(char *s, char *se, Ep *ep, int all)
 {
-	static char* dsnames[] = { "config", "enabled", "detached" };
+	static char* dsnames[] = { "config", "enabled", "detached", "reset" };
 	Udev *d;
 	int i;
 	int di;
@@ -657,7 +657,6 @@ usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
 Fail:
 	if(0)ddprint("fail\n");
 	return -1;
-
 }
 
 static Hci*
@@ -709,8 +708,8 @@ hciprobe(int cardno, int ctlrno)
 	 * modern machines have too many usb controllers to list on
 	 * the console.
 	 */
-//	print("#u/usb/ep%d.0: %s: port 0x%luX irq %d\n",
-//		epnb, hcitypes[cardno].type, hp->port, hp->irq);
+	dprint("#u/usb/ep%d.0: %s: port 0x%luX irq %d\n",
+		epnb, hcitypes[cardno].type, hp->port, hp->irq);
 	epnb++;
 	return hp;
 }
@@ -1142,7 +1141,7 @@ epctl(Ep *ep, Chan *c, void *a, long n)
 	if(ct == nil)
 		error(Ebadctl);
 	i = ct->index;
-	if(i == CMnew || i == CMspeed || i == CMhub)
+	if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset)
 		if(ep != ep->ep0)
 			error("allowed only on a setup endpoint");
 	if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname)
@@ -1317,6 +1316,14 @@ epctl(Ep *ep, Chan *c, void *a, long n)
 		if(ep->tmout != 0 && ep->tmout < Xfertmout)
 			ep->tmout = Xfertmout;
 		break;
+	case CMpreset:
+		deprint("usb epctl %s\n", cb->f[0]);
+		if(ep->ttype != Tctl)
+			error("not a control endpoint");
+		if(ep->dev->state != Denabled)
+			error("forbidden on devices not enabled");
+		ep->dev->state = Dreset;
+		break;
 	default:
 		panic("usb: unknown epctl %d", ct->index);
 	}
@@ -1355,18 +1362,6 @@ usbctl(void *a, long n)
 				putep(ep);
 			}
 		break;
-	case CMreset:
-		print("devusb: CMreset not implemented\n");
-		error("not implemented");
-#ifdef TODO
-		XXX: I'm not sure this is a good idea.
-		Usbd should not be restarted at all.
-		for(all eps)
-			closeep(ep);
-		do a global reset once more
-		recreate root hub devices in place.
-#endif
-		break;
 	case CMdump:
 		dumpeps();
 		break;

+ 5 - 4
sys/src/9/pc/usbehci.c

@@ -2478,10 +2478,11 @@ epctlio(Ep *ep, Ctlio *cio, void *a, long count)
 	}
 
 	/* set the address if unset and out of configuration state */
-	if(ep->dev->state != Dconfig && cio->usbid == 0){
-		cio->usbid = ((ep->nb&Epmax)<<7)|(ep->dev->nb&Devmax);
-		qhsetaddr(cio->qh, cio->usbid);
-	}
+	if(ep->dev->state != Dconfig && ep->dev->state != Dreset)
+		if(cio->usbid == 0){
+			cio->usbid = ((ep->nb&Epmax)<<7)|(ep->dev->nb&Devmax);
+			qhsetaddr(cio->qh, cio->usbid);
+		}
 	/* adjust maxpkt if the user has learned a different one */
 	if(qhmaxpkt(cio->qh) != ep->maxpkt)
 		qhsetmaxpkt(cio->qh, ep->maxpkt);

+ 1 - 1
sys/src/9/pc/usbehci.h

@@ -144,4 +144,4 @@ struct Edbgio
 };
 
 extern Ecapio *ehcidebugcapio;
-extern int ehcidebugport;
+extern int ehcidebugport;

+ 5 - 4
sys/src/9/pc/usbohci.c

@@ -1688,10 +1688,11 @@ epctlio(Ep *ep, Ctlio *cio, void *a, long count)
 	}
 
 	/* set the address if unset and out of configuration state */
-	if(ep->dev->state != Dconfig && cio->usbid == 0){
-		cio->usbid = (ep->nb<<7)|(ep->dev->nb & Devmax);
-		edsetaddr(cio->ed, cio->usbid);
-	}
+	if(ep->dev->state != Dconfig && ep->dev->state != Dreset)
+		if(cio->usbid == 0){
+			cio->usbid = (ep->nb<<7)|(ep->dev->nb & Devmax);
+			edsetaddr(cio->ed, cio->usbid);
+		}
 	/* adjust maxpkt if the user has learned a different one */
 	if(edmaxpkt(cio->ed) != ep->maxpkt)
 		edsetmaxpkt(cio->ed, ep->maxpkt);

+ 3 - 4
sys/src/9/pc/usbuhci.c

@@ -964,7 +964,6 @@ interrupt(Ureg*, void *a)
 		else if(qh->state == Qclose)
 			qhlinktd(qh, nil);
 	iunlock(ctlr);
-
 }
 
 /*
@@ -1502,8 +1501,9 @@ epctlio(Ep *ep, Ctlio *cio, void *a, long count)
 	}
 
 	/* set the address if unset and out of configuration state */
-	if(ep->dev->state != Dconfig && cio->usbid == 0)
-		cio->usbid = ((ep->nb&Epmax)<<7)|(ep->dev->nb&Devmax);
+	if(ep->dev->state != Dconfig && ep->dev->state != Dreset)
+		if(cio->usbid == 0)
+			cio->usbid = ((ep->nb&Epmax)<<7)|(ep->dev->nb&Devmax);
 	c = a;
 	cio->tok = Tdtoksetup;
 	cio->toggle = Tddata0;
@@ -1869,7 +1869,6 @@ cancelisoio(Ctlr *ctlr, Isoio *iso, int pollival, ulong load)
 	}
 	free(iso->data);
 	iso->data = nil;
-
 }
 
 static void

+ 1 - 1
sys/src/9/port/devmnt.c

@@ -1094,8 +1094,8 @@ mntfree(Mntrpc *r)
 	lock(&mntalloc);
 	if(mntalloc.nrpcfree >= 10){
 		free(r->rpc);
-		free(r);
 		freetag(r->request.tag);
+		free(r);
 	}
 	else{
 		r->list = mntalloc.rpcfree;

+ 42 - 13
sys/src/cmd/history.c

@@ -9,14 +9,18 @@ int	verb;
 int	uflag;
 int	force;
 int	diff;
-int	diffb;
 char*	sflag;
+char*	dargv[20] = {
+	"diff",
+};
+int	ndargv = 1;
 
 void	usage(void);
 void	ysearch(char*, char*);
 long	starttime(char*);
 void	lastbefore(ulong, char*, char*, char*);
 char*	prtime(ulong);
+void	darg(char*);
 
 void
 main(int argc, char *argv[])
@@ -28,20 +32,35 @@ main(int argc, char *argv[])
 	ARGBEGIN {
 	default:
 		usage();
-	case 'v':
-		verb = 1;
+	case 'a':
+		darg("-a");
 		break;
-	case 'f':
-		force = 1;
+	case 'b':
+		darg("-b");
 		break;
-	case 'd':
-		ndump = ARGF();
+	case 'c':
+		darg("-c");
+		break;
+	case 'e':
+		darg("-e");
+		break;
+	case 'm':
+		darg("-m");
+		break;
+	case 'n':
+		darg("-n");
+		break;
+	case 'w':
+		darg("-w");
 		break;
 	case 'D':
 		diff = 1;
 		break;
-	case 'b':
-		diffb = 1;
+	case 'd':
+		ndump = ARGF();
+		break;
+	case 'f':
+		force = 1;
 		break;
 	case 's':
 		sflag = ARGF();
@@ -49,6 +68,9 @@ main(int argc, char *argv[])
 	case 'u':
 		uflag = 1;
 		break;
+	case 'v':
+		verb = 1;
+		break;
 	} ARGEND
 
 	if(argc == 0)
@@ -59,6 +81,14 @@ main(int argc, char *argv[])
 	exits(0);
 }
 
+void
+darg(char* a)
+{
+	if(ndargv >= nelem(dargv)-3)
+		return;
+	dargv[ndargv++] = a;
+}
+
 void
 usage(void)
 {
@@ -167,10 +197,9 @@ ysearch(char *file, char *ndump)
 		if(diff && started){
 			switch(rfork(RFFDG|RFPROC)){
 			case 0:
-				if(diffb)
-					execl("/bin/diff", "diff", "-nb", pair[toggle ^ 1], pair[toggle], nil);
-				else
-					execl("/bin/diff", "diff", "-n", pair[toggle ^ 1], pair[toggle], nil);
+				dargv[ndargv] = pair[toggle];
+				dargv[ndargv+1] = pair[toggle ^ 1];
+				exec("/bin/diff", dargv);
 				fprint(2, "can't exec diff: %r\n");
 				exits(0);
 			case -1:

+ 2 - 2
sys/src/cmd/usb/ether/clether.c

@@ -643,7 +643,7 @@ finddevice(int *ctrlno, int *id)
 				p += 8;
 
 			csp = atol(p);
-			
+
 			if(Class(csp) != Clcomms)
 				continue;
 			switch(Subclass(csp)){
@@ -888,7 +888,7 @@ void (*dprinter[])(Device *, int, ulong, void *b, int n) = {
 	[FUNCTION] etherfunc,
 };
 
-Srv fs = 
+Srv fs =
 {
 .attach=		fsattach,
 .destroyfid=	fsdestroyfid,

+ 11 - 11
sys/src/cmd/usb/ether/ether.c

@@ -349,7 +349,6 @@ rootdirgen(Usbfs *fs, Qid, int i, Dir *d, void *)
 	}
 	filldir(fs, d, tab, cn);
 	return 0;
-
 }
 
 static int
@@ -731,9 +730,9 @@ etherbread(Ether *e, Buf *bp)
 	bp->rp = bp->data + Hdrsize;
 	bp->ndata = -1;
 	bp->ndata = read(e->epin->dfd, bp->rp, sizeof(bp->data)-Hdrsize);
-	if(bp->ndata < 0)
-		deprint(2, "%s: etherbread: %r\n", argv0);
-	else
+	if(bp->ndata < 0){
+		deprint(2, "%s: etherbread: %r\n", argv0);	/* keep { and }  */
+	}else
 		deprint(2, "%s: etherbread: got %d bytes\n", argv0, bp->ndata);
 	return bp->ndata;
 }
@@ -745,9 +744,9 @@ etherbwrite(Ether *e, Buf *bp)
 
 	deprint(2, "%s: etherbwrite %d bytes\n", argv0, bp->ndata);
 	n = write(e->epout->dfd, bp->rp, bp->ndata);
-	if(n < 0)
-		deprint(2, "%s: etherbwrite: %r\n", argv0);
-	else
+	if(n < 0){
+		deprint(2, "%s: etherbwrite: %r\n", argv0);	/* keep { and }  */
+	}else
 		deprint(2, "%s: etherbwrite wrote %ld bytes\n", argv0, n);
 	if(n <= 0)
 		return n;
@@ -789,7 +788,7 @@ fswrite(Usbfs *fs, Fid *fid, void *data, long count, vlong)
 			dumpframe("etherout", bp->rp, bp->ndata);
 		if(e->nblock == 0)
 			sendp(e->wc, bp);
-		else if(nbsendp(e->wc, bp) < 0){
+		else if(nbsendp(e->wc, bp) == 0){
 			deprint(2, "%s: (out) packet lost\n", argv0);
 			freebuf(e, bp);
 		}
@@ -909,7 +908,6 @@ etherfree(Ether *e)
 	shutdownchan(e->wc);
 	e->epin = e->epout = nil;
 	free(e);
-
 }
 
 static void
@@ -940,8 +938,10 @@ etherwriteproc(void *a)
 	wc = e->wc;
 	while(e->exiting == 0){
 		bp = recvp(wc);
-		if(bp == nil || e->exiting != 0)
+		if(bp == nil || e->exiting != 0){
+			free(bp);
 			break;
+		}
 		e->nout++;
 		if(e->bwrite(e, bp) < 0)
 			e->noerrs++;
@@ -1020,7 +1020,7 @@ etherreadproc(void *a)
 					dbp->ndata = n;
 					dbp->type = bp->type;
 				}
-				if(nbsendp(e->conns[i]->rc, dbp) < 0){
+				if(nbsendp(e->conns[i]->rc, dbp) == 0){
 					e->nierrs++;
 					freebuf(e, dbp);
 				}

+ 44 - 16
sys/src/cmd/usb/kb/kb.c

@@ -396,13 +396,44 @@ kbdbusy(uchar* buf, int n)
 	return 1;
 }
 
+static int
+setbootproto(KDev* f, int eid)
+{
+	int r, id;
+
+	r = Rh2d|Rclass|Riface;
+	id = f->dev->usb->ep[eid]->iface->id;
+	return usbcmd(f->dev, r, Setproto, Bootproto, id, nil, 0);
+}
+
+/*
+ * Try to recover from a babble error. A port reset is the only way out.
+ * BUG: we should be careful not to reset a bundle with several devices.
+ */
+static void
+recoverkb(KDev *f)
+{
+	int i;
+
+	close(f->dev->dfd);		/* it's for usbd now */
+	devctl(f->dev, "reset");
+	for(i = 0; i < 10; i++){
+		sleep(500);
+		if(opendevdata(f->dev, ORDWR) >= 0){
+			setbootproto(f, f->ep->id);
+			break;
+		}
+		/* else usbd still working... */
+	}
+}
+
 static void
 kbdwork(void *a)
 {
-	int c, i, kbdfd;
-	uchar buf[64], lbuf[64];
+	int c, i, kbdfd, nerrs;
+	uchar dk, buf[64], lbuf[64];
+	char err[128];
 	KDev *f = a;
-	uchar dk;
 
 	kbdfd = f->ep->dfd;
 
@@ -415,14 +446,20 @@ kbdwork(void *a)
 
 	proccreate(repeatproc, f, Stack);
 	memset(lbuf, 0, sizeof lbuf);
-	dk = 0;
+	dk = nerrs = 0;
 	for(;;){
 		memset(buf, 0, sizeof buf);
 		c = read(kbdfd, buf, f->ep->maxpkt);
 		assert(f->dev != nil);
 		assert(f->ep != nil);
-		if(c < 0)
-			dprint(2, "kb: %s: read: %r\n", f->ep->dir);
+		if(c < 0){
+			rerrstr(err, sizeof(err));
+			dprint(2, "kb: %s: read: %s\n", f->ep->dir, err);
+			if(strstr(err, "babble") != 0 && ++nerrs < 3){
+				recoverkb(f);
+				continue;
+			}
+		}
 		if(c <= 0)
 			kbfatal(f, nil);
 		if(c < 3)
@@ -437,19 +474,10 @@ kbdwork(void *a)
 		}
 		dk = putkeys(f, buf, lbuf, f->ep->maxpkt, dk);
 		memmove(lbuf, buf, c);
+		nerrs = 0;
 	}
 }
 
-static int
-setbootproto(KDev* f, int eid)
-{
-	int r, id;
-
-	r = Rh2d|Rclass|Riface;
-	id = f->dev->usb->ep[eid]->iface->id;
-	return usbcmd(f->dev, r, Setproto, Bootproto, id, nil, 0);
-}
-
 static void
 freekdev(void *a)
 {

+ 18 - 4
sys/src/cmd/usb/lib/dev.c

@@ -135,10 +135,19 @@ opendevdata(Dev *d, int mode)
 	return d->dfd;
 }
 
+enum
+{
+	/* Max device conf is also limited by max control request size
+	 * as limited by the kernel usb.h.
+	 * (both limits are arbitrary).
+	 */
+	Maxdevconf = 16 * 1024
+};
+
 int
 loaddevconf(Dev *d, int n)
 {
-	uchar buf[1024];	/* enough room for extra descriptors */
+	uchar *buf;
 	int nr;
 	int type;
 
@@ -147,13 +156,18 @@ loaddevconf(Dev *d, int n)
 		fprint(2, "%s: %r\n", argv0);
 		return -1;
 	}
+	buf = emallocz(Maxdevconf, 0);
 	type = Rd2h|Rstd|Rdev;
-	nr = usbcmd(d, type, Rgetdesc, Dconf<<8|n, 0, buf, 1024);
-	if(nr < Dconflen)
+	nr = usbcmd(d, type, Rgetdesc, Dconf<<8|n, 0, buf, Maxdevconf);
+	if(nr < Dconflen){
+		free(buf);
 		return -1;
+	}
 	if(d->usb->conf[n] == nil)
 		d->usb->conf[n] = emallocz(sizeof(Conf), 1);
-	return parseconf(d->usb, d->usb->conf[n], buf, nr);
+	nr = parseconf(d->usb, d->usb->conf[n], buf, nr);
+	free(buf);
+	return nr;
 }
 
 Ep*

+ 91 - 1
sys/src/cmd/usb/usbd/usbd.c

@@ -465,6 +465,95 @@ portdetach(Hub *h, int p)
 	}
 }
 
+/*
+ * 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)
+{
+	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)
+		return strncmp(buf, "reset", 5) == 0;
+	else
+		return 0;
+}
+
+static void
+portreset(Hub *h, int p)
+{
+	int sts;
+	Dev *d, *nd;
+	Port *pp;
+
+	d = h->dev;
+	pp = &h->port[p];
+	nd = pp->dev;
+	dprint(2, "%s: %s: port %d: resetting\n", argv0, d->dir, p);
+	if(hubfeature(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 < 0)
+		goto Fail;
+	if((sts & PSenable) == 0){
+		dprint(2, "%s: %s: port %d: not enabled?\n", argv0, d->dir, p);
+		hubfeature(h, p, Fportenable, 1);
+		sts = portstatus(h, p);
+		if((sts & PSenable) == 0)
+			goto Fail;
+	}
+	nd = pp->dev;
+	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);
+	return;
+Fail:
+	pp->state = Pdisabled;
+	pp->sts = 0;
+	if(pp->hub != nil)
+		pp->hub = nil;	/* hub closed by enumhub */
+	hubfeature(h, p, Fportenable, 0);
+	if(nd != nil)
+		devctl(nd, "detach");
+	closedev(nd);
+}
+
 static int
 portgone(Port *pp, int sts)
 {
@@ -513,6 +602,8 @@ enumhub(Hub *h, int p)
 				portdetach(h, p);
 	}else if(portgone(pp, sts))
 		portdetach(h, p);
+	else if(portresetwanted(h, p))
+		portreset(h, p);
 	else if(pp->sts != sts){
 		dprint(2, "%s: %s port %d: sts %s %#x ->",
 			argv0, d->dir, p, stsstr(pp->sts), pp->sts);
@@ -618,7 +709,6 @@ setdrvargs(char *name, char *args)
 			dt->args = estrdup(args);
 }
 
-
 static long
 cfswrite(Usbfs*, Fid *, void *data, long cnt, vlong )
 {

+ 2 - 2
sys/src/cmd/usb/usbd/usbd.h

@@ -60,7 +60,7 @@ enum
 	Pollms		= 250, 		/* port poll interval */
 	Chgdelay	= 100,		/* waiting for port become stable */
 	Chgtmout	= 1000,		/* ...but at most this much */
-	
+
 	/*
 	 * device tab for embedded usb drivers.
 	 */
@@ -113,7 +113,7 @@ struct Devtab
 {
 	char	*name;
 	int	(*init)(Dev*, int, char**);	/* nil if external */
- 	int	csps[4];
+	int	csps[4];
 	int	vid;
 	int	did;
 	char	*args;