Plan 9 from Bell Labs’s /usr/web/sources/extra/9hist/pc/devusb.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


## diffname pc/devusb.c 1999/1005
## diff -e /dev/null /n/emeliedump/1999/1005/sys/src/brazil/pc/devusb.c
0a
/*
 * UHCI USB driver
 *	(c) 1998, 1999 C H Forsyth, [email protected]
 * to do:
 *	endpoint open/close
 *	build Endpt on open from attributes stored in Udev?
 *	build data0/data1 rings for bulk and interrupt endpoints
 *	endpoint TD rings (can there be prefetch?)
 *	hubs?
 *	special handling of isochronous traffic?
 *	is use of Queues justified? (could have client clean TD rings on wakeup)
 *	bandwidth check
 */

#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"../port/error.h"

/*
#ifndef Inferno
#include	"devtab.h"
#include	"brazilia.h"
#endif
*/

#define Pprint
#define Chatty	1
#define DPRINT if(Chatty)print
#define XPRINT if(0)print

/*
 * USB packet definitions
 */
enum {
	TokIN = 0x69,
	TokOUT = 0xE1,
	TokSETUP = 0x2D,

	/* request type */
	RH2D = 0<<7,
	RD2H = 1<<7,
	Rstandard = 0<<5,
	Rclass = 1<<5,
	Rvendor = 2<<5,
	Rdevice = 0,
	Rinterface = 1,
	Rendpt = 2,
	Rother = 3,
};

typedef uchar byte;

typedef struct Ctlr Ctlr;
typedef struct Endpt Endpt;
typedef struct QTree QTree;
typedef struct Udev Udev;

/*
 * UHCI hardware structures, aligned on 16-byte boundary
 */
typedef struct QH QH;
typedef struct TD TD;

struct TD {
	ulong	link;
	ulong	status;	/* controller r/w */
	ulong	dev;
	ulong	buffer;

	/* software */
	ulong	flags;
	Block*	bp;
	Endpt*	ep;
	TD*	next;
};
#define	TFOL(p)	((TD*)KADDR((ulong)(p) & ~0xF))

struct QH {
	ulong	head;
	ulong	entries;	/* address of next TD or QH to process (updated by controller) */

	/* software */
	union {
		TD*	first;
		QH*	next;		/* free list */
	};
	TD*	last;
};
#define	QFOL(p)	((QH*)KADDR((ulong)(p) & ~0xF))

/*
 * UHCI interface registers and bits
 */
enum {
	/* i/o space */
	Cmd = 0,
	Status = 2,
	Usbintr = 4,
	Frnum = 6,
	Flbaseadd = 8,
	SOFMod = 0xC,
	Portsc0 = 0x10,
	Portsc1 = 0x12,

	/* port status */
	Suspend = 1<<12,
	PortReset = 1<<9,
	SlowDevice = 1<<8,
	ResumeDetect = 1<<6,
	PortChange = 1<<3,	/* write 1 to clear */
	PortEnable = 1<<2,
	StatusChange = 1<<1,	/* write 1 to clear */
	DevicePresent = 1<<0,

	FRAMESIZE=	4096,	/* fixed by hardware; aligned to same */
	NFRAME = 	FRAMESIZE/4,

	Vf = 1<<2,	/* TD only */
	IsQH = 1<<1,
	Terminate = 1<<0,

	/* TD.status */
	SPD = 1<<29,
	ErrLimit0 = 0<<27,
	ErrLimit1 = 1<<27,
	ErrLimit2 = 2<<27,
	ErrLimit3 = 3<<27,
	LowSpeed = 1<<26,
	IsoSelect = 1<<25,
	IOC = 1<<24,
	Active = 1<<23,
	Stalled = 1<<22,
	DataBufferErr = 1<<21,
	Babbling = 1<<20,
	NAKed = 1<<19,
	CRCorTimeout = 1<<18,
	BitstuffErr = 1<<17,
	AnyError = (Stalled | DataBufferErr | Babbling | NAKed | CRCorTimeout | BitstuffErr),

	/* TD.dev */
	IsDATA1 =	1<<19,

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

/*
 * software structures
 */
struct QTree {
	QLock;
	int	nel;
	int	depth;
	QH*	root;
	ulong*	bw;
};

#define	GET2(p)	((((p)[1]&0xFF)<<8)|((p)[0]&0xFF))
#define	PUT2(p,v)	(((p)[0] = (v)), ((p)[1] = (v)>>8))

/*
 * active USB device
 */
struct Udev {
	Ref;
	Lock;
	int	x;	/* index in usbdev[] */
	int	busy;
	int	state;
	int	id;
	byte	port;		/* port number on connecting hub */
	byte	class;
	byte	subclass;
	byte	proto;
	int	ls;
	int	npt;
	Endpt*	ep[16];		/* active end points */
	Udev*	ports;	/* active ports, if hub */
	Udev*	next;		/* next device on this hub */
};

/* device parameters */
enum {
	/* Udev.state */
	Disabled = 0,
	Attached,
	Enabled,
	Assigned,
	Configured,

	/* Udev.class */
	Noclass = 0,
	Hubclass = 9,
};

static char *devstates[] = {
	[Disabled]	"Disabled",
	[Attached] "Attached",
	[Enabled] "Enabled",
	[Assigned] "Assigned",
	[Configured] "Configured",
};

/*
 * device endpoint
 */
struct Endpt {
	Ref;
	Lock;
	int	x;	/* index in Udev.ep */
	int	id;		/* hardware endpoint address */
	int	maxpkt;	/* maximum packet size (from endpoint descriptor) */
	int	data01;	/* 0=DATA0, 1=DATA1 */
	byte	eof;
	byte	class;
	byte	subclass;
	byte	proto;
	byte	mode;	/* OREAD, OWRITE, ORDWR */
	byte	nbuf;	/* number of buffers allowed */
	byte	periodic;
	byte	iso;
	byte	debug;
	byte	active;	/* listed for examination by interrupts */
	int	sched;	/* schedule index; -1 if undefined or aperiodic */
	int	setin;
	ulong	bw;	/* bandwidth requirement */
	int	pollms;	/* polling interval in msec */
	QH*	epq;	/* queue of TDs for this endpoint */

	QLock	rlock;
	Rendez	rr;
	Queue*	rq;
	QLock	wlock;
	Rendez	wr;
	Queue*	wq;

	int	ntd;
	char*	err;

	Udev*	dev;	/* owning device */

	Endpt*	activef;	/* active endpoint list */

	ulong	nbytes;
	ulong	nblocks;
};

struct Ctlr {
	Lock;	/* protects state shared with interrupt (eg, free list) */
	int	io;
	ulong*	frames;	/* frame list */
	int	idgen;	/* version number to distinguish new connections */
	QLock	resetl;	/* lock controller during USB reset */

	TD*	tdpool;
	TD*	freetd;
	QH*	qhpool;
	QH*	freeqh;

	QTree*	tree;	/* tree for periodic Endpt i/o */
	QH*	ctlq;	/* queue for control i/o */
	QH*	bwsop;	/* empty bandwidth sop (to PIIX4 errata specifications) */
	QH*	bulkq;	/* queue for bulk i/o (points back to bandwidth sop) */

	Udev*	ports[2];
};
#define	IN(x)	ins(ub->io+(x))
#define	OUT(x, v)	outs(ub->io+(x), (v))

static	Ctlr	ubus;
static	char	Estalled[] = "usb endpoint stalled";

static	QLock	usbstate;	/* protects name space state */
static	Udev*	usbdev[32];
static struct {
	Lock;
	Endpt*	f;
} activends;

static long readusb(Endpt*, void*, long);
static long writeusb(Endpt*, void*, long, int);

static TD *
alloctd(Ctlr *ub)
{
	TD *t;

	ilock(ub);
	t = ub->freetd;
	if(t == nil)
		panic("alloctd");	/* TO DO */
	ub->freetd = t->next;
	t->next = nil;
	iunlock(ub);
	t->ep = nil;
	t->bp = nil;
	t->status = 0;
	t->link = Terminate;
	t->buffer = 0;
	t->flags = 0;
	return t;
}

static void
freetd(TD *t)
{
	Ctlr *ub;

	ub = &ubus;
	t->ep = nil;
	if(t->bp)
		freeb(t->bp);
	t->bp = nil;
	ilock(ub);
	t->buffer = 0xdeadbeef;
	t->next = ub->freetd;
	ub->freetd = t;
	iunlock(ub);
}

static void
dumpdata(Block *b, int n)
{
	int i;

	XPRINT("\tb %8.8lux[%d]: ", (ulong)b->rp, n);
	if(n > 16)
		n = 16;
	for(i=0; i<n; i++)
		XPRINT(" %2.2ux", b->rp[i]);
	XPRINT("\n");
}

static void
dumptd(TD *t, int follow)
{
	int i, n;
	char buf[20], *s;
	TD *t0;

	t0 = t;
	while(t){
		i = t->dev & 0xFF;
		if(i == TokOUT || i == TokSETUP)
			n = ((t->dev>>21) + 1) & 0x7FF;
		else if((t->status & Active) == 0)
			n = (t->status + 1) & 0x7FF;
		else
			n = 0;
		s = buf;
		if(t->status & Active)
			*s++ = 'A';
		if(t->status & Stalled)
			*s++ = 'S';
		if(t->status & DataBufferErr)
			*s++ = 'D';
		if(t->status & Babbling)
			*s++ = 'B';
		if(t->status & NAKed)
			*s++ = 'N';
		if(t->status & CRCorTimeout)
			*s++ = 'T';
		if(t->status & BitstuffErr)
			*s++ = 'b';
		if(t->status & LowSpeed)
			*s++ = 'L';
		*s = 0;
		print("td %8.8lux: l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n",
			t, t->link, t->status, t->dev, t->buffer, t->bp?(ulong)t->bp->rp:0, t->flags);
		print("\ts=%s,ep=%ld,d=%ld,D=%ld\n", buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1);
		if(t->bp)
			dumpdata(t->bp, n);
		if(!follow || t->link & Terminate || t->link & IsQH)
			break;
		t = TFOL(t->link);
		if(t == t0)
			break;	/* looped */
	}
}

static TD *
alloctde(Endpt *e, int pid, int n)
{
	TD *t;
	int tog, id;

	t = alloctd(&ubus);
	id = (e->x<<7)|(e->dev->x&0x7F);
	tog = 0;
	if(e->data01 && pid != TokSETUP)
		tog = IsDATA1;
	t->ep = e;
	t->status = ErrLimit3 | Active | IOC;	/* or put IOC only on last? */
	if(e->dev->ls)
		t->status |= LowSpeed;
	t->dev = ((n-1)<<21) | ((id&0x7FF)<<8) | pid | tog;
	return t;
}

static TD *
alloctdring(int nbuf, Endpt *e, int pid, int maxn)
{
	TD *t, *lt, *ft;
	int id, i;
	ulong status, dev;

	if(!e->iso)
		nbuf = (nbuf+1)&~1;
	id = (e->x<<7)|(e->dev->x&0x7F);
	dev = ((maxn-1)<<21) | ((id&0x7FF)<<8) | pid;
	status = 0;
	if(e->dev->ls)
		status |= LowSpeed;
	ft = lt = nil;
	for(i=0; i<nbuf; i++){
		t = alloctd(&ubus);
		t->ep = e;
		t->status = status;
		t->dev =  dev;
		if(!e->iso)
			dev ^= IsDATA1;
		if(pid == TokIN){
			t->bp = allocb(maxn);
			t->buffer = PADDR(t->bp->wp);
		}
		if(ft != nil){
			lt->next = t;
			lt->link = PADDR(t);
		}else
			ft = t;
		lt = t;
	}
	if(lt != nil)
		lt->link = PADDR(ft);	/* loop to form ring */
	return ft;
}

static QH *
allocqh(Ctlr *ub)
{
	QH *qh;

	ilock(ub);
	qh = ub->freeqh;
	if(qh == nil)
		panic("allocqh");	/* TO DO */
	ub->freeqh = qh->next;
	qh->next = nil;
	iunlock(ub);
	qh->head = Terminate;
	qh->entries = Terminate;
	qh->first = nil;
	qh->last = nil;
	return qh;
}

static void
freeqh(Ctlr *ub, QH *qh)
{
	ilock(ub);
	qh->next = ub->freeqh;
	ub->freeqh = qh;
	iunlock(ub);
}

static void
dumpqh(QH *q)
{
	int i;
	QH *q0;

	q0 = q;
	for(i = 0; q != nil && i < 10; i++){
		pprint("qh %8.8lux: %8.8lux %8.8lux\n", q, q->head, q->entries);
		if((q->entries & Terminate) == 0)
			dumptd(TFOL(q->entries), 1);
		if(q->head & Terminate)
			break;
		if((q->head & IsQH) == 0){
			pprint("head:");
			dumptd(TFOL(q->head), 1);
			break;
		}
		q = QFOL(q->head);
		if(q == q0)
			break;	/* looped */
	}
}

static void
queuetd(Ctlr *ub, QH *q, TD *t, int vf)
{
	TD *lt;

	for(lt = t; lt->next != nil; lt = lt->next)
		lt->link = PADDR(lt->next) | vf;
	lt->link = Terminate;
	ilock(ub);
	if(q->first != nil){
		q->last->link = PADDR(t) | vf;
		q->last->next = t;
	}else{
		q->first = t;
		q->entries = PADDR(t);
	}
	q->last = lt;
	iunlock(ub);
}

static void
cleantd(TD *t, int discard)
{
	Block *b;
	int n, err;

	XPRINT("cleanTD: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
	if(t->ep != nil && t->ep->debug)
		dumptd(t, 0);
	if(t->status & Active)
		panic("cleantd Active");
	err = t->status & (AnyError&~NAKed);
	/* TO DO: on t->status&AnyError, q->entries will not have advanced */
	if (err)
		print("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
	switch(t->dev&0xFF){
	case TokIN:
		if(discard || (t->flags & CancelTD) || t->ep == nil || t->ep->x!=0&&err){
			if(t->ep != nil){
				if(err != 0)
					t->ep->err = err==Stalled? Estalled: Eio;
				wakeup(&t->ep->rr);	/* in case anyone cares */
			}
			break;
		}
		b = t->bp;
		n = (t->status + 1) & 0x7FF;
		if(n > b->lim - b->wp)
			n = 0;
		b->wp += n;
		if(Chatty)
			dumpdata(b, n);
		t->bp = nil;
		t->ep->nbytes += n;
		t->ep->nblocks++;
		qpass(t->ep->rq, b);	/* TO DO: flow control */
		wakeup(&t->ep->rr);	/* TO DO */
		break;
	case TokSETUP:
		XPRINT("cleanTD: TokSETUP %lux\n", &t->ep);
		/* don't really need to wakeup: subsequent IN or OUT gives status */
		if(t->ep != nil) {
			wakeup(&t->ep->wr);	/* TO DO */
			XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr);
		}
		break;
	case TokOUT:
		/* TO DO: mark it done somewhere */
		XPRINT("cleanTD: TokOut %lux\n", &t->ep);
		if(t->ep != nil){
			if(t->bp){
				n = BLEN(t->bp);
				t->ep->nbytes += n;
				t->ep->nblocks++;
			}
			if(t->ep->x!=0 && err != 0)
				t->ep->err = err==Stalled? Estalled: Eio;
			if(--t->ep->ntd < 0)
				panic("cleantd ntd");
			wakeup(&t->ep->wr);	/* TO DO */
			XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr);
		}
		break;
	}
	freetd(t);
}

static void
cleanq(QH *q, int discard)
{
	TD *t;
	Ctlr *ub;

	ub = &ubus;
	ilock(ub);
	for(t = q->first; t != nil;){
//		XPRINT("cleanq: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
		if(t->status & Active){
			if(t->flags & CancelTD){
				XPRINT("cancelTD: %8.8lux\n", (ulong)t);
				t->status = (t->status & ~Active) | IOC;	/* ensure interrupt next frame */
				t = t->next;
				continue;
			}
			break;
		}
		t->status &= ~IOC;
		/* TO DO: need to deal with TDs as chain if necessary */
		q->first = t->next;
		if(q->first != nil)
			q->entries = PADDR(q->first);
		else
			q->entries = Terminate;
		t->next = nil;
		iunlock(ub);
		cleantd(t, discard);
		ilock(ub);
		t = q->first;
	}
	iunlock(ub);
}

static void
canceltds(Ctlr *ub, QH *q, Endpt *e)
{
	TD *t;

	if(q != nil){
		ilock(ub);
		for(t = q->first; t != nil; t = t->next)
			if(t->ep == e)
				t->flags |= CancelTD;
		iunlock(ub);
		pprint("cancel:\n");
		dumpqh(q);
	}
}

static void
eptcancel(Endpt *e)
{
	Ctlr *ub;

	if(e == nil)
		return;
	ub = &ubus;
	canceltds(ub, e->epq, e);
	canceltds(ub, ub->ctlq, e);
	canceltds(ub, ub->bulkq, e);
}

static void
eptactivate(Endpt *e)
{
	ilock(&activends);
	if(e->active == 0){
		e->active = 1;
		e->activef = activends.f;
		activends.f = e;
	}
	iunlock(&activends);
}

static void
eptdeactivate(Endpt *e)
{
	Endpt **l;

	/* could be O(1) but not worth it yet */
	ilock(&activends);
	if(e->active){
		e->active = 0;
		for(l = &activends.f; *l != e; l = &(*l)->activef)
			if(*l == nil){
				iunlock(&activends);
				panic("usb eptdeactivate");
			}
		*l = e->activef;
	}
	iunlock(&activends);
}

static QH*
qxmit(Endpt *e, Block *b, int pid)
{
	TD *t;
	int n, vf;
	Ctlr *ub;
	QH *qh;

	if(b != nil){
		n = BLEN(b);
		t = alloctde(e, pid, n);
		t->bp = b;
		t->buffer = PADDR(b->rp);
	}else
		t = alloctde(e, pid, 0);
	ub = &ubus;
	ilock(ub);
	e->ntd++;
	iunlock(ub);
if(e->debug)pprint("QTD: %8.8lux n=%ld\n", t, b?BLEN(b): 0);
	vf = 0;
	if(e->x == 0){
		qh = ub->ctlq;
		vf = 0;
	}else if((qh = e->epq) == nil || e->mode != OWRITE){
		qh = ub->bulkq;
		vf = Vf;
	}
	queuetd(ub, qh, t, vf);
	return qh;
}

static QH*
qrcv(Endpt *e)
{
	TD *t;
	Block *b;
	Ctlr *ub;
	QH *qh;
	int vf;

	t = alloctde(e, TokIN, e->maxpkt);
	b = allocb(e->maxpkt);
	t->bp = b;
	t->buffer = PADDR(b->wp);
	ub = &ubus;
	vf = 0;
	if(e->x == 0){
		qh = ub->ctlq;
		vf = 0;
	}else if((qh = e->epq) == nil || e->mode != OREAD){
		qh = ub->bulkq;
		vf = Vf;
	}
	queuetd(ub, qh, t, vf);
	return qh;
}

static Block *
usbreq(int type, int req, int value, int offset, int count)
{
	Block *b;

	b = allocb(8);
	b->wp[0] = type;
	b->wp[1] = req;
	PUT2(b->wp+2, value);
	PUT2(b->wp+4, offset);
	PUT2(b->wp+6, count);
	b->wp += 8;
	return b;
}

/*
 * return smallest power of 2 >= n
 */
static int
flog2(int n)
{
	int i;

	for(i=0; (1<<i)<n; i++)
		;
	return i;
}

/*
 * build the periodic scheduling tree:
 * framesize must be a multiple of the tree size
 */
static QTree *
mkqhtree(ulong *frame, int framesize, int maxms)
{
	int i, n, d, o, leaf0, depth;
	QH *tree, *qh;
	QTree *qt;

	depth = flog2(maxms);
	n = (1<<(depth+1))-1;
	qt = mallocz(sizeof(*qt), 1);
	if(qt == nil)
		return nil;
	qt->nel = n;
	qt->depth = depth;
	qt->bw = mallocz(n*sizeof(qt->bw), 1);
	if(qt->bw == nil){
		free(qt);
		return nil;
	}
	tree = xspanalloc(n*sizeof(QH), 16, 0);
	if(tree == nil){
		free(qt);
		return nil;
	}
	qt->root = tree;
	tree->head = Terminate;	/* root */
	tree->entries = Terminate;
	for(i=1; i<n; i++){
		qh = &tree[i];
		qh->head = PADDR(&tree[(i-1)/2]) | IsQH;
		qh->entries = Terminate;
	}
	/* distribute leaves evenly round the frame list */
	leaf0 = n/2;
	for(i=0; i<framesize; i++){
		o = 0;
		for(d=0; d<depth; d++){
			o <<= 1;
			if(i & (1<<d))
				o |= 1;
		}
		if(leaf0+o >= n){
			pprint("leaf0=%d o=%d i=%d n=%d\n", leaf0, o, i, n);
			break;
		}
		frame[i] = PADDR(&tree[leaf0+o]) | IsQH;
	}
	return qt;
}

static void
dumpframe(int f, int t)
{
	QH *q, *tree;
	ulong p, *frame;
	int i, n;

	n = ubus.tree->nel;
	tree = ubus.tree->root;
	frame = ubus.frames;
	if(f < 0)
		f = 0;
	if(t < 0)
		t = 32;
	for(i=f; i<t; i++){
		pprint("F%.2d %8.8lux %8.8lux\n", i, frame[i], QFOL(frame[i])->head);
		for(p=frame[i]; (p & IsQH) && (p &Terminate) == 0; p = q->head){
			q = QFOL(p);
			if(!(q >= tree && q < &tree[n])){
				pprint("Q: p=%8.8lux out of range\n", p);
				break;
			}
			pprint("  -> %8.8lux h=%8.8lux e=%8.8lux\n", p, q->head, q->entries);
		}
	}
}

static int
pickschedq(QTree *qt, int pollms, ulong bw, ulong limit)
{
	int i, j, d, ub, q;
	ulong best, worst, total;

	d = flog2(pollms);
	if(d > qt->depth)
		d = qt->depth;
	q = -1;
	worst = 0;
	best = ~0;
	ub = (1<<(d+1))-1;
	for(i=(1<<d)-1; i<ub; i++){
		total = qt->bw[0];
		for(j=i; j > 0; j=(j-1)/2)
			total += qt->bw[j];
		if(total < best){
			best = total;
			q = i;
		}
		if(total > worst)
			worst = total;
	}
	if(worst+bw >= limit)
		return -1;
	return q;
}

static int
schedendpt(Endpt *e)
{
	Ctlr *ub;
	QH *qh;
	int q;

	if(!e->periodic || e->sched >= 0)
		return 0;
	ub = &ubus;
	if(e->epq == nil){
		e->epq = allocqh(ub);
		if(e->epq == nil)
			return -1;
	}
	qlock(ub->tree);
	q = pickschedq(ub->tree, e->pollms, e->bw, ~0);	/* TO DO: bus bandwidth limit */
	if(q < 0)
		return -1;
	ub->tree->bw[q] += e->bw;
	qh = &ub->tree->root[q];
	e->sched = q;
	e->epq->head = qh->entries;
	e->epq->entries = Terminate;
	qh->entries = PADDR(e->epq) | IsQH;
	qunlock(ub->tree);
	return 0;
}

static void
unschedendpt(Endpt *e)
{
	Ctlr *ub;
	ulong p;
	QH *qh;
	int q;

	ub = &ubus;
	if(e->epq == nil || (q = e->sched) < 0)
		return;
	p = PADDR(e->epq) | IsQH;
	qlock(ub->tree);
	ub->tree->bw[q] -= e->bw;
	qh = &ub->tree->root[q];
	for(; qh->entries != p; qh = QFOL(qh->entries))
		if(qh->entries & Terminate || (qh->entries & IsQH) == 0){
			qunlock(ub->tree);
			panic("usb: unschedendpt");
		}
	qh->entries = e->epq->head;
	qunlock(ub->tree);
	e->epq->head = Terminate;
}

static Endpt *
devendpt(Udev *d, int id, int add)
{
	Endpt *e, **p;

	p = &d->ep[id&0xF];
	lock(d);
	if((e = *p) != nil){
		incref(e);
		unlock(d);
		return e;
	}
	unlock(d);
	if(!add)
		return nil;
	e = mallocz(sizeof(*e), 1);
	e->ref = 1;
	e->x = id&0xF;
	e->id = id;
	e->periodic = 0;
	e->sched = -1;
	e->maxpkt = 8;
	e->pollms = 0;
	e->bw = 0;
	e->nbuf = 1;
	e->dev = d;
	e->active = 0;
	lock(d);
	if(*p != nil){
		incref(*p);
		unlock(d);
		free(e);
		return *p;
	}
	*p = e;
	unlock(d);
	e->rq = qopen(8*1024, 0, nil, e);
	e->wq = qopen(8*1024, 0, nil, e);
	return e;
}

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

static void
usbdevreset(Udev *d)
{
	d->state = Disabled;
	if(d->class == Hubclass)
		for(d = d->ports; d != nil; d = d->next)
			usbdevreset(d);
}

static void
freedev(Udev *d)
{
	int i;

	if(d != nil && decref(d) == 0){
		for(i=0; i<nelem(d->ep); i++)
			freept(d->ep[i]);
		if(d->x >= 0)
			usbdev[d->x] = nil;
		free(d);
	}
}

static void
hubportreset(Udev *h, int p)
{
	USED(h, p);
	/* reset state of each attached device? */
}

static	int	ioports[] = {-1, Portsc0, Portsc1};

static void
portreset(int port)
{
	Ctlr *ub;
	int i, p;

	/* should check that device not being configured on other port? */
	p = ioports[port];
	ub = &ubus;
	qlock(&ub->resetl);
	if(waserror()){
		qunlock(&ub->resetl);
		nexterror();
	}
	XPRINT("r: %x\n", IN(p));
	ilock(ub);
	OUT(p, PortReset);
	delay(12);	/* BUG */
	XPRINT("r2: %x\n", IN(p));
	OUT(p, IN(p) & ~PortReset);
	XPRINT("r3: %x\n", IN(p));
	OUT(p, IN(p) | PortEnable);
	microdelay(64);
	for(i=0; i<1000 && (IN(p) & PortEnable) == 0; i++)
		;
	XPRINT("r': %x %d\n", IN(p), i);
	OUT(p, (IN(p) & ~PortReset)|PortEnable);
	iunlock(ub);
	hubportreset(nil, port);
	poperror();
	qunlock(&ub->resetl);
}

static void
portenable(int port, int on)
{
	Ctlr *ub;
	int w, p;

	/* should check that device not being configured on other port? */
	p = ioports[port];
	ub = &ubus;
	qlock(&ub->resetl);
	if(waserror()){
		qunlock(&ub->resetl);
		nexterror();
	}
	ilock(ub);
	w = IN(p);
	if(on)
		w |= PortEnable;
	else
		w &= ~PortEnable;
	OUT(p, w);
	microdelay(64);
	iunlock(ub);
	XPRINT("e: %x\n", IN(p));
	if(!on)
		hubportreset(nil, port);
	poperror();
	qunlock(&ub->resetl);
}

static int
portinfo(Ctlr *ub, int *p0, int *p1)
{
	int m, v;

	ilock(ub);
	m = 0;
	if((v = IN(Portsc0)) & PortChange){
		OUT(Portsc0, v);
		m |= 1<<0;
	}
	*p0 = v;
	if((v = IN(Portsc1)) & PortChange){
		OUT(Portsc1, v);
		m |= 1<<1;
	}
	*p1 = v;
	iunlock(ub);
	return m;
}

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

	ub = a;
	s = IN(Status);
	if (s & 0x1a) {
		print("usbint: #%x f%d\n", s, IN(Frnum));
		print("cmd #%x sofmod #%x\n", IN(Cmd), inb(ub->io+SOFMod));
		print("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1));
	}
	OUT(Status, s);

//	XPRINT("cleanq(ub->ctlq, 0)\n");
	cleanq(ub->ctlq, 0);
//	XPRINT("cleanq(ub->bulkq, 0)\n");
	cleanq(ub->bulkq, 0);
	ilock(&activends);
	for(e = activends.f; e != nil; e = e->activef)
		if(e->epq != nil) {
//			XPRINT("cleanq(e->epq, 0)\n");
			cleanq(e->epq, 0);
		}
	iunlock(&activends);
}

static void
resetusbctlr(void)
{
	Ctlr *ub;
	Pcidev *cfg;
	int i;
	ulong port;
	QTree *qt;
	TD *t;

	ub = &ubus;
	memset(&cfg, 0, sizeof(cfg));
	cfg = pcimatch(0, 0x8086, 0x7112);	/* Intel chipset PIIX 4*/
	if(cfg == nil) {
//		cfg = pcimatch(0, 0x8086, 0x7112);	/* Intel chipset PIIX 3*/
//		if(cfg == nil) {
			cfg = pcimatch(0, 0x1106, 0x0586);	/* Via chipset */
			if(cfg == nil) {
				DPRINT("No USB device found\n");
				return;
			}
//		}
	}
	port = cfg->mem[4].bar & ~0x0F;
	if (port == 0) {
		print("usb: failed to map registers\n");
		return;
	}

	print("USB: %x/%x port 0x%lux size 0x%x irq %d\n",
		cfg->vid, cfg->did, port, cfg->mem[4].size, cfg->intl);

	i = inb(port+SOFMod);
	if(0){
		OUT(Cmd, 4);	/* global reset */
		delay(15);
		OUT(Cmd, 0);	/* end reset */
		delay(4);
	}
	outb(port+SOFMod, i);
	// Interrupt handler
	intrenable(cfg->intl, interrupt, ub, cfg->tbdf, "usb");

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

	/*
	 * the root of the periodic (interrupt & isochronous) scheduling tree
	 * points to the control queue and the bandwidth sop for bulk traffic.
	 * this is looped following the instructions in PIIX4 errata 29773804.pdf:
	 * a QH links to a looped but inactive TD as its sole entry,
	 * with its head entry leading on to the bulk traffic, the last QH of which
	 * links back to the empty QH.
	 */
	ub->bulkq = allocqh(ub);
	t = alloctd(ub);	/* inactive TD, looped */
	t->link = PADDR(t);
	ub->bwsop = allocqh(ub);
	ub->bwsop->head = PADDR(ub->bulkq) | IsQH;
	ub->bwsop->entries = PADDR(t);
	ub->ctlq = allocqh(ub);
	ub->ctlq->head = PADDR(ub->bwsop) | IsQH;
//	ub->bulkq->head = PADDR(ub->bwsop) | IsQH;	/* loop back */
	print("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n",
		IN(Cmd), IN(Status), IN(Usbintr), inb(port+Frnum));
	print("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n",
		IN(Flbaseadd), inb(port+SOFMod), IN(Portsc0), IN(Portsc1));
	OUT(Cmd, 0);	/* stop */
	ub->frames = xspanalloc(FRAMESIZE, FRAMESIZE, 0);
	qt = mkqhtree(ub->frames, NFRAME, 32);
	if(qt == nil){
		print("usb: can't allocate scheduling tree\n");
		ub->io = 0;
		return;
	}
	qt->root->head = PADDR(ub->ctlq) | IsQH;
	ub->tree = qt;
	print("usb tree: nel=%d depth=%d\n", qt->nel, qt->depth);

	outl(port+Flbaseadd, PADDR(ub->frames));
	OUT(Frnum, 0);
	OUT(Usbintr, 0xF);	/* enable all interrupts */
	print("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(port+SOFMod));
	print("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1));
}

enum
{
	Qtopdir = 0,
	Q2nd,
	Qbusctl,
	Qnew,
	Qport,
	Q3rd,
	Qctl,
	Qsetup,
	Qdebug,
	Qstatus,
	Qep0,
	/* other endpoint files */
};

/*
 * Qid path is:
 *	 8 bits of file type (qids above)
 *	10 bits of slot number +1; 0 means not attached to device
 */
#define	QSHIFT	8	/* location in qid of device # */
#define	QMASK	((1<<QSHIFT)-1)

#define	QID(q)		((q).path&QMASK)
#define	DEVPATH(q)	(((q)&~CHDIR)>>QSHIFT)

static Dirtab usbdir2[] = {
	"new",	{Qnew},	0,	0666,
	"ctl",		{Qbusctl},	0,	0666,
	"port",	{Qport},	0,	0444,
};

static Dirtab usbdir3[]={
	"ctl",		{Qctl},	0,	0666,
	"setup",	{Qsetup},	0,	0666,
	"status",	{Qstatus},	0,	0444,
	"debug",	{Qdebug},	1,	0666,
	/* epNdata names are generated on demand */
};

static Udev *
usbdeviceofpath(ulong path)
{
	int s;

	s = DEVPATH(path);
	if(s == 0)
		return nil;
	return usbdev[s-1];
}

static Udev *
usbdevice(Chan *c)
{
	Udev *d;

	d = usbdeviceofpath(c->qid.path);
	if(d == nil || d->id != c->qid.vers || d->state == Disabled)
		error(Ehungup);
	return d;
}

static Udev *
usbnewdevice(void)
{
	Udev *d;
	Endpt *e;
	int i;

	d = nil;
	qlock(&usbstate);
	if(waserror()){
		qunlock(&usbstate);
		nexterror();
	}
	for(i=0; i<nelem(usbdev); i++)
		if(usbdev[i] == nil){
			ubus.idgen++;
			d = mallocz(sizeof(*d), 1);
			d->ref = 1;
			d->x = i;
			d->id = (ubus.idgen << 8) | i;
			d->state = Enabled;
			e = devendpt(d, 0, 1);	/* always provide control endpoint 0 */
			e->mode = ORDWR;
			e->periodic = 0;
			e->sched = -1;
			usbdev[i] = d;
			break;
		}
	poperror();
	qunlock(&usbstate);
	return d;
}

static void
usbreset(void)
{
	resetusbctlr();
}

void
usbinit(void)
{
	Udev *d;

	if(ubus.io != 0 && usbdev[0] == nil){
		d = usbnewdevice();	/* reserve device 0 for configuration */
		incref(d);
		d->state = Attached;
	}
}

Chan *
usbattach(char *spec)
{
	Ctlr *ub;

	ub = &ubus;
	if(ub->io == 0)
		error(Enodev);
	if((IN(Cmd)&1)==0 || *spec)
		OUT(Cmd, 1);	/* run */
//	pprint("at: c=%x s=%x c0=%x\n", IN(Cmd), IN(Status), IN(Portsc0));
	return devattach('U', spec);
}

Chan *
usbclone(Chan *c, Chan *nc)
{
	return devclone(c, nc);
}

static int
usbgen(Chan *c, Dirtab*, int, int s, Dir *dp)
{
	int t;
	Qid q;
	ulong path;
	Udev *d;
	Dirtab *tab;
	Endpt *e;
	char buf[NAMELEN];

	/*
	 * Top level directory contains the name of the device.
	 */
	if(c->qid.path == CHDIR){
		if(s == 0){
			devdir(c, (Qid){CHDIR|Q2nd, 0}, "usb", 0, eve, 0555, dp);
			return 1;
		}
		return -1;
	}

	/*
	 * Second level contains "new" plus all the clients.
	 */
	t = QID(c->qid);
	if(t < Q3rd){
		if(s < nelem(usbdir2)){
			tab = &usbdir2[s];
			devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
			return 1;
		}
		if((s -= nelem(usbdir2)) >= 0 && s < nelem(usbdev)){
			d = usbdev[s];
			if(d == nil)
				return 0;
			sprint(buf, "%d", s);
			q = (Qid){CHDIR|((s+1)<<QSHIFT)|Q3rd, d->id};
			devdir(c, q, buf, 0, eve, 0555, dp);
			return 1;
		}
		return -1;
	}

	/*
	 * Third level.
	 */
	path = c->qid.path&~(CHDIR|QMASK);	/* slot component */
	q.vers = c->qid.vers;
	if(s < nelem(usbdir3)){
		Dirtab *tab = &usbdir3[s];
		q.path = path | tab->qid.path;
		devdir(c, q, tab->name, tab->length, eve, tab->perm, dp);
		return 1;
	}

	/* active endpoints */
	d = usbdeviceofpath(path);
	if(d == nil)
		return -1;
	s -= nelem(usbdir3);
	if(s < 0 || s >= nelem(d->ep))
		return -1;
	if(s == 0 || (e = d->ep[s]) == nil)	/* ep0data is called "setup" */
		return 0;
	sprint(buf, "ep%ddata", s);
	q.path = path | (Qep0+s);
	devdir(c, q, buf, 0, eve, e->mode==OREAD? 0444: e->mode==OWRITE? 0222: 0666, dp);
	return 1;
}

int
usbwalk(Chan *c, char *name)
{
	if(strcmp(name, "..") == 0){
		switch(QID(c->qid)){
		case Qtopdir:
			return 1;
		case Q2nd:
			c->qid = (Qid){CHDIR|Qtopdir, 0};
			break;
		case Q3rd:
			c->qid = (Qid){CHDIR|Q2nd, 0};
			break;
		default:
			panic("usbwalk %lux", c->qid.path);
		}
		return 1;
	}
	return devwalk(c, name, nil, 0, usbgen);
}

void
usbstat(Chan *c, char *db)
{
	devstat(c, db, nil, 0, usbgen);
}

Chan *
usbopen(Chan *c, int omode)
{
	Udev *d;
	int f, s;

	if(c->qid.path & CHDIR)
		return devopen(c, omode, nil, 0, usbgen);

	f = 0;
	if(QID(c->qid) == Qnew){
		d = usbnewdevice();
		if(d == nil)
			error(Enodev);
		c->qid.path = Qctl|((d->x+1)<<QSHIFT);
		c->qid.vers = d->id;
		f = 1;
	}

	if(c->qid.path < Q3rd)
		return devopen(c, omode, nil, 0, usbgen);

	qlock(&usbstate);
	if(waserror()){
		qunlock(&usbstate);
		nexterror();
	}

	switch(QID(c->qid)){
	case Qctl:
		d = usbdevice(c);
		if(0&&d->busy)
			error(Einuse);
		d->busy = 1;
		if(!f)
			incref(d);
		break;

	default:
		d = usbdevice(c);
		s = QID(c->qid) - Qep0;
		if(s >= 0 && s < nelem(d->ep)){
			Endpt *e;
			if((e = d->ep[s]) == nil)
				error(Enodev);
			if(schedendpt(e) < 0)
				error("can't schedule USB endpoint");
			eptactivate(e);
		}
		incref(d);
		break;
	}
	poperror();
	qunlock(&usbstate);
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

void
usbcreate(Chan *c, char *name, int omode, ulong perm)
{
	USED(c, name, omode, perm);
	error(Eperm);
}

void
usbremove(Chan*)
{
	error(Eperm);
}

void
usbwstat(Chan *c, char *dp)
{
	USED(c, dp);
	error(Eperm);
}

void
usbclose(Chan *c)
{
	Udev *d;

	if(c->qid.path & CHDIR || c->qid.path < Q3rd)
		return;
	qlock(&usbstate);
	if(waserror()){
		qunlock(&usbstate);
		nexterror();
	}
	d = usbdevice(c);
	if(QID(c->qid) == Qctl)
		d->busy = 0;
	if(c->flag & COPEN)
		freedev(d);
	poperror();
	qunlock(&usbstate);
}

static int
eptinput(void *arg)
{
	Endpt *e;

	e = arg;
	return e->eof || e->err || qcanread(e->rq);
}

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

	qlock(&e->rlock);
	if(waserror()){
		qunlock(&e->rlock);
		eptcancel(e);
		nexterror();
	}
	p = a;
	do {
		if(e->eof) {
			pprint("e->eof\n");
			break;
		}
		if(e->err)
			error(e->err);
		qrcv(e);
		if(!e->iso)
			e->data01 ^= 1;
		sleep(&e->rr, eptinput, e);
		if(e->err)
			error(e->err);
		b = qget(e->rq);	/* TO DO */
		if(b == nil) {
			pprint("b == nil\n");
			break;
		}
		if(waserror()){
			freeb(b);
			nexterror();
		}
		l = BLEN(b);
		if((i = l) > n)
			i = n;
		if(i > 0){
			memmove(p, b->rp, i);
			p += i;
		}
		poperror();
		freeb(b);
		n -= i;
	} while (l == e->maxpkt && n > 0);
	poperror();
	qunlock(&e->rlock);
	return p-(uchar*)a;
}

long
usbread(Chan *c, void *a, long n, vlong offset)
{
	Endpt *e;
	Udev *d;
	char buf[48], *s;
	int t, w0, w1, ps, l, i;

	if(c->qid.path & CHDIR)
		return devdirread(c, a, n, nil, 0, usbgen);

	t = QID(c->qid);
	switch(t){
	case Qbusctl:
		snprint(buf, sizeof(buf), "%11d %11d ", 0, 0);
		return readstr(offset, a, n, buf);

	case Qport:
		ps = portinfo(&ubus, &w0, &w1);
		snprint(buf, sizeof(buf), "0x%ux 0x%ux 0x%ux ", ps, w0, w1);
		return readstr(offset, a, n, buf);

	case Qctl:
		d = usbdevice(c);
		sprint(buf, "%11d %11d ", d->x, d->id);
		return readstr(offset, a, n, buf);

	case Qsetup:	/* endpoint 0 */
		d = usbdevice(c);
		if((e = d->ep[0]) == nil)
			error(Eio);	/* can't happen */
		e->data01 = 1;
		n = readusb(e, a, n);
		if(e->setin){
			e->setin = 0;
			e->data01 = 1;
			writeusb(e, "", 0, TokOUT);
		}
		break;

	case Qdebug:
		n=0;
		break;

	case Qstatus:
		d = usbdevice(c);
		s = smalloc(READSTR);
		if(waserror()){
			free(s);
			nexterror();
		}
		l = snprint(s, READSTR, "%s %d %d %d\n", devstates[d->state], d->class, d->subclass, d->proto);
		for(i=0; i<nelem(d->ep); i++)
			if((e = d->ep[i]) != nil)	/* TO DO: freeze e */
				l += snprint(s+l, READSTR-l, "%d bytes %lud blocks %lud\n", i, e->nbytes, e->nblocks);
		n = readstr(offset, a, n, s);
		poperror();
		free(s);
		break;

	default:
		d = usbdevice(c);
		if((t -= Qep0) < 0 || t >= nelem(d->ep))
			error(Eio);
		if((e = d->ep[t]) == nil || e->mode == OWRITE)
			error(Eio);	/* can't happen */
		n=readusb(e, a, n);
		break;
	}
	return n;
}

static int
qisempty(void *arg)
{
	return ((QH*)arg)->entries & Terminate;
}

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

	p = a;
	qlock(&e->wlock);
	if(waserror()){
		qunlock(&e->wlock);
		eptcancel(e);
		nexterror();
	}
	do {
		int j;

		if(e->err)
			error(e->err);
		if((i = n) >= e->maxpkt)
			i = e->maxpkt;
		b = allocb(i);
		if(waserror()){
			freeb(b);
			nexterror();
		}
		XPRINT("out [%ld]", i);
		for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]);
		XPRINT("\n");
		memmove(b->wp, p, i);
		b->wp += i;
		p += i;
		n -= i;
		poperror();
		qh = qxmit(e, b, tok);
		tok = TokOUT;
		if(!e->iso)
			e->data01 ^= 1;
		if(e->ntd >= e->nbuf) {
			XPRINT("writeusb: sleep %lux\n", &e->wr);
			sleep(&e->wr, qisempty, qh);
			XPRINT("writeusb: awake\n");
		}
	} while(n > 0);
	poperror();
	qunlock(&e->wlock);
	return p-(uchar*)a;
}

int
getfields(char *ss, char **sp, int nptrs)
{
	uchar *s = (uchar*)ss;
	uchar **p = (uchar**)sp;
	uint c;

	c = 0;
	for(;;){
		if(--nptrs < 0)
			break;
		*p++ = s;
		do {
			c = *s++;
			if (c == ' ') break;
			if (c == '\t') break;
			if (c == '\n') break;
		} while(c);
		if(c == 0)
			break;
		s[-1] = 0;
	}
	if(nptrs > 0)
		*p = 0;
	else
	if(--s >= (uchar*)ss)
		*s = c;
	return p - (uchar**)sp;
}

long
usbwrite(Chan *c, void *a, long n, vlong)
{
	Udev *d;
	Endpt *e;
	int id, nw, nf, t, i;
	char cmd[50], *fields[10];

	if(c->qid.path & CHDIR)
		error(Egreg);
	t = QID(c->qid);
	if(t == Qbusctl){
		if(n >= sizeof(cmd)-1)
			n = sizeof(cmd)-1;
		memmove(cmd, a, n);
		cmd[n] = 0;
		nf = getfields(cmd, fields, nelem(fields));
		if(nf==1 && strcmp(fields[0], "dump")==0){
			dumpframe(-1, -1);
			return n;
		}
		if(nf < 2)
			error(Ebadarg);
		id = strtol(fields[1], nil, 0);
		if(id != 1 && id != 2)
			error(Ebadarg);	/* there are two ports on the root hub */
		if(strcmp(fields[0], "reset") == 0)
			portreset(id);
		else if(strcmp(fields[0], "enable") == 0)
			portenable(id, 1);
		else if(strcmp(fields[0], "disable") == 0)
			portenable(id, 0);
		else
			error(Ebadarg);
		return n;
	}
	d = usbdevice(c);
	t = QID(c->qid);
	switch(t){
	case Qctl:
		if(n >= sizeof(cmd)-1)
			n = sizeof(cmd)-1;
		memmove(cmd, a, n);
		cmd[n] = 0;
		nf = getfields(cmd, fields, nelem(fields));
		if(nf > 1 && strcmp(fields[0], "speed") == 0){
			d->ls = strtoul(fields[1], nil, 0) == 0;
		} else if(nf > 4 && strcmp(fields[0], "class") == 0){
			/* class class subclass proto */
			d->class = strtoul(fields[4], nil, 0);
			d->subclass = strtoul(fields[5], nil, 0);
			d->proto = strtoul(fields[6], nil, 0);
		}else if(nf > 2 && strcmp(fields[0], "data") == 0){
			i = strtoul(fields[1], nil, 0);
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
				error(Ebadarg);
			e = d->ep[i];
			e->data01 = strtoul(fields[2], nil, 0) != 0;
		}else if(nf > 2 && strcmp(fields[0], "maxpkt") == 0){
			i = strtoul(fields[1], nil, 0);
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
				error(Ebadarg);
			e = d->ep[i];
			e->maxpkt = strtoul(fields[2], nil, 0);
			if(e->maxpkt > 1500)
				e->maxpkt = 1500;
		}else if(nf > 2 && strcmp(fields[0], "debug") == 0){
			i = strtoul(fields[1], nil, 0);
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
				error(Ebadarg);
			e = d->ep[i];
			e->debug = strtoul(fields[2], nil, 0);
		}else if(nf > 1 && strcmp(fields[0], "unstall") == 0){
			i = strtoul(fields[1], nil, 0);
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
				error(Ebadarg);
			e = d->ep[i];
			e->err = nil;
		}else if(nf == 6 && strcmp(fields[0], "ep") == 0){
			/* ep n maxpkt mode poll nbuf */
			i = strtoul(fields[1], nil, 0);
			if(i < 0 || i >= nelem(d->ep))
				error(Ebadarg);
			if(d->ep[i] != nil)
				error(Einuse);
			e = devendpt(d, i, 1);
			if(waserror()){
				freept(e);
				nexterror();
			}
			i = strtoul(fields[2], nil, 0);
			if(i < 8 || i > 1023)
				i = 8;
			e->maxpkt = i;
			e->mode = strcmp(fields[3],"r")==0? OREAD: strcmp(fields[3],"w") == 0? OWRITE: ORDWR;
			e->periodic = 0;
			e->sched = -1;
			if(strcmp(fields[4], "bulk") != 0){
				e->periodic = 1;
				i = strtoul(fields[4], nil, 0);
				if(i > 0 && i <= 1000)
					e->pollms = i;
				else
					error(Ebadarg);
			}
			i = strtoul(fields[5], nil, 0);
			if(i >= 1 && i <= 32)
				e->nbuf = i;
			poperror();
		}else
			error(Ebadarg);
		return n;

	case Qsetup:	/* SETUP endpoint 0 */
		/* should canqlock etc */
		if((e = d->ep[0]) == nil)
			error(Eio);	/* can't happen */
		if(n < 8 || n > 1023)
			error(Eio);
		nw = *(uchar*)a & RD2H;
		e->data01 = 0;
		n = writeusb(e, a, n, TokSETUP);
		if(nw == 0){	/* host to device: use IN[DATA1] to ack */
			e->data01 = 1;
			nw = readusb(e, cmd, 8);
			if(nw != 0)
				error(Eio);	/* could provide more status */
		}else
			e->setin = 1;	/* two-phase */
		break;

	default:	/* sends DATA[01] */
		if((t -= Qep0) < 0 || t >= nelem(d->ep))
			error(Eio);
		if((e = d->ep[t]) == nil || e->mode == OREAD)
			error(Eio);	/* can't happen */
		n = writeusb(e, a, n, TokOUT);
		break;
	}
	return n;
}

Dev usbdevtab = {
	'U',
	"usb",

	usbreset,
	usbinit,
	usbattach,
	usbclone,
	usbwalk,
	usbstat,
	usbopen,
	devcreate,
	usbclose,
	usbread,
	devbread,
	usbwrite,
	devbwrite,
	devremove,
	devwstat,
};
.
## diffname pc/devusb.c 1999/1006
## diff -e /n/emeliedump/1999/1005/sys/src/brazil/pc/devusb.c /n/emeliedump/1999/1006/sys/src/brazil/pc/devusb.c
1883a
		}
.
1882c
		}else {
			pprint("command %s, fields %d\n", fields[0], nf);
.
1876a
				}
.
1875c
				else {
					pprint("field 4: 0 <= %d <= 1000\n", i);
.
1855a
			}
