usbxhci: basic iso write support (usb soundcard playback)
This commit is contained in:
parent
a397bfd48c
commit
aaf6d7c558
1 changed files with 164 additions and 40 deletions
|
@ -213,6 +213,8 @@ struct Ctlr
|
|||
Ring er[1]; /* event ring segment */
|
||||
Ring cr[1]; /* command ring segment */
|
||||
|
||||
u32int mfwrap;
|
||||
|
||||
QLock slotlock;
|
||||
Slot **slot; /* slots by slot id */
|
||||
|
||||
|
@ -232,8 +234,15 @@ struct Ctlr
|
|||
struct Epio
|
||||
{
|
||||
QLock;
|
||||
Block *cb;
|
||||
|
||||
Ring *ring;
|
||||
Block *b;
|
||||
|
||||
/* iso */
|
||||
int nleft;
|
||||
u32int frame;
|
||||
u32int incr;
|
||||
u32int tdsz;
|
||||
};
|
||||
|
||||
static char Ebadlen[] = "bad usb request length";
|
||||
|
@ -252,6 +261,18 @@ setrptr64(u32int *reg, u64int 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
|
||||
freering(Ring *r)
|
||||
{
|
||||
|
@ -370,7 +391,9 @@ init(Hci *hp)
|
|||
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))
|
||||
tsleep(&up->sleep, return0, nil, 10);
|
||||
}
|
||||
|
@ -512,16 +535,18 @@ static void
|
|||
completering(Ring *r, u32int *er)
|
||||
{
|
||||
Wait *w, **wp;
|
||||
u32int *td;
|
||||
u32int *td, x;
|
||||
u64int pa;
|
||||
|
||||
pa = (*(u64int*)er) & ~15ULL;
|
||||
ilock(r);
|
||||
|
||||
while((int)(r->wp - r->rp) > 0){
|
||||
td = &r->base[4*(r->rp++ & r->mask)];
|
||||
if((u64int)PADDR(td) == pa)
|
||||
for(x = r->rp; (int)(r->wp - x) > 0; x++){
|
||||
td = &r->base[4*(x++ & r->mask)];
|
||||
if((u64int)PADDR(td) == pa){
|
||||
r->rp = x;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
wp = &r->pending;
|
||||
|
@ -564,9 +589,6 @@ interrupt(Ureg*, void *arg)
|
|||
if((((x>>ring->shift)^td[3])&1) == 0)
|
||||
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){
|
||||
case ER_CMDCOMPL:
|
||||
completering(ctlr->cr, td);
|
||||
|
@ -580,16 +602,21 @@ interrupt(Ureg*, void *arg)
|
|||
break;
|
||||
completering(&slot->epr[(td[3]>>16)-1&31], td);
|
||||
break;
|
||||
case ER_MFINDEXWRAP:
|
||||
ctlr->mfwrap++;
|
||||
break;
|
||||
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;
|
||||
case ER_PORTSC:
|
||||
break;
|
||||
case ER_BWREQ:
|
||||
case ER_DOORBELL:
|
||||
case ER_DEVNOTE:
|
||||
case ER_MFINDEXWRAP:
|
||||
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;
|
||||
Slot *slot;
|
||||
Ring *ring;
|
||||
Epio *io;
|
||||
|
||||
if(ep->dev->isroot)
|
||||
|
@ -699,15 +727,17 @@ epclose(Ep *ep)
|
|||
w = slot->ibase;
|
||||
memset(w, 0, 32<<ctlr->csz);
|
||||
w[1] = 1;
|
||||
if(io[OREAD].ring != nil){
|
||||
w[0] |= 1 << io[OREAD].ring->id;
|
||||
if(io[OREAD].ring->id == slot->nep)
|
||||
if((ring = io[OREAD].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);
|
||||
}
|
||||
if(io[OWRITE].ring != nil){
|
||||
w[0] |= 1 << io[OWRITE].ring->id;
|
||||
if(io[OWRITE].ring->id == slot->nep)
|
||||
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);
|
||||
}
|
||||
|
||||
/* (input) slot context */
|
||||
|
@ -723,7 +753,8 @@ epclose(Ep *ep)
|
|||
freering(io[OREAD].ring);
|
||||
freering(io[OWRITE].ring);
|
||||
}
|
||||
freeb(io[OREAD].cb);
|
||||
freeb(io[OREAD].b);
|
||||
freeb(io[OWRITE].b);
|
||||
free(io);
|
||||
}
|
||||
|
||||
|
@ -732,7 +763,7 @@ initepctx(u32int *w, Ring *r, Ep *ep)
|
|||
{
|
||||
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++)
|
||||
;
|
||||
} else {
|
||||
|
@ -744,7 +775,23 @@ initepctx(u32int *w, Ring *r, Ep *ep)
|
|||
if(ep->ttype != Tiso)
|
||||
w[1] |= 3<<1;
|
||||
*((u64int*)&w[2]) = PADDR(r->base) | 1;
|
||||
|
||||
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
|
||||
|
@ -816,6 +863,10 @@ initep(Ep *ep)
|
|||
PADDR(slot->ibase), nil)) != nil){
|
||||
error(err);
|
||||
}
|
||||
if(ep->ttype == Tiso){
|
||||
initisoio(io+OWRITE, ep);
|
||||
initisoio(io+OREAD, ep);
|
||||
}
|
||||
poperror();
|
||||
}
|
||||
|
||||
|
@ -956,6 +1007,77 @@ unstall(Ring *r)
|
|||
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
|
||||
epread(Ep *ep, void *va, long n)
|
||||
{
|
||||
|
@ -965,21 +1087,21 @@ epread(Ep *ep, void *va, long n)
|
|||
Wait ws[1];
|
||||
|
||||
p = va;
|
||||
io = (Epio*)ep->aux + OREAD;
|
||||
|
||||
if(ep->ttype == Tctl){
|
||||
io = (Epio*)ep->aux + OREAD;
|
||||
qlock(io);
|
||||
if(io->cb == nil || BLEN(io->cb) == 0){
|
||||
if(io->b == nil || BLEN(io->b) == 0){
|
||||
qunlock(io);
|
||||
return 0;
|
||||
}
|
||||
if(n > BLEN(io->cb))
|
||||
n = BLEN(io->cb);
|
||||
memmove(p, io->cb->rp, n);
|
||||
io->cb->rp += n;
|
||||
if(n > BLEN(io->b))
|
||||
n = BLEN(io->b);
|
||||
memmove(p, io->b->rp, n);
|
||||
io->b->rp += n;
|
||||
qunlock(io);
|
||||
return n;
|
||||
}
|
||||
} else if(ep->ttype == Tiso)
|
||||
return isoread(ep, p, n);
|
||||
|
||||
if((uintptr)p <= KZERO){
|
||||
Block *b;
|
||||
|
@ -996,6 +1118,7 @@ epread(Ep *ep, void *va, long n)
|
|||
return n;
|
||||
}
|
||||
|
||||
io = (Epio*)ep->aux + OREAD;
|
||||
qlock(io);
|
||||
if((err = unstall(io->ring)) != nil){
|
||||
qunlock(io);
|
||||
|
@ -1041,20 +1164,20 @@ epwrite(Ep *ep, void *va, long n)
|
|||
qunlock(io);
|
||||
nexterror();
|
||||
}
|
||||
if(io->cb != nil){
|
||||
freeb(io->cb);
|
||||
io->cb = nil;
|
||||
if(io->b != nil){
|
||||
freeb(io->b);
|
||||
io->b = nil;
|
||||
}
|
||||
len = GET2(&p[6]);
|
||||
dir = (p[0] & Rd2h) != 0;
|
||||
if(len > 0){
|
||||
io->cb = allocb(len);
|
||||
io->b = allocb(len);
|
||||
if(dir == 0){ /* out */
|
||||
assert(len >= n-8);
|
||||
memmove(io->cb->wp, p+8, n-8);
|
||||
memmove(io->b->wp, p+8, n-8);
|
||||
} else {
|
||||
memset(io->cb->wp, 0, len);
|
||||
io->cb->wp += len;
|
||||
memset(io->b->wp, 0, 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]);
|
||||
if(len > 0)
|
||||
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]);
|
||||
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)
|
||||
error(err);
|
||||
if(dir != 0){
|
||||
io->cb->wp -= (ws[1].er[2] & 0xFFFFFF);
|
||||
if(io->cb->wp < io->cb->rp)
|
||||
io->cb->wp = io->cb->rp;
|
||||
io->b->wp -= (ws[1].er[2] & 0xFFFFFF);
|
||||
if(io->b->wp < io->b->rp)
|
||||
io->b->wp = io->b->rp;
|
||||
}
|
||||
}
|
||||
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();
|
||||
|
||||
return n;
|
||||
}
|
||||
} else if(ep->ttype == Tiso)
|
||||
return isowrite(ep, p, n);
|
||||
|
||||
if((uintptr)p <= KZERO){
|
||||
Block *b;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue