plan 9 kernel history: overview | file list | diff list

2001/1124/pc/devusb.c (diff list | history)

pc/devusb.c on 1999/1005
1999/1005    
/* 
 * 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" 
#include	"dat.h" 
#include	"fns.h" 
#include	"io.h" 
#include	"../port/error.h" 
 
#define Chatty	1 
#define DPRINT if(Chatty)print 
2001/0916    
#define XPRINT if(debug)iprint 
1999/1005    
 
1999/1117    
static int debug = 0; 
1999/1116    
 
1999/1005    
/* 
 * 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 struct Ctlr Ctlr; 
typedef struct Endpt Endpt; 
typedef struct Udev Udev; 
 
/* 
 * UHCI hardware structures, aligned on 16-byte boundary 
 */ 
typedef struct QH QH; 
typedef struct TD TD; 
 
2000/0724    
#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)) 
 
1999/1005    
struct TD { 
	ulong	link; 
	ulong	status;	/* controller r/w */ 
	ulong	dev; 
	ulong	buffer; 
 
	/* software */ 
	ulong	flags; 
2001/1010    
	union{ 
		Block*	bp;		/* non-iso */ 
		ulong	offset;	/* iso */ 
	}; 
1999/1005    
	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 */ 
1999/1117    
	QH*		hlink; 
	TD*		first; 
	QH*		next;		/* free list */ 
	TD*		last; 
	ulong	_d1;		/* fillers */ 
	ulong	_d2; 
1999/1005    
}; 
#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, 
 
2001/1019    
	NFRAME = 	1024, 
	FRAMESIZE=	NFRAME*sizeof(ulong),	/* fixed by hardware; aligned to same */ 
	FRAMEMASK=	FRAMESIZE-1,	/* fixed by hardware; aligned to same */ 
2001/0916    
	NISOTD = 4,			/* number of TDs for isochronous io per frame */ 
1999/1005    
 
	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, 
2001/1006    
	IsoClean=		1<<2, 
1999/1005    
}; 
 
#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; 
2001/0916    
	int		x;		/* index in usbdev[] */ 
1999/1120    
	int		busy; 
	int		state; 
	int		id; 
2001/1010    
	uchar	port;		/* port number on connecting hub */ 
2000/0724    
	ulong	csp; 
1999/1120    
	int		ls; 
	int		npt; 
2001/0916    
	Endpt*	ep[16];	/* active end points */ 
	Udev*	ports;	/* active ports, if hub */ 
1999/1005    
	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[] = { 
1999/1120    
	[Disabled]		"Disabled", 
2001/0626    
	[Attached]	"Attached", 
1999/1120    
	[Enabled]		"Enabled", 
2001/0626    
	[Assigned]	"Assigned", 
1999/1120    
	[Configured]	"Configured", 
1999/1005    
}; 
 
/* 
 * device endpoint 
 */ 
struct Endpt { 
	Ref; 
	Lock; 
2001/0916    
	int		x;		/* index in Udev.ep */ 
1999/1116    
	int		id;		/* hardware endpoint address */ 
	int		maxpkt;	/* maximum packet size (from endpoint descriptor) */ 
	int		data01;	/* 0=DATA0, 1=DATA1 */ 
2001/1010    
	uchar	eof; 
2000/0724    
	ulong	csp; 
2001/1016    
/*	uchar	isopen;	 ep operations forbidden on open endpoints */ 
2001/1010    
	uchar	mode;	/* OREAD, OWRITE, ORDWR */ 
	uchar	nbuf;	/* number of buffers allowed */ 
	uchar	iso; 
	uchar	debug; 
	uchar	active;	/* listed for examination by interrupts */ 
2001/1007    
	int		setin; 
	/* ISO related: */ 
2001/1019    
	void*	tdalloc; 
	void*	bpalloc; 
2001/0916    
	int		hz; 
2001/1007    
	int		remain;	/* for packet size calculations */ 
2001/0916    
	int		samplesz; 
1999/1116    
	int		sched;	/* schedule index; -1 if undefined or aperiodic */ 
	int		pollms;	/* polling interval in msec */ 
2001/1007    
	int		psize;	/* (remaining) size of this packet */ 
	int		off;		/* offset into packet */ 
2001/1010    
	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 */ 
2001/1011    
	/* 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 */ 
2001/1012    
	int		buffered;	/* bytes captured but unread, or written but unsent */ 
2001/1007    
	/* end ISO stuff */ 
2001/1010    
	QH	*	epq;		/* queue of TDs for this endpoint */ 
1999/1005    
	QLock	rlock; 
	Rendez	rr; 
	Queue*	rq; 
	QLock	wlock; 
	Rendez	wr; 
	Queue*	wq; 
 
1999/1116    
	int		ntd; 
1999/1005    
	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) */ 
2001/0916    
	int		io; 
1999/1005    
	ulong*	frames;	/* frame list */ 
2001/0916    
	ulong*	frameld;	/* real time load on each of the frame list entries */ 
	int		idgen;	/* version number to distinguish new connections */ 
1999/1005    
	QLock	resetl;	/* lock controller during USB reset */ 
 
2001/0916    
	TD*		tdpool;	/* first NFRAMES*NISOTD entries are preallocated */ 
	TD*		freetd; 
	QH*		qhpool; 
	QH*		freeqh; 
1999/1005    
 
2001/0916    
	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 */ 
1999/1005    
 
	Udev*	ports[2]; 
}; 
#define	IN(x)	ins(ub->io+(x)) 
#define	OUT(x, v)	outs(ub->io+(x), (v)) 
 
2001/1124    
static	long usbints; 
static	long framenumber; 
static	long frameptr; 
static	long	usbbogus; 
 
1999/1005    
static	Ctlr	ubus; 
static	char	Estalled[] = "usb endpoint stalled"; 
2001/1120    
static	char	Ebadusbmsg[] = "invalid parameters to USB ctl message"; 
1999/1005    
 
static	QLock	usbstate;	/* protects name space state */ 
static	Udev*	usbdev[32]; 
static struct { 
	Lock; 
	Endpt*	f; 
} activends; 
 
2001/1120    
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, 
}; 
 
1999/1005    
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; 
1999/1117    
		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); 
2001/0626    
		XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n", 
			buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1); 
1999/1118    
		if(debug && t->bp && (t->flags & CancelTD) == 0) 
			dumpdata(t->bp, n); 
1999/1005    
		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; 
1999/1117    
	qh->hlink = nil; 
1999/1005    
	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++){ 
1999/1007    
		XPRINT("qh %8.8lux: %8.8lux %8.8lux\n", q, q->head, q->entries); 
1999/1005    
		if((q->entries & Terminate) == 0) 
			dumptd(TFOL(q->entries), 1); 
		if(q->head & Terminate) 
			break; 
		if((q->head & IsQH) == 0){ 
1999/1007    
			XPRINT("head:"); 
1999/1005    
			dumptd(TFOL(q->head), 1); 
			break; 
		} 
		q = QFOL(q->head); 
		if(q == q0) 
			break;	/* looped */ 
	} 
} 
 
static void 
2001/1124    
queuetd(Ctlr *ub, QH *q, TD *t, int vf, char *why) 
1999/1005    
{ 
	TD *lt; 
 
	for(lt = t; lt->next != nil; lt = lt->next) 
		lt->link = PADDR(lt->next) | vf; 
	lt->link = Terminate; 
	ilock(ub); 
2001/1124    
	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); 
1999/1005    
	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; 
2001/1124    
	XPRINT("	t=%p q=%p first=%p last=%p entries=%.8lux\n", 
		t, q, q->first, q->last, q->entries); 
1999/1116    
	dumpqh(q); 
1999/1005    
	iunlock(ub); 
} 
 
static void 
cleantd(TD *t, int discard) 
{ 
	Block *b; 
	int n, err; 
 
2001/0626    
	XPRINT("cleanTD: %8.8lux %8.8lux %8.8lux %8.8lux\n", 
		t->link, t->status, t->dev, t->buffer); 
1999/1005    
	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) 
2001/0626    
		XPRINT("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", 
			t->link, t->status, t->dev, t->buffer); 
1999/1005    
	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 
1999/1116    
cleanq(QH *q, int discard, int vf) 
1999/1005    
{ 
1999/1116    
	TD *t, *tp; 
1999/1005    
	Ctlr *ub; 
 
	ub = &ubus; 
	ilock(ub); 
1999/1116    
	tp = nil; 
1999/1005    
	for(t = q->first; t != nil;){ 
2001/0626    
		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); 
1999/1005    
		if(t->status & Active){ 
1999/1116    
			if(t->status & NAKed){ 
				t->status = (t->status & ~NAKed) | IOC;	/* ensure interrupt next frame */ 
				tp = t; 
				t = t->next; 
				continue; 
			} 
1999/1005    
			if(t->flags & CancelTD){ 
				XPRINT("cancelTD: %8.8lux\n", (ulong)t); 
				t->status = (t->status & ~Active) | IOC;	/* ensure interrupt next frame */ 
1999/1116    
				tp = t; 
1999/1005    
				t = t->next; 
				continue; 
			} 
2001/0626    
			tp = t; 
			t = t->next; 
			continue; 
1999/1005    
			break; 
		} 
		t->status &= ~IOC; 
1999/1116    
		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; 
1999/1005    
		iunlock(ub); 
		cleantd(t, discard); 
		ilock(ub); 
1999/1116    
		if (tp) 
			t = tp->next; 
		else 
			t = q->first; 
		XPRINT("t = %8.8lux\n", t); 
		dumpqh(q); 
1999/1005    
	} 
2001/1124    
	if(q->first && q->entries != PADDR(q->first)){ 
		usbbogus++; 
		q->entries = PADDR(q->first); 
	} 
1999/1005    
	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); 
1999/1007    
		XPRINT("cancel:\n"); 
1999/1005    
		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){ 
2001/1016    
		XPRINT("activate 0x%p\n", e); 
1999/1005    
		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; 
2001/1016    
		XPRINT("deactivate 0x%p\n", e); 
1999/1005    
		for(l = &activends.f; *l != e; l = &(*l)->activef) 
			if(*l == nil){ 
				iunlock(&activends); 
				panic("usb eptdeactivate"); 
			} 
		*l = e->activef; 
	} 
	iunlock(&activends); 
} 
 
1999/1117    
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; 
} 
 
1999/1005    
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); 
1999/1118    
	if(e->debug) pprint("QTD: %8.8lux n=%ld\n", t, b?BLEN(b): 0); 
1999/1005    
	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; 
	} 
2001/1124    
	queuetd(ub, qh, t, vf, "qxmit"); 
1999/1005    
	return qh; 
} 
 
static QH* 
qrcv(Endpt *e) 
{ 
	TD *t; 
	Block *b; 
	Ctlr *ub; 
	QH *qh; 
	int vf; 
 
2001/0504    
	t = alloctde(e, TokIN, e->maxpkt); 
2001/0527    
	b = allocb(e->maxpkt); 
1999/1005    
	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; 
	} 
2001/1124    
	queuetd(ub, qh, t, vf, "qrcv"); 
1999/1005    
	return qh; 
} 
 
2001/0527    
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; 
} 
 
1999/1005    
/* 
 * return smallest power of 2 >= n 
 */ 
static int 
flog2(int n) 
{ 
	int i; 
 
	for(i=0; (1<<i)<n; i++) 
		; 
	return i; 
} 
 
2001/0916    
static int 
usbsched(	Ctlr *ub, int pollms, ulong load) 
1999/1005    
{ 
2001/1006    
	int i, d, q; 
2001/0916    
	ulong best, worst; 
1999/1005    
 
2001/0916    
	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; 
1999/1005    
		} 
2001/0916    
		if (worst < best){ 
			best = worst; 
			q = d; 
1999/1005    
		} 
	} 
	return q; 
} 
 
static int 
schedendpt(Endpt *e) 
{ 
	Ctlr *ub; 
2001/1006    
	TD *td; 
	uchar *bp; 
	int i, id, ix, size, frnum; 
1999/1005    
 
2001/0916    
	if(!e->iso || e->sched >= 0) 
2001/0626    
		return 0; 
	ub = &ubus; 
2001/0918    
 
2001/1016    
	if (e->active){ 
2001/0918    
		return -1; 
	} 
2001/1007    
	e->off = 0; 
2001/0916    
	e->sched = usbsched(ub, e->pollms, e->maxpkt); 
2001/1124    
	if(e->sched < 0) 
2001/0626    
		return -1; 
2001/0916    
 
2001/1006    
	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); 
2001/1010    
	e->bp0 = (uchar *)(((ulong)e->bpalloc + 0xf) & ~0xf); 
2001/1006    
	frnum = (IN(Frnum) + 1) & 0x3ff; 
	frnum = (frnum & ~(e->pollms - 1)) + e->sched; 
2001/1007    
	e->xtd = &e->td0[(frnum+8)&0x3ff];	/* Next td to finish */ 
	e->etd = nil; 
2001/1006    
	e->remain = 0; 
2001/1010    
	e->nbytes = 0; 
2001/1006    
	td = e->td0; 
2001/0916    
	for(i = e->sched; i < NFRAME; i += e->pollms){ 
2001/1010    
		bp = e->bp0 + e->maxpkt*i/e->pollms; 
		td->buffer = PADDR(bp); 
2001/0916    
		td->ep = e; 
2001/1006    
		td->next = &td[1]; 
		ub->frameld[i] += e->maxpkt; 
		td++; 
	} 
	td[-1].next = e->td0; 
	for(i = e->sched; i < NFRAME; i += e->pollms){ 
2001/1008    
		ix = (frnum+i) & 0x3ff; 
2001/1006    
		td = &e->td0[ix]; 
 
		id = (e->x<<7)|(e->dev->x&0x7F); 
		if (e->mode == OREAD) 
2001/0918    
			/* enable receive on this entry */ 
			td->dev = ((e->maxpkt-1)<<21) | ((id&0x7FF)<<8) | TokIN; 
2001/1006    
		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; 
2001/0918    
		} 
2001/1008    
		td->status = ErrLimit1 | Active | IsoSelect | IOC; 
2001/1006    
		td->link = ub->frames[ix]; 