.
1854c
			if(i < 0 || i >= nelem(d->ep)) {
				pprint("field 1: 0 <= %d < %d\n", i, nelem(d->ep));
.
1320c
	Ctlr *ub;
	Pcidev *cfg;
	int i;
	ulong port;
	QTree *qt;
	TD *t;

	ub = &ubus;
	memset(&cfg, 0, sizeof(cfg));
	cfg = pcimatch(0, 0x8086, 0x7112);	/* Intel chipset PIIX 4*/
	if(cfg == nil) {
//		cfg = pcimatch(0, 0x8086, 0x7112);	/* Intel chipset PIIX 3*/
//		if(cfg == nil) {
			cfg = pcimatch(0, 0x1106, 0x0586);	/* Via chipset */
			if(cfg == nil) {
				DPRINT("No USB device found\n");
				return;
			}
//		}
	}
	port = cfg->mem[4].bar & ~0x0F;
	if (port == 0) {
		print("usb: failed to map registers\n");
		return;
	}

	print("USB: %x/%x port 0x%lux size 0x%x irq %d\n",
		cfg->vid, cfg->did, port, cfg->mem[4].size, cfg->intl);

	i = inb(port+SOFMod);
	if(0){
		OUT(Cmd, 4);	/* global reset */
		delay(15);
		OUT(Cmd, 0);	/* end reset */
		delay(4);
	}
	outb(port+SOFMod, i);
	// Interrupt handler
	intrenable(cfg->intl, interrupt, ub, cfg->tbdf, "usb");

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

	/*
	 * the root of the periodic (interrupt & isochronous) scheduling tree
	 * points to the control queue and the bandwidth sop for bulk traffic.
	 * this is looped following the instructions in PIIX4 errata 29773804.pdf:
	 * a QH links to a looped but inactive TD as its sole entry,
	 * with its head entry leading on to the bulk traffic, the last QH of which
	 * links back to the empty QH.
	 */
	ub->bulkq = allocqh(ub);
	t = alloctd(ub);	/* inactive TD, looped */
	t->link = PADDR(t);
	ub->bwsop = allocqh(ub);
	ub->bwsop->head = PADDR(ub->bulkq) | IsQH;
	ub->bwsop->entries = PADDR(t);
	ub->ctlq = allocqh(ub);
	ub->ctlq->head = PADDR(ub->bwsop) | IsQH;
//	ub->bulkq->head = PADDR(ub->bwsop) | IsQH;	/* loop back */
	print("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n",
		IN(Cmd), IN(Status), IN(Usbintr), inb(port+Frnum));
	print("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n",
		IN(Flbaseadd), inb(port+SOFMod), IN(Portsc0), IN(Portsc1));
	OUT(Cmd, 0);	/* stop */
	ub->frames = xspanalloc(FRAMESIZE, FRAMESIZE, 0);
	qt = mkqhtree(ub->frames, NFRAME, 32);
	if(qt == nil){
		print("usb: can't allocate scheduling tree\n");
		ub->io = 0;
		return;
	}
	qt->root->head = PADDR(ub->ctlq) | IsQH;
	ub->tree = qt;
	print("usb tree: nel=%d depth=%d\n", qt->nel, qt->depth);

	outl(port+Flbaseadd, PADDR(ub->frames));
	OUT(Frnum, 0);
	OUT(Usbintr, 0xF);	/* enable all interrupts */
	print("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(port+SOFMod));
	print("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1));
.
1126,1220d
## diffname pc/devusb.c 1999/1007
## diff -e /n/emeliedump/1999/1006/sys/src/brazil/pc/devusb.c /n/emeliedump/1999/1007/sys/src/brazil/pc/devusb.c
1881c
			XPRINT("command %s, fields %d\n", fields[0], nf);
.
1872c
					XPRINT("field 4: 0 <= %d <= 1000\n", i);
.
1849c
				XPRINT("field 1: 0 <= %d < %d\n", i, nelem(d->ep));
.
1586c
			XPRINT("b == nil\n");
.
1573c
			XPRINT("e->eof\n");
.
1107,1109c
		XPRINT("usbint: #%x f%d\n", s, IN(Frnum));
		XPRINT("cmd #%x sofmod #%x\n", IN(Cmd), inb(ub->io+SOFMod));
		XPRINT("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1));
.
838c
			XPRINT("  -> %8.8lux h=%8.8lux e=%8.8lux\n", p, q->head, q->entries);
.
835c
				XPRINT("Q: p=%8.8lux out of range\n", p);
.
831c
		XPRINT("F%.2d %8.8lux %8.8lux\n", i, frame[i], QFOL(frame[i])->head);
.
808c
			XPRINT("leaf0=%d o=%d i=%d n=%d\n", leaf0, o, i, n);
.
627c
		XPRINT("cancel:\n");
.
528c
		XPRINT("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
.
484c
			XPRINT("head:");
.
478c
		XPRINT("qh %8.8lux: %8.8lux %8.8lux\n", q, q->head, q->entries);
.
404,441d
374c
		XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n", buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1);
.
372c
		XPRINT("td %8.8lux: l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n",
.
## diffname pc/devusb.c 1999/1116
## diff -e /n/emeliedump/1999/1007/sys/src/brazil/pc/devusb.c /n/emeliedump/1999/1116/sys/src/9/pc/devusb.c
1799,1800c
			if (i == -1)
				debug = 0;
			else {
				debug = 1;
				e = d->ep[i];
				e->debug = strtoul(fields[2], nil, 0);
			}
.
1797c
			if(i < -1 || i >= nelem(d->ep) || d->ep[i] == nil)
.
1526a
	XPRINT("got qlock(%p)\n", &e->rlock);
.
1525a
	XPRINT("qlock(%p)\n", &e->rlock);
.
1255c
	ub->bulkq->head = PADDR(ub->bwsop) | IsQH;	/* loop back */
.
1082,1083c
			XPRINT("cleanq(e->epq, 0, 0)\n");
			cleanq(e->epq, 0, 0);
.
1075,1078c
	XPRINT("cleanq(ub->ctlq, 0, 0)\n");
	cleanq(ub->ctlq, 0, 0);
	XPRINT("cleanq(ub->bulkq, 0, Vf)\n");
	cleanq(ub->bulkq, 0, Vf);
.
1069d
1067a
	XPRINT("usbint: #%x f%d\n", s, IN(Frnum));
.
914a
	e->epq = allocqh(ub);
	if(e->epq == nil)
		panic("devendpt");

.
688a
XPRINT("bulkenq\n");
.
687d
685a
XPRINT("enq\n");
.
678a
XPRINT("qrcv\n");
.
573c
		if (tp)
			t = tp->next;
		else
			t = q->first;
		XPRINT("t = %8.8lux\n", t);
		dumpqh(q);
.
563,569c
		if (tp == nil) {
			q->first = t->next;
			if(q->first != nil)
				q->entries = PADDR(q->first);
			else
				q->entries = Terminate;
		} else {
			tp->next = t->next;
			if (t->next != nil)
				tp->link = PADDR(t->next) | vf;
			else
				tp->link = Terminate;
		}
		if (q->last == t)
			q->last = tp;
.
559a
tp = t;
t = t->next;
continue;
.
556a
				tp = t;
.
553a
			if(t->status & NAKed){
				t->status = (t->status & ~NAKed) | IOC;	/* ensure interrupt next frame */
				tp = t;
				t = t->next;
				continue;
			}
.
552c
		XPRINT("cleanq: %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer, t->flags, t->next);
.
550a
	tp = nil;
.
546c
	TD *t, *tp;
.
544c
cleanq(QH *q, int discard, int vf)
.
472a
	dumpqh(q);
.
241c
	int		ntd;
.
231,232c
	int		pollms;	/* polling interval in msec */
	QH*		epq;	/* queue of TDs for this endpoint */
.
228,229c
	int		sched;	/* schedule index; -1 if undefined or aperiodic */
	int		setin;
.
214,217c
	int		x;	/* index in Udev.ep */
	int		id;		/* hardware endpoint address */
	int		maxpkt;	/* maximum packet size (from endpoint descriptor) */
	int		data01;	/* 0=DATA0, 1=DATA1 */
.
34a
static int debug = 1;

.
33c
#define XPRINT if(debug)print
.
## diffname pc/devusb.c 1999/1117
## diff -e /n/emeliedump/1999/1116/sys/src/9/pc/devusb.c /n/emeliedump/1999/1117/sys/src/9/pc/devusb.c
1868c
			if(strcmp(fields[4], "bulk") == 0){
				Ctlr *ub;

				ub = &ubus;
				/* Each bulk device gets a queue head hanging off the
				 * bulk queue head
				 */
				if (e->epq == nil) {
					e->epq = allocqh(ub);
					if(e->epq == nil)
						panic("usbwrite: allocqh");
				}
				queueqh(e->epq);
			} else {
.
1288c
	ub->bwsop->head = PADDR(ub->bulkq) | IsQH;
	ub->bulkq->head = PADDR(ub->recvq) | IsQH;
	ub->recvq->head = PADDR(ub->bwsop) | IsQH;	/* loop back */
.
1286d
1283,1284d
1280a
	ub->recvq = allocqh(ub);
XPRINT("bulkq: 0x%8.8lux\n", ub->bulkq);
XPRINT("recvq: 0x%8.8lux\n", ub->recvq);
XPRINT("bwsop: 0x%8.8lux\n", ub->bwsop);
XPRINT("ctlq: 0x%8.8lux\n", ub->ctlq);
.
1279a
	ub->ctlq = allocqh(ub);
	ub->bwsop = allocqh(ub);
.
1225a
	Ctlr *ub;
.
1220d
1111a
	XPRINT("clean recvq\n");
	for (q = ub->recvq->next; q; q = q->hlink) {
		XPRINT("cleanq(q, 0, Vf)\n");
		cleanq(q, 0, Vf);
	}
.
1097a
	QH *q;
.
921a
	ub = &ubus;
.
920a
	Ctlr *ub;
.
901c
	if(!e->periodic || (q = e->sched) < 0)
.
689a
XPRINT("bulkenq\n");
.
686a
XPRINT("enq\n");
.
672a
XPRINT("qxmit\n");
.
664a
static void
queueqh(QH *qh) {
	QH *q;
	Ctlr *ub;

	ub = &ubus;
	// See if it's already queued
	for (q = ub->recvq->next; q; q = q->hlink)
		if (q == qh)
			return;
	if ((qh->hlink = ub->recvq->next) == nil)
		qh->head = Terminate;
	else
		qh->head = PADDR(ub->recvq->next) | IsQH;
	ub->recvq->next = qh;
	ub->recvq->entries = PADDR(qh) | IsQH;
}

.
419a
	qh->hlink = nil;
.
377,378c
	//	if(t->bp && (t->flags & CancelTD) == 0 && (t->status & Active) == 0)
	//		dumpdata(t->bp, n);
.
374,375c
		XPRINT("td %8.8lux: ", t);
		XPRINT("l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n",
			t->link, t->status, t->dev, t->buffer, t->bp?(ulong)t->bp->rp:0, t->flags);
.
269a
	QH*	recvq;	/* receive queues for bulk i/o */
.
89,93c
	QH*		hlink;
	TD*		first;
	QH*		next;		/* free list */
	TD*		last;
	ulong	_d1;		/* fillers */
	ulong	_d2;
.
35c
static int debug = 0;
.
## diffname pc/devusb.c 1999/1118
## diff -e /n/emeliedump/1999/1117/sys/src/9/pc/devusb.c /n/emeliedump/1999/1118/sys/src/9/pc/devusb.c
1347,1348c
	XPRINT("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(port+SOFMod));
	XPRINT("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1));
.
1342c
	XPRINT("usb tree: nel=%d depth=%d\n", qt->nel, qt->depth);
.
1330c
	XPRINT("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n",
.
1328c
	XPRINT("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n",
.
1317,1320d
1279c
	DPRINT("USB: %x/%x port 0x%lux size 0x%x irq %d\n",
.
1188c
	"ctl",		{Qctl},		0,	0666,
.
1184c
	"port",		{Qport},	0,	0444,
.
1182c
	"new",		{Qnew},		0,	0666,
.
742d
739d
731d
714d
710d
707c
	if(e->debug) pprint("QTD: %8.8lux n=%ld\n", t, b?BLEN(b): 0);
.
695d
380,381c
		if(debug && t->bp && (t->flags & CancelTD) == 0)
			dumpdata(t->bp, n);
.
30d
## diffname pc/devusb.c 1999/1119
## diff -e /n/emeliedump/1999/1118/sys/src/9/pc/devusb.c /n/emeliedump/1999/1119/sys/src/9/pc/devusb.c
1841,1843c
			d->class = strtoul(fields[1], nil, 0);
			d->subclass = strtoul(fields[2], nil, 0);
			d->proto = strtoul(fields[3], nil, 0);
.
1839c
		} else if(nf > 3 && strcmp(fields[0], "class") == 0){
.
## diffname pc/devusb.c 1999/1120
## diff -e /n/emeliedump/1999/1119/sys/src/9/pc/devusb.c /n/emeliedump/1999/1120/sys/src/9/pc/devusb.c
1882,1884c
			if((e = d->ep[i]) == nil)
				e = devendpt(d, i, 1);
.
1839,1843c
		} else if(nf > 5 && strcmp(fields[0], "class") == 0){
			i = strtoul(fields[2], nil, 0);
			d->npt = strtoul(fields[1], nil, 0);
			/* class config# class subclass proto */
			if (i < 0 || i >= nelem(d->ep)
			 || d->npt > nelem(d->ep) || i >= d->npt)
				error(Ebadarg);
			if (i == 0) {
				d->class = strtoul(fields[3], nil, 0);
				d->subclass = strtoul(fields[4], nil, 0);
				d->proto = strtoul(fields[5], nil, 0);
			}
			if(d->ep[i] == nil)
				d->ep[i] = devendpt(d, i, 1);
			d->ep[i]->class = strtoul(fields[3], nil, 0);
			d->ep[i]->subclass = strtoul(fields[4], nil, 0);
			d->ep[i]->proto = strtoul(fields[5], nil, 0);
.
1688c
				l += snprint(s+l, READSTR-l, "%2d class 0x%2.2ux subclass 0x%2.2ux proto 0x%2.2ux bytes %10lud blocks %10lud\n", i, e->class, e->subclass, e->proto, e->nbytes, e->nblocks);
.
203,207c
	[Disabled]		"Disabled",
	[Attached]		"Attached",
	[Enabled]		"Enabled",
	[Assigned]		"Assigned",
	[Configured]	"Configured",
.
184c
	Udev*	ports;		/* active ports, if hub */
.
181,182c
	int		ls;
	int		npt;
.
173,176c
	int		x;	/* index in usbdev[] */
	int		busy;
	int		state;
	int		id;
.
## diffname pc/devusb.c 1999/1209
## diff -e /n/emeliedump/1999/1120/sys/src/9/pc/devusb.c /n/emeliedump/1999/1209/sys/src/9/pc/devusb.c
1283c
	/*
	 * Interrupt handler.
	 * Bail out if no IRQ assigned by the BIOS.
	 */
	if(cfg->intl == 0xFF)
		return;
.
## diffname pc/devusb.c 1999/1230
## diff -e /n/emeliedump/1999/1209/sys/src/9/pc/devusb.c /n/emeliedump/1999/1230/sys/src/9/pc/devusb.c
1425a
	if(s == DEVDOTDOT){
		devdir(c, (Qid){CHDIR|Q2nd, 0}, "usb", 0, eve, 0555, dp);
		return 1;
	}
.
1403a
		if(s == DEVDOTDOT){
			devdir(c, (Qid){CHDIR, 0}, "#U", 0, eve, 0555, dp);
			return 1;
		}
.
1391a
		if(s == DEVDOTDOT){
			devdir(c, (Qid){CHDIR, 0}, "#U", 0, eve, 0555, dp);
			return 1;
		}
.
## diffname pc/devusb.c 2000/0308
## diff -e /n/emeliedump/1999/1230/sys/src/9/pc/devusb.c /n/emeliedump/2000/0308/sys/src/9/pc/devusb.c
1853c
		nf = getfields(cmd, fields, nelem(fields), 0, " \t\n");
.
1825c
		nf = getfields(cmd, fields, nelem(fields), 0, " \t\n");
.
1779,1808d
## diffname pc/devusb.c 2000/0517
## diff -e /n/emeliedump/2000/0308/sys/src/9/pc/devusb.c /n/emeliedump/2000/0517/sys/src/9/pc/devusb.c
1287c
	if(cfg->intl == 0xFF || cfg->intl == 0)
.
## diffname pc/devusb.c 2000/0719
## diff -e /n/emeliedump/2000/0517/sys/src/9/pc/devusb.c /n/emeliedump/2000/0719/sys/src/9/pc/devusb.c
23,29d
## diffname pc/devusb.c 2000/0720
## diff -e /n/emeliedump/2000/0719/sys/src/9/pc/devusb.c /n/emeliedump/2000/0720/sys/src/9/pc/devusb.c
1250,1257c
		cfg = pcimatch(0, 0x1106, 0x0586);	/* Via chipset */
		if(cfg == nil) {
			DPRINT("No USB device found\n");
			return;
		}
.
## diffname pc/devusb.c 2000/0722
## diff -e /n/emeliedump/2000/0720/sys/src/9/pc/devusb.c /n/emeliedump/2000/0722/sys/src/9/pc/devusb.c
1695c
				l += snprint(s+l, READSTR-l, "%2d %ud.%ud.%ud bytes %10lud blocks %10lud\n", i, e->class, e->subclass, e->proto, e->nbytes, e->nblocks);
.
1692c
		l = snprint(s, READSTR, "%s %d.%d.%d\n", devstates[d->state], d->class, d->subclass, d->proto);
.
## diffname pc/devusb.c 2000/0724
## diff -e /n/emeliedump/2000/0722/sys/src/9/pc/devusb.c /n/emeliedump/2000/0724/sys/src/9/pc/devusb.c
1830,1832c
			d->ep[i]->csp = strtoul(fields[3], nil, 0);
.
1824,1826c
				d->csp = strtoul(fields[3], nil, 0);
.
1819c
			/* class config# csp ( == class subclass proto) */
.
1816c
		} else if(nf > 3 && strcmp(fields[0], "class") == 0){
.
1695c
				l += snprint(s+l, READSTR-l, "%2d %#6.6x %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks);
.
1692c
		l = snprint(s, READSTR, "%s %#6.6x\n", devstates[d->state], d->csp);
.
993c
	if(Class(d->csp) == Hubclass)
.
214,216c
	ulong	csp;
.
171,173c
	ulong	csp;
.
61a
#define Class(csp)		((csp)&0xff)
#define Subclass(csp)	(((csp)>>8)&0xff)
#define Proto(csp)		(((csp)>>16)&0xff)
#define CSP(c, s, p)	((c) | ((s)<<8) | ((p)<<16))

.
## diffname pc/devusb.c 2000/0725
## diff -e /n/emeliedump/2000/0724/sys/src/9/pc/devusb.c /n/emeliedump/2000/0725/sys/src/9/pc/devusb.c
1936a
		}
.
1935c
		}
		if((e = d->ep[t]) == nil || e->mode == OREAD) {
.
1933c
		if((t -= Qep0) < 0 || t >= nelem(d->ep)) {
			print("t = %d\n", t);
.
1878c
			e->mode = strcmp(fields[3],"r") == 0? OREAD :
					  strcmp(fields[3],"w") == 0? OWRITE : ORDWR;
.
1696c
				l += snprint(s+l, READSTR-l, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks);
.
1693c
		l = snprint(s, READSTR, "%s %#6.6lux\n", devstates[d->state], d->csp);
.
## diffname pc/devusb.c 2001/0126
## diff -e /n/emeliedump/2000/0725/sys/src/9/pc/devusb.c /n/emeliedump/2001/0126/sys/src/9/pc/devusb.c
1263c
	port = cfg->mem[4].bar & ~0x0F;

	DPRINT("USB: %x/%x port 0x%ux size 0x%x irq %d\n",
.
1261d
1257,1259c
	if(cfg == nil)
.
1255a
		if((cfg->mem[4].bar & ~0x0F) != 0)
			break;
.
1248,1254c
	for(cfg = pcimatch(nil, 0, 0); cfg != nil; cfg = pcimatch(cfg, 0, 0)){
		/*
		 * Look for devices with the correct class and
		 * sub-class code and known device and vendor ID.
		 */
		if(cfg->ccrb != 0x0C || cfg->ccru != 0x03)
			continue;
		switch((cfg->did<<16)|cfg->vid){
		default:
			continue;
		case (0x7112<<16)|0x8086:	/* 82371[AE]B (PIIX4[E]) */
		case (0x719A<<16)|0x8086:	/* 82443MX */
		case (0x1106<<16)|0x0586:	/* VIA 82C586 */
			break;
.
1241,1242c
	int i, port;
.
## diffname pc/devusb.c 2001/0131
## diff -e /n/emeliedump/2001/0126/sys/src/9/pc/devusb.c /n/emeliedump/2001/0131/sys/src/9/pc/devusb.c
1319a
}
.
1312a
if(0){ /* DISABLE ERRATA FOR NOW */
.
841c
			pprint("  -> %8.8lux h=%8.8lux e=%8.8lux\n", p, q->head, q->entries);
.
838c
				pprint("Q: p=%8.8lux out of range\n", p);
.
834c
		pprint("F%.2d %8.8lux %8.8lux\n", i, frame[i], QFOL(frame[i])->head);
.
800a
print("after loop tree root %8.8lux head %8.8lux entries %8.8lux\n", tree, tree->head, tree->entries);
.
793a
print("tree root %8.8lux\n", tree);
.
## diffname pc/devusb.c 2001/0201
## diff -e /n/emeliedump/2001/0131/sys/src/9/pc/devusb.c /n/emeliedump/2001/0201/sys/src/9/pc/devusb.c
1315c
if(0){
.
## diffname pc/devusb.c 2001/0216
## diff -e /n/emeliedump/2001/0201/sys/src/9/pc/devusb.c /n/emeliedump/2001/0216/sys/src/9/pc/devusb.c
802d
794d
## diffname pc/devusb.c 2001/0503
## diff -e /n/emeliedump/2001/0216/sys/src/9/pc/devusb.c /n/emeliedump/2001/0503/sys/src/9/pc/devusb.c
1815a
	if (d == nil)
		error(Ehungup);
.
1712a
		if (d == nil)
			error(Ehungup);
.
1696a
		if (d == nil)
			error(Ehungup);
.
1679a
		if (d == nil)
			error(Ehungup);
.
1674a
		if (d == nil)
			error(Ehungup);
.
1579,1584d
1575,1577c
	d = usbdeviceofpath(c->qid.path);
	if (d && d->id == c->qid.vers) {
		if(QID(c->qid) == Qctl)
			d->busy = 0;
		if(c->flag & COPEN)
			freedev(d);
.
1526a
		if (d == nil)
			error(Ehungup);
.
1517a
		if (d == nil)
			error(Ehungup);
.
1200c
		return nil;
.
## diffname pc/devusb.c 2001/0504
## diff -e /n/emeliedump/2001/0503/sys/src/9/pc/devusb.c /n/emeliedump/2001/0504/sys/src/9/pc/devusb.c
735,749d
722a
	t = alloctde(e, TokIN, e->maxpkt);
.
721d
## diffname pc/devusb.c 2001/0527
## diff -e /n/emeliedump/2001/0504/sys/src/9/pc/devusb.c /n/emeliedump/2001/0527/sys/src/9/pc/devusb.c
1810,1811d
1705,1706d
1687,1688d
1668,1669d
1661,1662d
1570a
	d = usbdevice(c);
	if(QID(c->qid) == Qctl)
		d->busy = 0;
	if(c->flag & COPEN)
		freedev(d);
	poperror();
.
1564,1569c
	if(waserror()){
		qunlock(&usbstate);
		nexterror();
.
1514,1515d
1503,1504d
1306d
1298d
1253,1255c
	DPRINT("USB: %x/%x port 0x%lux size 0x%x irq %d\n",
.
1251a
	}
.
1250c
	port = cfg->mem[4].bar & ~0x0F;
	if (port == 0) {
		print("usb: failed to map registers\n");
.
1247,1248d
1232,1245c
	memset(&cfg, 0, sizeof(cfg));
	cfg = pcimatch(0, 0x8086, 0x7112);	/* Intel chipset PIIX 4*/
	if(cfg == nil) {
		cfg = pcimatch(0, 0x1106, 0x0586);	/* Via chipset */
		if(cfg == nil) {
			DPRINT("No USB device found\n");
			return;
.
1226c
	int i;
	ulong port;
.
1185c
		error(Ehungup);
.
826c
			XPRINT("  -> %8.8lux h=%8.8lux e=%8.8lux\n", p, q->head, q->entries);
.
823c
				XPRINT("Q: p=%8.8lux out of range\n", p);
.
819c
		XPRINT("F%.2d %8.8lux %8.8lux\n", i, frame[i], QFOL(frame[i])->head);
.
736a
static Block *
usbreq(int type, int req, int value, int offset, int count)
{
	Block *b;

	b = allocb(8);
	b->wp[0] = type;
	b->wp[1] = req;
	PUT2(b->wp+2, value);
	PUT2(b->wp+4, offset);
	PUT2(b->wp+6, count);
	b->wp += 8;
	return b;
}

.
722a
	b = allocb(e->maxpkt);
.
721d
## diffname pc/devusb.c 2001/0624
## diff -e /n/emeliedump/2001/0527/sys/src/9/pc/devusb.c /n/emeliedump/2001/0624/sys/src/9/pc/devusb.c
882,1966c
	if(!e->periodic || e->sched
.
## diffname pc/devusb.c 2001/0626
## diff -e /n/emeliedump/2001/0624/sys/src/9/pc/devusb.c /n/emeliedump/2001/0626/sys/src/9/pc/devusb.c
882c
	if(!e->periodic || e->sched >= 0)
		return 0;
	ub = &ubus;
	if(e->epq == nil){
		e->epq = allocqh(ub);
		if(e->epq == nil)
			return -1;
	}
	qlock(ub->tree);
	q = pickschedq(ub->tree, e->pollms, e->bw, ~0);	/* TO DO: bus bandwidth limit */
	if(q < 0)
		return -1;
	ub->tree->bw[q] += e->bw;
	qh = &ub->tree->root[q];
	e->sched = q;
	e->epq->head = qh->entries;
	e->epq->entries = Terminate;
	qh->entries = PADDR(e->epq) | IsQH;
	qunlock(ub->tree);
	return 0;
}

static void
unschedendpt(Endpt *e)
{
	Ctlr *ub;
	ulong p;
	QH *qh;
	int q;

	ub = &ubus;
	if(!e->periodic || (q = e->sched) < 0)
		return;
	p = PADDR(e->epq) | IsQH;
	qlock(ub->tree);
	ub->tree->bw[q] -= e->bw;
	qh = &ub->tree->root[q];
	for(; qh->entries != p; qh = QFOL(qh->entries))
		if(qh->entries & Terminate || (qh->entries & IsQH) == 0){
			qunlock(ub->tree);
			panic("usb: unschedendpt");
		}
	qh->entries = e->epq->head;
	qunlock(ub->tree);
	e->epq->head = Terminate;
}

static Endpt *
devendpt(Udev *d, int id, int add)
{
	Endpt *e, **p;
	Ctlr *ub;

	ub = &ubus;
	p = &d->ep[id&0xF];
	lock(d);
	if((e = *p) != nil){
		incref(e);
		unlock(d);
		return e;
	}
	unlock(d);
	if(!add)
		return nil;
	e = mallocz(sizeof(*e), 1);
	e->ref = 1;
	e->x = id&0xF;
	e->id = id;
	e->periodic = 0;
	e->sched = -1;
	e->maxpkt = 8;
	e->pollms = 0;
	e->bw = 0;
	e->nbuf = 1;
	e->dev = d;
	e->active = 0;
	e->epq = allocqh(ub);
	if(e->epq == nil)
		panic("devendpt");

	lock(d);
	if(*p != nil){
		incref(*p);
		unlock(d);
		free(e);
		return *p;
	}
	*p = e;
	unlock(d);
	e->rq = qopen(8*1024, 0, nil, e);
	e->wq = qopen(8*1024, 0, nil, e);
	return e;
}

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

static void
usbdevreset(Udev *d)
{
	d->state = Disabled;
	if(Class(d->csp) == Hubclass)
		for(d = d->ports; d != nil; d = d->next)
			usbdevreset(d);
}

static void
freedev(Udev *d)
{
	int i;

	if(d != nil && decref(d) == 0){
		for(i=0; i<nelem(d->ep); i++)
			freept(d->ep[i]);
		if(d->x >= 0)
			usbdev[d->x] = nil;
		free(d);
	}
}

static void
hubportreset(Udev *h, int p)
{
	USED(h, p);
	/* reset state of each attached device? */
}

static	int	ioports[] = {-1, Portsc0, Portsc1};

static void
portreset(int port)
{
	Ctlr *ub;
	int i, p;

	/* should check that device not being configured on other port? */
	p = ioports[port];
	ub = &ubus;
	qlock(&ub->resetl);
	if(waserror()){
		qunlock(&ub->resetl);
		nexterror();
	}
	XPRINT("r: %x\n", IN(p));
	ilock(ub);
	OUT(p, PortReset);
	delay(12);	/* BUG */
	XPRINT("r2: %x\n", IN(p));
	OUT(p, IN(p) & ~PortReset);
	XPRINT("r3: %x\n", IN(p));
	OUT(p, IN(p) | PortEnable);
	microdelay(64);
	for(i=0; i<1000 && (IN(p) & PortEnable) == 0; i++)
		;
	XPRINT("r': %x %d\n", IN(p), i);
	OUT(p, (IN(p) & ~PortReset)|PortEnable);
	iunlock(ub);
	hubportreset(nil, port);
	poperror();
	qunlock(&ub->resetl);
}

static void
portenable(int port, int on)
{
	Ctlr *ub;
	int w, p;

	/* should check that device not being configured on other port? */
	p = ioports[port];
	ub = &ubus;
	qlock(&ub->resetl);
	if(waserror()){
		qunlock(&ub->resetl);
		nexterror();
	}
	ilock(ub);
	w = IN(p);
	if(on)
		w |= PortEnable;
	else
		w &= ~PortEnable;
	OUT(p, w);
	microdelay(64);
	iunlock(ub);
	XPRINT("e: %x\n", IN(p));
	if(!on)
		hubportreset(nil, port);
	poperror();
	qunlock(&ub->resetl);
}

static int
portinfo(Ctlr *ub, int *p0, int *p1)
{
	int m, v;

	ilock(ub);
	m = 0;
	if((v = IN(Portsc0)) & PortChange){
		OUT(Portsc0, v);
		m |= 1<<0;
	}
	*p0 = v;
	if((v = IN(Portsc1)) & PortChange){
		OUT(Portsc1, v);
		m |= 1<<1;
	}
	*p1 = v;
	iunlock(ub);
	return m;
}

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

	ub = a;
	s = IN(Status);
	OUT(Status, s);
	if ((s & 0x1f) == 0)
		return;
	XPRINT("usbint: #%x f%d\n", s, IN(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));
	}

	XPRINT("cleanq(ub->ctlq, 0, 0)\n");
	cleanq(ub->ctlq, 0, 0);
	XPRINT("cleanq(ub->bulkq, 0, Vf)\n");
	cleanq(ub->bulkq, 0, Vf);
	XPRINT("clean recvq\n");
	for (q = ub->recvq->next; q; q = q->hlink) {
		XPRINT("cleanq(q, 0, Vf)\n");
		cleanq(q, 0, Vf);
	}
	ilock(&activends);
	for(e = activends.f; e != nil; e = e->activef)
		if(e->epq != nil) {
			XPRINT("cleanq(e->epq, 0, 0)\n");
			cleanq(e->epq, 0, 0);
		}
	iunlock(&activends);
}

enum
{
	Qtopdir = 0,
	Q2nd,
	Qbusctl,
	Qnew,
	Qport,
	Q3rd,
	Qctl,
	Qsetup,
	Qdebug,
	Qstatus,
	Qep0,
	/* other endpoint files */
};

/*
 * Qid path is:
 *	 8 bits of file type (qids above)
 *	10 bits of slot number +1; 0 means not attached to device
 */
#define	QSHIFT	8	/* location in qid of device # */
#define	QMASK	((1<<QSHIFT)-1)

#define	QID(q)		((ulong)(q).path&QMASK)
#define	DEVPATH(p)	((p)>>QSHIFT)

static Dirtab usbdir2[] = {
	"new",	{Qnew},			0,	0666,
	"ctl",		{Qbusctl},			0,	0666,
	"port",	{Qport},			0,	0444,
};

static Dirtab usbdir3[]={
	"ctl",		{Qctl},			0,	0666,
	"setup",	{Qsetup},			0,	0666,
	"status",	{Qstatus},			0,	0444,
	"debug",	{Qdebug},			0,	0666,
	/* epNdata names are generated on demand */
};

static Udev *
usbdeviceofpath(ulong path)
{
	int s;

	s = DEVPATH(path);
	if(s == 0)
		return nil;
	return usbdev[s-1];
}

static Udev *
usbdevice(Chan *c)
{
	Udev *d;

	d = usbdeviceofpath(c->qid.path);
	if(d == nil || d->id != c->qid.vers || d->state == Disabled)
		error(Ehungup);
	return d;
}

static Udev *
usbnewdevice(void)
{
	Udev *d;
	Endpt *e;
	int i;

	d = nil;
	qlock(&usbstate);
	if(waserror()){
		qunlock(&usbstate);
		nexterror();
	}
	for(i=0; i<nelem(usbdev); i++)
		if(usbdev[i] == nil){
			ubus.idgen++;
			d = mallocz(sizeof(*d), 1);
			d->ref = 1;
			d->x = i;
			d->id = (ubus.idgen << 8) | i;
			d->state = Enabled;
			e = devendpt(d, 0, 1);	/* always provide control endpoint 0 */
			e->mode = ORDWR;
			e->periodic = 0;
			e->sched = -1;
			usbdev[i] = d;
			break;
		}
	poperror();
	qunlock(&usbstate);
	return d;
}

static void
usbreset(void)
{
	Pcidev *cfg;
	int i;
	ulong port;
	QTree *qt;
	TD *t;
	Ctlr *ub;
	ISAConf isa;

	if(isaconfig("usb", 0, &isa) == 0) {
		XPRINT("usb not in plan9.ini\n");
//		return;
	}
	ub = &ubus;
	memset(&cfg, 0, sizeof(cfg));
	cfg = pcimatch(0, 0x8086, 0x7112);	/* Intel chipset PIIX 4*/
	if(cfg == nil) {
		cfg = pcimatch(0, 0x1106, 0x0586);	/* Via chipset */
		if(cfg == nil) {
			DPRINT("No USB device found\n");
			return;
		}
	}
	port = cfg->mem[4].bar & ~0x0F;
	if (port == 0) {
		print("usb: failed to map registers\n");
		return;
	}

	DPRINT("USB: %x/%x port 0x%lux size 0x%x irq %d\n",
		cfg->vid, cfg->did, port, cfg->mem[4].size, cfg->intl);

	i = inb(port+SOFMod);
	if(1){
		OUT(Cmd, 4);	/* global reset */
		delay(15);
		OUT(Cmd, 0);	/* end reset */
		delay(4);
	}
	outb(port+SOFMod, i);
	/*
	 * Interrupt handler.
	 * Bail out if no IRQ assigned by the BIOS.
	 */
	if(cfg->intl == 0xFF || cfg->intl == 0)
		return;
	intrenable(cfg->intl, interrupt, ub, cfg->tbdf, "usb");

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

	/*
	 * the root of the periodic (interrupt & isochronous) scheduling tree
	 * points to the control queue and the bandwidth sop for bulk traffic.
	 * this is looped following the instructions in PIIX4 errata 29773804.pdf:
	 * a QH links to a looped but inactive TD as its sole entry,
	 * with its head entry leading on to the bulk traffic, the last QH of which
	 * links back to the empty QH.
	 */
	ub->ctlq = allocqh(ub);
	ub->bwsop = allocqh(ub);
	ub->bulkq = allocqh(ub);
	ub->recvq = allocqh(ub);
	t = alloctd(ub);	/* inactive TD, looped */
	t->link = PADDR(t);
	ub->bwsop->entries = PADDR(t);
	ub->ctlq->head = PADDR(ub->bwsop) | IsQH;
	ub->bwsop->head = PADDR(ub->bulkq) | IsQH;
	ub->bulkq->head = PADDR(ub->recvq) | IsQH;
	ub->recvq->head = PADDR(ub->bwsop) | IsQH;	/* loop back */
	XPRINT("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n",
		IN(Cmd), IN(Status), IN(Usbintr), inb(port+Frnum));
	XPRINT("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n",
		IN(Flbaseadd), inb(port+SOFMod), IN(Portsc0), IN(Portsc1));
	OUT(Cmd, 0);	/* stop */
	ub->frames = xspanalloc(FRAMESIZE, FRAMESIZE, 0);
	qt = mkqhtree(ub->frames, NFRAME, 32);
	if(qt == nil){
		print("usb: can't allocate scheduling tree\n");
		ub->io = 0;
		return;
	}
	qt->root->head = PADDR(ub->ctlq) | IsQH;
	ub->tree = qt;
	XPRINT("usb tree: nel=%d depth=%d\n", qt->nel, qt->depth);

	outl(port+Flbaseadd, PADDR(ub->frames));
	OUT(Frnum, 0);
	OUT(Usbintr, 0xF);	/* enable all interrupts */
	XPRINT("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(port+SOFMod));
	XPRINT("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1));
}

void
usbinit(void)
{
	Udev *d;

	if(ubus.io != 0 && usbdev[0] == nil){
		d = usbnewdevice();	/* reserve device 0 for configuration */
		incref(d);
		d->state = Attached;
	}
}

Chan *
usbattach(char *spec)
{
	Ctlr *ub;

	ub = &ubus;
	if(ub->io == 0) {
		XPRINT("usbattach failed\n");
		error(Enodev);
	}
	if((IN(Cmd)&1)==0 || *spec)
		OUT(Cmd, 1);	/* run */
//	pprint("at: c=%x s=%x c0=%x\n", IN(Cmd), IN(Status), IN(Portsc0));
	return devattach('U', spec);
}

static int
usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
{
	int t;
	Qid q;
	ulong path;
	Udev *d;
	Dirtab *tab;
	Endpt *e;

	/*
	 * Top level directory contains the name of the device.
	 */
	if(c->qid.path == Qtopdir){
		if(s == DEVDOTDOT){
			mkqid(&q, Qtopdir, 0, QTDIR);
			devdir(c, q, "#U", 0, eve, 0555, dp);
			return 1;
		}
		if(s == 0){
			mkqid(&q, Q2nd, 0, QTDIR);
			devdir(c, q, "usb", 0, eve, 0555, dp);
			return 1;
		}
		return -1;
	}

	/*
	 * Second level contains "new" plus all the clients.
	 */
	t = QID(c->qid);
	if(t < Q3rd){
		if(s == DEVDOTDOT){
			mkqid(&q, Qtopdir, 0, QTDIR);
			devdir(c, q, "#U", 0, eve, 0555, dp);
			return 1;
		}
		if(s < nelem(usbdir2)){
			tab = &usbdir2[s];
			devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
			return 1;
		}
		s -= nelem(usbdir2);
		if(s >= 0 && s < nelem(usbdev)){
			d = usbdev[s];
			if(d == nil)
				return -1;
			sprint(up->genbuf, "%d", s);
			mkqid(&q, ((s+1)<<QSHIFT)|Q3rd, d->id, QTDIR);
			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
			return 1;
		}
		return -1;
	}

	/*
	 * Third level.
	 */
	path = c->qid.path & ~QMASK;	/* slot component */
	if(s == DEVDOTDOT){
		mkqid(&q, Q2nd, c->qid.vers, QTDIR);
		devdir(c, q, "usb", 0, eve, 0555, dp);
		return 1;
	}
	if(s < nelem(usbdir3)){
		Dirtab *tab = &usbdir3[s];
		mkqid(&q, path | tab->qid.path, c->qid.vers, QTFILE);
		devdir(c, q, tab->name, tab->length, eve, tab->perm, dp);
		return 1;
	}

	/* active endpoints */
	d = usbdeviceofpath(path);
	if(d == nil)
		return -1;
	s -= nelem(usbdir3);
	if(s < 0 || s >= nelem(d->ep))
		return -1;
	if(s == 0 || (e = d->ep[s]) == nil)	/* ep0data is called "setup" */
		return 0;
	sprint(up->genbuf, "ep%ddata", s);
	mkqid(&q, path | (Qep0+s), c->qid.vers, QTFILE);
	devdir(c, q, up->genbuf, 0, eve, e->mode==OREAD? 0444: e->mode==OWRITE? 0222: 0666, dp);
	return 1;
}

static Walkqid*
usbwalk(Chan *c, Chan *nc, char **name, int nname)
{
	return devwalk(c, nc, name, nname, nil, 0, usbgen);
}

static int
usbstat(Chan *c, uchar *db, int n)
{
	return devstat(c, db, n, nil, 0, usbgen);
}

Chan *
usbopen(Chan *c, int omode)
{
	Udev *d;
	int f, s;

	if(c->qid.type == QTDIR)
		return devopen(c, omode, nil, 0, usbgen);

	f = 0;
	if(QID(c->qid) == Qnew){
		d = usbnewdevice();
		if(d == nil) {
			XPRINT("usbopen failed (usbnewdevice)\n");
			error(Enodev);
		}
		c->qid.path = Qctl|((d->x+1)<<QSHIFT);
		c->qid.vers = d->id;
		f = 1;
	}

	if(c->qid.path < Q3rd)
		return devopen(c, omode, nil, 0, usbgen);

	qlock(&usbstate);
	if(waserror()){
		qunlock(&usbstate);
		nexterror();
	}

	switch(QID(c->qid)){
	case Qctl:
		d = usbdevice(c);
		if(0&&d->busy)
			error(Einuse);
		d->busy = 1;
		if(!f)
			incref(d);
		break;

	default:
		d = usbdevice(c);
		s = QID(c->qid) - Qep0;
		if(s >= 0 && s < nelem(d->ep)){
			Endpt *e;
			if((e = d->ep[s]) == nil) {
				XPRINT("usbopen failed (endpoint)\n");
				error(Enodev);
			}
			if(schedendpt(e) < 0)
				error("can't schedule USB endpoint");
			eptactivate(e);
		}
		incref(d);
		break;
	}
	poperror();
	qunlock(&usbstate);
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

void
usbcreate(Chan *c, char *name, int omode, ulong perm)
{
	USED(c, name, omode, perm);
	error(Eperm);
}

void
usbremove(Chan*)
{
	error(Eperm);
}

void
usbwstat(Chan *c, char *dp)
{
	USED(c, dp);
	error(Eperm);
}

void
usbclose(Chan *c)
{
	Udev *d;

	if(c->qid.type == QTDIR || c->qid.path < Q3rd)
		return;
	qlock(&usbstate);
	if(waserror()){
		qunlock(&usbstate);
		nexterror();
	}
	d = usbdevice(c);
	if(QID(c->qid) == Qctl)
		d->busy = 0;
	if(c->flag & COPEN)
		freedev(d);
	poperror();
	qunlock(&usbstate);
}

static int
eptinput(void *arg)
{
	Endpt *e;

	e = arg;
	return e->eof || e->err || qcanread(e->rq);
}

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

	XPRINT("qlock(%p)\n", &e->rlock);
	qlock(&e->rlock);
	XPRINT("got qlock(%p)\n", &e->rlock);
	if(waserror()){
		qunlock(&e->rlock);
		eptcancel(e);
		nexterror();
	}
	p = a;
	do {
		if(e->eof) {
			XPRINT("e->eof\n");
			break;
		}
		if(e->err)
			error(e->err);
		qrcv(e);
		if(!e->iso)
			e->data01 ^= 1;
		sleep(&e->rr, eptinput, e);
		if(e->err)
			error(e->err);
		b = qget(e->rq);	/* TO DO */
		if(b == nil) {
			XPRINT("b == nil\n");
			break;
		}
		if(waserror()){
			freeb(b);
			nexterror();
		}
		l = BLEN(b);
		if((i = l) > n)
			i = n;
		if(i > 0){
			memmove(p, b->rp, i);
			p += i;
		}
		poperror();
		freeb(b);
		n -= i;
	} while (l == e->maxpkt && n > 0);
	poperror();
	qunlock(&e->rlock);
	return p-(uchar*)a;
}

long
usbread(Chan *c, void *a, long n, vlong offset)
{
	Endpt *e;
	Udev *d;
	char buf[48], *s;
	int t, w0, w1, ps, l, i;

	if(c->qid.type == QTDIR)
		return devdirread(c, a, n, nil, 0, usbgen);

	t = QID(c->qid);
	switch(t){
	case Qbusctl:
		snprint(buf, sizeof(buf), "%11d %11d ", 0, 0);
		return readstr(offset, a, n, buf);

	case Qport:
		ps = portinfo(&ubus, &w0, &w1);
		snprint(buf, sizeof(buf), "0x%ux 0x%ux 0x%ux ", ps, w0, w1);
		return readstr(offset, a, n, buf);

	case Qctl:
		d = usbdevice(c);
		sprint(buf, "%11d %11d ", d->x, d->id);
		return readstr(offset, a, n, buf);

	case Qsetup:	/* endpoint 0 */
		d = usbdevice(c);
		if((e = d->ep[0]) == nil)
			error(Eio);	/* can't happen */
		e->data01 = 1;
		n = readusb(e, a, n);
		if(e->setin){
			e->setin = 0;
			e->data01 = 1;
			writeusb(e, "", 0, TokOUT);
		}
		break;

	case Qdebug:
		n=0;
		break;

	case Qstatus:
		d = usbdevice(c);
		s = smalloc(READSTR);
		if(waserror()){
			free(s);
			nexterror();
		}
		l = snprint(s, READSTR, "%s %#6.6lux\n", devstates[d->state], d->csp);
		for(i=0; i<nelem(d->ep); i++)
			if((e = d->ep[i]) != nil)	/* TO DO: freeze e */
				l += snprint(s+l, READSTR-l, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks);
		n = readstr(offset, a, n, s);
		poperror();
		free(s);
		break;

	default:
		d = usbdevice(c);
		if((t -= Qep0) < 0 || t >= nelem(d->ep))
			error(Eio);
		if((e = d->ep[t]) == nil || e->mode == OWRITE)
			error(Eio);	/* can't happen */
		n=readusb(e, a, n);
		break;
	}
	return n;
}

static int
qisempty(void *arg)
{
	return ((QH*)arg)->entries & Terminate;
}

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

	p = a;
	qlock(&e->wlock);
	if(waserror()){
		qunlock(&e->wlock);
		eptcancel(e);
		nexterror();
	}
	do {
		int j;

		if(e->err)
			error(e->err);
		if((i = n) >= e->maxpkt)
			i = e->maxpkt;
		b = allocb(i);
		if(waserror()){
			freeb(b);
			nexterror();
		}
		XPRINT("out [%ld]", i);
		for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]);
		XPRINT("\n");
		memmove(b->wp, p, i);
		b->wp += i;
		p += i;
		n -= i;
		poperror();
		qh = qxmit(e, b, tok);
		tok = TokOUT;
		if(!e->iso)
			e->data01 ^= 1;
		if(e->ntd >= e->nbuf) {
			XPRINT("writeusb: sleep %lux\n", &e->wr);
			sleep(&e->wr, qisempty, qh);
			XPRINT("writeusb: awake\n");
		}
	} while(n > 0);
	poperror();
	qunlock(&e->wlock);
	return p-(uchar*)a;
}

long
usbwrite(Chan *c, void *a, long n, vlong)
{
	Udev *d;
	Endpt *e;
	int id, nw, nf, t, i;
	char cmd[50], *fields[10];

	if(c->qid.type == QTDIR)
		error(Egreg);
	t = QID(c->qid);
	if(t == Qbusctl){
		if(n >= sizeof(cmd)-1)
			n = sizeof(cmd)-1;
		memmove(cmd, a, n);
		cmd[n] = 0;
		nf = getfields(cmd, fields, nelem(fields), 0, " \t\n");
		if(nf==1 && strcmp(fields[0], "dump")==0){
			dumpframe(-1, -1);
			return n;
		}
		if(nf < 2)
			error(Ebadarg);
		id = strtol(fields[1], nil, 0);
		if(id != 1 && id != 2)
			error(Ebadarg);	/* there are two ports on the root hub */
		if(strcmp(fields[0], "reset") == 0)
			portreset(id);
		else if(strcmp(fields[0], "enable") == 0)
			portenable(id, 1);
		else if(strcmp(fields[0], "disable") == 0)
			portenable(id, 0);
		else
			error(Ebadarg);
		return n;
	}
	d = usbdevice(c);
	t = QID(c->qid);
	switch(t){
	case Qctl:
		if(n >= sizeof(cmd)-1)
			n = sizeof(cmd)-1;
		memmove(cmd, a, n);
		cmd[n] = 0;
		nf = getfields(cmd, fields, nelem(fields), 0, " \t\n");
		if(nf > 1 && strcmp(fields[0], "speed") == 0){
			d->ls = strtoul(fields[1], nil, 0) == 0;
		} else if(nf > 3 && strcmp(fields[0], "class") == 0){
			i = strtoul(fields[2], nil, 0);
			d->npt = strtoul(fields[1], nil, 0);
			/* class config# csp ( == class subclass proto) */
			if (i < 0 || i >= nelem(d->ep)
			 || d->npt > nelem(d->ep) || i >= d->npt)
				error(Ebadarg);
			if (i == 0) {
				d->csp = strtoul(fields[3], nil, 0);
			}
			if(d->ep[i] == nil)
				d->ep[i] = devendpt(d, i, 1);
			d->ep[i]->csp = strtoul(fields[3], nil, 0);
		}else if(nf > 2 && strcmp(fields[0], "data") == 0){
			i = strtoul(fields[1], nil, 0);
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
				error(Ebadarg);
			e = d->ep[i];
			e->data01 = strtoul(fields[2], nil, 0) != 0;
		}else if(nf > 2 && strcmp(fields[0], "maxpkt") == 0){
			i = strtoul(fields[1], nil, 0);
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
				error(Ebadarg);
			e = d->ep[i];
			e->maxpkt = strtoul(fields[2], nil, 0);
			if(e->maxpkt > 1500)
				e->maxpkt = 1500;
		}else if(nf > 2 && strcmp(fields[0], "debug") == 0){
			i = strtoul(fields[1], nil, 0);
			if(i < -1 || i >= nelem(d->ep) || d->ep[i] == nil)
				error(Ebadarg);
			if (i == -1)
				debug = 0;
			else {
				debug = 1;
				e = d->ep[i];
				e->debug = strtoul(fields[2], nil, 0);
			}
		}else if(nf > 1 && strcmp(fields[0], "unstall") == 0){
			i = strtoul(fields[1], nil, 0);
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
				error(Ebadarg);
			e = d->ep[i];
			e->err = nil;
		}else if(nf == 6 && strcmp(fields[0], "ep") == 0){
			/* ep n maxpkt mode poll nbuf */
			i = strtoul(fields[1], nil, 0);
			if(i < 0 || i >= nelem(d->ep)) {
				XPRINT("field 1: 0 <= %d < %d\n", i, nelem(d->ep));
				error(Ebadarg);
			}
			if((e = d->ep[i]) == nil)
				e = devendpt(d, i, 1);
			if(waserror()){
				freept(e);
				nexterror();
			}
			i = strtoul(fields[2], nil, 0);
			if(i < 8 || i > 1023)
				i = 8;
			e->maxpkt = i;
			e->mode = strcmp(fields[3],"r") == 0? OREAD :
					  strcmp(fields[3],"w") == 0? OWRITE : ORDWR;
			e->periodic = 0;
			e->sched = -1;
			if(strcmp(fields[4], "bulk") == 0){
				Ctlr *ub;

				ub = &ubus;
				/* Each bulk device gets a queue head hanging off the
				 * bulk queue head
				 */
				if (e->epq == nil) {
					e->epq = allocqh(ub);
					if(e->epq == nil)
						panic("usbwrite: allocqh");
				}
				queueqh(e->epq);
			} else {
				e->periodic = 1;
				i = strtoul(fields[4], nil, 0);
				if(i > 0 && i <= 1000)
					e->pollms = i;
				else {
					XPRINT("field 4: 0 <= %d <= 1000\n", i);
					error(Ebadarg);
				}
			}
			i = strtoul(fields[5], nil, 0);
			if(i >= 1 && i <= 32)
				e->nbuf = i;
			poperror();
		}else {
			XPRINT("command %s, fields %d\n", fields[0], nf);
			error(Ebadarg);
		}
		return n;

	case Qsetup:	/* SETUP endpoint 0 */
		/* should canqlock etc */
		if((e = d->ep[0]) == nil)
			error(Eio);	/* can't happen */
		if(n < 8 || n > 1023)
			error(Eio);
		nw = *(uchar*)a & RD2H;
		e->data01 = 0;
		n = writeusb(e, a, n, TokSETUP);
		if(nw == 0){	/* host to device: use IN[DATA1] to ack */
			e->data01 = 1;
			nw = readusb(e, cmd, 8);
			if(nw != 0)
				error(Eio);	/* could provide more status */
		}else
			e->setin = 1;	/* two-phase */
		break;

	default:	/* sends DATA[01] */
		if((t -= Qep0) < 0 || t >= nelem(d->ep)) {
			print("t = %d\n", t);
			error(Eio);
		}
		if((e = d->ep[t]) == nil || e->mode == OREAD) {
			error(Eio);	/* can't happen */
		}
		n = writeusb(e, a, n, TokOUT);
		break;
	}
	return n;
}

Dev usbdevtab = {
	'U',
	"usb",

	usbreset,
	usbinit,
	usbattach,
	usbwalk,
	usbstat,
	usbopen,
	devcreate,
	usbclose,
	usbread,
	devbread,
	usbwrite,
	devbwrite,
	devremove,
	devwstat,
};
.
568,570c
			tp = t;
			t = t->next;
			continue;
.
553c
		XPRINT("cleanq: %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux %8.8lux\n",
			t->link, t->status, t->dev, t->buffer, t->flags, t->next);
.
490c
		XPRINT("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n",
			t->link, t->status, t->dev, t->buffer);
.
482c
	XPRINT("cleanTD: %8.8lux %8.8lux %8.8lux %8.8lux\n",
		t->link, t->status, t->dev, t->buffer);
.
372c
		XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n",
			buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1);
.
218,223c
	byte		mode;	/* OREAD, OWRITE, ORDWR */
	byte		nbuf;	/* number of buffers allowed */
	byte		periodic;
	byte		iso;
	byte		debug;
	byte		active;	/* listed for examination by interrupts */
.
216c
	byte		eof;
.
202c
	[Assigned]	"Assigned",
.
200c
	[Attached]	"Attached",
.
## diffname pc/devusb.c 2001/0726
## diff -e /n/emeliedump/2001/0626/sys/src/9/pc/devusb.c /n/emeliedump/2001/0726/sys/src/9/pc/devusb.c
1256c
		return;
.
## diffname pc/devusb.c 2001/0916
## diff -e /n/emeliedump/2001/0726/sys/src/9/pc/devusb.c /n/emeliedump/2001/0916/sys/src/9/pc/devusb.c
1904,1906d
1902a
				e->mode = strcmp(fields[3],"r") == 0? OREAD :
					  	strcmp(fields[3],"w") == 0? OWRITE : ORDWR;
				i = strtoul(fields[4], nil, 0);
				if(i >= 1 && i <= 8){
					e->samplesz = i;
				}else {
					XPRINT("field 4: 0 < %d <= 8\n", i);
					error(Ebadarg);
				}
				i = strtoul(fields[5], nil, 0);
				if(i >= 1 && i <= 100000){
					/* Hz */
					e->hz = i;
					e->remain = 999/e->pollms;
				}else {
					XPRINT("field 5: 1 < %d <= 100000 Hz\n", i);
					error(Ebadarg);
				}
				e->maxpkt = (e->hz * e->pollms + 999)/1000 * e->samplesz;
				e->iso = 1;
.
1899c
				}else {
.
1897c
				if(i < 8 || i > 1023)
					i = 8;
				e->maxpkt = i;
				i = strtoul(fields[5], nil, 0);
				if(i >= 1 && i <= 32)
					e->nbuf = i;
			} else {
				/* ep n period mode samplesize KHz */
				i = strtoul(fields[2], nil, 0);
				if(i > 0 && i <= 1000){
.
1894,1895c
				e->mode = strcmp(fields[3],"r") == 0? OREAD :
					  	strcmp(fields[3],"w") == 0? OWRITE : ORDWR;
.
1883a
				/* ep n `bulk' mode maxpkt nbuf */
.
1873,1881c
			if (e->iso && e->sched >= 0){
				unschedendpt(e);
			}
			e->iso = 0;
			if(strcmp(fields[2], "bulk") == 0){
.
1861c
			/* ep n `bulk' mode maxpkt nbuf     OR
			 * ep n period mode samplesize KHz
			 */
.
1786,1789d
1746,1762d
1739,1744c
		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;
			}
			if (e->psize > e->maxpkt)
				panic("packet size > maximum");
			b = e->curblock;
			if (b == nil)
				panic("writeusb: block");
			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;
			p += i;
			n -= i;
			e->curbytes += i;
			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->curbytes = 0;
			e->curblock->wp = e->curblock->rp;
			e->nbytes += e->psize;
			e->nblocks++;
		}else{
			int j;

			if((i = n) >= e->maxpkt)
				i = e->maxpkt;
			b = allocb(i);
			if(waserror()){
				freeb(b);
				nexterror();
			}
			XPRINT("out [%ld]", i);
			for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]);
			XPRINT("\n");
			memmove(b->wp, p, i);
			b->wp += i;
			p += i;
			n -= i;
			poperror();
			qh = qxmit(e, b, tok);
			tok = TokOUT;
			if(!e->iso)
				e->data01 ^= 1;
			if(e->ntd >= e->nbuf) {
				XPRINT("writeusb: sleep %lux\n", &e->wr);
				sleep(&e->wr, qisempty, qh);
				XPRINT("writeusb: awake\n");
			}
.
1735d
1336,1338d
1330,1334c
	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;
.
1306,1307c
	 * the last entries of the periodic (interrupt & isochronous) scheduling TD entries
	 * point to the control queue and the bandwidth sop for bulk traffic.
.
1294,1295c
	ub->tdpool = xspanalloc((NFRAME*NISOTD+128)*sizeof(TD), 16, 0);
	for(i=NFRAME*NISOTD+128; --i>=NFRAME*NISOTD;){
.
1249d
1247c
	int i, j;
.
1233c
			e->iso = 0;
.
1143a
		if(e->iso && e->etd != nil) {
			wakeup(&e->wr);
		}
	}
.
1139,1140c
	for(e = activends.f; e != nil; e = e->activef){
		if(!e->iso && e->epq != nil) {
.
958d
954c
	e->iso = 0;
.
919,930c
	ilock(ub);
	
	for (td = e->etd; q < NFRAME; q += e->pollms){
		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);
	e->sched = -1;

	iunlock(ub);
.
917c
	if(!e->iso || (q = e->sched) < 0)
.
912,913c
	TD *td, *next;
.
898,904c

	/* 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;
	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->ep = e;
		if (td->bp)
			panic("usb: schedendpt: block not empty");
		td->bp = allocb(e->maxpkt);
		if (td->bp == nil)
			panic("schedept: allocb");
		if (i == e->sched){
			first = td;
			e->etd = td;
		}else{
			*prev = td;
		}
		prev = &td->next;
		ub->frameld[i] += e->maxpkt;
	}
	*prev = first;		/* complete circular link */
	iunlock(ub);
.
889,896c
	
	ilock(ub);
	e->sched = usbsched(ub, e->pollms, e->maxpkt);
	if(e->sched < 0)
.
886c
	e->curblock = allocb(e->maxpkt);
	if (e->curblock == nil)
		panic("schedept: allocb");
	e->curbytes = 0;
	if(!e->iso || e->sched >= 0)
.
883,884c
	TD *td, **prev, *first;
	int i, j;
.
874,875d
871,872d
847,869c
		if (worst < best){
			best = worst;
			q = d;
.
845c
			if (ub->frameld[i] + load > worst)
				worst = ub->frameld[i] + load;
.
780,842c
	best = 1000000;
	q = -1;
	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 */
.
776,778c
	int i, j, d, q;
	ulong best, worst;
.
769,774c
static int
usbsched(	Ctlr *ub, int pollms, ulong load)
.
683a
static int
tdisready(void *arg)
{
	return (((TD*)arg)->status & Active) == 0;
}

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

	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;
}

.
260,264c
	QH*		ctlq;	/* queue for control i/o */
	QH*		bwsop;	/* empty bandwidth sop (to PIIX4 errata specifications) */
	QH*		bulkq;	/* queue for bulk i/o (points back to bandwidth sop) */
	QH*		recvq;	/* receive queues for bulk i/o */
.
255,258c
	TD*		tdpool;	/* first NFRAMES*NISOTD entries are preallocated */
	TD*		freetd;
	QH*		qhpool;
	QH*		freeqh;
.
252c
	ulong*	frameld;	/* real time load on each of the frame list entries */
	int		idgen;	/* version number to distinguish new connections */
.
250c
	int		io;
.
228,229c
	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 */
.
226d
223a
	int		hz;
	int		remain;
	int		samplesz;
.
220d
212c
	int		x;		/* index in Udev.ep */
.
179,180c
	Endpt*	ep[16];	/* active end points */
	Udev*	ports;	/* active ports, if hub */
.
175c
	byte		port;		/* port number on connecting hub */
.
171c
	int		x;		/* index in usbdev[] */
.
151,161d
120a
	NISOTD = 4,			/* number of TDs for isochronous io per frame */
.
53d
25c
#define XPRINT if(debug)iprint
.
## diffname pc/devusb.c 2001/0917
## diff -e /n/emeliedump/2001/0916/sys/src/9/pc/devusb.c /n/emeliedump/2001/0917/sys/src/9/pc/devusb.c
1730d
1620,1630c
	} while (n > 0);
.
1618c
			n -= i;
			if (l != e->maxpkt)
				break;
.
1605,1616c
		if (e->iso){
			if (e->curbytes == 0){
				/* calculate size of next receive buffer */
				e->psize = (e->hz + e->remain)*e->pollms/1000;
				e->remain = (e->hz + e->remain)*e->pollms%1000;
				e->psize *= e->samplesz;
				if (e->psize > e->maxpkt)
					panic("packet size > maximum");
				e->curblock->wp = e->curblock->rp + e->psize;
				/* 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 (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{
			qrcv(e);
			if(!e->iso)
				e->data01 ^= 1;
			sleep(&e->rr, eptinput, e);
			if(e->err)
				error(e->err);
			b = qget(e->rq);	/* TO DO */
			if(b == nil) {
				XPRINT("b == nil\n");
				break;
			}
			if(waserror()){
				freeb(b);
				nexterror();
			}
			l = BLEN(b);
			if((i = l) > n)
				i = n;
			if(i > 0){
				memmove(p, b->rp, i);
				p += i;
			}
			poperror();
.
845a
	e->curblock = allocb(e->maxpkt);
	if (e->curblock == nil)
		panic("schedept: allocb");
	e->curbytes = 0;
.
838,841d
716a
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: isoxmit");
	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;
	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;
}

.
## diffname pc/devusb.c 2001/0918
## diff -e /n/emeliedump/2001/0917/sys/src/9/pc/devusb.c /n/emeliedump/2001/0918/sys/src/9/pc/devusb.c
2029a
			qunlock(&usbstate);
.
2021c
		//			e->remain = 999/e->pollms;
					e->remain = 0;
.
1978a
				e->iso = 0;
.
1972,1975c
			if (e->isopen)
				error(Eperm);
.
1969a
				qunlock(&usbstate);
.
1967a
			qlock(&usbstate);
.
1646,1651d
1557a
			e->isopen = 1;
.
1001a
		e->isopen = 0;
.
947,948d
934,935c

.
920d
910c
		if (e->mode == OREAD){
			int id;
			/* 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;
		}
		if(i == e->sched){
.
908c
		if(td->bp == nil)
.
905c
		if(td->bp)
.
887a
	}
.
886c
	if(e->sched < 0){
		qunlock(&usbstate);
.
884d
879c

	if (e->isopen){
		return -1;
	}
		
.
746,750c
	td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | pid;
	td->status = ErrLimit3 | Active | IsoSelect | IOC;
.
744d
727c
		panic("usb: isorecv");
.
206a
	byte		isopen;	/* ep operations forbidde on open endpoints */
.
## diffname pc/devusb.c 2001/0922
## diff -e /n/emeliedump/2001/0918/sys/src/9/pc/devusb.c /n/emeliedump/2001/0922/sys/src/9/pc/devusb.c
1610a
	s = QID(c->qid) - Qep0;
	if(s >= 0 && s < nelem(d->ep)){
		Endpt *e;
		if((e = d->ep[s]) == nil) {
			XPRINT("usbopen failed (endpoint)\n");
			error(Enodev);
		}
		e->isopen--;
	}
.
1601a
	int s;
.
1565d
1563a
			e->isopen++;
.
1008d
981d
978d
882a
		e->curblock = allocb(e->maxpkt);
	if (e->curblock == nil)
.
880,881d
207c
	byte		isopen;	/* ep operations forbidden on open endpoints */
.
## diffname pc/devusb.c 2001/0928
## diff -e /n/emeliedump/2001/0922/sys/src/9/pc/devusb.c /n/emeliedump/2001/0928/sys/src/9/pc/devusb.c
1968c
			 * ep n period mode samplesize hz
.
## diffname pc/devusb.c 2001/1003
## diff -e /n/emeliedump/2001/0928/sys/src/9/pc/devusb.c /n/emeliedump/2001/1003/sys/src/9/pc/devusb.c
710,713c
	td->status = ErrLimit1 | Active | IsoSelect | IOC;
.
695c
	while(td->status & Active && (td->dev & 0x7ff<21) != (0x7ff<21)){
.
## diffname pc/devusb.c 2001/1004
## diff -e /n/emeliedump/2001/1003/sys/src/9/pc/devusb.c /n/emeliedump/2001/1004/sys/src/9/pc/devusb.c
710c
	if ((ioc++ & 0xff) == 0)
		td->status = ErrLimit3 | Active | IsoSelect | IOC;
	else
		td->status = ErrLimit3 | Active | IsoSelect;
.
695c
	while(td->status & Active){
.
690a
	TD *td;
.
687,688d
## diffname pc/devusb.c 2001/1005
## diff -e /n/emeliedump/2001/1004/sys/src/9/pc/devusb.c /n/emeliedump/2001/1005/sys/src/9/pc/devusb.c
688c
	static int ioc, n, id;
.
## diffname pc/devusb.c 2001/1006
## diff -e /n/emeliedump/2001/1005/sys/src/9/pc/devusb.c /n/emeliedump/2001/1006/sys/src/9/pc/devusb.c
1967c
			 * ep n period mode samplesize KHz
.
1871,1872c
		} while(n > 0);
	}
.
1854c
			XPRINT("out [%d]", i);
.
1846a
			if(e->err)
				error(e->err);
.
1844c
		} while(n > 0);
		poperror();
	}else{
		do {
.
1841c
			e->psize = ((e->etd->dev >> 21) & 0x7ff) + 1;
			if (e->psize > e->maxpkt)
				panic("packet size > maximum");
.
1837,1839c
			e->etd = td->next;
.
1832d
1830a
			td->flags &= ~IsoLock;
.
1824,1827c
			memmove(q, p, i);
.
1817,1821c
			td = e->etd;
			e->isolock = 0;
			td->flags |= IsoLock;
			td->flags &= ~IsoClean;
			q = (uchar*)td->bp + e->curbytes;
.
1808,1815c
	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");
.
1799a
	TD *td;
.
1798c
	uchar *p, *q;
.
1796c
	int i;
.
1792a
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;
}

.
1708,1709c
		} while (n > 0);
	}
.
1670,1680c
			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);
.
1650,1668c
	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");
				}
.
1638,1639c
	uchar *p, *q;
	long l, i, id;
	TD *td;
.
1633a
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;
}

.
1615a
		if (e->isopen == 0){
			eptdeactivate(e);
			unschedendpt(e);
		}
.
1355,1374c
	for (i = 0; i < NFRAME; i++)
		ub->frames[i] = PADDR(ub->ctlq) | IsQH;
.
1317,1318c
	ub->tdpool = xspanalloc(128*sizeof(TD), 16, 0);
	for(i=128; --i>=0;){
.
1271c
	int i;
.
1164,1165c
		if(e->iso) {
			XPRINT("cleaniso(e)\n");
			cleaniso(e, frnum);
.
1143c
	frnum = IN(Frnum) & 0x3ff;
	XPRINT("usbint: #%x f%d\n", s, frnum);
.
1135c
	int s, frnum;
.
1130a
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
.
1007d
1004a
		eptdeactivate(e);
.
952c
	free(e->tdalloc);
	free(e->bpalloc);
	e->tdalloc = nil;
	e->bpalloc = nil;
	e->etd = nil;
	e->td0 = nil;
.
945,950d
943c
	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;
.
940c
	if(!e->iso || e->sched < 0)
.
936c
	TD *td;
	ulong *addr;
.
928c
	e->isolock = 0;
.
919,926c
		td->status = ErrLimit3 | Active | IsoSelect | IOC;
		td->link = ub->frames[ix];
		ub->frames[ix] = PADDR(td);
.
917c
		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;
.
914,915d
906,912c
		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)
.
898,904c
		td->bp = (Block *)(bp + e->maxpkt*i/e->pollms);
		td->buffer = PADDR(td->bp);
.
890,896c
	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;
.
879,882d
869,870c
	TD *td;
	uchar *bp;
	int i, id, ix, size, frnum;
.
847,853d
839c
	int i, d, q;
.
678,750d
227a
	int		isolock;
.
223c
	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 */
.
220d
212a
	byte *	tdalloc;
	byte *	bpalloc;
.
210c
	volatile byte	iso;
.
148a
	IsoLock=		1<<1,
	IsoClean=		1<<2,
.
119c
	NFRAME = 	(FRAMESIZE/4),
.
## diffname pc/devusb.c 2001/1007
## diff -e /n/emeliedump/2001/1006/sys/src/9/pc/devusb.c /n/emeliedump/2001/1007/sys/src/9/pc/devusb.c
1885a
		e->isolock = 0;
.
1878,1880c
			e->off = 0;
			e->psize = ((e->etd->dev >> 21) + 1) & 0x7ff;
.
1861,1876c
			if ((td = e->etd) == nil){
				iprint("0");
				ub = &ubus;
				frnum = (IN(Frnum) + 8) & 0x3ff;
				e->etd = e->td0 +frnum;
			}else{
				td->flags &= ~IsoClean;
				q = (uchar*)td->bp + e->off;
				if((i = n) >= e->psize)
					i = e->psize;
				memmove(q, p, i);
				p += i;
				n -= i;
				e->off += i;
				e->psize -= i;
				if (e->psize){
					if (n != 0)
						panic("usb iso: can't happen");
					break;
				}
				td->status = ErrLimit3 | Active | IsoSelect | IOC;
				e->etd = td->next;
.
1856,1859c
			while (isoready(e) == 0){
				iprint("i");
				sleep(&e->wr, isoready, e);
.
1854a
		e->isolock = 1;
.
1851,1852d
1837a
	Ctlr *ub;
.
1834c
	int i, frnum;
.
1804,1830d
1674,1683c
			e->off = 0;
			e->psize = (e->etd->status + 1) & 0x7ff;
			if (e->psize > e->maxpkt)
				panic("packet size > maximum");
			e->nbytes += e->psize;
			e->nblocks++;
		} while(n > 0);
		e->isolock = 0;
		poperror();
.
1672a
				td->status = ErrLimit3 | Active | IsoSelect | IOC;
				e->etd = td->next;
.
1667,1668c
				if (e->psize){
.
1665c
				e->off += i;
.
1655,1659c
			if ((td = e->etd) == nil){
				ub = &ubus;
				frnum = (IN(Frnum) + 8) & 0x3ff;
				e->etd = e->td0 +frnum;
			}else{
				q = (uchar*)td->bp + e->off;
.
1648,1653c
			while (isoready(e) == 0){
				XPRINT("readusb: sleep %lux\n", &e->wr);
				sleep(&e->wr, isoready, e);
				XPRINT("readusb: awake\n");
.
1646a
		if(waserror()){
			e->isolock = 0;
			nexterror();
		}
		e->isolock = 1;
.
1635a
	Ctlr *ub;
.
1634c
	int l, i, frnum;
.
1607,1626c
	return e->etd == nil || (e->etd->status & Active) == 0;
.
1602,1603d
1600c
isoready(void *arg)
.
1103,1104d
1100d
1093,1096c
		if (e->mode == OREAD){
			td->dev = ((e->maxpkt -1)<<21) | ((id&0x7FF)<<8) | TokIN;
		}else{
			n = (e->hz + e->remain)*e->pollms/1000;
			e->remain = (e->hz + e->remain)*e->pollms%1000;
			n *= e->samplesz;
			td->dev = ((n -1)<<21) | ((id&0x7FF)<<8) | TokOUT;
		}
		td = td->next;
		if (e->xtd == td){
			iprint("@");
			break;
		}
	} while ((td->status & Active) == 0);
	e->xtd = td;
	if (e->isolock){
		wakeup(&e->wr);
		return;
	}
	for (n = 2; n < 6; n++){
		td = e->td0 + ((frnum + n)&0x3ff);
		if (td->status & Active)
			continue;

		if (td == e->etd) {
			memset((byte*)td->bp+e->off, 0, e->maxpkt-e->off);
			if (e->off == 0)
				td->flags |= IsoClean;
			e->etd = nil;
		}else if ((td->flags & IsoClean) == 0){
.
1081,1090c
	do{
.
1072,1079c
	td = e->xtd;
	if (td->status & Active)
		return;
.
1070c
	int id, n;
.
854d
821,823c
	e->xtd = &e->td0[(frnum+8)&0x3ff];	/* Next td to finish */
	e->etd = nil;
.
806c
	e->off = 0;
.
233d
226,228d
223,224c
	int		psize;	/* (remaining) size of this packet */
	int		off;		/* offset into packet */
	int		isolock;	/* reader/writer interlock with interrupt */
	TD*		td0;		/* first td in array */
	TD*		etd;		/* pointer into circular list of TDs for isochronous ept */
	TD*		xtd;		/* next td to be cleaned */
	/* end ISO stuff */
.
221d
218c
	int		remain;	/* for packet size calculations */
.
214a
	int		setin;
	/* ISO related: */
.
149d
## diffname pc/devusb.c 2001/1008
## diff -e /n/emeliedump/2001/1007/sys/src/9/pc/devusb.c /n/emeliedump/2001/1008/sys/src/9/pc/devusb.c
1857a
		p = a;
.
1856a
		qunlock(&e->wlock);
		return n;
.
1815,1855c
		n = isoio(e, a, n, 1);
.
1813d
1806d
1803,1804d
1801c
	uchar *p;
.
1799c
	int i;
.
1676a
		p = a;
.
1675a
		qunlock(&e->rlock);
		return n;
.
1673,1674c
		}
		td->flags &= ~IsoClean;
		q = (uchar*)td->bp + e->off;
		if((i = n) >= e->psize)
			i = e->psize;
		if (w)
			memmove(q, p, i);
		else
			memmove(p, q, i);
		p += i;
		n -= i;
		e->off += i;
		e->psize -= i;
		if (e->psize){
			if (n != 0)
				panic("usb iso: can't happen");
			break;
		}
		td->status = ErrLimit3 | Active | IsoSelect | IOC;
		e->etd = td->next;
		e->off = 0;
	} while(n > 0);
	e->isolock = 0;
	poperror();
	return p-(uchar*)a;
}

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

	XPRINT("qlock(%p)\n", &e->rlock);
	qlock(&e->rlock);
	XPRINT("got qlock(%p)\n", &e->rlock);
	if(waserror()){
		qunlock(&e->rlock);
		eptcancel(e);
		nexterror();
	}
	if (e->iso){
		n = isoio(e, a, n, 0);
.
1667,1668c
			if (w)
				e->psize = ((td->dev >> 21) + 1) & 0x7ff;
			else
				e->psize = (e->etd->status + 1) & 0x7ff;
.
1646,1665c
			e->isolock = 1;
			if (e->etd == nil){
				XPRINT("!");
				continue;
.
1644d
1642d
1637,1640d
1633,1635c
	e->isolock = 1;
	do {
		td = e->etd;
		if (td == nil || e->off == 0){
			if (td == nil){
				XPRINT("0");
				ub = &ubus;
				if (w)
					frnum = (IN(Frnum) + 8) & 0x3ff;
				else
					frnum = (IN(Frnum) - 8) & 0x3ff;
				td = e->etd = e->td0 +frnum;
				e->off = 0;
			}
			/* New td, make sure it's ready */
.
1629,1630c
		e->isolock = 0;
.
1625,1627c
	p = a;
.
1623a
	TD *td;
.
1621,1622d
1619c
	int i, frnum;
.
1617c
isoio(Endpt *e, void *a, long n, int w)
.
1613c
	return e->etd == nil || (e->etd != e->xtd && (e->etd->status & Active) == 0);
.
1112a
	wakeup(&e->wr);
.
1111c
		td->status = ErrLimit1 | Active | IsoSelect | IOC;
.
1107a
			XPRINT("-");
.
1102a
			XPRINT("*");
.
1096d
1093,1094c
	if (e->isolock)
.
1088c
			XPRINT("@");
.
851a
		td->flags |= IsoClean;
.
850c
		td->status = ErrLimit1 | Active | IsoSelect | IOC;
.
847d
836c
		ix = (frnum+i) & 0x3ff;
.
## diffname pc/devusb.c 2001/1009
## diff -e /n/emeliedump/2001/1008/sys/src/9/pc/devusb.c /n/emeliedump/2001/1009/sys/src/9/pc/devusb.c
1805,1806c
			if((e = d->ep[i]) != nil){	/* TO DO: freeze e */
				l += snprint(s+l, READSTR-l, "%2d %#6.6lux %10lud bytes %10lud blocks", i, e->csp, e->nbytes, e->nblocks);
				if (e->iso){
					l += snprint(s+l, READSTR-l, "iso ");
				}
				l += snprint(s+l, READSTR-l, "\n");
			}
.
## diffname pc/devusb.c 2001/1010
## diff -e /n/emeliedump/2001/1009/sys/src/9/pc/devusb.c /n/emeliedump/2001/1010/sys/src/9/pc/devusb.c
2087c
		if (e->iso)
			n = isoio(e, a, n, offset, 1);
		else
			n = writeusb(e, a, n, TokOUT);
.
1893c
usbwrite(Chan *c, void *a, long n, vlong offset)
.
1859,1886c
		if(e->err)
			error(e->err);
		if((i = n) >= e->maxpkt)
			i = e->maxpkt;
		b = allocb(i);
		if(waserror()){
			freeb(b);
			nexterror();
		}
		XPRINT("out [%d]", i);
		for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]);
		XPRINT("\n");
		memmove(b->wp, p, i);
		b->wp += i;
		p += i;
		n -= i;
		poperror();
		qh = qxmit(e, b, tok);
		tok = TokOUT;
		e->data01 ^= 1;
		if(e->ntd >= e->nbuf) {
			XPRINT("writeusb: sleep %lux\n", &e->wr);
			sleep(&e->wr, qisempty, qh);
			XPRINT("writeusb: awake\n");
		}
	} while(n > 0);
.
1849,1857c
	p = a;
	do {
		int j;
.
1823c
		if (e->iso)
			n=isoio(e, a, n, offset, 0);
		else
			n=readusb(e, a, n);
.
1742,1746c
			nexterror();
		}
		l = BLEN(b);
		if((i = l) > n)
			i = n;
		if(i > 0){
			memmove(p, b->rp, i);
			p += i;
		}
		poperror();
		freeb(b);
		n -= i;
		if (l != e->maxpkt)
			break;
	} while (n > 0);
.
1704,1740c
	p = a;
	do {
		if(e->eof) {
			XPRINT("e->eof\n");
			break;
		}
		if(e->err)
			error(e->err);
		qrcv(e);
		if(!e->iso)
			e->data01 ^= 1;
		sleep(&e->rr, eptinput, e);
		if(e->err)
			error(e->err);
		b = qget(e->rq);	/* TO DO */
		if(b == nil) {
			XPRINT("b == nil\n");
			break;
		}
		if(waserror()){
.
1685a
	qunlock(&e->rlock);
.
1664c
		bp = e->bp0 + (td - e->td0) * e->maxpkt / e->pollms;
		q = bp + e->off;
.
1660,1661d
1629a
	p = a;
.
1627a
		qunlock(&e->rlock);
		eptcancel(e);
.
1625c
	qlock(&e->rlock);
.
1621c
	uchar *p, *q, *bp;
.
1618c
isoio(Endpt *e, void *a, long n, vlong offset, int w)
.
1528,1529c
			if(schedendpt(e) < 0){
				if (e->isopen)
					error("can't schedule USB endpoint, isopen");
				else
					error("can't schedule USB endpoint");
			}
.
1334c
	ub->recvq->head = PADDR(ub->bwsop) | IsQH;
	ub->bwsop->head = Terminate;	/* loop back */
//	ub->bwsop->head = PADDR(ub->bwsop) | IsQH;	/* loop back */
	ub->bwsop->entries = PADDR(t);

.
1330,1332c

	ub->ctlq->head = PADDR(ub->bulkq) | IsQH;
.
1289c
if(0){
.
1283a
	port = cfg->mem[4].bar & ~0x0F;
.
1279,1281c
	if(cfg == nil) {
		DPRINT("No USB device found\n");
.
1277a
		if((cfg->mem[4].bar & ~0x0F) != 0)
			break;
.
1270,1276c
	cfg = nil;
	while(cfg = pcimatch(cfg, 0, 0)){
		/*
		 * Look for devices with the correct class and
		 * sub-class code and known device and vendor ID.
		 */
		if(cfg->ccrb != 0x0C || cfg->ccru != 0x03)
			continue;
		switch(cfg->vid | cfg->did<<16){
		default:
			continue;
		case 0x8086 | 0x7112<<16:	/* 82371[AE]B (PIIX4[E]) */
		case 0x8086 | 0x719A<<16:	/* 82443MX */
		case 0x0586 | 0x1106<<16:	/* VIA 82C586 */
			break;
.
1156a
	XPRINT("cleanq(ub->ctlq, 0, 0)\n");
	cleanq(ub->ctlq, 0, 0);
	XPRINT("cleanq(ub->bulkq, 0, Vf)\n");
	cleanq(ub->bulkq, 0, Vf);
	XPRINT("clean recvq\n");
	for (q = ub->recvq->next; q; q = q->hlink) {
		XPRINT("cleanq(q, 0, Vf)\n");
		cleanq(q, 0, Vf);
	}
.
1136,1144d
1130c
//	iprint("usbint: #%x f%d\n", s, frnum);
.
1128a
sapecount2++;
.
1125a
sapestatus = s;
sapeintenb = IN(Usbintr);
sapeframe = IN(Frnum);
sapeport1 = IN(Portsc0);
sapeport2 = IN(Portsc1);
sapecmd = IN(Cmd);

.
1123a
sapecount1++;
.
1115a
static int sapecount1;
static int sapecount2;
static int sapecmd;
static int sapestatus;
static int sapeintenb;
static int sapeframe;
static int sapeport1;
static int sapeport2;

.
1108c
			memset(bp, 0, e->maxpkt);
.
1102c
			memset(bp+e->off, 0, e->maxpkt-e->off);
.
1096c
		i = ((frnum + n)&0x3ff);
		td = e->td0 + i;
		bp = e->bp0 + e->maxpkt*i/e->pollms;
.
1091a
	e->time = todget(nil);
.
1077a
		e->nbytes += (td->status + 1) & 0x3ff;
		if ((td->flags & IsoClean) == 0)
			e->nblocks++;
.
1075c
	do {
.
1069c
	int id, n, i;
	uchar *bp;
.
827,828c
		bp = e->bp0 + e->maxpkt*i/e->pollms;
		td->buffer = PADDR(bp);
.
824a
	e->nbytes = 0;
.
819c
	e->bp0 = (uchar *)(((ulong)e->bpalloc + 0xf) & ~0xf);
.
246a
	vlong	time;
.
230c
	QH	*	epq;		/* queue of TDs for this endpoint */
.
226,228c
	uchar*	bp0;		/* first block in array */
	TD	*	td0;		/* first td in array */
	TD	*	etd;		/* pointer into circular list of TDs for isochronous ept */
	TD	*	xtd;		/* next td to be cleaned */
.
216,217c
	uchar*	tdalloc;
	uchar*	bpalloc;
.
208,213c
	uchar	isopen;	/* ep operations forbidden on open endpoints */
	uchar	mode;	/* OREAD, OWRITE, ORDWR */
	uchar	nbuf;	/* number of buffers allowed */
	uchar	iso;
	uchar	debug;
	uchar	active;	/* listed for examination by interrupts */
.
206c
	uchar	eof;
.
165c
	uchar	port;		/* port number on connecting hub */
.
74c
	union{
		Block*	bp;		/* non-iso */
		ulong	offset;	/* iso */
	};
.
49,50d
## diffname pc/devusb.c 2001/1011
## diff -e /n/emeliedump/2001/1010/sys/src/9/pc/devusb.c /n/emeliedump/2001/1011/sys/src/9/pc/devusb.c
2125c
			n = isoio(e, a, n, (ulong)offset, 1);
.
1866c
			n=isoio(e, a, n, (ulong)offset, 0);
.
1849,1850c
				if (e->iso && e->toffset){
					l += snprint(s+l, READSTR-l, " %10lud offset %19lld time", e->toffset, e->time);
.
1735c
	return n;
.
1733a
	if (isolock)
		iunlock(&activends);
.
1732c
	n = p-(uchar*)a;
	e->foffset += n;
.
1709a
		isolock = 0;
		iunlock(&activends);
.
1698c
			ilock(&activends);
			isolock = 1;
.
1694c
			isolock = 0;
			iunlock(&activends);
.
1685d
1680a
		if (isolock == 0){
			ilock(&activends);
			isolock = 1;
		}
.
1679c
	ub = &ubus;
	if (offset != 0 && offset != e->foffset){
		/* Seek to a specific position */
		frnum = (IN(Frnum) + 8) & 0x3ff;
		td = e->td0 +frnum;
		if (offset < td->offset)
			error("ancient history");
		while (offset > e->toffset){
			tsleep(&e->wr, return0, 0, 500);
		}
		while (offset >= td->offset + ((w?(td->dev >> 21):td->status) + 1) & 0x7ff){
			td = td->next;
			if (td == e->xtd)
				iprint("trouble\n");
		}
		ilock(&activends);
		isolock = 1;
		e->off = td->offset - offset;
		if (e->off >= e->maxpkt){
			iprint("I can't program: %d\n", e->off);
			e->off = 0;
		}
		e->etd = td;
		e->foffset = offset;
	}
.
1673c
		if (isolock){
			isolock = 0;
			iunlock(&activends);
		}
.
1671a
	isolock = 0;
.
1666a
	volatile int isolock;
.
1664c
isoio(Endpt *e, void *a, long n, ulong offset, int w)
.
1157d
1147,1152d
1144d
1127,1135d
1102,1103d
1092a
			td->offset = e->poffset;
			e->poffset += n;
.
1088a
			e->toffset = td->offset;
.
1087a
			e->toffset = td->offset;
.
1086a
			e->poffset += (td->status + 1) & 0x3ff;
			td->offset = e->poffset;
.
828a
	e->foffset = 0;
	e->toffset = 0;
	e->poffset = 0;
.
249d
230a
	/* Real-time iso stuff */
	ulong	foffset;	/* file offset (to detect seeks) */
	ulong	poffset;	/* offset of next packet to be queued */
	ulong	toffset;	/* offset associated with time */
	vlong	time;		/* timeassociated with offset */
.
226d
## diffname pc/devusb.c 2001/1012
## diff -e /n/emeliedump/2001/1011/sys/src/9/pc/devusb.c /n/emeliedump/2001/1012/sys/src/9/pc/devusb.c
1880,1884c
				l += epstatus(s+l, READSTR-l, e, i);
.
1825a
int
epstatus(char *s, int n, Endpt *e, int i)
{
	int l;

	l = 0;
	l += snprint(s+l, n-l, "%2d %#6.6lux %10lud bytes %10lud blocks",
		i, e->csp, e->nbytes, e->nblocks);
	if (e->iso && e->toffset){
		l += snprint(s+l, n-l, " %6d buffered %10lud offset %19lld time",
			e->buffered, e->toffset, e->time);
	}
	l += snprint(s+l, n-l, "\n");
	return l;
}

.
1747a
			e->buffered -= i;
			if (e->buffered < 0){
				iprint("e->buffered %d?\n", e->buffered);
				e->buffered = 0;
			}
		}
.
1746c
			e->buffered += i;
		}else{
.
1744c
		if (w){
.
1718,1729d
1716a
			}else{
				/* New td, make sure it's ready */
				isolock = 0;
				iunlock(&activends);
				while (isoready(e) == 0){
					sleep(&e->wr, isoready, e);
				}
				ilock(&activends);
				isolock = 1;
				if (e->etd == nil){
					XPRINT("!");
					continue;
				}
.
1711,1715c
				if (w){
					frnum = (IN(Frnum) + 1) & 0x3ff;
					td = e->td0 + frnum;
					while(td->status & Active)
						td = td->next;
				}else{
					frnum = (IN(Frnum) - 4) & 0x3ff;
					td = e->td0 + frnum;
					while(td->next != e->xtd)
						td = td->next;
				}
				e->etd = td;
.
1126,1130c
			}
		} else {
			/* Unread bytes are now lost */
			e->buffered -= (td->status + 1) & 0x3ff;
.
1121,1124c
		if (e->mode == OWRITE){
			if (td == e->etd) {
				XPRINT("*");
				memset(bp+e->off, 0, e->maxpkt-e->off);
				if (e->off == 0)
					td->flags |= IsoClean;
				else
					e->buffered += (((td->dev>>21) +1) & 0x3ff) - e->off;
				e->etd = nil;
			}else if ((td->flags & IsoClean) == 0){
				XPRINT("-");
				memset(bp, 0, e->maxpkt);
.
1114c
	for (n = 2; n < 4; n++){
.
1097a
			if ((td->flags & IsoClean) == 0){
				e->buffered -= n;
				if (e->buffered < 0){
					iprint("e->buffered %d?\n", e->buffered);
					e->buffered = 0;
				}
			}
.
1092a
			e->buffered += n;
.
1089c
		n = (td->status + 1) & 0x3ff;
		e->nbytes += n;
.
834a
	e->buffered = 0;
.
234a
	int		buffered;	/* bytes captured but unread, or written but unsent */
.
## diffname pc/devusb.c 2001/1013
## diff -e /n/emeliedump/2001/1012/sys/src/9/pc/devusb.c /n/emeliedump/2001/1013/sys/src/9/pc/devusb.c
1516c
	len = e->buffered;
	devdir(c, q, up->genbuf, len, eve, e->mode==OREAD? 0444: e->mode==OWRITE? 0222: 0666, dp);
.
1491a
	len = 0;
.
1442a
	vlong len;
.
## diffname pc/devusb.c 2001/1015
## diff -e /n/emeliedump/2001/1013/sys/src/9/pc/devusb.c /n/emeliedump/2001/1015/sys/src/9/pc/devusb.c
1655d
1645,1649d
1641,1643c
		if(e = d->ep[s]) {
			if (e->isopen == 1){
				e->isopen = 0;
				eptdeactivate(e);
				unschedendpt(e);
			}
.
1633,1636d
## diffname pc/devusb.c 2001/1016
## diff -e /n/emeliedump/2001/1015/sys/src/9/pc/devusb.c /n/emeliedump/2001/1016/sys/src/9/pc/devusb.c
2106c
			if (e->active)
.
1867d
1864,1865c
		l += snprint(s+l, n-l, "bufsize %6d buffered %6d offset  %10lud time %19lld\n",
			e->maxpkt, e->buffered, e->toffset, e->time);
.
1861c
	l += snprint(s+l, n-l, "%2d %#6.6lux %10lud bytes %10lud blocks\n",
.
1648a
	}
.
1647c
	if(c->flag & COPEN){
		XPRINT("usbclose: freedev 0x%p\n", d);
.
1634,1644d
1630c
	if(c->qid.type == QTDIR || QID(c->qid) < Q3rd)
.
1628d
1590d
1585,1586c
				if (e->active)
					error("can't schedule USB endpoint, active");
.
1583a
			XPRINT("usbopen: dev 0x%p, ept 0x%p\n", d, e);
.
1577a
		XPRINT("usbopen, default 0x%p, %d\n", d, s);
.
1572a
		XPRINT("usbopen, Qctl 0x%p\n", d);
.
1557a
	}
.
1556c
	if(QID(c->qid) < Q3rd){
		XPRINT("usbopen, devopen < Q3rd\n");
.
1546a
		XPRINT("usbopen, new dev 0x%p\n", d);
.
1448c
	if(QID(c->qid) == Qtopdir){
.
811c
	if (e->active){
.
663a
		XPRINT("deactivate 0x%p\n", e);
.
647a
		XPRINT("activate 0x%p\n", e);
.
209c
/*	uchar	isopen;	 ep operations forbidden on open endpoints */
.
## diffname pc/devusb.c 2001/1017
## diff -e /n/emeliedump/2001/1016/sys/src/9/pc/devusb.c /n/emeliedump/2001/1017/sys/src/9/pc/devusb.c
1860,1862c
	if (e->iso){
		l += snprint(s+l, n-l, "bufsize %6d buffered %6d",
			e->maxpkt, e->buffered);
		if(e->toffset)
			l += snprint(s+l, n-l, " offset  %10lud time %19lld\n",
				e->toffset, e->time);
		if (n-l > 0)
			s[l++] = '\n';
		s[l] = '\0';
.
1689a
		iprint("offset %lud, foffset %lud\n", offset, e->foffset);
.
1597a
			e->foffset = 0;
			e->toffset = 0;
			e->poffset = 0;
			e->buffered = 0;
.
835,838d
## diffname pc/devusb.c 2001/1019
## diff -e /n/emeliedump/2001/1017/sys/src/9/pc/devusb.c /n/emeliedump/2001/1019/sys/src/9/pc/devusb.c
1783a
		if(w)
			td->offset = offset + (p-(uchar*)a) - (((td->dev >> 21) + 1) & 0x7ff);
.
1391,1392c
	ub->frames = (ulong*)(((ulong)xalloc(2*FRAMESIZE) + FRAMEMASK) & ~FRAMEMASK);
	ub->frameld = xallocz(FRAMESIZE, 1);
.
1358c
	ub->qhpool = (QH*)(((ulong)xalloc(32*sizeof(QH) + 0x10) + 0xf) & ~0xf);
.
1353c
	ub->tdpool = (TD*)(((ulong)xalloc(128*sizeof(TD) + 0x10) + 0xf) & ~0xf);
.
217,218c
	void*	tdalloc;
	void*	bpalloc;
.
119,120c
	NFRAME = 	1024,
	FRAMESIZE=	NFRAME*sizeof(ulong),	/* fixed by hardware; aligned to same */
	FRAMEMASK=	FRAMESIZE-1,	/* fixed by hardware; aligned to same */
.
## diffname pc/devusb.c 2001/1106
## diff -e /n/emeliedump/2001/1019/sys/src/9/pc/devusb.c /n/emeliedump/2001/1106/sys/src/9/pc/devusb.c
1626,1632d
## diffname pc/devusb.c 2001/1117
## diff -e /n/emeliedump/2001/1106/sys/src/9/pc/devusb.c /n/emeliedump/2001/1117/sys/src/9/pc/devusb.c
2041c
		nf = tokenize(cmd, fields, nelem(fields));
.
2017c
		nf = tokenize(cmd, fields, nelem(fields));
.
## diffname pc/devusb.c 2001/1120
## diff -e /n/emeliedump/2001/1117/sys/src/9/pc/devusb.c /n/emeliedump/2001/1120/sys/src/9/pc/devusb.c
2167a
	
		poperror();
		free(cb);
.
2164,2166d
2149c
				i = strtoul(cb->f[5], nil, 0);
.
2140,2142c
				e->mode = strcmp(cb->f[3],"r") == 0? OREAD :
					  	strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR;
				i = strtoul(cb->f[4], nil, 0);
.
2133c
				i = strtoul(cb->f[2], nil, 0);
.
2128c
				i = strtoul(cb->f[5], nil, 0);
.
2122,2124c
				e->mode = strcmp(cb->f[3],"r") == 0? OREAD :
					  	strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR;
				i = strtoul(cb->f[4], nil, 0);
.
2107c
			if(strcmp(cb->f[2], "bulk") == 0){
.
2092c
			i = strtoul(cb->f[1], nil, 0);
.
2088c
			break;
		case CMep:
.
2085c
				error(Ebadusbmsg);
.
2082,2083c
			break;
		case CMunstall:
			i = strtoul(cb->f[1], nil, 0);
.
2080c
				e->debug = strtoul(cb->f[2], nil, 0);
.
2074c
				error(Ebadusbmsg);
.
2071,2072c
			break;
		case CMdebug:
			i = strtoul(cb->f[1], nil, 0);
.
2068c
			e->maxpkt = strtoul(cb->f[2], nil, 0);
.
2066c
				error(Ebadusbmsg);
.
2062,2064c
			e->data01 = strtoul(cb->f[2], nil, 0) != 0;
			break;
		case CMmaxpkt:
			i = strtoul(cb->f[1], nil, 0);
.
2060c
				error(Ebadusbmsg);
.
2056,2058c
			d->ep[i]->csp = strtoul(cb->f[3], nil, 0);
			break;
		case CMdata:
			i = strtoul(cb->f[1], nil, 0);
.
2050,2053c
				cmderror(cb, Ebadusbmsg);
			if (i == 0)
				d->csp = strtoul(cb->f[3], nil, 0);
.
2037,2046c
		cb = parsecmd(a, n);
		if(waserror()){
			free(cb);
			nexterror();
		}

		ct = lookupcmd(cb, usbctlmsg, nelem(usbctlmsg));
		switch(ct->index){
		case CMspeed:
			d->ls = strtoul(cb->f[1], nil, 0) == 0;
			break;
		case CMclass:
			i = strtoul(cb->f[2], nil, 0);
			d->npt = strtoul(cb->f[1], nil, 0);
.
2029,2030c
			break;
		case BCMenable:
			portenable(id, 1);
			break;
		case BCMreset:
			portreset(id);
			break;
		}
	
		poperror();
		free(cb);
.
2022,2027c
			cmderror(cb, "usb port number not 1 or 2 in");
		switch(ct->index){
		case BCMdisable:
.
2013,2019c
		cb = parsecmd(a, n);
		if(waserror()){
			free(cb);
			nexterror();
		}

		ct = lookupcmd(cb, usbbusctlmsg, nelem(usbbusctlmsg));
.
2006c
	Cmdbuf *cb;
	Cmdtab *ct;
	int id, nw, t, i;
.
289a
enum
{
	BCMdisable,
	BCMenable,
	BCMreset,
};

enum
{
	CMclass,
	CMdata,
	CMdebug,
	CMep,
	CMmaxpkt,
	CMspeed,
	CMunstall,
};

static Cmdtab usbbusctlmsg[] =
{
	BCMdisable,	"disable",	2,
	BCMenable,	"enable",	2,
	BCMreset,	"reset",	2,
};

static Cmdtab usbctlmsg[] =
{
	CMclass,	"class",	4,
	CMdata,		"data",		3,
	CMdebug,	"debug",	3,
	CMep,		"ep",		6,
	CMmaxpkt,	"maxpkt",	3,
	CMspeed,	"speed",	2,
	CMunstall,	"unstall",	2,
};

.
281a
static	char	Ebadusbmsg[] = "invalid parameters to USB ctl message";
.
## diffname pc/devusb.c 2001/1122
## diff -e /n/emeliedump/2001/1120/sys/src/9/pc/devusb.c /n/emeliedump/2001/1122/sys/src/9/pc/devusb.c
1354,1361c
// 		switch(cfg->vid | cfg->did<<16){
// 		default:
// 			continue;
// 		case 0x8086 | 0x7112<<16:	/* 82371[AE]B (PIIX4[E]) */
// 		case 0x8086 | 0x719A<<16:	/* 82443MX */
// 		case 0x0586 | 0x1106<<16:	/* VIA 82C586 */
// 			break;
// 		}
.
## diffname pc/devusb.c 2001/1124
## diff -e /n/emeliedump/2001/1122/sys/src/9/pc/devusb.c /n/emeliedump/2001/1124/sys/src/9/pc/devusb.c
2059c
		id = strtol(cb->f[1], nil, 0);
.
2046c
	char cmd[50];
.
2027a
XPRINT("qh %s: q=%p first=%p last=%p entries=%.8lux\n",
 "writeusb sleep", qh, qh->first, qh->last, qh->entries);
.
1388c
	intrenable(cfg->intl, usbinterrupt, ub, cfg->tbdf, "usb");
.
1353a
		if(cfg->did == 0x2482 || cfg->did == 0x2487)
			continue;
.
1206d
1204a
	usbints++;
.
1201a
	frameptr = inl(ub->io+Flbaseadd);
	framenumber = IN(Frnum) & 0x3ff;
.
1192c
usbinterrupt(Ureg*, void *a)
.
859d
856,857c
	if(e->sched < 0)
.
785c
	queuetd(ub, qh, t, vf, "qrcv");
.
760c
	queuetd(ub, qh, t, vf, "qxmit");
.
648a
	if(q->first && q->entries != PADDR(q->first)){
		usbbogus++;
		q->entries = PADDR(q->first);
	}
.
518a
	XPRINT("	t=%p q=%p first=%p last=%p entries=%.8lux\n",
		t, q, q->first, q->last, q->entries);
.
510a
	XPRINT("queuetd %s: t=%p lt=%p q=%p first=%p last=%p entries=%.8lux\n",
		why, t, lt, q, q->first, q->last, q->entries);
.
503c
queuetd(Ctlr *ub, QH *q, TD *t, int vf, char *why)
.
279a
static	long usbints;
static	long framenumber;
static	long frameptr;
static	long	usbbogus;

.
## diffname pc/devusb.c 2001/1127
## diff -e /n/emeliedump/2001/1124/sys/src/9/pc/devusb.c /n/emeliedump/2001/1127/sys/src/9/pc/devusb.c
328c
	CMspeed,		"speed",	2,
.
323,325c
	CMclass,		"class",	4,
	CMdata,		"data",	3,
	CMdebug,		"debug",	3,
.
143c
	BitstuffErr =	1<<17,
.
129,141c
	SPD =		1<<29,
	ErrLimit0 =	0<<27,
	ErrLimit1 =	1<<27,
	ErrLimit2 =	2<<27,
	ErrLimit3 =	3<<27,
	LowSpeed =	1<<26,
	IsoSelect =	1<<25,
	IOC =		1<<24,
	Active =		1<<23,
	Stalled =		1<<22,
	DataBufferErr =	1<<21,
	Babbling =	1<<20,
	NAKed =		1<<19,
.
124,126c
	Vf =			1<<2,	/* TD only */
	IsQH =		1<<1,
	Terminate =	1<<0,
.
121c
	FRAMEMASK=	FRAMESIZE-1,
.
110,117c
	Suspend =		1<<12,
	PortReset =		1<<9,
	SlowDevice =		1<<8,
	ResumeDetect =	1<<6,
	PortChange =		1<<3,	/* write 1 to clear */
	PortEnable =		1<<2,
	StatusChange =	1<<1,	/* write 1 to clear */
	DevicePresent =	1<<0,
.
## diffname pc/devusb.c 2001/1130
## diff -e /n/emeliedump/2001/1127/sys/src/9/pc/devusb.c /n/emeliedump/2001/1130/sys/src/9/pc/devusb.c
1691a
	poperror();
.
1684a
	if(waserror()){
		qunlock(&usbstate);
		return;
	}
.
1152c
					print("e->buffered %d?\n", e->buffered);
.
## diffname pc/devusb.c 2001/1201
## diff -e /n/emeliedump/2001/1130/sys/src/9/pc/devusb.c /n/emeliedump/2001/1201/sys/src/9/pc/devusb.c
2173a
			}
.
2172c
			if((e = d->ep[i]) == nil){
				XPRINT("calling devendpt in usbwrite (CMep)\n");
.
2125a
			}
.
2124c
			if(d->ep[i] == nil){
				XPRINT("calling devendpt in usbwrite (CMclass)\n");
.
1822c
				XPRINT("e->buffered %d?\n", e->buffered);
.
1694c
		freedev(d, ept);
.
1692a
		ept = (QID(c->qid) != Qctl) ? QID(c->qid) - Qep0 : -1;
.
1691a
	XPRINT("usbclose: dev 0x%p\n", d);
.
1680a
	int ept;
.
1331a
			XPRINT("calling devendpt in usbnewdevice\n");
.
1028c
	} else {
		if(ept >= 0 && ept < nelem(d->ep)){
			e = d->ep[ept];
			XPRINT("freedev, freept 0x%p\n", e);
			if(e != nil){
				eptdeactivate(e);
				unschedendpt(e);
			}
		}
	}	
.
1022a
		XPRINT("freedev 0x%p, 0\n", d);
.
1020a
	Endpt *e;
.
1018c
freedev(Udev *d, int ept)
.
1005a
	if (e) XPRINT("decref(0x%p) in freept, new value %ld\n", e, e->ref);
	return 0;
.
1004a
		return 1;
.
994c
static int
.
982a
		XPRINT("incref(0x%p) in devendpt, new value %ld\n", *p, (*p)->ref);
.
960a
		XPRINT("incref(0x%p) in devendpt, new value %ld\n", e, e->ref);
.
## diffname pc/devusb.c 2001/1204
## diff -e /n/emeliedump/2001/1201/sys/src/9/pc/devusb.c /n/emeliedump/2001/1204/sys/src/9/pc/devusb.c
1705c
		nexterror();
.
1703c
	if(waserror()){	/* usbdevice can error */
.
## diffname pc/devusb.c 2002/0109
## diff -e /n/emeliedump/2001/1204/sys/src/9/pc/devusb.c /n/emeliedump/2002/0109/sys/src/9/pc/devusb.c
2309a
	devshutdown,
.
## diffname pc/devusb.c 2002/0212
## diff -e /n/emeliedump/2002/0109/sys/src/9/pc/devusb.c /n/emeliedump/2002/0212/sys/src/9/pc/devusb.c
2252d
2187c
			 * ep n period mode samplesize Hz
.
2165a
		case CMadjust:
			i = strtoul(cb->f[1], nil, 0);
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
				error(Ebadusbmsg);
			e = d->ep[i];
			if (e->iso == 0)
				error(Eperm);
			i = strtoul(cb->f[2], nil, 0);
			/* speed may not result in change of maxpkt */
			if (i < (e->maxpkt-1)/e->samplesz * 1000/e->pollms
			  || i > e->maxpkt/e->samplesz * 1000/e->pollms){
				snprint(cmd, sizeof(cmd), "%d < %d < %d?",
					(e->maxpkt-1)/e->samplesz * 1000/e->pollms,
					i,
					e->maxpkt/e->samplesz * 1000/e->pollms);
				error(cmd);
			}
			e->hz = i;
			break;
.
1865a
	}
	poperror();
.
1863,1864c
	if (isolock){
		isolock = 0;
.
1828,1829d
327a
	CMadjust,		"adjust",	3,
.
309a
	CMadjust,
.
## diffname pc/devusb.c 2002/0216
## diff -e /n/emeliedump/2002/0212/sys/src/9/pc/devusb.c /n/emeliedump/2002/0216/sys/src/9/pc/devusb.c
1867a
	if (isolock)
		iunlock(&activends);
.
1863,1866d
1840,1844d
1837d
1833,1834d
1829a
		if((i = n) >= e->psize)
			i = e->psize;
		if (w)
			e->buffered += i;
		else{
			e->buffered -= i;
			if (e->buffered < 0)
				e->buffered = 0;
		}
		isolock = 0;
		iunlock(&activends);
.
## diffname pc/devusb.c 2002/0219
## diff -e /n/emeliedump/2002/0216/sys/src/9/pc/devusb.c /n/emeliedump/2002/0219/sys/src/9/pc/devusb.c
2309,2319c
		t -= Qep0;
		if(t < 0 || t >= nelem(d->ep))
			error(Egreg);
		e = d->ep[t];
		if(e == nil || e->mode == OREAD)
			error(Egreg);
		n = uh->write(uh, e, a, n, offset, TokOUT);
.
2301c
			nw = uh->read(uh, e, cmd, 0LL, 8);
.
2298,2299c
		n = uh->write(uh, e, a, n, 0LL, TokSETUP);
		if(nw == 0) {	/* host to device: use IN[DATA1] to ack */
.
2292,2293c
		e = d->ep[0];
		if(e == nil || e->iso)
			error(Egreg);
.
2290c
	case Qep0:	/* SETUP endpoint 0 */
.
2283c
			qunlock(uh);
.
2281a
			e->mode = strcmp(cb->f[3],"r") == 0? OREAD :
				  	strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR;
			uh->epmode(uh, e);
.
2261,2262d
2253c
				/* ep n period mode samplesize Hz */
.
2233,2244c
				e->iso = 0;
.
2229,2231d
2226c
			if(e->active)
.
2223c
				qunlock(uh);
.
2220c
			qlock(uh);
.
2122,2125d
2114,2115c
		case PMreset:
			uh->portreset(uh, id);
.
2111,2112c
		case PMenable:
			uh->portenable(uh, id, 1);
.
2108,2109c
		case PMdisable:
			uh->portenable(uh, id, 0);
.
2105,2106d
2103c
		ct = lookupcmd(cb, usbportmsg, nelem(usbportmsg));
.
2095,2096c
	d = usbdevice(c);
	uh = d->uh;
	t = TYPE(c->qid);
	switch(t){
	case Qport:
.
2089a
	Cmdbuf *cb;
	Usbhost *uh;
.
2088d
2026,2082d
2022a
	n = readstr(offset, a, n, s);
	poperror();
	free(s);
.
2001,2021d
1995,1999c
		p = seprint(s, se, "%s %#6.6lux\n", devstates[d->state], d->csp);
		for(i=0; i<nelem(d->ep); i++) {
			e = d->ep[i];
			if(e == nil)
				continue;
			/* TO DO: freeze e */
			p = epstatus(p, se, e, i);
.
1990,1993d
1973,1987c
		seprint(s, se, "%11d %11d\n", d->x, d->id);
.
1968,1970c
		uh->portinfo(uh, s, se);
		break;
.
1963,1966d
1888,1961d
1884,1885c
		free(s);
.
1873,1882c
	s = smalloc(READSTR);
	se = s+READSTR;
.
1830,1871c
		return uh->read(uh, e, a, n, offset);
	}
.
1823,1828c
			return n;
.
1749,1821c
	if(t >= Qep0) {
		t -= Qep0;
		if(t >= nelem(d->ep))
			error(Eio);
		e = d->ep[t];
		if(e == nil || e->mode == OWRITE)
			error(Egreg);
		if(t == 0) {
			if(e->iso)
				error(Egreg);
			e->data01 = 1;
			n = uh->read(uh, e, a, n, 0LL);
			if(e->setin){
				e->setin = 0;
				e->data01 = 1;
				uh->write(uh, e, "", 0, 0LL, TokOUT);
.
1740,1747c
	d = usbdevice(c);
	uh = d->uh;
	t = TYPE(c->qid);
.
1736,1738c
	if(c->qid.type == QTDIR)
		return devdirread(c, a, n, nil, 0, usbgen);
.
1734a
	Usbhost *uh;
	char *s, *se, *p;
.
1733a
	int t, i;
	Udev *d;
.
1731,1732c
long
usbread(Chan *c, void *a, long n, vlong offset)
.
1727,1728c
	p = seprint(s, se, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks);
	if(e->iso){
		p = seprint(p, se, "bufsize %6d buffered %6d", e->maxpkt, e->buffered);
		if(e->toffset)
			p = seprint(p, se, " offset  %10lud time %19lld\n", e->toffset, e->time);
		p = seprint(p, se, "\n");
	}
	return p;
.
1725c
	char *p;
.
1722,1723c
static char *
epstatus(char *s, char *se, Endpt *e, int i)
.
1719c
	qunlock(uh);
.
1714c
		ept = (type != Qctl) ? type - Qep0 : -1;
.
1709,1710c
	if(type == Qctl)
.
1704,1706c
	d = usbdevice(c);
	uh = d->uh;
	qlock(uh);
	if(waserror()){
		qunlock(uh);
.
1702c
	type = TYPE(c->qid);
	if(c->qid.type == QTDIR || type < Q3rd)
.
1700c
	int ept, type;
	Usbhost *uh;
.
1684,1696d
1676c
	qunlock(uh);
.
1670d
1660,1665c
			uh->epopen(uh, e);
.
1654d
1650,1651c
		s = type - Qep0;
.
1640d
1638c
	switch(type){
.
1634c
		qunlock(uh);
.
1632c
	d = usbdevice(c);
	uh = d->uh;
	qlock(uh);
.
1627c
	if(type < Q3rd){
.
1622,1623c
		type = Qctl;
		mkqid(&c->qid, PATH(type, d->x, CTLR(c->qid)), d->id, QTFILE);
.
1615,1616c
	type = TYPE(c->qid);
	if(type == Qnew){
		d = usbdevice(c);
		d = usbnewdevice(d->uh);
.
1609c
	Endpt *e;
	int f, s, type;
	Usbhost *uh;
.
1605c
Chan*
.
1592a
static void
usbreset(void)
{
	int n, ctlrno;
	Usbhost *uh;
	char name[64], buf[128], *p, *ebuf, *type;

	uh = nil;
	for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){
		if(uh == nil)
			uh = malloc(sizeof(Usbhost));
		memset(uh, 0, sizeof(Usbhost));
		uh->tbdf = BUSUNKNOWN;
		if(isaconfig("usb", ctlrno, uh) == 0)
			continue;
		for(n = 0; usbtypes[n].type; n++){
			type = uh->type;
			if(type == nil || *type == '\0')
				type = "uhci";
			if(cistrcmp(usbtypes[n].type, type))
				continue;
			if(usbtypes[n].reset(uh))
				break;

			/*
			 * IRQ2 doesn't really exist, it's used to gang the interrupt
			 * controllers together. A device set to IRQ2 will appear on
			 * the second interrupt controller as IRQ9.
			 */
			if(uh->irq == 2)
				uh->irq = 9;
			snprint(name, sizeof(name), "usb%d", ctlrno);
			intrenable(uh->irq, uh->interrupt, uh, uh->tbdf, name);

			ebuf = buf + sizeof buf;
			p = seprint(buf, ebuf, "#U/usb%d: %s: port 0x%luX irq %lud", ctlrno, type, uh->port, uh->irq);
			if(uh->mem)
				p = seprint(p, ebuf, " addr 0x%luX", PADDR(uh->mem));
			if(uh->size)
				seprint(p, ebuf, " size 0x%luX", uh->size);
			print("%s\n", buf);

			usbhost[ctlrno] = uh;
			uh = nil;
			break;
		}
	}
	if(uh != nil)
		free(uh);
}

void
usbinit(void)
{
	Udev *d;
	int ctlrno;
	Usbhost *uh;

	for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){
		uh = usbhost[ctlrno];
		if(uh == nil)
			continue;
		if(uh->init != 0)
			uh->init(uh);

		/* reserve device for configuration */
		d = usbnewdevice(uh);
		incref(d);
		d->state = Attached;
	}
}

Chan *
usbattach(char *spec)
{
	return devattach('U', spec);
}

.
1587,1589c
	mkqid(&q, PATH(Qep0+s, slot, bus), c->qid.vers, QTFILE);
	switch(e->mode) {
	case OREAD:
		perm = 0444;
		break;
	case OWRITE:
		perm = 0222;
		break;
	default:
		perm = 0666;
		break;
	}
	devdir(c, q, up->genbuf, e->buffered, eve, perm, dp);
.
1581,1583d
1578,1579c
	d = usbdeviceofslot(uh, slot);
	if(d == nil || s >= nelem(d->ep))
.
1575a
	s -= nelem(usbdir3);
.
1570,1572c
	if(s < nelem(usbdir3)) {
		tab = &usbdir3[s];
		mkqid(&q, PATH(tab->qid.path, slot, bus), c->qid.vers, QTFILE);
.
1563,1567c
	slot = SLOT(c->qid);
	if(s == DEVDOTDOT) {
		mkqid(&q, PATH(Q2nd, 0, bus), c->qid.vers, QTDIR);
		snprint(up->genbuf, sizeof up->genbuf, "usb%d", bus);
		devdir(c, q, up->genbuf, 0, eve, 0555, dp);
.
1553c
			mkqid(&q, PATH(Q3rd, s, bus), d->id, QTDIR);
.
1551c
				return 0;
.
1548,1549c
		if(s >= 0 && s < nelem(uh->dev)) {
			d = uh->dev[s];
.
1544c
			mkqid(&q, PATH(tab->qid.path, 0, bus), d->id, QTFILE);
			devdir(c, q, tab->name, tab->length, eve, tab->perm, dp);
.
1542a
			d = uh->dev[0];
			if(d == nil)
				return -1;
.
1535c
	t = TYPE(c->qid);
.
1533c
	 * Second level contains "new", "port", and a numbered
	 * directory for each enumerated device on the bus.
.
1530a
	bus = CTLR(c->qid);
	if(bus >= nelem(usbhost) || (uh = usbhost[bus]) == nil)
			return -1;
.
1524,1529c
		if(s >= nelem(usbhost) || usbhost[s] == nil)
			return -1;
		mkqid(&q, PATH(Q2nd, 0, s), 0, QTDIR);
		snprint(up->genbuf, sizeof up->genbuf, "usb%d", s);
		devdir(c, q, up->genbuf, 0, eve, 0555, dp);
		return 1;
.
1518c
	if(c->qid.path == Qtopdir){
.
1516c
	 * Top level directory contains the controller names.
.
1513c
	Dirtab *tab;
	Usbhost *uh;
	int t, bus, slot, perm;
.
1511d
1509d
1507d
1476,1503d
1373,1473c
	uh = d->uh;
	if(decref(d) == 0){
		XPRINT("freedev 0x%p, 0\n", d);
		for(i=0; i<nelem(d->ep); i++)
			freept(d->ep[i]);
		if(d->x >= 0)
			uh->dev[d->x] = nil;
		free(d);
	} else {
		if(ept >= 0 && ept < nelem(d->ep)){
			e = d->ep[ept];
			XPRINT("freedev, freept 0x%p\n", e);
			if(e != nil)
				uh->epclose(uh, e);
		}
	}	
.
1368,1371c
	Endpt *e;
	Usbhost *uh;
.
1366d
1364c
freedev(Udev *d, int ept)
.
1359c
	qunlock(uh);
.
1355c
			uh->dev[i] = d;
.
1348c
			d->id = (uh->idgen << 8) | i;
.
1345a
			d->uh = uh;
.
1342,1344c
	for(i=0; i<nelem(uh->dev); i++)
		if(uh->dev[i] == nil){
			uh->idgen++;
.
1339c
		qunlock(uh);
.
1337c
	qlock(uh);
.
1334d
1322,1332d
1028,1320d
1018,1026d
1015,1016c
static Udev*
usbnewdevice(Usbhost *uh)
.
1011,1012d
1009d
1006,1007c
		uh->epfree(uh, e);
.
1003,1004c
		uh = e->dev->uh;
		uh->epclose(uh, e);
.
1000a
	Usbhost *uh;

.
998c
static void
.
987a
		uh->epfree(uh, e);
.
982a
	uh = d->uh;
	uh->epalloc(uh, e);

.
979,981d
969a

.
961c
	e = *p;
	if(e != nil){
.
958d
956d
954a
	Usbhost *uh;
.
437,951d
434a
	d = usbdeviceofslot(uh, SLOT(c->qid));
	if(d == nil || d->id != c->qid.vers || d->state == Disabled)
		error(Ehungup);
	return d;
.
395,433c
	bus = CTLR(c->qid);
	if(bus > nelem(usbhost) || (uh = usbhost[bus]) == nil) {
		error(Egreg);
		return nil;		/* for compiler */
.
391,393c
	int bus;
	Udev *d;
	Usbhost *uh;
.
388,389c
static Udev*
usbdevice(Chan *c)
.
378,385c
	if(s < 0 || s > nelem(uh->dev))
		return nil;
	return uh->dev[s];
.
375,376c
static Udev*
usbdeviceofslot(Usbhost *uh, int s)
.
363,372c
	if(ntype == MaxUsb)
		panic("too many USB host interface types");
	usbtypes[ntype].type = t;
	usbtypes[ntype].reset = r;
	ntype++;
.
361c
	static int ntype;
.
342,359c
void
addusbtype(char* t, int (*r)(Usbhost*))
.
340c
	char*	type;
	int	(*reset)(Usbhost*);
} usbtypes[MaxUsb+1];
.
334,338c
static struct
.
317,319c
	PMdisable,	"disable",	2,
	PMenable,		"enable",	2,
	PMreset,		"reset",	2,
.
315c
static Cmdtab usbportmsg[] =
.
298,300c
	PMdisable,
	PMenable,
	PMreset,
.
280,295d
277,278d
257,275c
static Dirtab usbdir3[]={
	"ctl",		{Qctl},			0,	0666,
	"status",	{Qstatus},			0,	0444,
	"setup",	{Qep0},			0,	0666,
	/* epNdata names are generated on demand */
.
253,254c
static Dirtab usbdir2[] = {
	"new",	{Qnew},			0,	0666,
	"port",	{Qport},			0,	0666,
.
251c
#define	TYPE(q)		(((ulong)(q).path)&TYPEMASK)
#define	SLOT(q)		((((ulong)(q).path)>>SLOTSHIFT)&SLOTMASK)
#define	CTLR(q)		((((ulong)(q).path)>>CTLRSHIFT)&CTLRMASK)
#define	PATH(t, s, c)	((t)|((s)<<SLOTSHIFT)|((c)<<CTLRSHIFT))
.
249c
	TYPEMASK	= (1<<TYPEBITS)-1,
	SLOTMASK	= (1<<SLOTBITS)-1,
	CTLRMASK	= (1<<CTLRBITS)-1,
};
.
246,247c
	SLOTSHIFT	= TYPEBITS,
	CTLRSHIFT	= SLOTSHIFT+SLOTBITS,
.
201,244c
enum {
	TYPEBITS	= 8,
	SLOTBITS	= 8,
	CTLRBITS	= 4,
.
199c
 * Qid path is:
 *	8 bits of file type (qids above)
 *	8 bits of slot number; default address 0 used for per-controller files
 *	4 bits of controller number
.
197a
static	char	Ebadusbmsg[] = "invalid parameters to USB ctl message";

enum
{
	Qtopdir = 0,
	Q2nd,
	Qnew,
	Qport,
	Q3rd,
	Qctl,
	Qstatus,
	Qep0,
	/* other endpoint files */
};

.
29,189d
27c
Usbhost*	usbhost[MaxUsb];
.
22a
#include	"usb.h"

static int debug = 0;

.
1,14d
## diffname pc/devusb.c 2002/0221
## diff -e /n/emeliedump/2002/0219/sys/src/9/pc/devusb.c /n/emeliedump/2002/0221/sys/src/9/pc/devusb.c
835c
				if(i >= 1 && i*e->samplesz <= 12*1000*1000){
.
## diffname pc/devusb.c 2002/0403
## diff -e /n/emeliedump/2002/0221/sys/src/9/pc/devusb.c /n/emeliedump/2002/0403/sys/src/9/pc/devusb.c
434,435d
432a
		if((uh = usbprobe(cardno, ctlrno)) == nil){
			cardno++;
			continue;
		}
		usbhost[ctlrno] = uh;
		ctlrno++;
.
429,431c
	ebuf = buf + sizeof buf;
	p = seprint(buf, ebuf, "#U/usb%d: %s: port 0x%luX irq %lud", ctlrno, usbtypes[cardno].type, uh->port, uh->irq);
	if(uh->mem)
		p = seprint(p, ebuf, " addr 0x%luX", PADDR(uh->mem));
	if(uh->size)
		seprint(p, ebuf, " size 0x%luX", uh->size);
	print("%s\n", buf);

	return uh;
}

static void
usbreset(void)
{
	int cardno, ctlrno;
	Usbhost *uh;

	for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){
		if((uh = usbprobe(-1, ctlrno)) == nil)
			continue;
		usbhost[ctlrno] = uh;
	}

	cardno = ctlrno = 0;
	while(usbtypes[cardno].type != nil && ctlrno < MaxUsb){
		if(usbhost[ctlrno] != nil){
			ctlrno++;
			continue;
.
421,427c
	/*
	 * IRQ2 doesn't really exist, it's used to gang the interrupt
	 * controllers together. A device set to IRQ2 will appear on
	 * the second interrupt controller as IRQ9.
	 */
	if(uh->irq == 2)
		uh->irq = 9;
	snprint(name, sizeof(name), "usb%d", ctlrno);
	intrenable(uh->irq, uh->interrupt, uh, uh->tbdf, name);
.
411,419c
	if(cardno >= MaxUsb || usbtypes[cardno].type == nil){
		free(uh);
		return nil;
	}
	if(usbtypes[cardno].reset(uh) < 0){
		free(uh);
		return nil;
	}
.
408,409c
			break;
		}
	}
.
406c
			if(cistrcmp(usbtypes[cardno].type, type))
.
404c
			if(type==nil || *type==0)
.
394,402c
	uh = malloc(sizeof(Usbhost));
	memset(uh, 0, sizeof(Usbhost));
	uh->tbdf = BUSUNKNOWN;

	if(cardno < 0){
		if(isaconfig("usb", ctlrno, uh) == 0){
			free(uh);
			return nil;
		}
		for(cardno = 0; usbtypes[cardno].type; cardno++){
.
392c
	char buf[128], *ebuf, name[64], *p, *type;
.
390d
387,388c
static Usbhost*
usbprobe(int cardno, int ctlrno)
.
## diffname pc/devusb.c 2002/0502
## diff -e /n/emeliedump/2002/0403/sys/src/9/pc/devusb.c /n/emeliedump/2002/0502/sys/src/9/pc/devusb.c
453a
	if(getconf("*nousbprobe"))
		return;

.
## diffname pc/devusb.c 2002/0512
## diff -e /n/emeliedump/2002/0502/sys/src/9/pc/devusb.c /n/emeliedump/2002/0512/sys/src/9/pc/devusb.c
896c
		if(n < 8)
.
## diffname pc/devusb.c 2003/0301
## diff -e /n/emeliedump/2002/0512/sys/src/9/pc/devusb.c /n/emeliedump/2003/0301/sys/src/9/pc/devusb.c
432c
	p = seprint(buf, ebuf, "#U/usb%d: %s: port 0x%luX irq %d", ctlrno, usbtypes[cardno].type, uh->port, uh->irq);
.

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].