shithub: riscv

Download patch

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();