shithub: front

Download patch

ref: ebf3e9067d2a56c3c2bb9f574d7767173601e983
parent: acf707cbbc3e8c1e0c59936a4cde347211b11a36
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Nov 3 07:11:45 EST 2024

devusb: destruct usb tree from the leaves, split epclose() into epstop()/epclose()

For xhci, we want to keep the hubs around until all its
attached devices have been released, so have the Udev
take a reference to its parent hubs ep0.

This also means that we can now use just a pointer
to the tthub instead of duplicating the properties
needed for xhci and ythe code becomes trivial.

Do a non-recursive implementation of putep() to
conserve stack space.

For device detaches, we want to immediately cancel
all I/O so that the driver can release the device
as soon as possible.

For this, we add epstop callback in the Hci struct.

--- a/sys/src/9/bcm/usbdwc.c
+++ b/sys/src/9/bcm/usbdwc.c
@@ -180,8 +180,8 @@
 		hcc |= Lspddev;
 		/* fall through */
 	case Fullspeed:
-		if(ep->dev->tthub != 0 && ep->dev->ttport != 0){
-			hc->hcsplt = Spltena | POS_ALL | ep->dev->tthub<<OHubaddr | ep->dev->ttport;
+		if(ep->dev->tthub != nil){
+			hc->hcsplt = Spltena | POS_ALL | ep->dev->tthub->addr<<OHubaddr | ep->dev->ttport;
 			break;
 		}
 		/* fall through */
@@ -582,10 +582,8 @@
 		datalen = GET2(req+Rcount);
 		if(datalen <= 0 || datalen > Maxctllen)
 			error(Ebadlen);
-		/* XXX cache madness */
 		epio->cb = b = allocb(ROUND(datalen, ep->maxpkt));
 		assert(((uintptr)b->wp & (BLOCKALIGN-1)) == 0);
-		memset(b->wp, 0x55, b->lim - b->wp);
 		cachedwbinvse(b->wp, b->lim - b->wp);
 		data = b->wp;
 	}else{
@@ -604,7 +602,7 @@
 	chansetup(hc, ep);
 	chanio(ep, hc, Epout, SETUP, req, Rsetuplen);
 	if(req[Rtype] & Rd2h){
-		if(ep->dev->hub <= 1){
+		if(ep->dev->depth == 0){
 			ep->toggle[Read] = DATA1;
 			b->wp += multitrans(ep, hc, Read, data, datalen);
 		}else
--- a/sys/src/9/pc/usbohci.c
+++ b/sys/src/9/pc/usbohci.c
@@ -2147,21 +2147,19 @@
 }
 
 static void
-epclose(Ep *ep)
+epstop(Ep *ep)
 {
 	Ctlio *cio;
 	Isoio *iso;
 	Qio *io;
 
-	deprint("ohci: epclose ep%d.%d\n", ep->dev->nb, ep->nb);
+	deprint("ohci: epstop ep%d.%d\n", ep->dev->nb, ep->nb);
 	if(ep->aux == nil)
-		panic("ohci: epclose called with closed ep");
+		panic("ohci: epstop called with closed ep");
 	switch(ep->ttype){
 	case Tctl:
 		cio = ep->aux;
 		cancelio(ep, cio);
-		free(cio->data);
-		cio->data = nil;
 		break;
 	case Tbulk:
 	case Tintr:
@@ -2182,6 +2180,29 @@
 		cancelio(ep, iso);
 		break;
 	default:
+		panic("epstop: bad ttype %d", ep->ttype);
+	}
+}
+
+static void
+epclose(Ep *ep)
+{
+	Ctlio *cio;
+
+	deprint("ohci: epclose ep%d.%d\n", ep->dev->nb, ep->nb);
+	if(ep->aux == nil)
+		panic("ohci: epclose called with closed ep");
+	switch(ep->ttype){
+	case Tctl:
+		cio = ep->aux;
+		free(cio->data);
+		cio->data = nil;
+		break;
+	case Tbulk:
+	case Tintr:
+	case Tiso:
+		break;
+	default:
 		panic("epclose: bad ttype %d", ep->ttype);
 	}
 
@@ -2598,6 +2619,7 @@
 	hp->init = init;
 	hp->interrupt = interrupt;
 	hp->epopen = epopen;
+	hp->epstop = epstop;
 	hp->epclose = epclose;
 	hp->epread = epread;
 	hp->epwrite = epwrite;
--- a/sys/src/9/pc/usbuhci.c
+++ b/sys/src/9/pc/usbuhci.c
@@ -1909,7 +1909,7 @@
 }
 
 static void
-epclose(Ep *ep)
+epstop(Ep *ep)
 {
 	Ctlr *ctlr;
 	Ctlio *cio;
@@ -1917,16 +1917,15 @@
 	Qio *io;
 
 	ctlr = ep->hp->aux;
-	deprint("uhci: epclose ep%d.%d\n", ep->dev->nb, ep->nb);
+	deprint("uhci: epstop ep%d.%d\n", ep->dev->nb, ep->nb);
 
 	if(ep->aux == nil)
-		panic("uhci: epclose called with closed ep");
+		panic("uhci: epstop called with closed ep");
+
 	switch(ep->ttype){
 	case Tctl:
 		cio = ep->aux;
 		cancelio(ctlr, cio);
-		free(cio->data);
-		cio->data = nil;
 		break;
 	case Tbulk:
 	case Tintr:
@@ -1948,12 +1947,35 @@
 		cancelisoio(ctlr, iso, ep->pollival, ep->load);
 		break;
 	default:
-		panic("epclose: bad ttype %d", ep->ttype);
+		panic("epstop: bad ttype %d", ep->ttype);
 	}
+}
 
+static void
+epclose(Ep *ep)
+{
+	Ctlio *cio;
+
+	deprint("uhci: epclose ep%d.%d\n", ep->dev->nb, ep->nb);
+
+	if(ep->aux == nil)
+		panic("uhci: epclose called with closed ep");
+
+	switch(ep->ttype){
+	case Tctl:
+		cio = ep->aux;
+		free(cio->data);
+		cio->data = nil;
+		break;
+	case Tbulk:
+	case Tintr:
+	case Tiso:
+		break;
+	default:
+		panic("epclose: bad ttype %d", ep->ttype);
+	}
 	free(ep->aux);
 	ep->aux = nil;
-
 }
 
 static char*
@@ -2327,6 +2349,7 @@
 	hp->init = init;
 	hp->interrupt = interrupt;
 	hp->epopen = epopen;
+	hp->epstop = epstop;
 	hp->epclose = epclose;
 	hp->epread = epread;
 	hp->epwrite = epwrite;
--- a/sys/src/9/port/devusb.c
+++ b/sys/src/9/port/devusb.c
@@ -326,7 +326,7 @@
 	s = seprint(s, se, " samplesz %ld", ep->samplesz);
 	s = seprint(s, se, " hz %ld", ep->hz);
 	s = seprint(s, se, " uframes %d", ep->uframes);
-	s = seprint(s, se, " hub %d", ep->dev->hubnb);
+	s = seprint(s, se, " hub %d", ep->dev->hub? ep->dev->hub->nb: 0);
 	s = seprint(s, se, " port %d", ep->dev->port);
 	s = seprint(s, se, " rootport %d", ep->dev->rootport);
 	s = seprint(s, se, " addr %d", ep->dev->addr);
@@ -409,33 +409,38 @@
 putep(Ep *ep)
 {
 	Udev *d;
+	Ep *next;
 
-	if(ep == nil || decref(ep) > 0)
-		return;
-	d = ep->dev;
-	deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep);
-	qlock(ep->ep0);
-	qlock(&epslck);
-	assert(d->eps[ep->nb] == ep);
-	d->eps[ep->nb] = nil;
-	assert(eps[ep->idx] == ep);
-	eps[ep->idx] = nil;
-	if(ep->idx == epmax-1){
-		while(epmax > 0 && eps[epmax-1] == nil)
-			epmax--;
+	for(; ep != nil; ep = next){
+		if(decref(ep) > 0)
+			return;
+		assert(ep->inuse == 0);
+		d = ep->dev;
+		deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep);
+		qlock(ep->ep0);
+		qlock(&epslck);
+		assert(d->eps[ep->nb] == ep);
+		d->eps[ep->nb] = nil;
+		assert(eps[ep->idx] == ep);
+		eps[ep->idx] = nil;
+		if(ep->idx == epmax-1){
+			while(epmax > 0 && eps[epmax-1] == nil)
+				epmax--;
+		}
+		qunlock(&epslck);
+		qunlock(ep->ep0);
+		if(ep->ep0 != ep)
+			next = ep->ep0;
+		else {
+			next = d->hub != nil? d->hub->eps[0]: nil;
+			if(ep->hp->devclose != nil)
+				(*ep->hp->devclose)(d);
+			free(d);
+		}
+		free(ep->info);
+		free(ep->name);
+		free(ep);
 	}
-	qunlock(&epslck);
-	qunlock(ep->ep0);
-	if(ep->ep0 != ep)
-		putep(ep->ep0);
-	else {
-		if(d->free != nil)
-			(*d->free)(d->aux);
-		free(d);
-	}
-	free(ep->info);
-	free(ep->name);
-	free(ep);
 }
 
 static int
@@ -497,22 +502,22 @@
 
 	d->nports = 0;
 	d->rootport = d->routestr = 0;
-	d->tthub = d->ttport = d->ttt = d->mtt = 0;
+	d->ttport = d->ttt = d->mtt = 0;
+	d->tthub = nil;
 
 	if(hub != nil){
-		d->hub = hub->dev->addr;
-		d->hubnb = hub->dev->nb;
-		d->depth = hub->dev->depth+1;
+		d->hub = hub->dev;
+		d->depth = d->hub->depth+1;
 		if(d->depth > 0){
 			assert(d->depth <= 5);
-			d->routestr = hub->dev->routestr | (d->port<15? d->port: 15) << 4*(d->depth-1);
+			d->routestr = d->hub->routestr | (d->port<15? d->port: 15) << 4*(d->depth-1);
 			if(speed < Highspeed){
-				if(hub->dev->speed == Highspeed){
-					d->tthub = hub->dev->addr;
+				if(d->hub->speed == Highspeed){
+					d->tthub = d->hub;
 					d->ttport = port;
 				}else {
-					d->tthub = hub->dev->tthub;
-					d->ttport = hub->dev->ttport;
+					d->tthub = d->hub->tthub;
+					d->ttport = d->hub->ttport;
 				}
 			}
 		}
@@ -520,8 +525,7 @@
 		if(d->rootport == 0)
 			error(Ebadport);
 	} else {
-		d->hub = 0;
-		d->hubnb = 0;
+		d->hub = nil;
 		d->depth = -1;
 	}
 
@@ -548,8 +552,9 @@
 	ep->ep0 = ep;			/* no ref counted here */
 	ep->dev = d;
 	d->eps[0] = ep;
+	if(hub != nil)
+		incref(hub);
 	incref(ep);
-
 	qunlock(&epslck);
 	poperror();
 	poperror();
@@ -1049,18 +1054,21 @@
 		return;
 
 	deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref);
-
 	qlock(ep);
 	if(!ep->inuse)
 		qunlock(ep);
 	else {
 		if(!waserror()){
-			(*ep->hp->epclose)(ep);
+			if(ep->hp->epstop != nil)
+				(*ep->hp->epstop)(ep);
+			if(ep->hp->epclose != nil)
+				(*ep->hp->epclose)(ep);
 			poperror();
 		}
 		ep->inuse = 0;
+		ep->aux = nil;
 		qunlock(ep);
-		putep(ep);	/* release ref kept since usbopen */
+		putep(ep);	/* release ref from usbopen() */
 	}
 	putep(ep);		/* release ref of getep() above */
 }
