diff options
-rw-r--r-- | sys/src/9/port/usbxhci.c | 122 |
1 files changed, 109 insertions, 13 deletions
diff --git a/sys/src/9/port/usbxhci.c b/sys/src/9/port/usbxhci.c index 5842a7b18..1f87fddbe 100644 --- a/sys/src/9/port/usbxhci.c +++ b/sys/src/9/port/usbxhci.c @@ -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(); |