2001/1008    
		td->flags |= IsoClean; 
2001/1006    
		ub->frames[ix] = PADDR(td); 
2001/0916    
	} 
2001/0626    
	return 0; 
} 
 
static void 
unschedendpt(Endpt *e) 
{ 
	Ctlr *ub; 
2001/1006    
	TD *td; 
	ulong *addr; 
2001/0626    
	int q; 
 
	ub = &ubus; 
2001/1006    
	if(!e->iso || e->sched < 0) 
2001/0626    
		return; 
2001/0918    
 
2001/1006    
	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; 
2001/0916    
		ub->frameld[q] -= e->maxpkt; 
	} 
2001/1006    
	free(e->tdalloc); 
	free(e->bpalloc); 
	e->tdalloc = nil; 
	e->bpalloc = nil; 
	e->etd = nil; 
	e->td0 = nil; 
2001/0916    
	e->sched = -1; 
2001/0626    
} 
 
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->sched = -1; 
	e->maxpkt = 8; 
	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); 
2001/1006    
		eptdeactivate(e); 
2001/0626    
		unschedendpt(e); 
		e->dev->ep[e->x] = nil; 
		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 
2001/1006    
cleaniso(Endpt *e, int frnum) 
{ 
	TD *td; 
2001/1010    
	int id, n, i; 
	uchar *bp; 
2001/1006    
 
2001/1007    
	td = e->xtd; 
	if (td->status & Active) 
		return; 
2001/1006    
	id = (e->x<<7)|(e->dev->x&0x7F); 
2001/1010    
	do { 
2001/1006    
		if (td->status & AnyError) 
			iprint("usbisoerror 0x%lux\n", td->status); 
2001/1012    
		n = (td->status + 1) & 0x3ff; 
		e->nbytes += n; 
2001/1010    
		if ((td->flags & IsoClean) == 0) 
			e->nblocks++; 
2001/1007    
		if (e->mode == OREAD){ 
2001/1012    
			e->buffered += n; 
2001/1011    
			e->poffset += (td->status + 1) & 0x3ff; 
			td->offset = e->poffset; 
2001/1007    
			td->dev = ((e->maxpkt -1)<<21) | ((id&0x7FF)<<8) | TokIN; 
2001/1011    
			e->toffset = td->offset; 
2001/1007    
		}else{ 
2001/1012    
			if ((td->flags & IsoClean) == 0){ 
				e->buffered -= n; 
				if (e->buffered < 0){ 
					iprint("e->buffered %d?\n", e->buffered); 
					e->buffered = 0; 
				} 
			} 
2001/1011    
			e->toffset = td->offset; 
2001/1007    
			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; 
2001/1011    
			td->offset = e->poffset; 
			e->poffset += n; 
2001/1007    
		} 
		td = td->next; 
		if (e->xtd == td){ 
2001/1008    
			XPRINT("@"); 
2001/1007    
			break; 
		} 
	} while ((td->status & Active) == 0); 
2001/1010    
	e->time = todget(nil); 
2001/1007    
	e->xtd = td; 
2001/1012    
	for (n = 2; n < 4; n++){ 
2001/1010    
		i = ((frnum + n)&0x3ff); 
		td = e->td0 + i; 
		bp = e->bp0 + e->maxpkt*i/e->pollms; 
2001/1007    
		if (td->status & Active) 
			continue; 
 
2001/1012    
		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); 
2001/1007    
				td->flags |= IsoClean; 
2001/1012    
			} 
		} else { 
			/* Unread bytes are now lost */ 
			e->buffered -= (td->status + 1) & 0x3ff; 
2001/1006    
		} 
2001/1008    
		td->status = ErrLimit1 | Active | IsoSelect | IOC; 
2001/1006    
	} 
2001/1008    
	wakeup(&e->wr); 
2001/1006    
} 
 
static void 
2001/1124    
usbinterrupt(Ureg*, void *a) 
2001/0626    
{ 
	Ctlr *ub; 
	Endpt *e; 
2001/1006    
	int s, frnum; 
2001/0626    
	QH *q; 
 
	ub = a; 
	s = IN(Status); 
2001/1010    
 
2001/1124    
	frameptr = inl(ub->io+Flbaseadd); 
	framenumber = IN(Frnum) & 0x3ff; 
2001/0626    
	OUT(Status, s); 
	if ((s & 0x1f) == 0) 
		return; 
2001/1124    
	usbints++; 
2001/1006    
	frnum = IN(Frnum) & 0x3ff; 
2001/0626    
	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); 
2001/0916    
	for(e = activends.f; e != nil; e = e->activef){ 
		if(!e->iso && e->epq != nil) { 
2001/0626    
			XPRINT("cleanq(e->epq, 0, 0)\n"); 
			cleanq(e->epq, 0, 0); 
		} 
2001/1006    
		if(e->iso) { 
			XPRINT("cleaniso(e)\n"); 
			cleaniso(e, frnum); 
2001/0916    
		} 
	} 
2001/0626    
	iunlock(&activends); 
2001/1010    
	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); 
	} 
2001/0626    
} 
 
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; 
2001/0916    
			e->iso = 0; 
2001/0626    
			e->sched = -1; 
			usbdev[i] = d; 
			break; 
		} 
	poperror(); 
	qunlock(&usbstate); 
	return d; 
} 
 
static void 
usbreset(void) 
{ 
	Pcidev *cfg; 
2001/1006    
	int i; 
2001/0626    
	ulong port; 
	TD *t; 
	Ctlr *ub; 
	ISAConf isa; 
 
	if(isaconfig("usb", 0, &isa) == 0) { 
		XPRINT("usb not in plan9.ini\n"); 
2001/0726    
		return; 
2001/0626    
	} 
	ub = &ubus; 
2001/1010    
	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; 
2001/1124    
		if(cfg->did == 0x2482 || cfg->did == 0x2487) 
			continue; 
2001/1122    
// 		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; 
// 		} 
2001/1010    
		if((cfg->mem[4].bar & ~0x0F) != 0) 
			break; 
2001/0626    
	} 
2001/1010    
	if(cfg == nil) { 
		DPRINT("No USB device found\n"); 
2001/0626    
		return; 
	} 
2001/1010    
	port = cfg->mem[4].bar & ~0x0F; 
2001/0626    
 
	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); 
2001/1010    
if(0){ 
2001/0626    
		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; 
2001/1124    
	intrenable(cfg->intl, usbinterrupt, ub, cfg->tbdf, "usb"); 
2001/0626    
 
	ub->io = port; 
2001/1019    
	ub->tdpool = (TD*)(((ulong)xalloc(128*sizeof(TD) + 0x10) + 0xf) & ~0xf); 
2001/1006    
	for(i=128; --i>=0;){ 
2001/0626    
		ub->tdpool[i].next = ub->freetd; 
		ub->freetd = &ub->tdpool[i]; 
	} 
2001/1019    
	ub->qhpool = (QH*)(((ulong)xalloc(32*sizeof(QH) + 0x10) + 0xf) & ~0xf); 
2001/0626    
	for(i=32; --i>=0;){ 
		ub->qhpool[i].next = ub->freeqh; 
		ub->freeqh = &ub->qhpool[i]; 
	} 
 
	/* 
2001/0916    
	 * the last entries of the periodic (interrupt & isochronous) scheduling TD entries 
	 * point to the control queue and the bandwidth sop for bulk traffic. 
2001/0626    
	 * 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); 
2001/1010    
 
	ub->ctlq->head = PADDR(ub->bulkq) | IsQH; 
2001/0626    
	ub->bulkq->head = PADDR(ub->recvq) | IsQH; 
2001/1010    
	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); 
 
2001/0626    
	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 */ 
2001/1019    
	ub->frames = (ulong*)(((ulong)xalloc(2*FRAMESIZE) + FRAMEMASK) & ~FRAMEMASK); 
	ub->frameld = xallocz(FRAMESIZE, 1); 
2001/0916    
 
2001/1006    
	for (i = 0; i < NFRAME; i++) 
		ub->frames[i] = PADDR(ub->ctlq) | IsQH; 
2001/0626    
 
	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; 
2001/1013    
	vlong len; 
2001/0626    
 
	/* 
	 * Top level directory contains the name of the device. 
	 */ 
2001/1016    
	if(QID(c->qid) == Qtopdir){ 
2001/0626    
		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. 
	 */ 
2001/1013    
	len = 0; 
2001/0626    
	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); 
2001/1013    
	len = e->buffered; 
	devdir(c, q, up->genbuf, len, eve, e->mode==OREAD? 0444: e->mode==OWRITE? 0222: 0666, dp); 
2001/0626    
	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(); 
2001/1016    
		XPRINT("usbopen, new dev 0x%p\n", d); 
2001/0626    
		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; 
	} 
 
2001/1016    
	if(QID(c->qid) < Q3rd){ 
		XPRINT("usbopen, devopen < Q3rd\n"); 
2001/0626    
		return devopen(c, omode, nil, 0, usbgen); 
2001/1016    
	} 
2001/0626    
 
	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); 
2001/1016    
		XPRINT("usbopen, Qctl 0x%p\n", d); 
2001/0626    
		break; 
 
	default: 
		d = usbdevice(c); 
		s = QID(c->qid) - Qep0; 
2001/1016    
		XPRINT("usbopen, default 0x%p, %d\n", d, s); 
