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 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;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue