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.
This commit is contained in:
cinap_lenrek 2024-11-03 12:11:45 +00:00
parent acf707cbbc
commit ebf3e9067d
7 changed files with 219 additions and 108 deletions

View file

@ -180,8 +180,8 @@ chansetup(Hostchan *hc, Ep *ep)
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 @@ ctltrans(Ep *ep, uchar *req, long n)
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 @@ ctltrans(Ep *ep, uchar *req, long n)
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

View file

@ -2147,21 +2147,19 @@ cancelio(Ep *ep, Qio *io)
}
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:
@ -2181,6 +2179,29 @@ epclose(Ep *ep)
iso = ep->aux;
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 @@ reset(Hci *hp)
hp->init = init;
hp->interrupt = interrupt;
hp->epopen = epopen;
hp->epstop = epstop;
hp->epclose = epclose;
hp->epread = epread;
hp->epwrite = epwrite;

View file

@ -1909,7 +1909,7 @@ cancelisoio(Ctlr *ctlr, Isoio *iso, int pollival, ulong load)
}
static void
epclose(Ep *ep)
epstop(Ep *ep)
{
Ctlr *ctlr;
Ctlio *cio;
@ -1917,16 +1917,15 @@ epclose(Ep *ep)
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:
@ -1947,13 +1946,36 @@ epclose(Ep *ep)
iso = ep->aux;
cancelisoio(ctlr, iso, ep->pollival, ep->load);
break;
default:
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 @@ reset(Hci *hp)
hp->init = init;
hp->interrupt = interrupt;
hp->epopen = epopen;
hp->epstop = epstop;
hp->epclose = epclose;
hp->epread = epread;
hp->epwrite = epwrite;

View file

@ -326,7 +326,7 @@ seprintep(char *s, char *se, Ep *ep, int all)
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 @@ static void
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 @@ newdev(Hci *hp, Ep *hub, int port, int speed)
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 @@ newdev(Hci *hp, Ep *hub, int port, int speed)
if(d->rootport == 0)
error(Ebadport);
} else {
d->hub = 0;
d->hubnb = 0;
d->hub = nil;
d->depth = -1;
}
@ -548,8 +552,9 @@ newdev(Hci *hp, Ep *hub, int port, int speed)
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 @@ usbclose(Chan *c)
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 @@ usbread(Chan *c, void *a, long n, vlong offset)
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 @@ epctl(Ep *ep, Chan *c, void *a, long n)
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)

View file

@ -90,7 +90,8 @@ enum
* 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 @@ enum
* 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 @@ struct Hciimpl
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,23 +188,22 @@ struct Udev
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 */
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*));

View file

@ -682,8 +682,10 @@ qhalloc(Ctlr *ctlr, Ep *ep, Qio *io, char* tag)
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 @@ isofsinit(Ep *ep, Isoio *iso)
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 @@ cancelisoio(Ctlr *ctlr, Isoio *iso, ulong load)
}
static void
epclose(Ep *ep)
epstop(Ep *ep)
{
Qio *io;
Ctlio *cio;
@ -3103,16 +3106,15 @@ epclose(Ep *ep)
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,12 +3130,36 @@ epclose(Ep *ep)
if(io[OWRITE].toggle == Tddata1)
ep->toggle[OWRITE] = 1;
}
coherence();
break;
case Tiso:
iso = ep->aux;
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");
}
@ -3292,6 +3318,7 @@ ehcilinkage(Hci *hp)
hp->init = init;
hp->interrupt = interrupt;
hp->epopen = epopen;
hp->epstop = epstop;
hp->epclose = epclose;
hp->epread = epread;
hp->epwrite = epwrite;

View file

@ -928,13 +928,10 @@ interrupt(Ureg*, void *arg)
}
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 @@ initdevctx(Ctlr *ctlr, Slot *slot)
{
Udev *dev = slot->dev;
u32int *w;
int i;
/* (input) control context */
w = slot->ibase;
@ -1044,31 +1040,45 @@ initdevctx(Ctlr *ctlr, Slot *slot)
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;
}
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)
{
@ -1097,13 +1107,11 @@ epclose(Ep *ep)
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 */
@ -1271,6 +1279,12 @@ initep(Ep *ep)
poperror();
}
static void
devclose(Udev *dev)
{
freeslot(dev->aux);
}
static void
epopen(Ep *ep)
{
@ -1338,10 +1352,16 @@ epopen(Ep *ep)
/* (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 @@ xhcilinkage(Hci *hp, Xhci *ctlr)
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;