2001/0626    
		if(s >= 0 && s < nelem(d->ep)){ 
			Endpt *e; 
			if((e = d->ep[s]) == nil) { 
				XPRINT("usbopen failed (endpoint)\n"); 
				error(Enodev); 
			} 
2001/1016    
			XPRINT("usbopen: dev 0x%p, ept 0x%p\n", d, e); 
2001/1010    
			if(schedendpt(e) < 0){ 
2001/1016    
				if (e->active) 
					error("can't schedule USB endpoint, active"); 
2001/1010    
				else 
					error("can't schedule USB endpoint"); 
			} 
2001/1017    
			e->foffset = 0; 
			e->toffset = 0; 
			e->poffset = 0; 
			e->buffered = 0; 
2001/0626    
			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 
usbclose(Chan *c) 
{ 
	Udev *d; 
 
2001/1016    
	if(c->qid.type == QTDIR || QID(c->qid) < Q3rd) 
2001/0626    
		return; 
	qlock(&usbstate); 
	d = usbdevice(c); 
	if(QID(c->qid) == Qctl) 
		d->busy = 0; 
2001/1016    
	if(c->flag & COPEN){ 
		XPRINT("usbclose: freedev 0x%p\n", d); 
2001/0626    
		freedev(d); 
2001/1016    
	} 
2001/0626    
	qunlock(&usbstate); 
} 
 
static int 
eptinput(void *arg) 
{ 
	Endpt *e; 
 
	e = arg; 
	return e->eof || e->err || qcanread(e->rq); 
} 
 
2001/1006    
static int 
2001/1007    
isoready(void *arg) 
2001/1006    
{ 
	Endpt *e; 
 
	e = arg; 
2001/1008    
	return e->etd == nil || (e->etd != e->xtd && (e->etd->status & Active) == 0); 
2001/1006    
} 
 
2001/0626    
static long 
2001/1011    
isoio(Endpt *e, void *a, long n, ulong offset, int w) 
2001/0626    
{ 
2001/1008    
	int i, frnum; 
2001/1011    
	volatile int isolock; 
2001/1010    
	uchar *p, *q, *bp; 
2001/1007    
	Ctlr *ub; 
2001/1008    
	TD *td; 
2001/0626    
 
2001/1010    
	qlock(&e->rlock); 
2001/1011    
	isolock = 0; 
2001/0626    
	if(waserror()){ 
2001/1011    
		if (isolock){ 
			isolock = 0; 
			iunlock(&activends); 
		} 
2001/1010    
		qunlock(&e->rlock); 
		eptcancel(e); 
2001/0626    
		nexterror(); 
	} 
2001/1010    
	p = a; 
2001/1011    
	ub = &ubus; 
	if (offset != 0 && offset != e->foffset){ 
2001/1017    
		iprint("offset %lud, foffset %lud\n", offset, e->foffset); 
2001/1011    
		/* 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; 
	} 
2001/1008    
	do { 
2001/1011    
		if (isolock == 0){ 
			ilock(&activends); 
			isolock = 1; 
		} 
2001/1008    
		td = e->etd; 
		if (td == nil || e->off == 0){ 
			if (td == nil){ 
				XPRINT("0"); 
2001/1012    
				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; 
2001/1008    
				e->off = 0; 
2001/1012    
			}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; 
				} 
2001/1008    
			} 
			if (w) 
				e->psize = ((td->dev >> 21) + 1) & 0x7ff; 
			else 
				e->psize = (e->etd->status + 1) & 0x7ff; 
2001/1007    
			if (e->psize > e->maxpkt) 
				panic("packet size > maximum"); 
2001/1008    
		} 
2001/1011    
		isolock = 0; 
		iunlock(&activends); 
2001/1008    
		td->flags &= ~IsoClean; 
2001/1010    
		bp = e->bp0 + (td - e->td0) * e->maxpkt / e->pollms; 
		q = bp + e->off; 
2001/1008    
		if((i = n) >= e->psize) 
			i = e->psize; 
2001/1012    
		if (w){ 
2001/1008    
			memmove(q, p, i); 
2001/1012    
			e->buffered += i; 
		}else{ 
2001/1008    
			memmove(p, q, i); 
2001/1012    
			e->buffered -= i; 
			if (e->buffered < 0){ 
				iprint("e->buffered %d?\n", e->buffered); 
				e->buffered = 0; 
			} 
		} 
2001/1008    
		p += i; 
		n -= i; 
		e->off += i; 
		e->psize -= i; 
		if (e->psize){ 
			if (n != 0) 
				panic("usb iso: can't happen"); 
			break; 
		} 
2001/1019    
		if(w) 
			td->offset = offset + (p-(uchar*)a) - (((td->dev >> 21) + 1) & 0x7ff); 
2001/1008    
		td->status = ErrLimit3 | Active | IsoSelect | IOC; 
		e->etd = td->next; 
		e->off = 0; 
	} while(n > 0); 
2001/1011    
	n = p-(uchar*)a; 
	e->foffset += n; 
2001/1008    
	poperror(); 
2001/1011    
	if (isolock) 
		iunlock(&activends); 
2001/1010    
	qunlock(&e->rlock); 
2001/1011    
	return n; 
2001/1008    
} 
 
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(); 
	} 
2001/1010    
	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()){ 
2001/0626    
			freeb(b); 
2001/1010    
			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); 
2001/0626    
	poperror(); 
	qunlock(&e->rlock); 
	return p-(uchar*)a; 
} 
 
2001/1012    
int 
epstatus(char *s, int n, Endpt *e, int i) 
{ 
	int l; 
 
	l = 0; 
2001/1016    
	l += snprint(s+l, n-l, "%2d %#6.6lux %10lud bytes %10lud blocks\n", 
2001/1012    
		i, e->csp, e->nbytes, e->nblocks); 
2001/1017    
	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'; 
2001/1012    
	} 
	return l; 
} 
 
2001/0626    
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++) 
2001/1009    
			if((e = d->ep[i]) != nil){	/* TO DO: freeze e */ 
2001/1012    
				l += epstatus(s+l, READSTR-l, e, i); 
2001/1009    
			} 
2001/0626    
		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 */ 
2001/1010    
		if (e->iso) 
2001/1011    
			n=isoio(e, a, n, (ulong)offset, 0); 
2001/1010    
		else 
			n=readusb(e, a, n); 
2001/0626    
		break; 
	} 
	return n; 
} 
 
