usbxhci: implement command timeouts and aborts, serialize unstall

This commit is contained in:
cinap_lenrek 2017-07-20 19:57:14 +02:00
parent 40dc39bf7d
commit 4fd68773e2

View file

@ -165,7 +165,6 @@ struct Ring
u32int *base; u32int *base;
u32int size;
u32int mask; u32int mask;
u32int shift; u32int shift;
@ -234,7 +233,7 @@ struct Epio
{ {
QLock; QLock;
Block *cb; Block *cb;
Ring *ring[2]; /* OREAD, OWRITE */ Ring *ring;
}; };
static char Ebadlen[] = "bad usb request length"; static char Ebadlen[] = "bad usb request length";
@ -263,7 +262,7 @@ freering(Ring *r)
memset(r, 0, sizeof(*r)); memset(r, 0, sizeof(*r));
} }
static void static Ring*
initring(Ring *r, int shift) initring(Ring *r, int shift)
{ {
r->id = 0; r->id = 0;
@ -272,14 +271,14 @@ initring(Ring *r, int shift)
r->doorbell = nil; r->doorbell = nil;
r->pending = nil; r->pending = nil;
r->shift = shift; r->shift = shift;
r->size = 1<<shift; r->mask = (1<<shift)-1;
r->mask = r->size-1;
r->rp = r->wp = 0; r->rp = r->wp = 0;
r->base = mallocalign(r->size*16, 64, 0, 64*1024); r->base = mallocalign(4*4<<shift, 64, 0, 64*1024);
if(r->base == nil){ if(r->base == nil){
freering(r); freering(r);
error(Enomem); error(Enomem);
} }
return r;
} }
static void static void
@ -341,8 +340,8 @@ init(Hci *hp)
ctlr->setrptr(&ctlr->opr[DCBAAP], PADDR(ctlr->dcba)); ctlr->setrptr(&ctlr->opr[DCBAAP], PADDR(ctlr->dcba));
initring(ctlr->cr, 8); /* 256 entries */ initring(ctlr->cr, 8); /* 256 entries */
ctlr->cr->doorbell = &ctlr->dba[0];
ctlr->cr->id = 0; ctlr->cr->id = 0;
ctlr->cr->doorbell = &ctlr->dba[0];
coherence(); coherence();
ctlr->setrptr(&ctlr->opr[CRCR], PADDR(ctlr->cr[0].base) | 1); ctlr->setrptr(&ctlr->opr[CRCR], PADDR(ctlr->cr[0].base) | 1);
@ -358,7 +357,7 @@ init(Hci *hp)
initring(&ctlr->er[i], 8); /* 256 entries */ initring(&ctlr->er[i], 8); /* 256 entries */
ctlr->erst[i] = mallocalign(16, 64, 0, 0); ctlr->erst[i] = mallocalign(16, 64, 0, 0);
*((u64int*)ctlr->erst[i]) = PADDR(ctlr->er[i].base); *((u64int*)ctlr->erst[i]) = PADDR(ctlr->er[i].base);
ctlr->erst[i][2] = ctlr->er[i].size; ctlr->erst[i][2] = ctlr->er[i].mask+1;
ctlr->erst[i][3] = 0; ctlr->erst[i][3] = 0;
irs[ERSTSZ] = 1; /* just one segment */ irs[ERSTSZ] = 1; /* just one segment */
@ -464,23 +463,34 @@ waitdone(void *a)
return ((Wait*)a)->z == nil; return ((Wait*)a)->z == nil;
} }
static void static char*
kickring(Ring *r) ctlrcmd(Ctlr *ctlr, u32int c, u32int s, u64int p, u32int *er);
{
coherence();
*r->doorbell = r->id;
}
static char* static char*
waittd(Wait *w, u32int *er) waittd(Ctlr *ctlr, Wait *w, int tmout, u32int *er)
{ {
while(!waitdone(w)){ Ring *ring = w->ring;
while(waserror())
; coherence();
kickring(w->ring); *ring->doorbell = ring->id;
sleep(&up->sleep, waitdone, w);
poperror(); while(waserror()){
if(ring == ctlr->cr)
ctlr->opr[CRCR] |= CA;
else
ctlrcmd(ctlr, CR_STOPEP | (ring->id<<16) | (ring->slot->id<<24), 0, 0, nil);
tmout = 0;
} }
if(tmout > 0){
tsleep(&up->sleep, waitdone, w, tmout);
if(!waitdone(w))
error("timed out");
} else {
while(!waitdone(w))
sleep(&up->sleep, waitdone, w);
}
poperror();
if(er != nil) if(er != nil)
memmove(er, w->er, 4*4); memmove(er, w->er, 4*4);
return ccerrstr(w->er[2]>>24); return ccerrstr(w->er[2]>>24);
@ -489,12 +499,13 @@ waittd(Wait *w, u32int *er)
static char* static char*
ctlrcmd(Ctlr *ctlr, u32int c, u32int s, u64int p, u32int *er) ctlrcmd(Ctlr *ctlr, u32int c, u32int s, u64int p, u32int *er)
{ {
Wait w[1]; Wait ws[1];
ilock(ctlr->cr); ilock(ctlr->cr);
queuetd(ctlr->cr, c, s, p, w); queuetd(ctlr->cr, c, s, p, ws);
iunlock(ctlr->cr); iunlock(ctlr->cr);
return waittd(w, er);
return waittd(ctlr, ws, 5000, er);
} }
static void static void
@ -569,10 +580,12 @@ interrupt(Ureg*, void *arg)
break; break;
completering(&slot->epr[(td[3]>>16)-1&31], td); completering(&slot->epr[(td[3]>>16)-1&31], td);
break; break;
case ER_HCE:
iprint("xhci: host controller error: %ux %ux %ux %ux\n", td[0], td[1], td[2], td[3]);
break;
case ER_PORTSC: case ER_PORTSC:
case ER_BWREQ: case ER_BWREQ:
case ER_DOORBELL: case ER_DOORBELL:
case ER_HCE:
case ER_DEVNOTE: case ER_DEVNOTE:
case ER_MFINDEXWRAP: case ER_MFINDEXWRAP:
default: default:
@ -679,21 +692,21 @@ epclose(Ep *ep)
ctlr = ep->hp->aux; ctlr = ep->hp->aux;
slot = ep->dev->aux; slot = ep->dev->aux;
if(ep->nb > 0 && (io->ring[OREAD] != nil || io->ring[OWRITE] != nil)){ if(ep->nb > 0 && (io[OREAD].ring != nil || io[OWRITE].ring != nil)){
u32int *w; u32int *w;
/* input control context */ /* input control context */
w = slot->ibase; w = slot->ibase;
memset(w, 0, 32<<ctlr->csz); memset(w, 0, 32<<ctlr->csz);
w[1] = 1; w[1] = 1;
if(io->ring[OREAD] != nil){ if(io[OREAD].ring != nil){
w[0] |= 2 << ep->nb*2; w[0] |= 1 << io[OREAD].ring->id;
if(ep->nb*2+1 == slot->nep) if(io[OREAD].ring->id == slot->nep)
slot->nep--; slot->nep--;
} }
if(io->ring[OWRITE] != nil){ if(io[OWRITE].ring != nil){
w[0] |= 1 << ep->nb*2; w[0] |= 1 << io[OWRITE].ring->id;
if(ep->nb*2 == slot->nep) if(io[OWRITE].ring->id == slot->nep)
slot->nep--; slot->nep--;
} }
@ -707,10 +720,10 @@ epclose(Ep *ep)
ctlrcmd(ctlr, CR_CONFIGEP | (slot->id<<24), 0, PADDR(slot->ibase), nil); ctlrcmd(ctlr, CR_CONFIGEP | (slot->id<<24), 0, PADDR(slot->ibase), nil);
freering(io->ring[OREAD]); freering(io[OREAD].ring);
freering(io->ring[OWRITE]); freering(io[OWRITE].ring);
} }
freeb(io->cb); freeb(io[OREAD].cb);
free(io); free(io);
} }
@ -720,6 +733,7 @@ initep(Ep *ep)
Epio *io; Epio *io;
Ctlr *ctlr; Ctlr *ctlr;
Slot *slot; Slot *slot;
Ring *ring;
u32int *w; u32int *w;
int ival; int ival;
char *err; char *err;
@ -728,9 +742,9 @@ initep(Ep *ep)
slot = ep->dev->aux; slot = ep->dev->aux;
ctlr = ep->hp->aux; ctlr = ep->hp->aux;
io->ring[OREAD] = io->ring[OWRITE] = nil; io[OREAD].ring = io[OWRITE].ring = nil;
if(ep->nb == 0){ if(ep->nb == 0){
io->ring[OWRITE] = &slot->epr[0]; io[OWRITE].ring = &slot->epr[0];
return; return;
} }
@ -740,31 +754,29 @@ initep(Ep *ep)
w[1] = 1; w[1] = 1;
if(waserror()){ if(waserror()){
freering(io->ring[OWRITE]), io->ring[OWRITE] = nil; freering(io[OWRITE].ring), io[OWRITE].ring = nil;
freering(io->ring[OREAD]), io->ring[OREAD] = nil; freering(io[OREAD].ring), io[OREAD].ring = nil;
nexterror(); nexterror();
} }
if(ep->mode != OREAD){ if(ep->mode != OREAD){
initring(io->ring[OWRITE] = &slot->epr[ep->nb*2-1], 8); ring = initring(io[OWRITE].ring = &slot->epr[ep->nb*2-1], 8);
io->ring[OWRITE]->doorbell = &ctlr->dba[slot->id]; ring->id = ep->nb*2;
io->ring[OWRITE]->slot = slot; if(ring->id > slot->nep)
io->ring[OWRITE]->ctx = &slot->obase[(ep->nb*2+0)*8<<ctlr->csz]; slot->nep = ring->id;
io->ring[OWRITE]->id = ep->nb*2; ring->slot = slot;
ring->doorbell = &ctlr->dba[slot->id];
w[1] |= 1 << ep->nb*2; ring->ctx = &slot->obase[ring->id*8<<ctlr->csz];
if(ep->nb*2 > slot->nep) w[1] |= 1 << ring->id;
slot->nep = ep->nb*2;
} }
if(ep->mode != OWRITE){ if(ep->mode != OWRITE){
initring(io->ring[OREAD] = &slot->epr[ep->nb*2], 8); ring = initring(io[OREAD].ring = &slot->epr[ep->nb*2], 8);
io->ring[OREAD]->doorbell = &ctlr->dba[slot->id]; ring->id = ep->nb*2+1;
io->ring[OREAD]->slot = slot; if(ring->id > slot->nep)
io->ring[OREAD]->ctx = &slot->obase[(ep->nb*2+1)*8<<ctlr->csz]; slot->nep = ring->id;
io->ring[OREAD]->id = ep->nb*2+1; ring->slot = slot;
ring->doorbell = &ctlr->dba[slot->id];
w[1] |= 2 << ep->nb*2; ring->ctx = &slot->obase[ring->id*8<<ctlr->csz];
if(ep->nb*2+1 > slot->nep) w[1] |= 1 << ring->id;
slot->nep = ep->nb*2+1;
} }
/* (input) slot context */ /* (input) slot context */
@ -784,19 +796,19 @@ initep(Ep *ep)
} }
/* out */ /* out */
if(io->ring[OWRITE] != nil){ if(io[OWRITE].ring != nil){
w[0] = ival<<16; w[0] = ival<<16;
w[1] = 3<<1 | ((ep->ttype - Tctl)|0)<<3 | ep->maxpkt<<16; w[1] = 3<<1 | ((ep->ttype - Tctl)|0)<<3 | ep->maxpkt<<16;
*((u64int*)&w[2]) = PADDR(io->ring[OWRITE]->base) | 1; *((u64int*)&w[2]) = PADDR(io[OWRITE].ring->base) | 1;
w[4] = 1; w[4] = 1;
} }
/* in */ /* in */
w += 8<<ctlr->csz; w += 8<<ctlr->csz;
if(io->ring[OREAD] != nil){ if(io[OREAD].ring != nil){
w[0] = ival<<16; w[0] = ival<<16;
w[1] = 3<<1 | ((ep->ttype - Tctl)|4)<<3 | ep->maxpkt<<16; w[1] = 3<<1 | ((ep->ttype - Tctl)|4)<<3 | ep->maxpkt<<16;
*((u64int*)&w[2]) = PADDR(io->ring[OREAD]->base) | 1; *((u64int*)&w[2]) = PADDR(io[OREAD].ring->base) | 1;
w[4] = 1; w[4] = 1;
} }
@ -824,6 +836,7 @@ epopen(Ep *ep)
{ {
Ctlr *ctlr = ep->hp->aux; Ctlr *ctlr = ep->hp->aux;
Slot *slot, *hub; Slot *slot, *hub;
Ring *ring;
Epio *io; Epio *io;
Udev *dev; Udev *dev;
char *err; char *err;
@ -833,7 +846,7 @@ epopen(Ep *ep)
if(ep->dev->isroot) if(ep->dev->isroot)
return; return;
io = malloc(sizeof(Epio)); io = malloc(sizeof(Epio)*2);
if(io == nil) if(io == nil)
error(Enomem); error(Enomem);
ep->aux = io; ep->aux = io;
@ -856,12 +869,12 @@ epopen(Ep *ep)
slot = allocslot(ctlr, dev); slot = allocslot(ctlr, dev);
/* allocate control ep 0 ring */ /* allocate control ep 0 ring */
initring(io->ring[OWRITE] = &slot->epr[0], 8); ring = initring(io[OWRITE].ring = &slot->epr[0], 4);
io->ring[OWRITE]->doorbell = &ctlr->dba[slot->id]; ring->id = 1;
io->ring[OWRITE]->slot = slot;
io->ring[OWRITE]->ctx = &slot->obase[8];
io->ring[OWRITE]->id = 1;
slot->nep = 1; slot->nep = 1;
ring->slot = slot;
ring->doorbell = &ctlr->dba[slot->id];
ring->ctx = &slot->obase[8];
/* (input) control context */ /* (input) control context */
w = slot->ibase; w = slot->ibase;
@ -901,7 +914,7 @@ epopen(Ep *ep)
/* (input) ep context 0 */ /* (input) ep context 0 */
w += 8<<ctlr->csz; w += 8<<ctlr->csz;
w[1] = 3<<1 | 4<<3 | ep->maxpkt<<16; w[1] = 3<<1 | 4<<3 | ep->maxpkt<<16;
*((u64int*)&w[2]) = PADDR(io->ring[OWRITE]->base) | 1; *((u64int*)&w[2]) = PADDR(io[OWRITE].ring->base) | 1;
if((err = ctlrcmd(ctlr, CR_ADDRESSDEV | (slot->id<<24), 0, if((err = ctlrcmd(ctlr, CR_ADDRESSDEV | (slot->id<<24), 0,
PADDR(slot->ibase), nil)) != nil){ PADDR(slot->ibase), nil)) != nil){
@ -914,22 +927,34 @@ epopen(Ep *ep)
poperror(); poperror();
} }
static void static char*
unstall(Ring *r) unstall(Ring *r)
{ {
u64int qp; u64int qp;
char *err;
switch(r->ctx[0]&7){ switch(r->ctx[0]&7){
case 2: case 0: /* disabled */
case 4: case 1: /* running */
case 3: /* stopped */
break;
case 2: /* halted */
case 4: /* error */
ilock(r); ilock(r);
r->rp = r->wp; r->rp = r->wp;
qp = PADDR(&r->base[4*(r->wp & r->mask)]) | ((~r->wp>>r->shift) & 1); qp = PADDR(&r->base[4*(r->wp & r->mask)]) | ((~r->wp>>r->shift) & 1);
iunlock(r); iunlock(r);
ctlrcmd(r->slot->ctlr, CR_RESETEP | (r->id<<16) | (r->slot->id<<24), 0, 0, nil); err = ctlrcmd(r->slot->ctlr, CR_RESETEP | (r->id<<16) | (r->slot->id<<24), 0, 0, nil);
ctlrcmd(r->slot->ctlr, CR_SETTRDQP | (r->id<<16) | (r->slot->id<<24), 0, qp, nil); ctlrcmd(r->slot->ctlr, CR_SETTRDQP | (r->id<<16) | (r->slot->id<<24), 0, qp, nil);
if(err != nil)
return err;
} }
if(r->wp - r->rp >= r->mask)
return "Ring Full";
return nil;
} }
static long static long
@ -937,12 +962,11 @@ epread(Ep *ep, void *va, long n)
{ {
Epio *io; Epio *io;
uchar *p; uchar *p;
Ring *ring;
char *err; char *err;
Wait ws[1]; Wait ws[1];
p = va; p = va;
io = ep->aux; io = (Epio*)ep->aux + OREAD;
if(ep->ttype == Tctl){ if(ep->ttype == Tctl){
qlock(io); qlock(io);
@ -973,13 +997,17 @@ epread(Ep *ep, void *va, long n)
return n; return n;
} }
ring = io->ring[OREAD]; qlock(io);
unstall(ring); if((err = unstall(io->ring)) != nil){
ilock(ring); qunlock(io);
queuetd(ring, TR_NORMAL | 1<<16 | 1<<5, n, PADDR(p), ws); error(err);
iunlock(ring); }
ilock(io->ring);
queuetd(io->ring, TR_NORMAL | 1<<16 | 1<<5, n, PADDR(p), ws);
iunlock(io->ring);
qunlock(io);
if((err = waittd(ws, nil)) != nil) if((err = waittd((Ctlr*)ep->hp->aux, ws, ep->tmout, nil)) != nil)
error(err); error(err);
n -= (ws->er[2] & 0xFFFFFF); n -= (ws->er[2] & 0xFFFFFF);
@ -993,16 +1021,14 @@ static long
epwrite(Ep *ep, void *va, long n) epwrite(Ep *ep, void *va, long n)
{ {
Wait ws[3]; Wait ws[3];
Ring *ring;
Epio *io; Epio *io;
uchar *p; uchar *p;
char *err; char *err;
p = va; p = va;
io = ep->aux;
if(ep->ttype == Tctl){ if(ep->ttype == Tctl){
int dir, len; int dir, len;
Ring *ring;
if(n < 8) if(n < 8)
error(Eshort); error(Eshort);
@ -1010,6 +1036,7 @@ epwrite(Ep *ep, void *va, long n)
if(p[0] == 0x00 && p[1] == 0x05) if(p[0] == 0x00 && p[1] == 0x05)
return n; return n;
io = (Epio*)ep->aux + OREAD;
qlock(io); qlock(io);
if(waserror()){ if(waserror()){
qunlock(io); qunlock(io);
@ -1032,8 +1059,10 @@ epwrite(Ep *ep, void *va, long n)
} }
} }
ring = io->ring[OWRITE]; ring = io[OWRITE-OREAD].ring;
unstall(ring); if((err = unstall(ring)) != nil)
error(err);
ilock(ring); ilock(ring);
queuetd(ring, TR_SETUPSTAGE | (len > 0 ? 2+dir : 0)<<16 | 1<<6 | 1<<5, 8, queuetd(ring, TR_SETUPSTAGE | (len > 0 ? 2+dir : 0)<<16 | 1<<6 | 1<<5, 8,
p[0] | p[1]<<8 | GET2(&p[2])<<16 | p[0] | p[1]<<8 | GET2(&p[2])<<16 |
@ -1044,10 +1073,10 @@ epwrite(Ep *ep, void *va, long n)
queuetd(ring, TR_STATUSSTAGE | (len == 0 || !dir)<<16 | 1<<5, 0, 0, &ws[2]); queuetd(ring, TR_STATUSSTAGE | (len == 0 || !dir)<<16 | 1<<5, 0, 0, &ws[2]);
iunlock(ring); iunlock(ring);
if((err = waittd(&ws[0], nil)) != nil) if((err = waittd((Ctlr*)ep->hp->aux, &ws[0], ep->tmout, nil)) != nil)
error(err); error(err);
if(len > 0){ if(len > 0){
if((err = waittd(&ws[1], nil)) != nil) if((err = waittd((Ctlr*)ep->hp->aux, &ws[1], ep->tmout, nil)) != nil)
error(err); error(err);
if(dir != 0){ if(dir != 0){
io->cb->wp -= (ws[1].er[2] & 0xFFFFFF); io->cb->wp -= (ws[1].er[2] & 0xFFFFFF);
@ -1055,10 +1084,12 @@ epwrite(Ep *ep, void *va, long n)
io->cb->wp = io->cb->rp; io->cb->wp = io->cb->rp;
} }
} }
if((err = waittd(&ws[2], nil)) != nil) if((err = waittd((Ctlr*)ep->hp->aux, &ws[2], ep->tmout, nil)) != nil)
error(err); error(err);
qunlock(io); qunlock(io);
poperror(); poperror();
return n; return n;
} }
@ -1077,13 +1108,18 @@ epwrite(Ep *ep, void *va, long n)
return n; return n;
} }
ring = io->ring[OWRITE]; io = (Epio*)ep->aux + OWRITE;
unstall(ring); qlock(io);
ilock(ring); if((err = unstall(io->ring)) != nil){
queuetd(ring, TR_NORMAL | 1<<16 | 1<<5, n, PADDR(p), ws); qunlock(io);
iunlock(ring); error(err);
}
ilock(io->ring);
queuetd(io->ring, TR_NORMAL | 1<<16 | 1<<5, n, PADDR(p), ws);
iunlock(io->ring);
qunlock(io);
if((err = waittd(ws, nil)) != nil) if((err = waittd((Ctlr*)ep->hp->aux, ws, ep->tmout, nil)) != nil)
error(err); error(err);
return n; return n;