~kris/9p

9hist

5079ac3025f884563b293d0e832ad2fa5d579bf1 — David du Colombier 24 years ago 634fcd6
Plan 9 from Bell Labs 2002-02-19
2 files changed, 435 insertions(+), 1880 deletions(-)

M pc/devusb.c
M pc/usbuhci.c
M pc/devusb.c => pc/devusb.c +390 -1826
@@ 1,17 1,3 @@
/*
 * UHCI USB driver
 *	(c) 1998, 1999 C H Forsyth, forsyth@caldo.demon.co.uk
 * 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"


@@ 20,284 6,80 @@
#include	"io.h"
#include	"../port/error.h"

#include	"usb.h"

static int debug = 0;

#define Chatty	1
#define DPRINT if(Chatty)print
#define XPRINT if(debug)iprint

static int debug = 0;
Usbhost*	usbhost[MaxUsb];

/*
 * 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,
static char *devstates[] = {
	[Disabled]		"Disabled",
	[Attached]	"Attached",
	[Enabled]		"Enabled",
	[Assigned]	"Assigned",
	[Configured]	"Configured",
};

typedef struct Ctlr Ctlr;
typedef struct Endpt Endpt;
typedef struct Udev Udev;
static	char	Ebadusbmsg[] = "invalid parameters to USB ctl message";

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

#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))

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

	/* software */
	ulong	flags;
	union{
		Block*	bp;		/* non-iso */
		ulong	offset;	/* iso */
	};
	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 */
	QH*		hlink;
	TD*		first;
	QH*		next;		/* free list */
	TD*		last;
	ulong	_d1;		/* fillers */
	ulong	_d2;
enum
{
	Qtopdir = 0,
	Q2nd,
	Qnew,
	Qport,
	Q3rd,
	Qctl,
	Qstatus,
	Qep0,
	/* other endpoint files */
};
#define	QFOL(p)	((QH*)KADDR((ulong)(p) & ~0xF))

/*
 * UHCI interface registers and bits
 * 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
 */
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,

	NFRAME = 	1024,
	FRAMESIZE=	NFRAME*sizeof(ulong),	/* fixed by hardware; aligned to same */
	FRAMEMASK=	FRAMESIZE-1,
	NISOTD = 4,			/* number of TDs for isochronous io per frame */

	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,
	IsoClean=		1<<2,
};
	TYPEBITS	= 8,
	SLOTBITS	= 8,
	CTLRBITS	= 4,

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

/*
 * active USB device
 */
struct Udev {
	Ref;
	Lock;
	int		x;		/* index in usbdev[] */
	int		busy;
	int		state;
	int		id;
	uchar	port;		/* port number on connecting hub */
	ulong	csp;
	int		ls;
	int		npt;
	Endpt*	ep[16];	/* active end points */
	Udev*	ports;	/* active ports, if hub */
	Udev*	next;		/* next device on this hub */
	TYPEMASK	= (1<<TYPEBITS)-1,
	SLOTMASK	= (1<<SLOTBITS)-1,
	CTLRMASK	= (1<<CTLRBITS)-1,
};

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

	/* Udev.class */
	Noclass = 0,
	Hubclass = 9,
};
#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))

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 */
	uchar	eof;
	ulong	csp;
/*	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 */
	int		setin;
	/* ISO related: */
	void*	tdalloc;
	void*	bpalloc;
	int		hz;
	int		remain;	/* for packet size calculations */
	int		samplesz;
	int		sched;	/* schedule index; -1 if undefined or aperiodic */
	int		pollms;	/* polling interval in msec */
	int		psize;	/* (remaining) size of this packet */
	int		off;		/* offset into packet */
	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 */
	/* 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 */
	int		buffered;	/* bytes captured but unread, or written but unsent */
	/* end ISO stuff */
	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;
static Dirtab usbdir2[] = {
	"new",	{Qnew},			0,	0666,
	"port",	{Qport},			0,	0666,
};

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

	TD*		tdpool;	/* first NFRAMES*NISOTD entries are preallocated */
	TD*		freetd;
	QH*		qhpool;
	QH*		freeqh;

	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 */

	Udev*	ports[2];
static Dirtab usbdir3[]={
	"ctl",		{Qctl},			0,	0666,
	"status",	{Qstatus},			0,	0444,
	"setup",	{Qep0},			0,	0666,
	/* epNdata names are generated on demand */
};
#define	IN(x)	ins(ub->io+(x))
#define	OUT(x, v)	outs(ub->io+(x), (v))

static	long usbints;
static	long framenumber;
static	long frameptr;
static	long	usbbogus;

static	Ctlr	ubus;
static	char	Estalled[] = "usb endpoint stalled";
static	char	Ebadusbmsg[] = "invalid parameters to USB ctl message";

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

enum
{
	BCMdisable,
	BCMenable,
	BCMreset,
	PMdisable,
	PMenable,
	PMreset,
};

enum


