ref: 55d8082842367540b7c3918253c04f71fc9361bb
parent: 4fb65ae3e84f536a9b924082b7c59a30d5802e6b
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Jun 21 17:42:13 EDT 2020
usbxhci: implement isochronous in transfers (for webcam, audio recording)
--- a/sys/src/9/port/usbxhci.c
+++ b/sys/src/9/port/usbxhci.c
@@ -187,6 +187,7 @@
int stopped;
+ int *residue;
Wait *pending;
Lock;
};
@@ -270,6 +271,10 @@
u32int incr;
u32int tdsz;
+ /* isoread */
+ u32int rp0;
+ u32int frame0;
+
int nleft;
};
@@ -308,6 +313,8 @@
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 @@
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 @@
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 @@
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 @@
{
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 @@
}
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 @@
}
µ = 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 @@
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();