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