@@ 312,11 94,11 @@ enum
	CMunstall,
};

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

static Cmdtab usbctlmsg[] =


@@ 331,634 113,60 @@ static Cmdtab usbctlmsg[] =
	CMunstall,	"unstall",	2,
};

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;
		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);
		XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n",
			buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1);
		if(debug && t->bp && (t->flags & CancelTD) == 0)
			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 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->hlink = nil;
	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)
static struct
{
	int i;
	QH *q0;

	q0 = q;
	for(i = 0; q != nil && i < 10; i++){
		XPRINT("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){
			XPRINT("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, char *why)
{
	TD *lt;

	for(lt = t; lt->next != nil; lt = lt->next)
		lt->link = PADDR(lt->next) | vf;
	lt->link = Terminate;
	ilock(ub);
	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);
	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;
	XPRINT("	t=%p q=%p first=%p last=%p entries=%.8lux\n",
		t, q, q->first, q->last, q->entries);
	dumpqh(q);
	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)
		XPRINT("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);
}
	char*	type;
	int	(*reset)(Usbhost*);
} usbtypes[MaxUsb+1];

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

	ub = &ubus;
	ilock(ub);
	tp = nil;
	for(t = q->first; t != nil;){
		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);
		if(t->status & Active){
			if(t->status & NAKed){
				t->status = (t->status & ~NAKed) | IOC;	/* ensure interrupt next frame */
				tp = t;
				t = t->next;
				continue;
			}
			if(t->flags & CancelTD){
				XPRINT("cancelTD: %8.8lux\n", (ulong)t);
				t->status = (t->status & ~Active) | IOC;	/* ensure interrupt next frame */
				tp = t;
				t = t->next;
				continue;
			}
			tp = t;
			t = t->next;
			continue;
			break;
		}
		t->status &= ~IOC;
		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;
		iunlock(ub);
		cleantd(t, discard);
		ilock(ub);
		if (tp)
			t = tp->next;
		else
			t = q->first;
		XPRINT("t = %8.8lux\n", t);
		dumpqh(q);
	}
	if(q->first && q->entries != PADDR(q->first)){
		usbbogus++;
		q->entries = PADDR(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);
		XPRINT("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){
		XPRINT("activate 0x%p\n", e);
		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;
		XPRINT("deactivate 0x%p\n", e);
		for(l = &activends.f; *l != e; l = &(*l)->activef)
			if(*l == nil){
				iunlock(&activends);
				panic("usb eptdeactivate");
			}
		*l = e->activef;
	}
	iunlock(&activends);
}

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

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, "qxmit");
	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;
	}else if((qh = e->epq) == nil || e->mode != OREAD){
		qh = ub->bulkq;
		vf = Vf;
	}
	queuetd(ub, qh, t, vf, "qrcv");
	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)
void
addusbtype(char* t, int (*r)(Usbhost*))
{
	int i;
	static int ntype;

	for(i=0; (1<<i)<n; i++)
		;
	return i;
	if(ntype == MaxUsb)
		panic("too many USB host interface types");
	usbtypes[ntype].type = t;
	usbtypes[ntype].reset = r;
	ntype++;
}

static int
usbsched(	Ctlr *ub, int pollms, ulong load)
static Udev*
usbdeviceofslot(Usbhost *uh, int s)
{
	int i, d, q;
	ulong best, worst;

	best = 1000000;
	q = -1;
	for (d = 0; d < pollms; d++){
		worst = 0;
		for (i = d; i < NFRAME; i++){
			if (ub->frameld[i] + load > worst)
				worst = ub->frameld[i] + load;
		}
		if (worst < best){
			best = worst;
			q = d;
		}
	}
	return q;
}

static int
schedendpt(Endpt *e)
{
	Ctlr *ub;
	TD *td;
	uchar *bp;
	int i, id, ix, size, frnum;

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

	if (e->active){
		return -1;
	}
	e->off = 0;
	e->sched = usbsched(ub, e->pollms, e->maxpkt);
	if(e->sched < 0)
		return -1;

	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);
	e->bp0 = (uchar *)(((ulong)e->bpalloc + 0xf) & ~0xf);
	frnum = (IN(Frnum) + 1) & 0x3ff;
	frnum = (frnum & ~(e->pollms - 1)) + e->sched;
	e->xtd = &e->td0[(frnum+8)&0x3ff];	/* Next td to finish */
	e->etd = nil;
	e->remain = 0;
	e->nbytes = 0;
	td = e->td0;
	for(i = e->sched; i < NFRAME; i += e->pollms){
		bp = e->bp0 + e->maxpkt*i/e->pollms;
		td->buffer = PADDR(bp);
		td->ep = e;
		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) & 0x3ff;
		td = &e->td0[ix];

		id = (e->x<<7)|(e->dev->x&0x7F);
		if (e->mode == OREAD)
			/* enable receive on this entry */
			td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | TokIN;
		else{
			size = (e->hz + e->remain)*e->pollms/1000;
			e->remain = (e->hz + e->remain)*e->pollms%1000;
			size *= e->samplesz;
			td->dev = ((size-1)<<21) | ((id&0x7FF)<<8) | TokOUT;
		}
		td->status = ErrLimit1 | Active | IsoSelect | IOC;
		td->link = ub->frames[ix];
		td->flags |= IsoClean;
		ub->frames[ix] = PADDR(td);
	}
	return 0;
	if(s < 0 || s > nelem(uh->dev))
		return nil;
	return uh->dev[s];
}

static void
unschedendpt(Endpt *e)
static Udev*
usbdevice(Chan *c)
{
	Ctlr *ub;
	TD *td;
	ulong *addr;
	int q;

	ub = &ubus;
	if(!e->iso || e->sched < 0)
		return;
	int bus;
	Udev *d;
	Usbhost *uh;

	if (e->tdalloc == nil)
		panic("tdalloc");
	for (q = e->sched; q < NFRAME; q += e->pollms){
		td = e->td0++;
		addr = &ub->frames[q];
		while (*addr != PADDR(td)){
			if (*addr & IsQH)
				panic("usb: TD expected");
			addr = &TFOL(*addr)->link;
		}
		*addr = td->link;
		ub->frameld[q] -= e->maxpkt;
	bus = CTLR(c->qid);
	if(bus > nelem(usbhost) || (uh = usbhost[bus]) == nil) {
		error(Egreg);
		return nil;		/* for compiler */
	}
	free(e->tdalloc);
	free(e->bpalloc);
	e->tdalloc = nil;
	e->bpalloc = nil;
	e->etd = nil;
	e->td0 = nil;
	e->sched = -1;
	d = usbdeviceofslot(uh, SLOT(c->qid));
	if(d == nil || d->id != c->qid.vers || d->state == Disabled)
		error(Ehungup);
	return d;
}

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

	ub = &ubus;
	p = &d->ep[id&0xF];
	lock(d);
	if((e = *p) != nil){
	e = *p;
	if(e != nil){
		incref(e);
		XPRINT("incref(0x%p) in devendpt, new value %ld\n", e, e->ref);
		unlock(d);


@@ 967,6 175,7 @@ devendpt(Udev *d, int id, int add)
	unlock(d);
	if(!add)
		return nil;

	e = mallocz(sizeof(*e), 1);
	e->ref = 1;
	e->x = id&0xF;


@@ 976,563 185,136 @@ devendpt(Udev *d, int id, int add)
	e->nbuf = 1;
	e->dev = d;
	e->active = 0;
	e->epq = allocqh(ub);
	if(e->epq == nil)
		panic("devendpt");

	uh = d->uh;
	uh->epalloc(uh, e);

	lock(d);
	if(*p != nil){
		incref(*p);
		XPRINT("incref(0x%p) in devendpt, new value %ld\n", *p, (*p)->ref);
		unlock(d);
		uh->epfree(uh, e);
		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 int
freept(Endpt *e)
{
	if(e != nil && decref(e) == 0){
		XPRINT("freept(%d,%d)\n", e->dev->x, e->x);
		eptdeactivate(e);
		unschedendpt(e);
		e->dev->ep[e->x] = nil;
		if(e->epq != nil)
			freeqh(&ubus, e->epq);
		free(e);
		return 1;
	}
	if (e) XPRINT("decref(0x%p) in freept, new value %ld\n", e, e->ref);
	return 0;
}

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 ept)
{
	int i;
	Endpt *e;

	if(d != nil && 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)
			usbdev[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){
				eptdeactivate(e);
				unschedendpt(e);
			}
		}
	}	
}

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
cleaniso(Endpt *e, int frnum)
{
	TD *td;
	int id, n, i;
	uchar *bp;

	td = e->xtd;
	if (td->status & Active)
		return;
	id = (e->x<<7)|(e->dev->x&0x7F);
	do {
		if (td->status & AnyError)
			iprint("usbisoerror 0x%lux\n", td->status);
		n = (td->status + 1) & 0x3ff;
		e->nbytes += n;
		if ((td->flags & IsoClean) == 0)
			e->nblocks++;
		if (e->mode == OREAD){
			e->buffered += n;
			e->poffset += (td->status + 1) & 0x3ff;
			td->offset = e->poffset;
			td->dev = ((e->maxpkt -1)<<21) | ((id&0x7FF)<<8) | TokIN;
			e->toffset = td->offset;
		}else{
			if ((td->flags & IsoClean) == 0){
				e->buffered -= n;
				if (e->buffered < 0){
					print("e->buffered %d?\n", e->buffered);
					e->buffered = 0;
				}
			}
			e->toffset = td->offset;
			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->offset = e->poffset;
			e->poffset += n;
		}
		td = td->next;
		if (e->xtd == td){
			XPRINT("@");
			break;
		}
	} while ((td->status & Active) == 0);
	e->time = todget(nil);
	e->xtd = td;
	for (n = 2; n < 4; n++){
		i = ((frnum + n)&0x3ff);
		td = e->td0 + i;
		bp = e->bp0 + e->maxpkt*i/e->pollms;
		if (td->status & Active)
			continue;

		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);
				td->flags |= IsoClean;
			}
		} else {
			/* Unread bytes are now lost */
			e->buffered -= (td->status + 1) & 0x3ff;
		}
		td->status = ErrLimit1 | Active | IsoSelect | IOC;
	}
	wakeup(&e->wr);
}

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

	ub = a;
	s = IN(Status);

	frameptr = inl(ub->io+Flbaseadd);
	framenumber = IN(Frnum) & 0x3ff;
	OUT(Status, s);
	if ((s & 0x1f) == 0)
		return;
	usbints++;
	frnum = IN(Frnum) & 0x3ff;
	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));
	}

	ilock(&activends);
	for(e = activends.f; e != nil; e = e->activef){
		if(!e->iso && e->epq != nil) {
			XPRINT("cleanq(e->epq, 0, 0)\n");
			cleanq(e->epq, 0, 0);
		}
		if(e->iso) {
			XPRINT("cleaniso(e)\n");
			cleaniso(e, frnum);
		}
	}
	iunlock(&activends);
	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);
	}
}

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];
	e->rq = qopen(8*1024, 0, nil, e);
	e->wq = qopen(8*1024, 0, nil, e);
	return e;
}

static Udev *
usbdevice(Chan *c)
static void
freept(Endpt *e)
{
	Udev *d;
	Usbhost *uh;

	d = usbdeviceofpath(c->qid.path);
	if(d == nil || d->id != c->qid.vers || d->state == Disabled)
		error(Ehungup);
	return d;
	if(e != nil && decref(e) == 0){
		XPRINT("freept(%d,%d)\n", e->dev->x, e->x);
		uh = e->dev->uh;
		uh->epclose(uh, e);
		e->dev->ep[e->x] = nil;
		uh->epfree(uh, e);
		free(e);
	}
}

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

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

static void
usbreset(void)
freedev(Udev *d, int ept)
{
	Pcidev *cfg;
	int i;
	ulong port;
	TD *t;
	Ctlr *ub;
	ISAConf isa;

	if(isaconfig("usb", 0, &isa) == 0) {
		XPRINT("usb not in plan9.ini\n");
		return;
	}
	ub = &ubus;
	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;
		if(cfg->did == 0x2482 || cfg->did == 0x2487)
			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;
// 		}
		if((cfg->mem[4].bar & ~0x0F) != 0)
			break;
	}
	if(cfg == nil) {
		DPRINT("No USB device found\n");
		return;
	}
	port = cfg->mem[4].bar & ~0x0F;

	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(0){
		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, usbinterrupt, ub, cfg->tbdf, "usb");

	ub->io = port;
	ub->tdpool = (TD*)(((ulong)xalloc(128*sizeof(TD) + 0x10) + 0xf) & ~0xf);
	for(i=128; --i>=0;){
		ub->tdpool[i].next = ub->freetd;
		ub->freetd = &ub->tdpool[i];
	}
	ub->qhpool = (QH*)(((ulong)xalloc(32*sizeof(QH) + 0x10) + 0xf) & ~0xf);
	for(i=32; --i>=0;){
		ub->qhpool[i].next = ub->freeqh;
		ub->freeqh = &ub->qhpool[i];
	}

	/*
	 * the last entries of the periodic (interrupt & isochronous) scheduling TD entries
	 * point 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->ctlq->head = PADDR(ub->bulkq) | IsQH;
	ub->bulkq->head = PADDR(ub->recvq) | IsQH;
	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);

	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 = (ulong*)(((ulong)xalloc(2*FRAMESIZE) + FRAMEMASK) & ~FRAMEMASK);
	ub->frameld = xallocz(FRAMESIZE, 1);

	for (i = 0; i < NFRAME; i++)
		ub->frames[i] = PADDR(ub->ctlq) | IsQH;

	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;
	Endpt *e;
	Usbhost *uh;

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

static int
usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
{
	int t;
	Qid q;
	ulong path;
	Udev *d;
	Dirtab *tab;
	Endpt *e;
	vlong len;
	Dirtab *tab;
	Usbhost *uh;
	int t, bus, slot, perm;

	/*
	 * Top level directory contains the name of the device.
	 * Top level directory contains the controller names.
	 */
	if(QID(c->qid) == Qtopdir){
	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;
		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;
	}
	bus = CTLR(c->qid);
	if(bus >= nelem(usbhost) || (uh = usbhost[bus]) == nil)
			return -1;

	/*
	 * Second level contains "new" plus all the clients.
	 * Second level contains "new", "port", and a numbered
	 * directory for each enumerated device on the bus.
	 */
	t = QID(c->qid);
	t = TYPE(c->qid);
	if(t < Q3rd){
		if(s == DEVDOTDOT){
			mkqid(&q, Qtopdir, 0, QTDIR);


@@ 1540,17 322,21 @@ usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
			return 1;
		}
		if(s < nelem(usbdir2)){
			d = uh->dev[0];
			if(d == nil)
				return -1;
			tab = &usbdir2[s];
			devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
			mkqid(&q, PATH(tab->qid.path, 0, bus), d->id, QTFILE);
			devdir(c, q, tab->name, tab->length, eve, tab->perm, dp);
			return 1;
		}
		s -= nelem(usbdir2);
		if(s >= 0 && s < nelem(usbdev)){
			d = usbdev[s];
		if(s >= 0 && s < nelem(uh->dev)) {
			d = uh->dev[s];
			if(d == nil)
				return -1;
				return 0;
			sprint(up->genbuf, "%d", s);
			mkqid(&q, ((s+1)<<QSHIFT)|Q3rd, d->id, QTDIR);
			mkqid(&q, PATH(Q3rd, s, bus), d->id, QTDIR);
			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
			return 1;
		}


@@ 1560,36 346,122 @@ usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
	/*
	 * Third level.
	 */
	len = 0;
	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);
	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);
		return 1;
	}
	if(s < nelem(usbdir3)){
		Dirtab *tab = &usbdir3[s];
		mkqid(&q, path | tab->qid.path, c->qid.vers, QTFILE);
	if(s < nelem(usbdir3)) {
		tab = &usbdir3[s];
		mkqid(&q, PATH(tab->qid.path, slot, bus), c->qid.vers, QTFILE);
		devdir(c, q, tab->name, tab->length, eve, tab->perm, dp);
		return 1;
	}
	s -= nelem(usbdir3);

	/* active endpoints */
	d = usbdeviceofpath(path);
	if(d == nil)
		return -1;
	s -= nelem(usbdir3);
	if(s < 0 || s >= nelem(d->ep))
	d = usbdeviceofslot(uh, slot);
	if(d == nil || 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);
	len = e->buffered;
	devdir(c, q, up->genbuf, len, eve, e->mode==OREAD? 0444: e->mode==OWRITE? 0222: 0666, dp);
	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);
	return 1;
}

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

static Walkqid*
usbwalk(Chan *c, Chan *nc, char **name, int nname)
{


@@ 1602,42 474,47 @@ usbstat(Chan *c, uchar *db, int n)
	return devstat(c, db, n, nil, 0, usbgen);
}

Chan *
Chan*
usbopen(Chan *c, int omode)
{
	Udev *d;
	int f, s;
	Endpt *e;
	int f, s, type;
	Usbhost *uh;

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

	f = 0;
	if(QID(c->qid) == Qnew){
		d = usbnewdevice();
	type = TYPE(c->qid);
	if(type == Qnew){
		d = usbdevice(c);
		d = usbnewdevice(d->uh);
		XPRINT("usbopen, new dev 0x%p\n", d);
		if(d == nil) {
			XPRINT("usbopen failed (usbnewdevice)\n");
			error(Enodev);
		}
		c->qid.path = Qctl|((d->x+1)<<QSHIFT);
		c->qid.vers = d->id;
		type = Qctl;
		mkqid(&c->qid, PATH(type, d->x, CTLR(c->qid)), d->id, QTFILE);
		f = 1;
	}

	if(QID(c->qid) < Q3rd){
	if(type < Q3rd){
		XPRINT("usbopen, devopen < Q3rd\n");
		return devopen(c, omode, nil, 0, usbgen);
	}

	qlock(&usbstate);
	d = usbdevice(c);
	uh = d->uh;
	qlock(uh);
	if(waserror()){
		qunlock(&usbstate);
		qunlock(uh);
		nexterror();
	}

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


@@ 1647,33 524,25 @@ usbopen(Chan *c, int omode)
		break;

	default:
		d = usbdevice(c);
		s = QID(c->qid) - Qep0;
		s = type - Qep0;
		XPRINT("usbopen, default 0x%p, %d\n", d, s);
		if(s >= 0 && s < nelem(d->ep)){
			Endpt *e;
			if((e = d->ep[s]) == nil) {
				XPRINT("usbopen failed (endpoint)\n");
				error(Enodev);
			}
			XPRINT("usbopen: dev 0x%p, ept 0x%p\n", d, e);
			if(schedendpt(e) < 0){
				if (e->active)
					error("can't schedule USB endpoint, active");
				else
					error("can't schedule USB endpoint");
			}
			uh->epopen(uh, e);
			e->foffset = 0;
			e->toffset = 0;
			e->poffset = 0;
			e->buffered = 0;
			eptactivate(e);
		}
		incref(d);
		break;
	}
	poperror();
	qunlock(&usbstate);
	qunlock(uh);
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;


@@ 1681,403 550,116 @@ usbopen(Chan *c, int omode)
}

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

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

void
usbclose(Chan *c)
{
	Udev *d;
	int ept;
	int ept, type;
	Usbhost *uh;

	if(c->qid.type == QTDIR || QID(c->qid) < Q3rd)
	type = TYPE(c->qid);
	if(c->qid.type == QTDIR || type < Q3rd)
		return;
	qlock(&usbstate);
	if(waserror()){	/* usbdevice can error */
		qunlock(&usbstate);
	d = usbdevice(c);
	uh = d->uh;
	qlock(uh);
	if(waserror()){
		qunlock(uh);
		nexterror();
	}
	d = usbdevice(c);
	if(QID(c->qid) == Qctl)
	if(type == Qctl)
		d->busy = 0;
	XPRINT("usbclose: dev 0x%p\n", d);
	if(c->flag & COPEN){
		ept = (QID(c->qid) != Qctl) ? QID(c->qid) - Qep0 : -1;
		ept = (type != Qctl) ? type - Qep0 : -1;
		XPRINT("usbclose: freedev 0x%p\n", d);
		freedev(d, ept);
	}
	poperror();
	qunlock(&usbstate);
}

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

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

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

	e = arg;
	return e->etd == nil || (e->etd != e->xtd && (e->etd->status & Active) == 0);
}

static long
isoio(Endpt *e, void *a, long n, ulong offset, int w)
{
	int i, frnum;
	volatile int isolock;
	uchar *p, *q, *bp;
	Ctlr *ub;
	TD *td;

	qlock(&e->rlock);
	isolock = 0;
	if(waserror()){
		if (isolock){
			isolock = 0;
			iunlock(&activends);
		}
		qunlock(&e->rlock);
		eptcancel(e);
		nexterror();
	}
	p = a;
	ub = &ubus;
	if (offset != 0 && offset != e->foffset){
		iprint("offset %lud, foffset %lud\n", 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;
	}
	do {
		if (isolock == 0){
			ilock(&activends);
			isolock = 1;
		}
		td = e->etd;
		if (td == nil || e->off == 0){
			if (td == nil){
				XPRINT("0");
				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;
				e->off = 0;
			}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;
				}
			}
			if (w)
				e->psize = ((td->dev >> 21) + 1) & 0x7ff;
			else
				e->psize = (e->etd->status + 1) & 0x7ff;
			if (e->psize > e->maxpkt)
				panic("packet size > maximum");
		}
		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);
		td->flags &= ~IsoClean;
		bp = e->bp0 + (td - e->td0) * e->maxpkt / e->pollms;
		q = bp + e->off;
		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;
		}
		if(w)
			td->offset = offset + (p-(uchar*)a) - (((td->dev >> 21) + 1) & 0x7ff);
		td->status = ErrLimit3 | Active | IsoSelect | IOC;
		e->etd = td->next;
		e->off = 0;
	} while(n > 0);
	n = p-(uchar*)a;
	e->foffset += n;
	poperror();
	if (isolock)
		iunlock(&activends);
	qunlock(&e->rlock);
	return n;
	qunlock(uh);
}

static long
readusb(Endpt *e, void *a, long n)
static char *
epstatus(char *s, char *se, Endpt *e, int i)
{
	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();
	}
	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;
		if (l != e->maxpkt)
			break;
	} while (n > 0);
	poperror();
	qunlock(&e->rlock);
	return p-(uchar*)a;
}
	char *p;

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\n",
		i, e->csp, e->nbytes, e->nblocks);
	if (e->iso){
		l += snprint(s+l, n-l, "bufsize %6d buffered %6d",
			e->maxpkt, e->buffered);
	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)
			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';
			p = seprint(p, se, " offset  %10lud time %19lld\n", e->toffset, e->time);
		p = seprint(p, se, "\n");
	}
	return l;
	return p;
}

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

	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);
	d = usbdevice(c);
	uh = d->uh;
	t = TYPE(c->qid);

	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;
	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;
			writeusb(e, "", 0, TokOUT);
			n = uh->read(uh, e, a, n, 0LL);
			if(e->setin){
				e->setin = 0;
				e->data01 = 1;
				uh->write(uh, e, "", 0, 0LL, TokOUT);
			}
			return n;
		}
		break;

	case Qdebug:
		n=0;
		break;
		return uh->read(uh, e, a, n, offset);
	}

	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 += epstatus(s+l, READSTR-l, e, i);
			}
		n = readstr(offset, a, n, s);
		poperror();
	s = smalloc(READSTR);
	se = s+READSTR;
	if(waserror()){
		free(s);
		nexterror();
	}
	switch(t){
	case Qport:
		uh->portinfo(uh, s, se);
		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 */
		if (e->iso)
			n=isoio(e, a, n, (ulong)offset, 0);
		else
			n=readusb(e, a, n);
	case Qctl:
		seprint(s, se, "%11d %11d\n", d->x, d->id);
		break;
	}
	return n;
}

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

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

	qlock(&e->wlock);
	if(waserror()){
		qunlock(&e->wlock);
		eptcancel(e);
		nexterror();
	}
	p = a;
	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 [%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("qh %s: q=%p first=%p last=%p entries=%.8lux\n",
 "writeusb sleep", qh, qh->first, qh->last, qh->entries);
			XPRINT("writeusb: sleep %lux\n", &e->wr);
			sleep(&e->wr, qisempty, qh);
			XPRINT("writeusb: awake\n");
	case Qstatus:
		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);
		}
	} while(n > 0);
	}
	n = readstr(offset, a, n, s);
	poperror();
	qunlock(&e->wlock);
	return p-(uchar*)a;
	free(s);
	return n;
}

long


@@ 2085,44 667,42 @@ usbwrite(Chan *c, void *a, long n, vlong offset)
{
	Udev *d;
	Endpt *e;
	Cmdbuf *cb;
	Cmdtab *ct;
	Cmdbuf *cb;
	Usbhost *uh;
	int id, nw, t, i;
	char cmd[50];

	if(c->qid.type == QTDIR)
		error(Egreg);
	t = QID(c->qid);
	if(t == Qbusctl){
	d = usbdevice(c);
	uh = d->uh;
	t = TYPE(c->qid);
	switch(t){
	case Qport:
		cb = parsecmd(a, n);
		if(waserror()){
			free(cb);
			nexterror();
		}

		ct = lookupcmd(cb, usbbusctlmsg, nelem(usbbusctlmsg));
		ct = lookupcmd(cb, usbportmsg, nelem(usbportmsg));
		id = strtol(cb->f[1], nil, 0);
		if(id != 1 && id != 2)
			cmderror(cb, "usb port number not 1 or 2 in");
		switch(ct->index){
		case BCMdisable:
			portenable(id, 0);
		case PMdisable:
			uh->portenable(uh, id, 0);
			break;
		case BCMenable:
			portenable(id, 1);
		case PMenable:
			uh->portenable(uh, id, 1);
			break;
		case BCMreset:
			portreset(id);
		case PMreset:
			uh->portreset(uh, id);
			break;
		}
	
		poperror();
		free(cb);
		return n;
	}
	d = usbdevice(c);
	t = QID(c->qid);
	switch(t){
	case Qctl:
		cb = parsecmd(a, n);
		if(waserror()){


@@ 2217,31 797,17 @@ usbwrite(Chan *c, void *a, long n, vlong offset)
				XPRINT("calling devendpt in usbwrite (CMep)\n");
				e = devendpt(d, i, 1);
			}
			qlock(&usbstate);
			qlock(uh);
			if(waserror()){
				freept(e);
				qunlock(&usbstate);
				qunlock(uh);
				nexterror();
			}
			if (e->active)
			if(e->active)
				error(Eperm);
			if(strcmp(cb->f[2], "bulk") == 0){
				Ctlr *ub;

				e->iso = 0;
				/* ep n `bulk' mode maxpkt nbuf */
				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);
				e->mode = strcmp(cb->f[3],"r") == 0? OREAD :
					  	strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR;
				e->iso = 0;
				i = strtoul(cb->f[4], nil, 0);
				if(i < 8 || i > 1023)
					i = 8;


@@ 2250,7 816,7 @@ usbwrite(Chan *c, void *a, long n, vlong offset)
				if(i >= 1 && i <= 32)
					e->nbuf = i;
			} else {
				/* ep n period mode samplesize KHz */
				/* ep n period mode samplesize Hz */
				i = strtoul(cb->f[2], nil, 0);
				if(i > 0 && i <= 1000){
					e->pollms = i;


@@ 2258,8 824,6 @@ usbwrite(Chan *c, void *a, long n, vlong offset)
					XPRINT("field 4: 0 <= %d <= 1000\n", i);
					error(Ebadarg);
				}
				e->mode = strcmp(cb->f[3],"r") == 0? OREAD :
					  	strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR;
				i = strtoul(cb->f[4], nil, 0);
				if(i >= 1 && i <= 8){
					e->samplesz = i;


@@ 2279,26 843,30 @@ usbwrite(Chan *c, void *a, long n, vlong offset)
				e->maxpkt = (e->hz * e->pollms + 999)/1000 * e->samplesz;
				e->iso = 1;
			}
			e->mode = strcmp(cb->f[3],"r") == 0? OREAD :
				  	strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR;
			uh->epmode(uh, e);
			poperror();
			qunlock(&usbstate);
			qunlock(uh);
		}
	
		poperror();
		free(cb);
		return n;

	case Qsetup:	/* SETUP endpoint 0 */
	case Qep0:	/* SETUP endpoint 0 */
		/* should canqlock etc */
		if((e = d->ep[0]) == nil)
			error(Eio);	/* can't happen */
		e = d->ep[0];
		if(e == nil || e->iso)
			error(Egreg);
		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 */
		n = uh->write(uh, e, a, n, 0LL, TokSETUP);
		if(nw == 0) {	/* host to device: use IN[DATA1] to ack */
			e->data01 = 1;
			nw = readusb(e, cmd, 8);
			nw = uh->read(uh, e, cmd, 0LL, 8);
			if(nw != 0)
				error(Eio);	/* could provide more status */
		}else


@@ 2306,17 874,13 @@ usbwrite(Chan *c, void *a, long n, vlong offset)
		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 */
		}
		if (e->iso)
			n = isoio(e, a, n, (ulong)offset, 1);
		else
			n = writeusb(e, a, n, TokOUT);
		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);
		break;
	}
	return n;

M pc/usbuhci.c => pc/usbuhci.c +45 -54
@@ 1198,21 1198,24 @@ isoio(Ctlr *ctlr, Endpt *e, void *a, long n, ulong offset, int w)
			if(e->psize > e->maxpkt)
				panic("packet size > maximum");
		}
		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(&ctlr->activends);
		td->flags &= ~IsoClean;
		bp = x->bp0 + (td - x->td0) * e->maxpkt / e->pollms;
		q = bp + e->off;
		if((i = n) >= e->psize)
			i = e->psize;
		if (w){
			memmove(q, p, i);
			e->buffered += i;
		}else{
			memmove(p, q, i);
			e->buffered -= i;
			if (e->buffered < 0){
				XPRINT("e->buffered %d?\n", e->buffered);
				e->buffered = 0;
			}
		}
		p += i;
		n -= i;


@@ 1231,11 1234,9 @@ isoio(Ctlr *ctlr, Endpt *e, void *a, long n, ulong offset, int w)
	} while(n > 0);
	n = p-(uchar*)a;
	e->foffset += n;
	if (isolock){
		isolock = 0;
		iunlock(&ctlr->activends);
	}
	poperror();
	if (isolock)
		iunlock(&ctlr->activends);
	qunlock(&e->rlock);
	return n;
}


@@ 1361,24 1362,10 @@ XPRINT("qh %s: q=%p first=%p last=%p entries=%.8lux\n",
	return p-(uchar*)a;
}

static struct
{
	int	vid;
	int	did;
}
supported[] =
{
	{ 0x8086, 0x7112 },		/* Intel chipset PIIX 4 */
	{ 0x8086, 0x2442 },		/* Intel chipset 815E */
	{ 0x8086, 0x2444 },		/* Intel chipset 815E */
	{ 0x1106, 0x0586 },		/* Via chipset */
	{ 0x1106, 0x3038 },		/* Via chipset */
};

static void
scanpci(void)
{
	int i, io;
	int io;
	Ctlr *ctlr;
	Pcidev *p;
	static int already = 0;


@@ 1386,35 1373,39 @@ scanpci(void)
	if(already)
		return;
	already = 1;
	for(i = 0; i < nelem(supported); i++) {
		p = nil;
		while(p = pcimatch(p, supported[i].vid, supported[i].did)) {
			io = p->mem[4].bar & ~0x0F;
			if(io == 0) {
				print("usbuhci: failed to map registers\n");
				continue;
			}
			if(ioalloc(io, p->mem[4].size, 0, "usbuhci") < 0){
				print("usbuhci: port %d in use\n", io);
				continue;
			}
			if(p->intl == 0xFF || p->intl == 0) {
				print("usbuhci: no irq assigned for port %d\n", io);
				continue;
			}
	p = nil;
	while(p = pcimatch(p, 0, 0)) {
		/*
		 * Find UHCI controllers.  Class = 12 (serial controller),
		 * Sub-class = 3 (USB) and Programming Interface = 0.
		 */
		if(p->ccrb != 0x0C || p->ccru != 0x03 || p->ccrp != 0x00)
			continue;
		io = p->mem[4].bar & ~0x0F;
		if(io == 0) {
			print("usbuhci: failed to map registers\n");
			continue;
		}
		if(ioalloc(io, p->mem[4].size, 0, "usbuhci") < 0){
			print("usbuhci: port %d in use\n", io);
			continue;
		}
		if(p->intl == 0xFF || p->intl == 0) {
			print("usbuhci: no irq assigned for port %d\n", io);
			continue;
		}

			XPRINT("usbuhci: %x/%x port 0x%ux size 0x%x irq %d\n",
				p->vid, p->did, io, p->mem[4].size, p->intl);
		XPRINT("usbuhci: %x/%x port 0x%ux size 0x%x irq %d\n",
			p->vid, p->did, io, p->mem[4].size, p->intl);

			ctlr = malloc(sizeof(Ctlr));
			ctlr->pcidev = p;
			ctlr->io = io;
			if(ctlrhead != nil)
				ctlrtail->next = ctlr;
			else
				ctlrhead = ctlr;
			ctlrtail = ctlr;
		}
		ctlr = malloc(sizeof(Ctlr));
		ctlr->pcidev = p;
		ctlr->io = io;
		if(ctlrhead != nil)
			ctlrtail->next = ctlr;
		else
			ctlrhead = ctlr;
		ctlrtail = ctlr;
	}
}