@@ -1219,7 +1227,7 @@
 		ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",q,c->fid,n,offset);
 	Again:
 		nr = (*ep->hp->epread)(ep, a, n);
-		if(nr == 0 && ep->ttype == Tiso){
+		if(nr == 0 && ep->ttype == Tiso && ep->dev->state != Ddetach){
 			tsleep(&up->sleep, return0, nil, 2*ep->pollival);
 			goto Again;
 		}
@@ -1315,9 +1323,18 @@
 		d->state = Ddetach;
 		qunlock(ep);
 		poperror();
-		/* Release file system ref. for its endpoints */
-		for(i = 0; i < nelem(d->eps); i++)
-			putep(d->eps[i]);
+		for(i = 0; i < nelem(d->eps); i++){
+			ep = d->eps[i];
+			if(ep != nil){
+				qlock(ep);
+				if(ep->inuse && ep->hp->epstop != nil && !waserror()){
+					(*ep->hp->epstop)(ep);
+					poperror();
+				}
+				qunlock(ep);
+				putep(ep);
+			}
+		}
 		goto Unlocked;
 	case CMpreset:
 		if(d->state != Denabled)
--- a/sys/src/9/port/usb.h
+++ b/sys/src/9/port/usb.h
@@ -90,7 +90,8 @@
  * Services provided by the driver.
  * epopen allocates hardware structures to prepare the endpoint
  * for I/O. This happens when the user opens the data file.
- * epclose releases them. This happens when the data file is closed.
+ * epstop canceles in-flight I/O and epclose releases them.
+ * This happens when the data file is closed.
  * epwrite tries to write the given bytes, waiting until all of them
  * have been written (or failed) before returning; but not for Iso.
  * epread does the same for reading.
@@ -102,7 +103,7 @@
  * hubs. Port status must return bits as a hub request would do.
  * Toggle handling and other details are left for the controller driver
  * to avoid mixing too much the controller and the comon device.
- * While an endpoint is closed, its toggles are saved in the Ep struct.
+ * While an endpoint is stopped, its toggles are saved in the Ep struct.
  */
 struct Hciimpl
 {
@@ -110,10 +111,12 @@
 	void	(*init)(Hci*);			/* init. controller */
 	void	(*interrupt)(Ureg*, void*);	/* service interrupt */
 	void	(*epopen)(Ep*);			/* prepare ep. for I/O */
-	void	(*epclose)(Ep*);		/* terminate I/O on ep. */
+	void	(*epstop)(Ep*);			/* cancel I/O on ep. */
+	void	(*epclose)(Ep*);		/* release I/O on ep. (after epstop) */
 	long	(*epread)(Ep*,void*,long);	/* transmit data for ep */
 	long	(*epwrite)(Ep*,void*,long);	/* receive data for ep */
 	char*	(*seprintep)(char*,char*,Ep*);	/* debug */
+	void	(*devclose)(Udev*);		/* release the device */
 	int	(*portenable)(Hci*, int, int);	/* enable/disable port */
 	int	(*portreset)(Hci*, int, int);	/* set/clear port reset */
 	int	(*portstatus)(Hci*, int);	/* get port status */
@@ -185,8 +188,6 @@
 	int	state;		/* state for the device */
 	int	nports;		/* number of downstream ports for hub */
 	int	speed;		/* Full/Low/High/Super -speed */
-	int	hubnb;		/* USB device number for the parent hub */
-	int	hub;		/* device address of parent hub */
 	int	port;		/* port number on parent hub */
 	int	addr;		/* device address */
 	int	depth;		/* hub depth from root port -1 */
@@ -193,15 +194,16 @@
 	int	rootport;	/* port number on root hub */
 	int	routestr;	/* route string */
 
-	int	tthub;		/* device address of TT HS hub */
+	Udev	*hub;		/* parent hub; keeping ref to hub->eps[0] */
+
+	Udev	*tthub;		/* the TT HS hub */
 	int	ttport;		/* port number on TT HS hub */
 	int	ttt;		/* TT Think-Time for HS hub */
 	int	mtt;		/* Multi TT enabled for HS hub */
 
 	void	*aux;
-	void	(*free)(void*);
 
-	Ep*	eps[Ndeveps];	/* end points for this device (cached) */
+	Ep	*eps[Ndeveps];	/* end points for this device (cached) */
 };
 
 void	addhcitype(char *type, int (*reset)(Hci*));
--- a/sys/src/9/port/usbehci.c
+++ b/sys/src/9/port/usbehci.c
@@ -682,8 +682,10 @@
 		coherence();
 		qhsetaddr(qh, io->usbid);
 		qh->eps1 = (ep->ntds & Qhmultmask) << Qhmultshift;
-		qh->eps1 |= ep->dev->ttport << Qhportshift;
-		qh->eps1 |= ep->dev->tthub << Qhhubshift;
+		if(ep->dev->tthub != nil){
+			qh->eps1 |= ep->dev->tthub->addr << Qhhubshift;
+			qh->eps1 |= ep->dev->ttport << Qhportshift;
+		}
 		qh->eps1 |= 034 << Qhscmshift;
 		if(ep->ttype == Tintr)
 			qh->eps1 |= 1 << Qhismshift;	/* intr. start µf. */
@@ -2725,10 +2727,11 @@
 	for(i = 0; i < iso->nframes; i++){
 		td = sitdalloc(ctlr);
 		td->data = iso->data + i * ep->maxpkt;
-		td->epc = ep->dev->ttport << Stdportshift;
-		td->epc |= ep->dev->tthub << Stdhubshift;
-		td->epc |= (ep->nb&Epmax) << Stdepshift;
-		td->epc |= ep->dev->addr << Stddevshift;
+		td->epc = (ep->nb&Epmax) << Stdepshift | ep->dev->addr << Stddevshift;
+		if(ep->dev->tthub != nil){
+			td->epc |= ep->dev->tthub->addr << Stdhubshift;
+			td->epc |= ep->dev->ttport << Stdportshift;
+		}
 		td->mfs = 034 << Stdscmshift | 1 << Stdssmshift;
 		if(ep->mode == OREAD){
 			td->epc |= Stdin;
@@ -3095,7 +3098,7 @@
 }
 
 static void
-epclose(Ep *ep)
+epstop(Ep *ep)
 {
 	Qio *io;
 	Ctlio *cio;
@@ -3103,16 +3106,15 @@
 	Ctlr *ctlr;
 
 	ctlr = ep->hp->aux;
-	deprint("ehci: epclose ep%d.%d\n", ep->dev->nb, ep->nb);
+	deprint("ehci: epstop ep%d.%d\n", ep->dev->nb, ep->nb);
 
 	if(ep->aux == nil)
-		panic("ehci: epclose called with closed ep");
+		panic("ehci: epstop called with closed ep");
+
 	switch(ep->ttype){
 	case Tctl:
 		cio = ep->aux;
 		cancelio(ctlr, cio);
-		free(cio->data);
-		cio->data = nil;
 		break;
 	case Tintr:
 	case Tbulk:
@@ -3128,7 +3130,6 @@
 			if(io[OWRITE].toggle == Tddata1)
 				ep->toggle[OWRITE] = 1;
 		}
-		coherence();
 		break;
 	case Tiso:
 		iso = ep->aux;
@@ -3135,6 +3136,31 @@
 		cancelisoio(ctlr, iso, ep->load);
 		break;
 	default:
+		panic("epstop: bad ttype");
+	}
+}
+
+static void
+epclose(Ep *ep)
+{
+	Ctlio *cio;
+
+	deprint("ehci: epclose ep%d.%d\n", ep->dev->nb, ep->nb);
+
+	if(ep->aux == nil)
+		panic("ehci: epclose called with closed ep");
+
+	switch(ep->ttype){
+	case Tctl:
+		cio = ep->aux;
+		free(cio->data);
+		cio->data = nil;
+		break;
+	case Tintr:
+	case Tbulk:
+	case Tiso:
+		break;
+	default:
 		panic("epclose: bad ttype");
 	}
 	free(ep->aux);
@@ -3292,6 +3318,7 @@
 	hp->init = init;
 	hp->interrupt = interrupt;
 	hp->epopen = epopen;
+	hp->epstop = epstop;
 	hp->epclose = epclose;
 	hp->epread = epread;
 	hp->epwrite = epwrite;
--- a/sys/src/9/port/usbxhci.c
+++ b/sys/src/9/port/usbxhci.c
@@ -928,13 +928,10 @@
 }
 
 static void
-freeslot(void *arg)
+freeslot(Slot *slot)
 {
-	Slot *slot;
-
-	if(arg == nil)
+	if(slot == nil)
 		return;
-	slot = arg;
 	if(slot->id > 0){
 		Ctlr *ctlr = slot->ctlr;
 		qlock(&ctlr->slotlock);
@@ -1026,7 +1023,6 @@
 {
 	Udev *dev = slot->dev;
 	u32int *w;
-	int i;
 
 	/* (input) control context */
 	w = slot->ibase;
@@ -1044,26 +1040,12 @@
 		w[2] |= dev->ttt<<16;
 	}
 
-	if(dev->speed < Highspeed && dev->tthub != 0 && dev->ttport != 0){
-		qlock(&ctlr->slotlock);
-		for(i=1; i<=ctlr->nslots; i++){
-			Slot *hub = ctlr->slot[i];
-
-			if(hub == nil || hub->dev == nil || hub->dev->aux != hub)
-				continue;
-			if(hub == slot || hub->dev == dev)
-				continue;
-
-			if(hub->dev->addr != dev->tthub)
-				continue;
-			if(hub->dev->rootport != dev->rootport)
-				continue;
-
+	if(dev->tthub != nil){
+		Slot *hub = dev->tthub->aux;
+		if(hub != nil && hub->dev == dev->tthub){
 			w[0] |= hub->dev->mtt<<25;
 			w[2] |= hub->id | dev->ttport<<8;
-			break;
 		}
-		qunlock(&ctlr->slotlock);
 	}
 
 	return slot->ibase;
@@ -1070,6 +1052,34 @@
 }
 
 static void
+epstop(Ep *ep)
+{
+	Ctlr *ctlr;
+	Slot *slot;
+	Ring *ring;
+	Epio *io;
+
+	if(ep->nb == 0 || ep->dev->depth < 0)
+		return;
+
+	io = ep->aux;
+	if(io == nil)
+		return;
+
+	ctlr = ep->hp->aux;
+	slot = ep->dev->aux;
+
+	if((ring = io[OREAD].ring) != nil && ring->stopped == 0){
+		ctlrcmd(ctlr, CR_STOPEP | (ring->id<<16) | (slot->id<<24), 0, 0, nil);
+		ring->stopped = 1;
+	}
+	if((ring = io[OWRITE].ring) != nil && ring->stopped == 0){
+		ctlrcmd(ctlr, CR_STOPEP | (ring->id<<16) | (slot->id<<24), 0, 0, nil);
+		ring->stopped = 1;
+	}
+}
+
+static void
 epclose(Ep *ep)
 {
 	Ctlr *ctlr;
@@ -1097,13 +1107,11 @@
 			w[0] |= 1 << ring->id;
 			if(ring->id == slot->nep)
 				slot->nep--;
-			ctlrcmd(ctlr, CR_STOPEP | (ring->id<<16) | (slot->id<<24), 0, 0, nil);
 		}
 		if((ring = io[OWRITE].ring) != nil){
 			w[0] |= 1 << ring->id;
 			if(ring->id == slot->nep)
 				slot->nep--;
-			ctlrcmd(ctlr, CR_STOPEP | (ring->id<<16) | (slot->id<<24), 0, 0, nil);
 		}
 
 		/* find largest index still in use */ 
@@ -1272,6 +1280,12 @@
 }
 
 static void
+devclose(Udev *dev)
+{
+	freeslot(dev->aux);
+}
+
+static void
 epopen(Ep *ep)
 {
 	Ctlr *ctlr = ep->hp->aux;
@@ -1338,10 +1352,16 @@
 	/* (output) slot context */
 	w = slot->obase;
 
+	if(((w[3] >> 27) & 0x1F) < 2)
+		error("xhci did not set device address");
+
 	dev->addr = w[3] & 0xFF;
+	if(dev->addr == 0 || dev->addr > Devmax){
+		dev->addr = 0;
+		error("xhci returned invalid device address");
+	}
 
 	dev->aux = slot;
-	dev->free = freeslot;
 
 	poperror();
 	poperror();
@@ -1823,10 +1843,12 @@
 
 	hp->interrupt = interrupt;
 	hp->epopen = epopen;
+	hp->epstop = epstop;
 	hp->epclose = epclose;
 	hp->epread = epread;
 	hp->epwrite = epwrite;
 	hp->seprintep = seprintep;
+	hp->devclose = devclose;
 	hp->portenable = portenable;
 	hp->portreset = portreset;
 	hp->portstatus = portstatus;
--