usbxhci: implement isochronous in transfers (for webcam, audio recording)
This commit is contained in:
parent
4fb65ae3e8
commit
55d8082842
1 changed files with 109 additions and 13 deletions
|
@ -187,6 +187,7 @@ struct Ring
|
|||
|
||||
int stopped;
|
||||
|
||||
int *residue;
|
||||
Wait *pending;
|
||||
Lock;
|
||||
};
|
||||
|
@ -270,6 +271,10 @@ struct Epio
|
|||
u32int incr;
|
||||
u32int tdsz;
|
||||
|
||||
/* isoread */
|
||||
u32int rp0;
|
||||
u32int frame0;
|
||||
|
||||
int nleft;
|
||||
};
|
||||
|
||||
|
@ -308,6 +313,8 @@ freering(Ring *r)
|
|||
dmaflush(0, r->base, 4*4<<r->shift);
|
||||
free(r->base);
|
||||
}
|
||||
if(r->residue != nil)
|
||||
free(r->residue);
|
||||
memset(r, 0, sizeof(*r));
|
||||
}
|
||||
|
||||
|
@ -319,6 +326,7 @@ initring(Ring *r, int shift)
|
|||
r->slot = nil;
|
||||
r->doorbell = nil;
|
||||
r->pending = nil;
|
||||
r->residue = nil;
|
||||
r->stopped = 0;
|
||||
r->shift = shift;
|
||||
r->mask = (1<<shift)-1;
|
||||
|
@ -694,6 +702,8 @@ queuetd(Ring *r, u32int c, u32int s, u64int p, Wait *w)
|
|||
r->pending = w;
|
||||
iunlock(r);
|
||||
}
|
||||
if(r->residue != nil)
|
||||
r->residue[x & r->mask] = s;
|
||||
coherence();
|
||||
*(u64int*)td = p;
|
||||
td[2] = s;
|
||||
|
@ -828,10 +838,12 @@ completering(Ring *r, u32int *er)
|
|||
pa = (*(u64int*)er) & ~15ULL;
|
||||
ilock(r);
|
||||
|
||||
for(x = r->rp; (int)(r->wp - x) > 0;){
|
||||
td = &r->base[4*(x++ & r->mask)];
|
||||
for(x = r->rp; (int)(r->wp - x) > 0; x++){
|
||||
td = &r->base[4*(x & r->mask)];
|
||||
if((u64int)PCIWADDR(td) == pa){
|
||||
r->rp = x;
|
||||
if(r->residue != nil)
|
||||
r->residue[x & r->mask] = er[2] & 0xFFFFFF;
|
||||
r->rp = x+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1088,10 +1100,17 @@ initisoio(Epio *io, Ep *ep)
|
|||
{
|
||||
if(io->ring == nil)
|
||||
return;
|
||||
io->frame = 0;
|
||||
io->period = ep->pollival<<3*(ep->dev->speed == Fullspeed);
|
||||
io->incr = (ep->hz*io->period<<8)/8000;
|
||||
io->tdsz = (io->incr+255>>8)*ep->samplesz;
|
||||
io->rp0 = io->ring->wp;
|
||||
io->frame0 = io->frame = 0;
|
||||
io->period = ep->pollival << 3*(ep->dev->speed == Fullspeed || ep->dev->speed == Lowspeed);
|
||||
if(io->ring->id & 1){
|
||||
io->ring->residue = smalloc((io->ring->mask+1)*sizeof(io->ring->residue[0]));
|
||||
io->incr = 0;
|
||||
io->tdsz = ep->maxpkt*ep->ntds;
|
||||
} else {
|
||||
io->incr = ((vlong)ep->hz*ep->pollival<<8)/1000;
|
||||
io->tdsz = (io->incr+255>>8)*ep->samplesz;
|
||||
}
|
||||
io->b = allocb((io->ring->mask+1)*io->tdsz);
|
||||
}
|
||||
|
||||
|
@ -1300,10 +1319,80 @@ epopen(Ep *ep)
|
|||
}
|
||||
|
||||
static long
|
||||
isoread(Ep *, uchar *, long)
|
||||
isoread(Ep *ep, uchar *p, long n)
|
||||
{
|
||||
error(Egreg);
|
||||
return 0;
|
||||
uchar *s, *d;
|
||||
Ctlr *ctlr;
|
||||
Epio *io;
|
||||
u32int i, µ;
|
||||
long m;
|
||||
|
||||
s = p;
|
||||
|
||||
io = (Epio*)ep->aux + OREAD;
|
||||
qlock(io);
|
||||
if(waserror()){
|
||||
qunlock(io);
|
||||
nexterror();
|
||||
}
|
||||
µ = io->period;
|
||||
ctlr = ep->hp->aux;
|
||||
Again:
|
||||
if(needrecover(ctlr))
|
||||
error(Erecover);
|
||||
|
||||
for(i = io->frame0; (int)(io->ring->rp - io->rp0) > 0 && n > 0; i++) {
|
||||
if((io->rp0 & io->ring->mask) == io->ring->mask)
|
||||
io->rp0++;
|
||||
m = io->tdsz - io->ring->residue[io->rp0 & io->ring->mask];
|
||||
if(m > 0){
|
||||
d = io->b->rp + (i&io->ring->mask)*io->tdsz;
|
||||
d += io->nleft, m -= io->nleft;
|
||||
if(n < m){
|
||||
dmaflush(0, d, n);
|
||||
memmove(p, d, n);
|
||||
io->nleft += n;
|
||||
p += n;
|
||||
n = 0;
|
||||
break;
|
||||
}
|
||||
dmaflush(0, d, m);
|
||||
memmove(p, d, m);
|
||||
p += m, n -= m;
|
||||
|
||||
if(ep->uframes == 1)
|
||||
n = 0;
|
||||
}
|
||||
io->nleft = 0;
|
||||
io->rp0++;
|
||||
}
|
||||
io->frame0 = i;
|
||||
|
||||
for(i = io->frame;; i++){
|
||||
m = (int)(io->ring->wp - io->rp0);
|
||||
if(m <= 0) {
|
||||
i = (80 + µframe(ctlr))/µ;
|
||||
io->frame0 = i;
|
||||
io->rp0 = io->ring->wp;
|
||||
io->nleft = 0;
|
||||
} else if(m+1 >= io->ring->mask)
|
||||
break;
|
||||
m = io->tdsz;
|
||||
d = io->b->rp + (i&io->ring->mask)*io->tdsz;
|
||||
dmaflush(1, d, m);
|
||||
queuetd(io->ring, TR_ISOCH | (i*µ/8 & 0x7ff)<<20 | TR_IOC, m, PCIWADDR(d), nil);
|
||||
}
|
||||
io->frame = i;
|
||||
|
||||
*io->ring->doorbell = io->ring->id;
|
||||
if(p == s){
|
||||
tsleep(&up->sleep, return0, nil, 5);
|
||||
goto Again;
|
||||
}
|
||||
qunlock(io);
|
||||
poperror();
|
||||
|
||||
return p - s;
|
||||
}
|
||||
|
||||
static long
|
||||
|
@ -1324,15 +1413,17 @@ isowrite(Ep *ep, uchar *p, long n)
|
|||
}
|
||||
µ = io->period;
|
||||
ctlr = ep->hp->aux;
|
||||
if(needrecover(ctlr))
|
||||
error(Erecover);
|
||||
|
||||
for(i = io->frame;; i++){
|
||||
for(;;){
|
||||
if(needrecover(ctlr))
|
||||
error(Erecover);
|
||||
m = (int)(io->ring->wp - io->ring->rp);
|
||||
if(m <= 0)
|
||||
i = (80 + µframe(ctlr))/µ;
|
||||
if(m < io->ring->mask)
|
||||
if(m+1 < io->ring->mask)
|
||||
break;
|
||||
|
||||
*io->ring->doorbell = io->ring->id;
|
||||
tsleep(&up->sleep, return0, nil, 5);
|
||||
}
|
||||
|
@ -1353,14 +1444,19 @@ isowrite(Ep *ep, uchar *p, long n)
|
|||
queuetd(io->ring, TR_ISOCH | (i*µ/8 & 0x7ff)<<20 | TR_IOC, m, PCIWADDR(d), nil);
|
||||
}
|
||||
io->frame = i;
|
||||
|
||||
while(io->ring->rp != io->ring->wp){
|
||||
int d = (int)(i*µ - µframe(ctlr))/8;
|
||||
d -= ep->sampledelay*1000 / ep->hz;
|
||||
if(d < 5)
|
||||
break;
|
||||
|
||||
*io->ring->doorbell = io->ring->id;
|
||||
tsleep(&up->sleep, return0, nil, d);
|
||||
if(needrecover(ctlr))
|
||||
error(Erecover);
|
||||
}
|
||||
|
||||
qunlock(io);
|
||||
poperror();
|
||||
|
||||
|
|
Loading…
Reference in a new issue