static int 
qisempty(void *arg) 
{ 
	return ((QH*)arg)->entries & Terminate; 
} 
 
static long 
writeusb(Endpt *e, void *a, long n, int tok) 
{ 
2001/1008    
	int i; 
2001/0626    
	Block *b; 
2001/1008    
	uchar *p; 
2001/0626    
	QH *qh; 
 
	qlock(&e->wlock); 
	if(waserror()){ 
		qunlock(&e->wlock); 
		eptcancel(e); 
		nexterror(); 
	} 
2001/1010    
	p = a; 
	do { 
		int j; 
2001/0916    
 
2001/1010    
		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) { 
2001/1124    
XPRINT("qh %s: q=%p first=%p last=%p entries=%.8lux\n", 
 "writeusb sleep", qh, qh->first, qh->last, qh->entries); 
2001/1010    
			XPRINT("writeusb: sleep %lux\n", &e->wr); 
			sleep(&e->wr, qisempty, qh); 
			XPRINT("writeusb: awake\n"); 
		} 
	} while(n > 0); 
2001/0626    
	poperror(); 
	qunlock(&e->wlock); 
	return p-(uchar*)a; 
} 
 
long 
2001/1010    
usbwrite(Chan *c, void *a, long n, vlong offset) 
2001/0626    
{ 
	Udev *d; 
	Endpt *e; 
2001/1120    
	Cmdbuf *cb; 
	Cmdtab *ct; 
	int id, nw, t, i; 
2001/1124    
	char cmd[50]; 
2001/0626    
 
	if(c->qid.type == QTDIR) 
		error(Egreg); 
	t = QID(c->qid); 
	if(t == Qbusctl){ 
2001/1120    
		cb = parsecmd(a, n); 
		if(waserror()){ 
			free(cb); 
			nexterror(); 
		} 
 
		ct = lookupcmd(cb, usbbusctlmsg, nelem(usbbusctlmsg)); 
2001/1124    
		id = strtol(cb->f[1], nil, 0); 
2001/0626    
		if(id != 1 && id != 2) 
2001/1120    
			cmderror(cb, "usb port number not 1 or 2 in"); 
		switch(ct->index){ 
		case BCMdisable: 
2001/0626    
			portenable(id, 0); 
2001/1120    
			break; 
		case BCMenable: 
			portenable(id, 1); 
			break; 
		case BCMreset: 
			portreset(id); 
			break; 
		} 
	 
		poperror(); 
		free(cb); 
2001/0626    
		return n; 
	} 
	d = usbdevice(c); 
	t = QID(c->qid); 
	switch(t){ 
	case Qctl: 
2001/1120    
		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); 
2001/0626    
			/* class config# csp ( == class subclass proto) */ 
			if (i < 0 || i >= nelem(d->ep) 
			 || d->npt > nelem(d->ep) || i >= d->npt) 
2001/1120    
				cmderror(cb, Ebadusbmsg); 
			if (i == 0) 
				d->csp = strtoul(cb->f[3], nil, 0); 
2001/0626    
			if(d->ep[i] == nil) 
				d->ep[i] = devendpt(d, i, 1); 
2001/1120    
			d->ep[i]->csp = strtoul(cb->f[3], nil, 0); 
			break; 
		case CMdata: 
			i = strtoul(cb->f[1], nil, 0); 
2001/0626    
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) 
2001/1120    
				error(Ebadusbmsg); 
