~kris/9p

9hist

00f9c62276b2a26a2832f7612178a7b04cb59c55 — David du Colombier 24 years ago df6c1bb
Plan 9 from Bell Labs 2001-10-06
1 files changed, 271 insertions(+), 225 deletions(-)

M pc/devusb.c
M pc/devusb.c => pc/devusb.c +271 -225
@@ 116,7 116,7 @@ enum {
	DevicePresent = 1<<0,

	FRAMESIZE=	4096,	/* fixed by hardware; aligned to same */
	NFRAME = 	FRAMESIZE/4,
	NFRAME = 	(FRAMESIZE/4),
	NISOTD = 4,			/* number of TDs for isochronous io per frame */

	Vf = 1<<2,	/* TD only */


@@ 146,6 146,8 @@ enum {

	/* TD.flags (software) */
	CancelTD=	1<<0,
	IsoLock=		1<<1,
	IsoClean=		1<<2,
};

#define	GET2(p)	((((p)[1]&0xFF)<<8)|((p)[0]&0xFF))


@@ 207,9 209,11 @@ struct Endpt {
	byte		isopen;	/* ep operations forbidden on open endpoints */
	byte		mode;	/* OREAD, OWRITE, ORDWR */
	byte		nbuf;	/* number of buffers allowed */
	byte		iso;
	volatile byte	iso;
	byte		debug;
	byte		active;	/* listed for examination by interrupts */
	byte *	tdalloc;
	byte *	bpalloc;
	int		hz;
	int		remain;
	int		samplesz;


@@ 217,14 221,16 @@ struct Endpt {
	int		setin;
	int		pollms;	/* polling interval in msec */
	int		psize;	/* size of this packet */
	Block	*curblock;	/* half full block between iso writes */
	int		curbytes;		/* # of bytes in that block */
	QH*		epq;		/* queue of TDs for this endpoint */
	TD*		etd;		/* pointer into circular list of TDs for isochronous ept */
	TD*		td0;		/* pointer to list of TDs for isochronous ept */
	volatile TD*	etd;	/* reader/writer pointer into list of TDs for isochronous ept */
	volatile TD*	xtd;	/* next td to be cleaned */
	QLock	rlock;
	Rendez	rr;
	Queue*	rq;
	QLock	wlock;
	int		isolock;
	Rendez	wr;
	Queue*	wq;



@@ 675,79 681,6 @@ queueqh(QH *qh) {
	ub->recvq->entries = PADDR(qh) | IsQH;
}

static int
tdisready(void *arg)
{
	return (((TD*)arg)->status & Active) == 0;
}

static Block *
isoxmit(Endpt *e, Block *b, int pid)
{
	Block *ob;
	static int ioc, n, id;
	TD *td;

	td = e->etd;
	if (td == nil)
		panic("usb: isoxmit");
	while(td->status & Active){
		XPRINT("isoxmit: sleep %lux\n", &e->wr);
		sleep(&e->wr, tdisready, e->etd);
		XPRINT("isoxmit: awake\n");
	}
	if (td->status & AnyError)
		iprint("usbisoerror 0x%lux\n", td->status);
	ob = td->bp;
	if (ob == nil)
		panic("isoxmit: null block");
	td->buffer = PADDR(b->rp);
	td->bp = b;
	n = BLEN(b);
	id = (e->x<<7)|(e->dev->x&0x7F);
	td->dev = ((n-1)<<21) | ((id&0x7FF)<<8) | pid;
	if ((ioc++ & 0xff) == 0)
		td->status = ErrLimit3 | Active | IsoSelect | IOC;
	else
		td->status = ErrLimit3 | Active | IsoSelect;
	e->etd = td->next;
	return ob;
}

static Block*
isorecv(Endpt *e, Block *b, int pid)
{
	TD *td;
	int n, id;
	Block *ob;
	static int ioc;

	td = e->etd;
	if (td == nil)
		panic("usb: isorecv");
	while(td->status & Active){
		XPRINT("isorecv: sleep %lux\n", &e->wr);
		sleep(&e->wr, tdisready, e->etd);
		XPRINT("isorecv: awake\n");
	}
	if (td->status & AnyError)
		iprint("usbisoerror 0x%lux\n", td->status);
	ob = td->bp;
	if (ob == nil)
		panic("isorecv: null block");
	n = (td->status + 1) & 0x7FF;
	if(n > ob->lim - b->wp)
		n = 0;
	ob->wp = ob->rp + n;
	td->buffer = PADDR(b->rp);
	td->bp = b;
	id = (e->x<<7)|(e->dev->x&0x7F);
	td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | pid;
	td->status = ErrLimit3 | Active | IsoSelect | IOC;
	e->etd = td->next;
	return ob;
}

static QH*
qxmit(Endpt *e, Block *b, int pid)
{


@@ 836,7 769,7 @@ flog2(int n)
static int
usbsched(	Ctlr *ub, int pollms, ulong load)
{
	int i, j, d, q;
	int i, d, q;
	ulong best, worst;

	best = 1000000;


@@ 844,13 777,6 @@ usbsched(	Ctlr *ub, int pollms, ulong load)
	for (d = 0; d < pollms; d++){
		worst = 0;
		for (i = d; i < NFRAME; i++){
			for(j = 0; j < NISOTD; j++)
				if(ub->tdpool[NISOTD*i + j].ep == nil)
					break;
			if(j == NISOTD){
				worst = 1000000;	/* No free TDs */
				break;
			}
			if (ub->frameld[i] + load > worst)
				worst = ub->frameld[i] + load;
		}


@@ 866,8 792,9 @@ static int
schedendpt(Endpt *e)
{
	Ctlr *ub;
	TD *td, **prev, *first;
	int i, j;
	TD *td;
	uchar *bp;
	int i, id, ix, size, frnum;

	if(!e->iso || e->sched >= 0)
		return 0;


@@ 876,10 803,6 @@ schedendpt(Endpt *e)
	if (e->isopen){
		return -1;
	}
	if (e->curblock == nil)
		e->curblock = allocb(e->maxpkt);
	if (e->curblock == nil)
		panic("schedept: allocb");
	e->curbytes = 0;
	e->sched = usbsched(ub, e->pollms, e->maxpkt);
	if(e->sched < 0){


@@ 887,45 810,48 @@ schedendpt(Endpt *e)
		return -1;
	}

	/* Allocate TDs from the NISOTD entries on each of the
	 * frames.  Link them circularly so they can be walked easily
	 */

	prev = nil;
	first = nil;
	td = nil;
	if (e->tdalloc || e->bpalloc)
		panic("usb: tdalloc/bpalloc");
	e->tdalloc = mallocz(0x10 + NFRAME*sizeof(TD), 1);
	e->bpalloc = mallocz(0x10 + e->maxpkt*NFRAME/e->pollms, 1);
	e->td0 = (TD*)(((ulong)e->tdalloc + 0xf) & ~0xf);
	bp = (byte *)(((ulong)e->bpalloc + 0xf) & ~0xf);
	frnum = (IN(Frnum) + 1) & 0x3ff;
	frnum = (frnum & ~(e->pollms - 1)) + e->sched;
	e->isolock = 1;
	e->xtd = &e->td0[frnum];	/* Next td to finish */
	e->etd = nil;	/* Writer is idle */
	e->remain = 0;
	td = e->td0;
	for(i = e->sched; i < NFRAME; i += e->pollms){
		for(j = 0; j < NISOTD; j++){
			td = &ub->tdpool[i*NISOTD + j];
			if(td->ep == nil)
				break;
		}
		if(j == NISOTD)
			panic("usb: no td entries despite schedulability test");
		td->bp = (Block *)(bp + e->maxpkt*i/e->pollms);
		td->buffer = PADDR(td->bp);
		td->ep = e;
		if(td->bp)
			panic("usb: schedendpt: block not empty");
		td->bp = allocb(e->maxpkt);
		if(td->bp == nil)
			panic("schedept: allocb");
		if (e->mode == OREAD){
			int id;
		td->next = &td[1];
		ub->frameld[i] += e->maxpkt;
		td++;
	}
	td[-1].next = e->td0;
	for(i = e->sched; i < NFRAME; i += e->pollms){
		ix = (frnum+i)%NFRAME;
		td = &e->td0[ix];

		id = (e->x<<7)|(e->dev->x&0x7F);
		if (e->mode == OREAD)
			/* enable receive on this entry */
			td->buffer = PADDR(td->bp->rp);
			id = (e->x<<7)|(e->dev->x&0x7F);
			td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | TokIN;
			td->status = ErrLimit3 | Active | IsoSelect | IOC;
		else{
			size = (e->hz + e->remain)*e->pollms/1000;
			e->remain = (e->hz + e->remain)*e->pollms%1000;
			size *= e->samplesz;
			memset((byte*)td->bp, 0, size);
			td->dev = ((size-1)<<21) | ((id&0x7FF)<<8) | TokOUT;
		}
		if(i == e->sched){
			first = td;
			e->etd = td;
		}else{
			*prev = td;
		}
		prev = &td->next;
		ub->frameld[i] += e->maxpkt;
		td->status = ErrLimit3 | Active | IsoSelect | IOC;
		td->link = ub->frames[ix];
		ub->frames[ix] = PADDR(td);
	}
	*prev = first;		/* complete circular link */
	e->isolock = 0;
	return 0;
}



@@ 933,23 859,33 @@ static void
unschedendpt(Endpt *e)
{
	Ctlr *ub;
	TD *td, *next;
	TD *td;
	ulong *addr;
	int q;

	ub = &ubus;
	if(!e->iso || (q = e->sched) < 0)
	if(!e->iso || e->sched < 0)
		return;

	for (td = e->etd; q < NFRAME; q += e->pollms){
	if (e->tdalloc == nil)
		panic("tdalloc");
	for (q = e->sched; q < NFRAME; q += e->pollms){
		td = e->td0++;
		addr = &ub->frames[q];
		while (*addr != PADDR(td)){
			if (*addr & IsQH)
				panic("usb: TD expected");
			addr = &TFOL(*addr)->link;
		}
		*addr = td->link;
		ub->frameld[q] -= e->maxpkt;
		next = td->next;
		td->ep = nil;
		td->next = nil;
		freeb(td->bp);
		td->bp = nil;
		td = next;
	}
	freeb(e->curblock);
	free(e->tdalloc);
	free(e->bpalloc);
	e->tdalloc = nil;
	e->bpalloc = nil;
	e->etd = nil;
	e->td0 = nil;
	e->sched = -1;
}



@@ 1002,9 938,9 @@ freept(Endpt *e)
{
	if(e != nil && decref(e) == 0){
		XPRINT("freept(%d,%d)\n", e->dev->x, e->x);
		eptdeactivate(e);
		unschedendpt(e);
		e->dev->ep[e->x] = nil;
		eptdeactivate(e);
		if(e->epq != nil)
			freeqh(&ubus, e->epq);
		free(e);


@@ 1128,11 1064,52 @@ portinfo(Ctlr *ub, int *p0, int *p1)
}

static void
cleaniso(Endpt *e, int frnum)
{
	TD *td;
	int tok, id, size;

	if (e->xtd->status & Active){
		td = e->td0 + ((frnum -1)&0x3ff);
		if (td->status & Active)
			return;
		iprint("%ld active -> %ld", e->xtd - e->td0, td - e->td0);
		e->xtd = td;
	}
	tok = (e->mode == OREAD)?TokIN:TokOUT;
	id = (e->x<<7)|(e->dev->x&0x7F);
	for(td = e->xtd; (td->status & Active) == 0; td = td->next){
		if (td->flags & IsoLock)
			iprint("usbinterrupt: locked?\n");
		if (e->isolock == 0 && td == e->etd){
			/* if isolock is set, don't touch e->etd */
			if (e->xtd == e->etd)
				wakeup(&e->wr);	/* writer had to wait */
			else
				e->etd = nil;		/* writer was overtaken */
		}
		if (td->status & AnyError)
			iprint("usbisoerror 0x%lux\n", td->status);
		size = (e->hz + e->remain)*e->pollms/1000;
		e->remain = (e->hz + e->remain)*e->pollms%1000;
		size *= e->samplesz;
		if ((td->flags & IsoClean) == 0){
			memset((byte*)td->bp, 0, e->maxpkt);
			td->flags |= IsoClean;
		}
		td->dev = ((size-1)<<21) | ((id&0x7FF)<<8) | tok;
		td->status = ErrLimit3 | Active | IsoSelect | IOC;
	}
	e->xtd = td;
	if (e->etd) wakeup(&e->wr);
}

static void
interrupt(Ureg*, void *a)
{
	Ctlr *ub;
	Endpt *e;
	int s;
	int s, frnum;
	QH *q;

	ub = a;


@@ 1140,7 1117,8 @@ interrupt(Ureg*, void *a)
	OUT(Status, s);
	if ((s & 0x1f) == 0)
		return;
	XPRINT("usbint: #%x f%d\n", s, IN(Frnum));
	frnum = IN(Frnum) & 0x3ff;
	XPRINT("usbint: #%x f%d\n", s, frnum);
	if (s & 0x1a) {
		XPRINT("cmd #%x sofmod #%x\n", IN(Cmd), inb(ub->io+SOFMod));
		XPRINT("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1));


@@ 1161,8 1139,9 @@ interrupt(Ureg*, void *a)
			XPRINT("cleanq(e->epq, 0, 0)\n");
			cleanq(e->epq, 0, 0);
		}
		if(e->iso && e->etd != nil) {
			wakeup(&e->wr);
		if(e->iso) {
			XPRINT("cleaniso(e)\n");
			cleaniso(e, frnum);
		}
	}
	iunlock(&activends);


@@ 1268,7 1247,7 @@ static void
usbreset(void)
{
	Pcidev *cfg;
	int i, j;
	int i;
	ulong port;
	TD *t;
	Ctlr *ub;


@@ 1314,8 1293,8 @@ usbreset(void)
	intrenable(cfg->intl, interrupt, ub, cfg->tbdf, "usb");

	ub->io = port;
	ub->tdpool = xspanalloc((NFRAME*NISOTD+128)*sizeof(TD), 16, 0);
	for(i=NFRAME*NISOTD+128; --i>=NFRAME*NISOTD;){
	ub->tdpool = xspanalloc(128*sizeof(TD), 16, 0);
	for(i=128; --i>=0;){
		ub->tdpool[i].next = ub->freetd;
		ub->freetd = &ub->tdpool[i];
	}


@@ 1352,26 1331,8 @@ usbreset(void)
	ub->frames = xspanalloc(FRAMESIZE, FRAMESIZE, 0);
	ub->frameld = mallocz(FRAMESIZE, 1);

	for (i = 0; i < NFRAME; i++){
		j = NISOTD*i;
		ub->frames[i] = PADDR(&ub->tdpool[j]);	/* breadth 1st, is TD, no termin. */
		ub->tdpool[j+0].link = PADDR(&ub->tdpool[j+1]);
		ub->tdpool[j+0].status = IsoSelect;
		ub->tdpool[j+0].dev = 0;
		ub->tdpool[j+0].buffer = 0;
		ub->tdpool[j+1].link = PADDR(&ub->tdpool[j+2]);
		ub->tdpool[j+1].status = IsoSelect;
		ub->tdpool[j+1].dev = 0;
		ub->tdpool[j+1].buffer = 0;
		ub->tdpool[j+2].link = PADDR(&ub->tdpool[j+3]);
		ub->tdpool[j+2].status = IsoSelect;
		ub->tdpool[j+2].dev = 0;
		ub->tdpool[j+2].buffer = 0;
		ub->tdpool[j+3].link = PADDR(ub->ctlq) | IsQH;
		ub->tdpool[j+3].status = IsoSelect;
		ub->tdpool[j+3].dev = 0;
		ub->tdpool[j+3].buffer = 0;
	}
	for (i = 0; i < NFRAME; i++)
		ub->frames[i] = PADDR(ub->ctlq) | IsQH;

	outl(port+Flbaseadd, PADDR(ub->frames));
	OUT(Frnum, 0);


@@ 1613,6 1574,10 @@ usbclose(Chan *c)
			error(Enodev);
		}
		e->isopen--;
		if (e->isopen == 0){
			eptdeactivate(e);
			unschedendpt(e);
		}
	}
	if(QID(c->qid) == Qctl)
		d->busy = 0;


@@ 1631,12 1596,43 @@ eptinput(void *arg)
	return e->eof || e->err || qcanread(e->rq);
}

static int
isorready(void *arg)
{
	int frnum;
	Ctlr *ub;
	Endpt *e;

	e = arg;
	e->isolock = 1;
	ub = &ubus;
	frnum = IN(Frnum) & 0x3ff;
	if (e->etd == nil){
		/* reader was idle, find a place to start */
		e->etd = e->td0 + (frnum & 0x3ff);
	}
	if (e->etd->status & Active){
		/* not yet ready */
		e->isolock = 0;
		return 0;
	}
	/* leave locked if succesful */
	e->psize = (e->etd->status + 1) & 0x7FF;
	e->curbytes = 0;
	e->nbytes += e->psize;
	e->nblocks++;
	if (e->psize > e->maxpkt)
		panic("packet size > maximum");
	return 1;
}

static long
readusb(Endpt *e, void *a, long n)
{
	Block *b;
	uchar *p;
	long l, i;
	uchar *p, *q;
	long l, i, id;
	TD *td;

	XPRINT("qlock(%p)\n", &e->rlock);
	qlock(&e->rlock);


@@ 1647,37 1643,52 @@ readusb(Endpt *e, void *a, long n)
		nexterror();
	}
	p = a;
	do {
		if(e->eof) {
			XPRINT("e->eof\n");
			break;
		}
		if(e->err)
			error(e->err);
		if (e->iso){
			if (e->curbytes == 0){
				/* calculate size of next receive buffer */
				/* put empty buffer on receive queue and get next full one */
				b = isorecv(e, e->curblock, TokIN);
				/* bytes to consume in b: */
				e->curbytes = BLEN(b);
				b->wp = b->rp;
				e->nbytes += e->curbytes;
				e->nblocks++;
			}else{
				b = e->curblock;
	if (e->iso){
		do {
			if (e->psize == 0){
				while(isorready(e) == 0){
					XPRINT("readusb: sleep %lux\n", &e->wr);
					sleep(&e->wr, isorready, e);
					XPRINT("readusb: awake\n");
				}
			}
			if (b == nil)
				panic("readusb: block");
			if((i = n) >= e->curbytes)
				i = e->curbytes;
			memmove(p, b->wp, i);
			b->wp += i;
			p += i;
			n -= i;
			e->curbytes -= i;
			e->curblock = b;
		}else{
			td = e->etd;
			e->isolock = 0;

			if (e->psize > 0){
				q = (byte*)td->bp + e->curbytes;
				if((i = n) >= e->psize)
					i = e->psize;
				memmove(p, q, i);
				p += i;
				n -= i;
				e->curbytes += i;
				e->psize -= i;
				if (e->psize > 0){
					/* Read done, return */
					if (n != 0)
						panic("usb iso: can't happen");
					break;
				}
			}
			e->isolock = 1;
			/* Finished a packet */
			id = (e->x<<7)|(e->dev->x&0x7F);
			td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | TokIN;
			td->status = ErrLimit3 | Active | IsoSelect | IOC;
			if (e->xtd == td)
				e->xtd = td->next;
			/* Get next buffer, wait for it to become ready */
			e->etd = td->next;
		} while (n > 0);
	}else{
		do {
			if(e->eof) {
				XPRINT("e->eof\n");
				break;
			}
			if(e->err)
				error(e->err);
			qrcv(e);
			if(!e->iso)
				e->data01 ^= 1;


@@ 1705,8 1716,8 @@ readusb(Endpt *e, void *a, long n)
			n -= i;
			if (l != e->maxpkt)
				break;
		}
	} while (n > 0);
		} while (n > 0);
	}
	poperror();
	qunlock(&e->rlock);
	return p-(uchar*)a;


@@ 1790,13 1801,41 @@ qisempty(void *arg)
	return ((QH*)arg)->entries & Terminate;
}

static int
isowready(void *arg)
{
	int frnum;
	Ctlr *ub;
	Endpt *e;

	e = arg;
	e->isolock = 1;
	ub = &ubus;
	frnum = IN(Frnum) & 0x3ff;
	if (e->etd == nil){
		/* writer was idle, find a place to start */
		e->etd = e->td0 + ((frnum + 4) & 0x3ff);
		e->curbytes = 0;
		e->psize = ((e->etd->dev >> 21) & 0x7ff) + 1;
		if (e->psize > e->maxpkt)
			panic("packet size > maximum");
	}	
	if (e->etd != e->xtd && e->etd->next != e->xtd){
		/* leave locked if succesful */
		return 1;
	}
	e->isolock = 0;
	return 0;
}

static long
writeusb(Endpt *e, void *a, long n, int tok)
{
	long i;
	int i;
	Block *b;
	uchar *p;
	uchar *p, *q;
	QH *qh;
	TD *td;

	p = a;
	qlock(&e->wlock);


@@ 1805,45 1844,52 @@ writeusb(Endpt *e, void *a, long n, int tok)
		eptcancel(e);
		nexterror();
	}
	do {
		if(e->err)
			error(e->err);
		if (e->iso){
			if (e->curbytes == 0){
				e->psize = (e->hz + e->remain)*e->pollms/1000;
				e->remain = (e->hz + e->remain)*e->pollms%1000;
				e->psize *= e->samplesz;
	e->td0 = (TD*)(((ulong)e->tdalloc + 0xf) & ~0xf);
	if (e->iso){
		if(waserror()){
			e->isolock = 0;
			if (td = e->etd)
				td->flags &= ~IsoLock;
			nexterror();
		}
		do {
			while (isowready(e) == 0){
				XPRINT("writeusb: sleep %lux\n", &e->wr);
				sleep(&e->wr, isowready, e);
				XPRINT("writeusb: awake\n");
			}
			if (e->psize > e->maxpkt)
				panic("packet size > maximum");
			b = e->curblock;
			if (b == nil)
				panic("writeusb: block");
			td = e->etd;
			e->isolock = 0;
			td->flags |= IsoLock;
			td->flags &= ~IsoClean;
			q = (uchar*)td->bp + e->curbytes;
			if((i = n) >= e->psize - e->curbytes)
				i = e->psize - e->curbytes;
			if (e->curbytes != BLEN(b))
				iprint("mismatch: curbytes %d, blen %ld\n", e->curbytes, BLEN(b));
			memmove(b->wp, p, i);
			b->wp += i;
			memmove(q, p, i);
			p += i;
			n -= i;
			e->curbytes += i;
			td->flags &= ~IsoLock;
			if (e->curbytes < e->psize){
				e->curblock = b;
				if (n != 0)
					panic("usb iso: can't happen");
				break;
			}
			if (BLEN(b) != e->psize)
				panic("usbwrite: length mismatch");
			e->curblock = isoxmit(e, b, TokOUT);
			e->etd = td->next;
			e->curbytes = 0;
			e->curblock->wp = e->curblock->rp;
			e->psize = ((e->etd->dev >> 21) & 0x7ff) + 1;
			if (e->psize > e->maxpkt)
				panic("packet size > maximum");
			e->nbytes += e->psize;
			e->nblocks++;
		}else{
		} while(n > 0);
		poperror();
	}else{
		do {
			int j;

			if(e->err)
				error(e->err);
			if((i = n) >= e->maxpkt)
				i = e->maxpkt;
			b = allocb(i);


@@ 1851,7 1897,7 @@ writeusb(Endpt *e, void *a, long n, int tok)
				freeb(b);
				nexterror();
			}
			XPRINT("out [%ld]", i);
			XPRINT("out [%d]", i);
			for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]);
			XPRINT("\n");
			memmove(b->wp, p, i);


@@ 1868,8 1914,8 @@ writeusb(Endpt *e, void *a, long n, int tok)
				sleep(&e->wr, qisempty, qh);
				XPRINT("writeusb: awake\n");
			}
		}
	} while(n > 0);
		} while(n > 0);
	}
	poperror();
	qunlock(&e->wlock);
	return p-(uchar*)a;


@@ 1964,7 2010,7 @@ usbwrite(Chan *c, void *a, long n, vlong)
			e->err = nil;
		}else if(nf == 6 && strcmp(fields[0], "ep") == 0){
			/* ep n `bulk' mode maxpkt nbuf     OR
			 * ep n period mode samplesize hz
			 * ep n period mode samplesize KHz
			 */
			i = strtoul(fields[1], nil, 0);
			if(i < 0 || i >= nelem(d->ep)) {