usbxhci: basic iso write support (usb soundcard playback)

This commit is contained in:
cinap_lenrek 2017-07-24 23:48:50 +02:00
parent a397bfd48c
commit aaf6d7c558

View file

@ -213,6 +213,8 @@ struct Ctlr
Ring er[1]; /* event ring segment */ Ring er[1]; /* event ring segment */
Ring cr[1]; /* command ring segment */ Ring cr[1]; /* command ring segment */
u32int mfwrap;
QLock slotlock; QLock slotlock;
Slot **slot; /* slots by slot id */ Slot **slot; /* slots by slot id */
@ -232,8 +234,15 @@ struct Ctlr
struct Epio struct Epio
{ {
QLock; QLock;
Block *cb;
Ring *ring; Ring *ring;
Block *b;
/* iso */
int nleft;
u32int frame;
u32int incr;
u32int tdsz;
}; };
static char Ebadlen[] = "bad usb request length"; static char Ebadlen[] = "bad usb request length";
@ -252,6 +261,18 @@ setrptr64(u32int *reg, u64int pa)
*((u64int*)reg) = pa; *((u64int*)reg) = pa;
} }
static u32int
mfindex(Ctlr *ctlr)
{
u32int lo, hi;
do {
hi = ctlr->mfwrap;
lo = ctlr->rts[MFINDEX];
} while(hi != ctlr->mfwrap);
return (lo & (1<<14)-1) | hi<<14;
}
static void static void
freering(Ring *r) freering(Ring *r)
{ {
@ -370,7 +391,9 @@ init(Hci *hp)
irs[IMOD] = 0; irs[IMOD] = 0;
} }
ctlr->opr[USBCMD] = RUNSTOP|INTE|HSEE; ctlr->mfwrap = 0;
ctlr->opr[USBCMD] = RUNSTOP|INTE|HSEE|EWE;
while(ctlr->opr[USBSTS] & (CNR|HCH)) while(ctlr->opr[USBSTS] & (CNR|HCH))
tsleep(&up->sleep, return0, nil, 10); tsleep(&up->sleep, return0, nil, 10);
} }
@ -512,16 +535,18 @@ static void
completering(Ring *r, u32int *er) completering(Ring *r, u32int *er)
{ {
Wait *w, **wp; Wait *w, **wp;
u32int *td; u32int *td, x;
u64int pa; u64int pa;
pa = (*(u64int*)er) & ~15ULL; pa = (*(u64int*)er) & ~15ULL;
ilock(r); ilock(r);
while((int)(r->wp - r->rp) > 0){ for(x = r->rp; (int)(r->wp - x) > 0; x++){
td = &r->base[4*(r->rp++ & r->mask)]; td = &r->base[4*(x++ & r->mask)];
if((u64int)PADDR(td) == pa) if((u64int)PADDR(td) == pa){
r->rp = x;
break; break;
}
} }
wp = &r->pending; wp = &r->pending;
@ -564,9 +589,6 @@ interrupt(Ureg*, void *arg)
if((((x>>ring->shift)^td[3])&1) == 0) if((((x>>ring->shift)^td[3])&1) == 0)
break; break;
if(0) iprint("xhci interrupt: event %ud: %ux %ux %ux %ux\n",
x, td[0], td[1], td[2], td[3]);
switch(td[3] & 0xFC00){ switch(td[3] & 0xFC00){
case ER_CMDCOMPL: case ER_CMDCOMPL:
completering(ctlr->cr, td); completering(ctlr->cr, td);
@ -580,16 +602,21 @@ 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_MFINDEXWRAP:
ctlr->mfwrap++;
break;
case ER_HCE: case ER_HCE:
iprint("xhci: host controller error: %ux %ux %ux %ux\n", td[0], td[1], td[2], td[3]); iprint("xhci: host controller error: %ux %ux %ux %ux\n",
td[0], td[1], td[2], td[3]);
break; break;
case ER_PORTSC: case ER_PORTSC:
break;
case ER_BWREQ: case ER_BWREQ:
case ER_DOORBELL: case ER_DOORBELL:
case ER_DEVNOTE: case ER_DEVNOTE:
case ER_MFINDEXWRAP:
default: default:
break; iprint("xhci: event %ud: %ux %ux %ux %ux\n",
x, td[0], td[1], td[2], td[3]);
} }
} }
@ -679,6 +706,7 @@ epclose(Ep *ep)
{ {
Ctlr *ctlr; Ctlr *ctlr;
Slot *slot; Slot *slot;
Ring *ring;
Epio *io; Epio *io;
if(ep->dev->isroot) if(ep->dev->isroot)
@ -699,15 +727,17 @@ epclose(Ep *ep)
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[OREAD].ring != nil){ if((ring = io[OREAD].ring) != nil){
w[0] |= 1 << io[OREAD].ring->id; w[0] |= 1 << ring->id;
if(io[OREAD].ring->id == slot->nep) if(ring->id == slot->nep)
slot->nep--; slot->nep--;
ctlrcmd(ctlr, CR_STOPEP | (ring->id<<16) | (slot->id<<24), 0, 0, nil);
} }
if(io[OWRITE].ring != nil){ if((ring = io[OWRITE].ring) != nil){
w[0] |= 1 << io[OWRITE].ring->id; w[0] |= 1 << ring->id;
if(io[OWRITE].ring->id == slot->nep) if(ring->id == slot->nep)
slot->nep--; slot->nep--;
ctlrcmd(ctlr, CR_STOPEP | (ring->id<<16) | (slot->id<<24), 0, 0, nil);
} }
/* (input) slot context */ /* (input) slot context */
@ -723,7 +753,8 @@ epclose(Ep *ep)
freering(io[OREAD].ring); freering(io[OREAD].ring);
freering(io[OWRITE].ring); freering(io[OWRITE].ring);
} }
freeb(io[OREAD].cb); freeb(io[OREAD].b);
freeb(io[OWRITE].b);
free(io); free(io);
} }
@ -732,7 +763,7 @@ initepctx(u32int *w, Ring *r, Ep *ep)
{ {
int ival; int ival;
if(ep->ttype == Tintr && (ep->dev->speed == Fullspeed || ep->dev->speed == Lowspeed)){ if(ep->dev->speed == Lowspeed || ep->dev->speed == Fullspeed){
for(ival=3; ival < 11 && (1<<ival) < ep->pollival; ival++) for(ival=3; ival < 11 && (1<<ival) < ep->pollival; ival++)
; ;
} else { } else {
@ -744,7 +775,23 @@ initepctx(u32int *w, Ring *r, Ep *ep)
if(ep->ttype != Tiso) if(ep->ttype != Tiso)
w[1] |= 3<<1; w[1] |= 3<<1;
*((u64int*)&w[2]) = PADDR(r->base) | 1; *((u64int*)&w[2]) = PADDR(r->base) | 1;
w[4] = ep->maxpkt; w[4] = ep->maxpkt;
if(ep->ttype == Tintr || ep->ttype == Tiso)
w[4] |= (ep->maxpkt*ep->ntds)<<16;
}
static void
initisoio(Epio *io, Ep *ep)
{
if(io->ring == nil)
return;
io->frame = 0;
io->incr = (ep->hz<<8)/1000;
io->tdsz = (io->incr+255>>8)*ep->samplesz;
if(io->tdsz > ep->maxpkt*ep->ntds)
error(Egreg);
io->b = allocb((io->ring->mask+1)*io->tdsz);
} }
static void static void
@ -816,6 +863,10 @@ initep(Ep *ep)
PADDR(slot->ibase), nil)) != nil){ PADDR(slot->ibase), nil)) != nil){
error(err); error(err);
} }
if(ep->ttype == Tiso){
initisoio(io+OWRITE, ep);
initisoio(io+OREAD, ep);
}
poperror(); poperror();
} }
@ -956,6 +1007,77 @@ unstall(Ring *r)
return nil; return nil;
} }
static long
isoread(Ep *, uchar *, long)
{
error(Egreg);
return 0;
}
static long
isowrite(Ep *ep, uchar *p, long n)
{
uchar *s, *d;
Ctlr *ctlr;
Epio *io;
u32int x;
long m;
s = p;
io = (Epio*)ep->aux + OWRITE;
qlock(io);
if(waserror()){
qunlock(io);
nexterror();
}
ctlr = ep->hp->aux;
for(x = io->frame;; x++){
for(;;){
m = (int)(io->ring->wp - io->ring->rp);
if(m <= 0)
x = 10 + mfindex(ctlr)/8;
if(m < io->ring->mask-1)
break;
coherence();
*io->ring->doorbell = io->ring->id;
tsleep(&up->sleep, return0, nil, 5);
}
m = ((io->incr + (x*io->incr&255))>>8)*ep->samplesz;
d = io->b->rp + (x&io->ring->mask)*io->tdsz;
m -= io->nleft, d += io->nleft;
if(n < m){
memmove(d, p, n);
p += n;
io->nleft += n;
break;
}
memmove(d, p, m);
p += m, n -= m;
m += io->nleft, d -= io->nleft;
io->nleft = 0;
ilock(io->ring);
queuetd(io->ring, TR_ISOCH | (x & 0x7ff)<<20 | 1<<5, m, PADDR(d), nil);
iunlock(io->ring);
}
io->frame = x;
coherence();
*io->ring->doorbell = io->ring->id;
qunlock(io);
poperror();
for(;;){
int d = (int)(x - mfindex(ctlr)/8);
d -= ep->sampledelay*1000 / ep->hz;
if(d < 5)
break;
tsleep(&up->sleep, return0, nil, d);
}
return p - s;
}
static long static long
epread(Ep *ep, void *va, long n) epread(Ep *ep, void *va, long n)
{ {
@ -965,21 +1087,21 @@ epread(Ep *ep, void *va, long n)
Wait ws[1]; Wait ws[1];
p = va; p = va;
io = (Epio*)ep->aux + OREAD;
if(ep->ttype == Tctl){ if(ep->ttype == Tctl){
io = (Epio*)ep->aux + OREAD;
qlock(io); qlock(io);
if(io->cb == nil || BLEN(io->cb) == 0){ if(io->b == nil || BLEN(io->b) == 0){
qunlock(io); qunlock(io);
return 0; return 0;
} }
if(n > BLEN(io->cb)) if(n > BLEN(io->b))
n = BLEN(io->cb); n = BLEN(io->b);
memmove(p, io->cb->rp, n); memmove(p, io->b->rp, n);
io->cb->rp += n; io->b->rp += n;
qunlock(io); qunlock(io);
return n; return n;
} } else if(ep->ttype == Tiso)
return isoread(ep, p, n);
if((uintptr)p <= KZERO){ if((uintptr)p <= KZERO){
Block *b; Block *b;
@ -996,6 +1118,7 @@ epread(Ep *ep, void *va, long n)
return n; return n;
} }
io = (Epio*)ep->aux + OREAD;
qlock(io); qlock(io);
if((err = unstall(io->ring)) != nil){ if((err = unstall(io->ring)) != nil){
qunlock(io); qunlock(io);
@ -1041,20 +1164,20 @@ epwrite(Ep *ep, void *va, long n)
qunlock(io); qunlock(io);
nexterror(); nexterror();
} }
if(io->cb != nil){ if(io->b != nil){
freeb(io->cb); freeb(io->b);
io->cb = nil; io->b = nil;
} }
len = GET2(&p[6]); len = GET2(&p[6]);
dir = (p[0] & Rd2h) != 0; dir = (p[0] & Rd2h) != 0;
if(len > 0){ if(len > 0){
io->cb = allocb(len); io->b = allocb(len);
if(dir == 0){ /* out */ if(dir == 0){ /* out */
assert(len >= n-8); assert(len >= n-8);
memmove(io->cb->wp, p+8, n-8); memmove(io->b->wp, p+8, n-8);
} else { } else {
memset(io->cb->wp, 0, len); memset(io->b->wp, 0, len);
io->cb->wp += len; io->b->wp += len;
} }
} }
@ -1080,7 +1203,7 @@ epwrite(Ep *ep, void *va, long n)
(u64int)(GET2(&p[4]) | len<<16)<<32, &ws[0]); (u64int)(GET2(&p[4]) | len<<16)<<32, &ws[0]);
if(len > 0) if(len > 0)
queuetd(ring, TR_DATASTAGE | dir<<16 | 1<<5, len, queuetd(ring, TR_DATASTAGE | dir<<16 | 1<<5, len,
PADDR(io->cb->rp), &ws[1]); PADDR(io->b->rp), &ws[1]);
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);
@ -1090,9 +1213,9 @@ epwrite(Ep *ep, void *va, long n)
if((err = waittd((Ctlr*)ep->hp->aux, &ws[1], ep->tmout, 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->b->wp -= (ws[1].er[2] & 0xFFFFFF);
if(io->cb->wp < io->cb->rp) if(io->b->wp < io->b->rp)
io->cb->wp = io->cb->rp; io->b->wp = io->b->rp;
} }
} }
if((err = waittd((Ctlr*)ep->hp->aux, &ws[2], ep->tmout, nil)) != nil) if((err = waittd((Ctlr*)ep->hp->aux, &ws[2], ep->tmout, nil)) != nil)
@ -1101,7 +1224,8 @@ epwrite(Ep *ep, void *va, long n)
poperror(); poperror();
return n; return n;
} } else if(ep->ttype == Tiso)
return isowrite(ep, p, n);
if((uintptr)p <= KZERO){ if((uintptr)p <= KZERO){
Block *b; Block *b;