2001/0626    
			e = d->ep[i]; 
2001/1120    
			e->data01 = strtoul(cb->f[2], nil, 0) != 0; 
			break; 
		case CMmaxpkt: 
			i = strtoul(cb->f[1], nil, 0); 
2001/0626    
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) 
2001/1120    
				error(Ebadusbmsg); 
2001/0626    
			e = d->ep[i]; 
2001/1120    
			e->maxpkt = strtoul(cb->f[2], nil, 0); 
2001/0626    
			if(e->maxpkt > 1500) 
				e->maxpkt = 1500; 
2001/1120    
			break; 
		case CMdebug: 
			i = strtoul(cb->f[1], nil, 0); 
2001/0626    
			if(i < -1 || i >= nelem(d->ep) || d->ep[i] == nil) 
2001/1120    
				error(Ebadusbmsg); 
2001/0626    
			if (i == -1) 
				debug = 0; 
			else { 
				debug = 1; 
				e = d->ep[i]; 
2001/1120    
				e->debug = strtoul(cb->f[2], nil, 0); 
2001/0626    
			} 
2001/1120    
			break; 
		case CMunstall: 
			i = strtoul(cb->f[1], nil, 0); 
2001/0626    
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) 
2001/1120    
				error(Ebadusbmsg); 
2001/0626    
			e = d->ep[i]; 
			e->err = nil; 
2001/1120    
			break; 
		case CMep: 
2001/0916    
			/* ep n `bulk' mode maxpkt nbuf     OR 
2001/1006    
			 * ep n period mode samplesize KHz 
2001/0916    
			 */ 
2001/1120    
			i = strtoul(cb->f[1], nil, 0); 
2001/0626    
			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); 
2001/0918    
			qlock(&usbstate); 
2001/0626    
			if(waserror()){ 
				freept(e); 
2001/0918    
				qunlock(&usbstate); 
2001/0626    
				nexterror(); 
			} 
2001/1016    
			if (e->active) 
2001/0918    
				error(Eperm); 
2001/1120    
			if(strcmp(cb->f[2], "bulk") == 0){ 
2001/0626    
				Ctlr *ub; 
 
2001/0918    
				e->iso = 0; 
2001/0916    
				/* ep n `bulk' mode maxpkt nbuf */ 
2001/0626    
				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); 
2001/1120    
				e->mode = strcmp(cb->f[3],"r") == 0? OREAD : 
					  	strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR; 
				i = strtoul(cb->f[4], nil, 0); 
2001/0916    
				if(i < 8 || i > 1023) 
					i = 8; 
				e->maxpkt = i; 
2001/1120    
				i = strtoul(cb->f[5], nil, 0); 
2001/0916    
				if(i >= 1 && i <= 32) 
					e->nbuf = i; 
			} else { 
				/* ep n period mode samplesize KHz */ 
2001/1120    
				i = strtoul(cb->f[2], nil, 0); 
2001/0916    
				if(i > 0 && i <= 1000){ 
2001/0626    
					e->pollms = i; 
2001/0916    
				}else { 
2001/0626    
					XPRINT("field 4: 0 <= %d <= 1000\n", i); 
					error(Ebadarg); 
				} 
2001/1120    
				e->mode = strcmp(cb->f[3],"r") == 0? OREAD : 
					  	strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR; 
				i = strtoul(cb->f[4], nil, 0); 
2001/0916    
				if(i >= 1 && i <= 8){ 
					e->samplesz = i; 
				}else { 
					XPRINT("field 4: 0 < %d <= 8\n", i); 
					error(Ebadarg); 
				} 
2001/1120    
				i = strtoul(cb->f[5], nil, 0); 
2001/0916    
				if(i >= 1 && i <= 100000){ 
					/* Hz */ 
					e->hz = i; 
2001/0918    
		//			e->remain = 999/e->pollms; 
					e->remain = 0; 
2001/0916    
				}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; 
2001/0626    
			} 
			poperror(); 
2001/0918    
			qunlock(&usbstate); 
2001/0626    
		} 
2001/1120    
	 
		poperror(); 
		free(cb); 
2001/0626    
		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 */ 
		} 
2001/1010    
		if (e->iso) 
2001/1011    
			n = isoio(e, a, n, (ulong)offset, 1); 
2001/1010    
		else 
			n = writeusb(e, a, n, TokOUT); 
2001/0626    
		break; 
	} 
	return n; 
} 
 
Dev usbdevtab = { 
	'U', 
	"usb", 
 
	usbreset, 
	usbinit, 
	usbattach, 
	usbwalk, 
	usbstat, 
	usbopen, 
	devcreate, 
	usbclose, 
	usbread, 
	devbread, 
	usbwrite, 
	devbwrite, 
	devremove, 
	devwstat, 
}; 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)