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

1999/1006/pc/devusb.c (diff list | history)

1999/1005/sys/src/9/pc/devusb.c:1123,12231999/1006/sys/src/9/pc/devusb.c:1123,1128 (short | long)
1999/1005    
	iunlock(&activends); 
} 
 
static void 
resetusbctlr(void) 
{ 
	Ctlr *ub; 
	Pcidev *cfg; 
	int i; 
	ulong port; 
	QTree *qt; 
	TD *t; 
                 
	ub = &ubus; 
	memset(&cfg, 0, sizeof(cfg)); 
	cfg = pcimatch(0, 0x8086, 0x7112);	/* Intel chipset PIIX 4*/ 
	if(cfg == nil) { 
//		cfg = pcimatch(0, 0x8086, 0x7112);	/* Intel chipset PIIX 3*/ 
//		if(cfg == nil) { 
			cfg = pcimatch(0, 0x1106, 0x0586);	/* Via chipset */ 
			if(cfg == nil) { 
				DPRINT("No USB device found\n"); 
				return; 
			} 
//		} 
	} 
	port = cfg->mem[4].bar & ~0x0F; 
	if (port == 0) { 
		print("usb: failed to map registers\n"); 
		return; 
	} 
                 
	print("USB: %x/%x port 0x%lux size 0x%x irq %d\n", 
		cfg->vid, cfg->did, port, cfg->mem[4].size, cfg->intl); 
                 
	i = inb(port+SOFMod); 
	if(0){ 
		OUT(Cmd, 4);	/* global reset */ 
		delay(15); 
		OUT(Cmd, 0);	/* end reset */ 
		delay(4); 
	} 
	outb(port+SOFMod, i); 
	// Interrupt handler 
	intrenable(cfg->intl, interrupt, ub, cfg->tbdf, "usb"); 
                 
	ub->io = port; 
	ub->tdpool = xspanalloc(128*sizeof(TD), 16, 0); 
	for(i=128; --i>=0;){ 
		ub->tdpool[i].next = ub->freetd; 
		ub->freetd = &ub->tdpool[i]; 
	} 
	ub->qhpool = xspanalloc(32*sizeof(QH), 16, 0); 
	for(i=32; --i>=0;){ 
		ub->qhpool[i].next = ub->freeqh; 
		ub->freeqh = &ub->qhpool[i]; 
	} 
                 
	/* 
	 * the root of the periodic (interrupt & isochronous) scheduling tree 
	 * points to the control queue and the bandwidth sop for bulk traffic. 
	 * this is looped following the instructions in PIIX4 errata 29773804.pdf: 
	 * a QH links to a looped but inactive TD as its sole entry, 
	 * with its head entry leading on to the bulk traffic, the last QH of which 
	 * links back to the empty QH. 
	 */ 
	ub->bulkq = allocqh(ub); 
	t = alloctd(ub);	/* inactive TD, looped */ 
	t->link = PADDR(t); 
	ub->bwsop = allocqh(ub); 
	ub->bwsop->head = PADDR(ub->bulkq) | IsQH; 
	ub->bwsop->entries = PADDR(t); 
	ub->ctlq = allocqh(ub); 
	ub->ctlq->head = PADDR(ub->bwsop) | IsQH; 
//	ub->bulkq->head = PADDR(ub->bwsop) | IsQH;	/* loop back */ 
	print("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n", 
		IN(Cmd), IN(Status), IN(Usbintr), inb(port+Frnum)); 
	print("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n", 
		IN(Flbaseadd), inb(port+SOFMod), IN(Portsc0), IN(Portsc1)); 
	OUT(Cmd, 0);	/* stop */ 
	ub->frames = xspanalloc(FRAMESIZE, FRAMESIZE, 0); 
	qt = mkqhtree(ub->frames, NFRAME, 32); 
	if(qt == nil){ 
		print("usb: can't allocate scheduling tree\n"); 
		ub->io = 0; 
		return; 
	} 
	qt->root->head = PADDR(ub->ctlq) | IsQH; 
	ub->tree = qt; 
	print("usb tree: nel=%d depth=%d\n", qt->nel, qt->depth); 
                 
	outl(port+Flbaseadd, PADDR(ub->frames)); 
	OUT(Frnum, 0); 
	OUT(Usbintr, 0xF);	/* enable all interrupts */ 
	print("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(port+SOFMod)); 
	print("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1)); 
} 
                 
enum 
{ 
	Qtopdir = 0, 
1999/1005/sys/src/9/pc/devusb.c:1317,13231999/1006/sys/src/9/pc/devusb.c:1222,1317
1999/1005    
static void 
usbreset(void) 
{ 
	resetusbctlr(); 
1999/1006    
	Ctlr *ub; 
	Pcidev *cfg; 
	int i; 
	ulong port; 
	QTree *qt; 
	TD *t; 
 
	ub = &ubus; 
	memset(&cfg, 0, sizeof(cfg)); 
	cfg = pcimatch(0, 0x8086, 0x7112);	/* Intel chipset PIIX 4*/ 
	if(cfg == nil) { 
//		cfg = pcimatch(0, 0x8086, 0x7112);	/* Intel chipset PIIX 3*/ 
//		if(cfg == nil) { 
			cfg = pcimatch(0, 0x1106, 0x0586);	/* Via chipset */ 
			if(cfg == nil) { 
				DPRINT("No USB device found\n"); 
				return; 
			} 
//		} 
	} 
	port = cfg->mem[4].bar & ~0x0F; 
	if (port == 0) { 
		print("usb: failed to map registers\n"); 
		return; 
	} 
 
	print("USB: %x/%x port 0x%lux size 0x%x irq %d\n", 
		cfg->vid, cfg->did, port, cfg->mem[4].size, cfg->intl); 
 
	i = inb(port+SOFMod); 
	if(0){ 
		OUT(Cmd, 4);	/* global reset */ 
		delay(15); 
		OUT(Cmd, 0);	/* end reset */ 
		delay(4); 
	} 
	outb(port+SOFMod, i); 
	// Interrupt handler 
	intrenable(cfg->intl, interrupt, ub, cfg->tbdf, "usb"); 
 
	ub->io = port; 
	ub->tdpool = xspanalloc(128*sizeof(TD), 16, 0); 
	for(i=128; --i>=0;){ 
		ub->tdpool[i].next = ub->freetd; 
		ub->freetd = &ub->tdpool[i]; 
	} 
	ub->qhpool = xspanalloc(32*sizeof(QH), 16, 0); 
	for(i=32; --i>=0;){ 
		ub->qhpool[i].next = ub->freeqh; 
		ub->freeqh = &ub->qhpool[i]; 
	} 
 
	/* 
	 * the root of the periodic (interrupt & isochronous) scheduling tree 
	 * points to the control queue and the bandwidth sop for bulk traffic. 
	 * this is looped following the instructions in PIIX4 errata 29773804.pdf: 
	 * a QH links to a looped but inactive TD as its sole entry, 
	 * with its head entry leading on to the bulk traffic, the last QH of which 
	 * links back to the empty QH. 
	 */ 
	ub->bulkq = allocqh(ub); 
	t = alloctd(ub);	/* inactive TD, looped */ 
	t->link = PADDR(t); 
	ub->bwsop = allocqh(ub); 
	ub->bwsop->head = PADDR(ub->bulkq) | IsQH; 
	ub->bwsop->entries = PADDR(t); 
	ub->ctlq = allocqh(ub); 
	ub->ctlq->head = PADDR(ub->bwsop) | IsQH; 
//	ub->bulkq->head = PADDR(ub->bwsop) | IsQH;	/* loop back */ 
	print("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n", 
		IN(Cmd), IN(Status), IN(Usbintr), inb(port+Frnum)); 
	print("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n", 
		IN(Flbaseadd), inb(port+SOFMod), IN(Portsc0), IN(Portsc1)); 
	OUT(Cmd, 0);	/* stop */ 
	ub->frames = xspanalloc(FRAMESIZE, FRAMESIZE, 0); 
	qt = mkqhtree(ub->frames, NFRAME, 32); 
	if(qt == nil){ 
		print("usb: can't allocate scheduling tree\n"); 
		ub->io = 0; 
		return; 
	} 
	qt->root->head = PADDR(ub->ctlq) | IsQH; 
	ub->tree = qt; 
	print("usb tree: nel=%d depth=%d\n", qt->nel, qt->depth); 
 
	outl(port+Flbaseadd, PADDR(ub->frames)); 
	OUT(Frnum, 0); 
	OUT(Usbintr, 0xF);	/* enable all interrupts */ 
	print("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(port+SOFMod)); 
	print("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1)); 
1999/1005    
} 
 
void 
1999/1005/sys/src/9/pc/devusb.c:1851,18581999/1006/sys/src/9/pc/devusb.c:1845,1854
1999/1005    
		}else if(nf == 6 && strcmp(fields[0], "ep") == 0){ 
			/* ep n maxpkt mode poll nbuf */ 
			i = strtoul(fields[1], nil, 0); 
			if(i < 0 || i >= nelem(d->ep)) 
1999/1006    
			if(i < 0 || i >= nelem(d->ep)) { 
				pprint("field 1: 0 <= %d < %d\n", i, nelem(d->ep)); 
1999/1005    
				error(Ebadarg); 
1999/1006    
			} 
1999/1005    
			if(d->ep[i] != nil) 
				error(Einuse); 
			e = devendpt(d, i, 1); 
1999/1005/sys/src/9/pc/devusb.c:1872,18861999/1006/sys/src/9/pc/devusb.c:1868,1886
1999/1005    
				i = strtoul(fields[4], nil, 0); 
				if(i > 0 && i <= 1000) 
					e->pollms = i; 
				else 
1999/1006    
				else { 
					pprint("field 4: 0 <= %d <= 1000\n", i); 
1999/1005    
					error(Ebadarg); 
1999/1006    
				} 
1999/1005    
			} 
			i = strtoul(fields[5], nil, 0); 
			if(i >= 1 && i <= 32) 
				e->nbuf = i; 
			poperror(); 
		}else 
1999/1006    
		}else { 
			pprint("command %s, fields %d\n", fields[0], nf); 
1999/1005    
			error(Ebadarg); 
1999/1006    
		} 
1999/1005    
		return n; 
 
	case Qsetup:	/* SETUP endpoint 0 */ 
1999/1006/sys/src/9/pc/devusb.c:369,3771999/1007/sys/src/9/pc/devusb.c:369,377 (short | long)
1999/1005    
		if(t->status & LowSpeed) 
			*s++ = 'L'; 
		*s = 0; 
		print("td %8.8lux: l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n", 
1999/1007    
		XPRINT("td %8.8lux: l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n", 
1999/1005    
			t, t->link, t->status, t->dev, t->buffer, t->bp?(ulong)t->bp->rp:0, t->flags); 
		print("\ts=%s,ep=%ld,d=%ld,D=%ld\n", buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1); 
1999/1007    
		XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n", buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1); 
1999/1005    
		if(t->bp) 
			dumpdata(t->bp, n); 
		if(!follow || t->link & Terminate || t->link & IsQH) 
1999/1006/sys/src/9/pc/devusb.c:401,4441999/1007/sys/src/9/pc/devusb.c:401,406
1999/1005    
	return t; 
} 
 
static TD * 
alloctdring(int nbuf, Endpt *e, int pid, int maxn) 
{ 
	TD *t, *lt, *ft; 
	int id, i; 
	ulong status, dev; 
                 
	if(!e->iso) 
		nbuf = (nbuf+1)&~1; 
	id = (e->x<<7)|(e->dev->x&0x7F); 
	dev = ((maxn-1)<<21) | ((id&0x7FF)<<8) | pid; 
	status = 0; 
	if(e->dev->ls) 
		status |= LowSpeed; 
	ft = lt = nil; 
	for(i=0; i<nbuf; i++){ 
		t = alloctd(&ubus); 
		t->ep = e; 
		t->status = status; 
		t->dev =  dev; 
		if(!e->iso) 
			dev ^= IsDATA1; 
		if(pid == TokIN){ 
			t->bp = allocb(maxn); 
			t->buffer = PADDR(t->bp->wp); 
		} 
		if(ft != nil){ 
			lt->next = t; 
			lt->link = PADDR(t); 
		}else 
			ft = t; 
		lt = t; 
	} 
	if(lt != nil) 
		lt->link = PADDR(ft);	/* loop to form ring */ 
	return ft; 
} 
                 
static QH * 
allocqh(Ctlr *ub) 
{ 
1999/1006/sys/src/9/pc/devusb.c:475,4871999/1007/sys/src/9/pc/devusb.c:437,449
1999/1005    
 
	q0 = q; 
	for(i = 0; q != nil && i < 10; i++){ 
		pprint("qh %8.8lux: %8.8lux %8.8lux\n", q, q->head, q->entries); 
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){ 
			pprint("head:"); 
1999/1007    
			XPRINT("head:"); 
1999/1005    
			dumptd(TFOL(q->head), 1); 
			break; 
		} 
1999/1006/sys/src/9/pc/devusb.c:525,5311999/1007/sys/src/9/pc/devusb.c:487,493
1999/1005    
	err = t->status & (AnyError&~NAKed); 
	/* TO DO: on t->status&AnyError, q->entries will not have advanced */ 
	if (err) 
		print("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); 
1999/1007    
		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){ 
1999/1006/sys/src/9/pc/devusb.c:624,6301999/1007/sys/src/9/pc/devusb.c:586,592
1999/1005    
			if(t->ep == e) 
				t->flags |= CancelTD; 
		iunlock(ub); 
		pprint("cancel:\n"); 
1999/1007    
		XPRINT("cancel:\n"); 
1999/1005    
		dumpqh(q); 
	} 
} 
1999/1006/sys/src/9/pc/devusb.c:805,8111999/1007/sys/src/9/pc/devusb.c:767,773
1999/1005    
				o |= 1; 
		} 
		if(leaf0+o >= n){ 
			pprint("leaf0=%d o=%d i=%d n=%d\n", leaf0, o, i, n); 
1999/1007    
			XPRINT("leaf0=%d o=%d i=%d n=%d\n", leaf0, o, i, n); 
1999/1005    
			break; 
		} 
		frame[i] = PADDR(&tree[leaf0+o]) | IsQH; 
1999/1006/sys/src/9/pc/devusb.c:828,8411999/1007/sys/src/9/pc/devusb.c:790,803
1999/1005    
	if(t < 0) 
		t = 32; 
	for(i=f; i<t; i++){ 
		pprint("F%.2d %8.8lux %8.8lux\n", i, frame[i], QFOL(frame[i])->head); 
1999/1007    
		XPRINT("F%.2d %8.8lux %8.8lux\n", i, frame[i], QFOL(frame[i])->head); 
1999/1005    
		for(p=frame[i]; (p & IsQH) && (p &Terminate) == 0; p = q->head){ 
			q = QFOL(p); 
			if(!(q >= tree && q < &tree[n])){ 
				pprint("Q: p=%8.8lux out of range\n", p); 
1999/1007    
				XPRINT("Q: p=%8.8lux out of range\n", p); 
1999/1005    
				break; 
			} 
			pprint("  -> %8.8lux h=%8.8lux e=%8.8lux\n", p, q->head, q->entries); 
1999/1007    
			XPRINT("  -> %8.8lux h=%8.8lux e=%8.8lux\n", p, q->head, q->entries); 
1999/1005    
		} 
	} 
} 
1999/1006/sys/src/9/pc/devusb.c:1104,11121999/1007/sys/src/9/pc/devusb.c:1066,1074
1999/1005    
	ub = a; 
	s = IN(Status); 
	if (s & 0x1a) { 
		print("usbint: #%x f%d\n", s, IN(Frnum)); 
		print("cmd #%x sofmod #%x\n", IN(Cmd), inb(ub->io+SOFMod)); 
		print("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1)); 
1999/1007    
		XPRINT("usbint: #%x f%d\n", s, IN(Frnum)); 
		XPRINT("cmd #%x sofmod #%x\n", IN(Cmd), inb(ub->io+SOFMod)); 
		XPRINT("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1)); 
1999/1005    
	} 
	OUT(Status, s); 
 
1999/1006/sys/src/9/pc/devusb.c:1570,15761999/1007/sys/src/9/pc/devusb.c:1532,1538
1999/1005    
	p = a; 
	do { 
		if(e->eof) { 
			pprint("e->eof\n"); 
1999/1007    
			XPRINT("e->eof\n"); 
1999/1005    
			break; 
		} 
		if(e->err) 
1999/1006/sys/src/9/pc/devusb.c:1583,15891999/1007/sys/src/9/pc/devusb.c:1545,1551
1999/1005    
			error(e->err); 
		b = qget(e->rq);	/* TO DO */ 
		if(b == nil) { 
			pprint("b == nil\n"); 
1999/1007    
			XPRINT("b == nil\n"); 
1999/1005    
			break; 
		} 
		if(waserror()){ 
1999/1006/sys/src/9/pc/devusb.c:1846,18521999/1007/sys/src/9/pc/devusb.c:1808,1814
1999/1005    
			/* ep n maxpkt mode poll nbuf */ 
			i = strtoul(fields[1], nil, 0); 
1999/1006    
			if(i < 0 || i >= nelem(d->ep)) { 
				pprint("field 1: 0 <= %d < %d\n", i, nelem(d->ep)); 
1999/1007    
				XPRINT("field 1: 0 <= %d < %d\n", i, nelem(d->ep)); 
1999/1005    
				error(Ebadarg); 
1999/1006    
			} 
1999/1005    
			if(d->ep[i] != nil) 
1999/1006/sys/src/9/pc/devusb.c:1869,18751999/1007/sys/src/9/pc/devusb.c:1831,1837
1999/1005    
				if(i > 0 && i <= 1000) 
					e->pollms = i; 
1999/1006    
				else { 
					pprint("field 4: 0 <= %d <= 1000\n", i); 
1999/1007    
					XPRINT("field 4: 0 <= %d <= 1000\n", i); 
1999/1005    
					error(Ebadarg); 
1999/1006    
				} 
1999/1005    
			} 
1999/1006/sys/src/9/pc/devusb.c:1878,18841999/1007/sys/src/9/pc/devusb.c:1840,1846
1999/1005    
				e->nbuf = i; 
			poperror(); 
1999/1006    
		}else { 
			pprint("command %s, fields %d\n", fields[0], nf); 
1999/1007    
			XPRINT("command %s, fields %d\n", fields[0], nf); 
1999/1005    
			error(Ebadarg); 
1999/1006    
		} 
1999/1005    
		return n; 
1999/1007/sys/src/9/pc/devusb.c:30,371999/1116/sys/src/9/pc/devusb.c:30,39 (short | long)
1999/1005    
#define Pprint 
#define Chatty	1 
#define DPRINT if(Chatty)print 
#define XPRINT if(0)print 
1999/1116    
#define XPRINT if(debug)print 
1999/1005    
 
1999/1116    
static int debug = 1; 
 
1999/1005    
/* 
 * USB packet definitions 
 */ 
1999/1007/sys/src/9/pc/devusb.c:211,2201999/1116/sys/src/9/pc/devusb.c:213,222
1999/1005    
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 */ 
1999/1116    
	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 */ 
1999/1005    
	byte	eof; 
	byte	class; 
	byte	subclass; 
1999/1007/sys/src/9/pc/devusb.c:225,2351999/1116/sys/src/9/pc/devusb.c:227,237
1999/1005    
	byte	iso; 
	byte	debug; 
	byte	active;	/* listed for examination by interrupts */ 
	int	sched;	/* schedule index; -1 if undefined or aperiodic */ 
	int	setin; 
1999/1116    
	int		sched;	/* schedule index; -1 if undefined or aperiodic */ 
	int		setin; 
1999/1005    
	ulong	bw;	/* bandwidth requirement */ 
	int	pollms;	/* polling interval in msec */ 
	QH*	epq;	/* queue of TDs for this endpoint */ 
1999/1116    
	int		pollms;	/* polling interval in msec */ 
	QH*		epq;	/* queue of TDs for this endpoint */ 
1999/1005    
 
	QLock	rlock; 
	Rendez	rr; 
1999/1007/sys/src/9/pc/devusb.c:238,2441999/1116/sys/src/9/pc/devusb.c:240,246
1999/1005    
	Rendez	wr; 
	Queue*	wq; 
 
	int	ntd; 
1999/1116    
	int		ntd; 
1999/1005    
	char*	err; 
 
	Udev*	dev;	/* owning device */ 
1999/1007/sys/src/9/pc/devusb.c:470,4751999/1116/sys/src/9/pc/devusb.c:472,478
1999/1005    
		q->entries = PADDR(t); 
	} 
	q->last = lt; 
1999/1116    
	dumpqh(q); 
1999/1005    
	iunlock(ub); 
} 
 
1999/1007/sys/src/9/pc/devusb.c:541,5761999/1116/sys/src/9/pc/devusb.c:544,603
1999/1005    
} 
 
static void 
cleanq(QH *q, int discard) 
1999/1116    
cleanq(QH *q, int discard, int vf) 
1999/1005    
{ 
	TD *t; 
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;){ 
//		XPRINT("cleanq: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); 
1999/1116    
		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; 
			} 
1999/1116    
tp = t; 
t = t->next; 
continue; 
1999/1005    
			break; 
		} 
		t->status &= ~IOC; 
		/* TO DO: need to deal with TDs as chain if necessary */ 
		q->first = t->next; 
		if(q->first != nil) 
			q->entries = PADDR(q->first); 
		else 
			q->entries = Terminate; 
		t->next = nil; 
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); 
		t = q->first; 
1999/1116    
		if (tp) 
			t = tp->next; 
		else 
			t = q->first; 
		XPRINT("t = %8.8lux\n", t); 
		dumpqh(q); 
1999/1005    
	} 
	iunlock(ub); 
} 
1999/1007/sys/src/9/pc/devusb.c:676,6811999/1116/sys/src/9/pc/devusb.c:703,709
1999/1005    
	QH *qh; 
	int vf; 
 
1999/1116    
XPRINT("qrcv\n"); 
1999/1005    
	t = alloctde(e, TokIN, e->maxpkt); 
	b = allocb(e->maxpkt); 
	t->bp = b; 
1999/1007/sys/src/9/pc/devusb.c:683,6911999/1116/sys/src/9/pc/devusb.c:711,720
1999/1005    
	ub = &ubus; 
	vf = 0; 
	if(e->x == 0){ 
1999/1116    
XPRINT("enq\n"); 
1999/1005    
		qh = ub->ctlq; 
		vf = 0; 
	}else if((qh = e->epq) == nil || e->mode != OREAD){ 
1999/1116    
XPRINT("bulkenq\n"); 
1999/1005    
		qh = ub->bulkq; 
		vf = Vf; 
	} 
1999/1007/sys/src/9/pc/devusb.c:912,9171999/1116/sys/src/9/pc/devusb.c:941,950
1999/1005    
	e->nbuf = 1; 
	e->dev = d; 
	e->active = 0; 
1999/1116    
	e->epq = allocqh(ub); 
	if(e->epq == nil) 
		panic("devendpt"); 
 
1999/1005    
	lock(d); 
	if(*p != nil){ 
		incref(*p); 
1999/1007/sys/src/9/pc/devusb.c:1065,10861999/1116/sys/src/9/pc/devusb.c:1098,1119
1999/1005    
 
	ub = a; 
	s = IN(Status); 
1999/1116    
	XPRINT("usbint: #%x f%d\n", s, IN(Frnum)); 
1999/1005    
	if (s & 0x1a) { 
1999/1007    
		XPRINT("usbint: #%x f%d\n", s, IN(Frnum)); 
		XPRINT("cmd #%x sofmod #%x\n", IN(Cmd), inb(ub->io+SOFMod)); 
		XPRINT("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1)); 
1999/1005    
	} 
	OUT(Status, s); 
 
//	XPRINT("cleanq(ub->ctlq, 0)\n"); 
	cleanq(ub->ctlq, 0); 
//	XPRINT("cleanq(ub->bulkq, 0)\n"); 
	cleanq(ub->bulkq, 0); 
1999/1116    
	XPRINT("cleanq(ub->ctlq, 0, 0)\n"); 
	cleanq(ub->ctlq, 0, 0); 
	XPRINT("cleanq(ub->bulkq, 0, Vf)\n"); 
	cleanq(ub->bulkq, 0, Vf); 
1999/1005    
	ilock(&activends); 
	for(e = activends.f; e != nil; e = e->activef) 
		if(e->epq != nil) { 
//			XPRINT("cleanq(e->epq, 0)\n"); 
			cleanq(e->epq, 0); 
1999/1116    
			XPRINT("cleanq(e->epq, 0, 0)\n"); 
			cleanq(e->epq, 0, 0); 
1999/1005    
		} 
	iunlock(&activends); 
} 
1999/1007/sys/src/9/pc/devusb.c:1252,12581999/1116/sys/src/9/pc/devusb.c:1285,1291
1999/1006    
	ub->bwsop->entries = PADDR(t); 
	ub->ctlq = allocqh(ub); 
	ub->ctlq->head = PADDR(ub->bwsop) | IsQH; 
//	ub->bulkq->head = PADDR(ub->bwsop) | IsQH;	/* loop back */ 
1999/1116    
	ub->bulkq->head = PADDR(ub->bwsop) | IsQH;	/* loop back */ 
1999/1006    
	print("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n", 
		IN(Cmd), IN(Status), IN(Usbintr), inb(port+Frnum)); 
	print("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n", 
1999/1007/sys/src/9/pc/devusb.c:1523,15291999/1116/sys/src/9/pc/devusb.c:1556,1564
1999/1005    
	uchar *p; 
	long l, i; 
 
1999/1116    
	XPRINT("qlock(%p)\n", &e->rlock); 
1999/1005    
	qlock(&e->rlock); 
1999/1116    
	XPRINT("got qlock(%p)\n", &e->rlock); 
1999/1005    
	if(waserror()){ 
		qunlock(&e->rlock); 
		eptcancel(e); 
1999/1007/sys/src/9/pc/devusb.c:1794,18031999/1116/sys/src/9/pc/devusb.c:1829,1843
1999/1005    
				e->maxpkt = 1500; 
		}else if(nf > 2 && strcmp(fields[0], "debug") == 0){ 
			i = strtoul(fields[1], nil, 0); 
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) 
1999/1116    
			if(i < -1 || i >= nelem(d->ep) || d->ep[i] == nil) 
1999/1005    
				error(Ebadarg); 
			e = d->ep[i]; 
			e->debug = strtoul(fields[2], nil, 0); 
1999/1116    
			if (i == -1) 
				debug = 0; 
			else { 
				debug = 1; 
				e = d->ep[i]; 
				e->debug = strtoul(fields[2], nil, 0); 
			} 
1999/1005    
		}else if(nf > 1 && strcmp(fields[0], "unstall") == 0){ 
			i = strtoul(fields[1], nil, 0); 
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) 
1999/1116/sys/src/9/pc/devusb.c:32,381999/1117/sys/src/9/pc/devusb.c:32,38 (short | long)
1999/1005    
#define DPRINT if(Chatty)print 
1999/1116    
#define XPRINT if(debug)print 
1999/1005    
 
1999/1116    
static int debug = 1; 
1999/1117    
static int debug = 0; 
1999/1116    
 
1999/1005    
/* 
 * USB packet definitions 
1999/1116/sys/src/9/pc/devusb.c:86,961999/1117/sys/src/9/pc/devusb.c:86,97
1999/1005    
	ulong	entries;	/* address of next TD or QH to process (updated by controller) */ 
 
	/* software */ 
	union { 
		TD*	first; 
		QH*	next;		/* free list */ 
	}; 
	TD*	last; 
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)) 
 
1999/1116/sys/src/9/pc/devusb.c:267,2721999/1117/sys/src/9/pc/devusb.c:268,274
1999/1005    
	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) */ 
1999/1117    
	QH*	recvq;	/* receive queues for bulk i/o */ 
1999/1005    
 
	Udev*	ports[2]; 
}; 
1999/1116/sys/src/9/pc/devusb.c:371,3811999/1117/sys/src/9/pc/devusb.c:373,384
1999/1005    
		if(t->status & LowSpeed) 
			*s++ = 'L'; 
		*s = 0; 
1999/1007    
		XPRINT("td %8.8lux: l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n", 
1999/1005    
			t, t->link, t->status, t->dev, t->buffer, t->bp?(ulong)t->bp->rp:0, t->flags); 
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); 
1999/1007    
		XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n", buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1); 
1999/1005    
		if(t->bp) 
			dumpdata(t->bp, n); 
1999/1117    
	//	if(t->bp && (t->flags & CancelTD) == 0 && (t->status & Active) == 0) 
	//		dumpdata(t->bp, n); 
1999/1005    
		if(!follow || t->link & Terminate || t->link & IsQH) 
			break; 
		t = TFOL(t->link); 
1999/1116/sys/src/9/pc/devusb.c:417,4221999/1117/sys/src/9/pc/devusb.c:420,426
1999/1005    
	iunlock(ub); 
	qh->head = Terminate; 
	qh->entries = Terminate; 
1999/1117    
	qh->hlink = nil; 
1999/1005    
	qh->first = nil; 
	qh->last = nil; 
	return qh; 
1999/1116/sys/src/9/pc/devusb.c:662,6671999/1117/sys/src/9/pc/devusb.c:666,689
1999/1005    
	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) 
{ 
1999/1116/sys/src/9/pc/devusb.c:670,6751999/1117/sys/src/9/pc/devusb.c:692,698
1999/1005    
	Ctlr *ub; 
	QH *qh; 
 
1999/1117    
XPRINT("qxmit\n"); 
1999/1005    
	if(b != nil){ 
		n = BLEN(b); 
		t = alloctde(e, pid, n); 
1999/1116/sys/src/9/pc/devusb.c:684,6921999/1117/sys/src/9/pc/devusb.c:707,717
1999/1005    
if(e->debug)pprint("QTD: %8.8lux n=%ld\n", t, b?BLEN(b): 0); 
	vf = 0; 
	if(e->x == 0){ 
1999/1117    
XPRINT("enq\n"); 
1999/1005    
		qh = ub->ctlq; 
		vf = 0; 
	}else if((qh = e->epq) == nil || e->mode != OWRITE){ 
1999/1117    
XPRINT("bulkenq\n"); 
1999/1005    
		qh = ub->bulkq; 
		vf = Vf; 
	} 
1999/1116/sys/src/9/pc/devusb.c:898,9041999/1117/sys/src/9/pc/devusb.c:923,929
1999/1005    
	int q; 
 
	ub = &ubus; 
	if(e->epq == nil || (q = e->sched) < 0) 
1999/1117    
	if(!e->periodic || (q = e->sched) < 0) 
1999/1005    
		return; 
	p = PADDR(e->epq) | IsQH; 
	qlock(ub->tree); 
1999/1116/sys/src/9/pc/devusb.c:918,9241999/1117/sys/src/9/pc/devusb.c:943,951
1999/1005    
devendpt(Udev *d, int id, int add) 
{ 
	Endpt *e, **p; 
1999/1117    
	Ctlr *ub; 
1999/1005    
 
1999/1117    
	ub = &ubus; 
1999/1005    
	p = &d->ep[id&0xF]; 
	lock(d); 
	if((e = *p) != nil){ 
1999/1116/sys/src/9/pc/devusb.c:1095,11001999/1117/sys/src/9/pc/devusb.c:1122,1128
1999/1005    
	Ctlr *ub; 
	Endpt *e; 
	int s; 
1999/1117    
	QH *q; 
1999/1005    
 
	ub = a; 
	s = IN(Status); 
1999/1116/sys/src/9/pc/devusb.c:1109,11141999/1117/sys/src/9/pc/devusb.c:1137,1147
1999/1116    
	cleanq(ub->ctlq, 0, 0); 
	XPRINT("cleanq(ub->bulkq, 0, Vf)\n"); 
	cleanq(ub->bulkq, 0, Vf); 
1999/1117    
	XPRINT("clean recvq\n"); 
	for (q = ub->recvq->next; q; q = q->hlink) { 
		XPRINT("cleanq(q, 0, Vf)\n"); 
		cleanq(q, 0, Vf); 
	} 
1999/1005    
	ilock(&activends); 
	for(e = activends.f; e != nil; e = e->activef) 
		if(e->epq != nil) { 
1999/1116/sys/src/9/pc/devusb.c:1217,12281999/1117/sys/src/9/pc/devusb.c:1250,1261
1999/1005    
static void 
usbreset(void) 
{ 
1999/1006    
	Ctlr *ub; 
	Pcidev *cfg; 
	int i; 
	ulong port; 
	QTree *qt; 
	TD *t; 
1999/1117    
	Ctlr *ub; 
1999/1006    
 
	ub = &ubus; 
	memset(&cfg, 0, sizeof(cfg)); 
1999/1116/sys/src/9/pc/devusb.c:1277,12911999/1117/sys/src/9/pc/devusb.c:1310,1330
1999/1006    
	 * with its head entry leading on to the bulk traffic, the last QH of which 
	 * links back to the empty QH. 
	 */ 
1999/1117    
	ub->ctlq = allocqh(ub); 
	ub->bwsop = allocqh(ub); 
1999/1006    
	ub->bulkq = allocqh(ub); 
1999/1117    
	ub->recvq = allocqh(ub); 
XPRINT("bulkq: 0x%8.8lux\n", ub->bulkq); 
XPRINT("recvq: 0x%8.8lux\n", ub->recvq); 
XPRINT("bwsop: 0x%8.8lux\n", ub->bwsop); 
XPRINT("ctlq: 0x%8.8lux\n", ub->ctlq); 
1999/1006    
	t = alloctd(ub);	/* inactive TD, looped */ 
	t->link = PADDR(t); 
	ub->bwsop = allocqh(ub); 
	ub->bwsop->head = PADDR(ub->bulkq) | IsQH; 
	ub->bwsop->entries = PADDR(t); 
	ub->ctlq = allocqh(ub); 
	ub->ctlq->head = PADDR(ub->bwsop) | IsQH; 
1999/1116    
	ub->bulkq->head = PADDR(ub->bwsop) | IsQH;	/* loop back */ 
1999/1117    
	ub->bwsop->head = PADDR(ub->bulkq) | IsQH; 
	ub->bulkq->head = PADDR(ub->recvq) | IsQH; 
	ub->recvq->head = PADDR(ub->bwsop) | IsQH;	/* loop back */ 
1999/1006    
	print("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n", 
		IN(Cmd), IN(Status), IN(Usbintr), inb(port+Frnum)); 
	print("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n", 
1999/1116/sys/src/9/pc/devusb.c:1865,18711999/1117/sys/src/9/pc/devusb.c:1904,1923
1999/1005    
			e->mode = strcmp(fields[3],"r")==0? OREAD: strcmp(fields[3],"w") == 0? OWRITE: ORDWR; 
			e->periodic = 0; 
			e->sched = -1; 
			if(strcmp(fields[4], "bulk") != 0){ 
1999/1117    
			if(strcmp(fields[4], "bulk") == 0){ 
				Ctlr *ub; 
 
				ub = &ubus; 
				/* Each bulk device gets a queue head hanging off the 
				 * bulk queue head 
				 */ 
				if (e->epq == nil) { 
					e->epq = allocqh(ub); 
					if(e->epq == nil) 
						panic("usbwrite: allocqh"); 
				} 
				queueqh(e->epq); 
			} else { 
1999/1005    
				e->periodic = 1; 
				i = strtoul(fields[4], nil, 0); 
				if(i > 0 && i <= 1000) 
1999/1117/sys/src/9/pc/devusb.c:27,331999/1118/sys/src/9/pc/devusb.c:27,32 (short | long)
1999/1005    
#endif 
*/ 
 
#define Pprint 
#define Chatty	1 
#define DPRINT if(Chatty)print 
1999/1116    
#define XPRINT if(debug)print 
1999/1117/sys/src/9/pc/devusb.c:377,3841999/1118/sys/src/9/pc/devusb.c:376,383
1999/1117    
		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); 
1999/1007    
		XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n", buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1); 
1999/1117    
	//	if(t->bp && (t->flags & CancelTD) == 0 && (t->status & Active) == 0) 
	//		dumpdata(t->bp, n); 
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); 
1999/1117/sys/src/9/pc/devusb.c:692,6981999/1118/sys/src/9/pc/devusb.c:691,696
1999/1005    
	Ctlr *ub; 
	QH *qh; 
 
1999/1117    
XPRINT("qxmit\n"); 
1999/1005    
	if(b != nil){ 
		n = BLEN(b); 
		t = alloctde(e, pid, n); 
1999/1117/sys/src/9/pc/devusb.c:704,7171999/1118/sys/src/9/pc/devusb.c:702,713
1999/1005    
	ilock(ub); 
	e->ntd++; 
	iunlock(ub); 
if(e->debug)pprint("QTD: %8.8lux n=%ld\n", t, b?BLEN(b): 0); 
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){ 
1999/1117    
XPRINT("enq\n"); 
1999/1005    
		qh = ub->ctlq; 
		vf = 0; 
	}else if((qh = e->epq) == nil || e->mode != OWRITE){ 
1999/1117    
XPRINT("bulkenq\n"); 
1999/1005    
		qh = ub->bulkq; 
		vf = Vf; 
	} 
1999/1117/sys/src/9/pc/devusb.c:728,7341999/1118/sys/src/9/pc/devusb.c:724,729
1999/1005    
	QH *qh; 
	int vf; 
 
1999/1116    
XPRINT("qrcv\n"); 
1999/1005    
	t = alloctde(e, TokIN, e->maxpkt); 
	b = allocb(e->maxpkt); 
	t->bp = b; 
1999/1117/sys/src/9/pc/devusb.c:736,7451999/1118/sys/src/9/pc/devusb.c:731,738
1999/1005    
	ub = &ubus; 
	vf = 0; 
	if(e->x == 0){ 
1999/1116    
XPRINT("enq\n"); 
1999/1005    
		qh = ub->ctlq; 
	}else if((qh = e->epq) == nil || e->mode != OREAD){ 
1999/1116    
XPRINT("bulkenq\n"); 
1999/1005    
		qh = ub->bulkq; 
		vf = Vf; 
	} 
1999/1117/sys/src/9/pc/devusb.c:1179,11911999/1118/sys/src/9/pc/devusb.c:1172,1184
1999/1005    
#define	DEVPATH(q)	(((q)&~CHDIR)>>QSHIFT) 
 
static Dirtab usbdir2[] = { 
	"new",	{Qnew},	0,	0666, 
1999/1118    
	"new",		{Qnew},		0,	0666, 
1999/1005    
	"ctl",		{Qbusctl},	0,	0666, 
	"port",	{Qport},	0,	0444, 
1999/1118    
	"port",		{Qport},	0,	0444, 
1999/1005    
}; 
 
static Dirtab usbdir3[]={ 
	"ctl",		{Qctl},	0,	0666, 
1999/1118    
	"ctl",		{Qctl},		0,	0666, 
1999/1005    
	"setup",	{Qsetup},	0,	0666, 
	"status",	{Qstatus},	0,	0444, 
	"debug",	{Qdebug},	1,	0666, 
1999/1117/sys/src/9/pc/devusb.c:1276,12821999/1118/sys/src/9/pc/devusb.c:1269,1275
1999/1006    
		return; 
	} 
 
	print("USB: %x/%x port 0x%lux size 0x%x irq %d\n", 
1999/1118    
	DPRINT("USB: %x/%x port 0x%lux size 0x%x irq %d\n", 
1999/1006    
		cfg->vid, cfg->did, port, cfg->mem[4].size, cfg->intl); 
 
	i = inb(port+SOFMod); 
1999/1117/sys/src/9/pc/devusb.c:1314,13231999/1118/sys/src/9/pc/devusb.c:1307,1312
1999/1117    
	ub->bwsop = allocqh(ub); 
1999/1006    
	ub->bulkq = allocqh(ub); 
1999/1117    
	ub->recvq = allocqh(ub); 
XPRINT("bulkq: 0x%8.8lux\n", ub->bulkq); 
XPRINT("recvq: 0x%8.8lux\n", ub->recvq); 
XPRINT("bwsop: 0x%8.8lux\n", ub->bwsop); 
XPRINT("ctlq: 0x%8.8lux\n", ub->ctlq); 
1999/1006    
	t = alloctd(ub);	/* inactive TD, looped */ 
	t->link = PADDR(t); 
	ub->bwsop->entries = PADDR(t); 
1999/1117/sys/src/9/pc/devusb.c:1325,13331999/1118/sys/src/9/pc/devusb.c:1314,1322
1999/1117    
	ub->bwsop->head = PADDR(ub->bulkq) | IsQH; 
	ub->bulkq->head = PADDR(ub->recvq) | IsQH; 
	ub->recvq->head = PADDR(ub->bwsop) | IsQH;	/* loop back */ 
1999/1006    
	print("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n", 
1999/1118    
	XPRINT("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n", 
1999/1006    
		IN(Cmd), IN(Status), IN(Usbintr), inb(port+Frnum)); 
	print("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n", 
1999/1118    
	XPRINT("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n", 
1999/1006    
		IN(Flbaseadd), inb(port+SOFMod), IN(Portsc0), IN(Portsc1)); 
	OUT(Cmd, 0);	/* stop */ 
	ub->frames = xspanalloc(FRAMESIZE, FRAMESIZE, 0); 
1999/1117/sys/src/9/pc/devusb.c:1339,13511999/1118/sys/src/9/pc/devusb.c:1328,1340
1999/1006    
	} 
	qt->root->head = PADDR(ub->ctlq) | IsQH; 
	ub->tree = qt; 
	print("usb tree: nel=%d depth=%d\n", qt->nel, qt->depth); 
1999/1118    
	XPRINT("usb tree: nel=%d depth=%d\n", qt->nel, qt->depth); 
1999/1006    
 
	outl(port+Flbaseadd, PADDR(ub->frames)); 
	OUT(Frnum, 0); 
	OUT(Usbintr, 0xF);	/* enable all interrupts */ 
	print("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(port+SOFMod)); 
	print("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1)); 
1999/1118    
	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)); 
1999/1005    
} 
 
void 
1999/1118/sys/src/9/pc/devusb.c:1836,18461999/1119/sys/src/9/pc/devusb.c:1836,1846 (short | long)
1999/1005    
		nf = getfields(cmd, fields, nelem(fields)); 
		if(nf > 1 && strcmp(fields[0], "speed") == 0){ 
			d->ls = strtoul(fields[1], nil, 0) == 0; 
		} else if(nf > 4 && strcmp(fields[0], "class") == 0){ 
1999/1119    
		} else if(nf > 3 && strcmp(fields[0], "class") == 0){ 
1999/1005    
			/* class class subclass proto */ 
			d->class = strtoul(fields[4], nil, 0); 
			d->subclass = strtoul(fields[5], nil, 0); 
			d->proto = strtoul(fields[6], nil, 0); 
1999/1119    
			d->class = strtoul(fields[1], nil, 0); 
			d->subclass = strtoul(fields[2], nil, 0); 
			d->proto = strtoul(fields[3], nil, 0); 
1999/1005    
		}else if(nf > 2 && strcmp(fields[0], "data") == 0){ 
			i = strtoul(fields[1], nil, 0); 
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) 
1999/1119/sys/src/9/pc/devusb.c:170,1871999/1120/sys/src/9/pc/devusb.c:170,187 (short | long)
1999/1005    
struct Udev { 
	Ref; 
	Lock; 
	int	x;	/* index in usbdev[] */ 
	int	busy; 
	int	state; 
	int	id; 
1999/1120    
	int		x;	/* index in usbdev[] */ 
	int		busy; 
	int		state; 
	int		id; 
1999/1005    
	byte	port;		/* port number on connecting hub */ 
	byte	class; 
	byte	subclass; 
	byte	proto; 
	int	ls; 
	int	npt; 
1999/1120    
	int		ls; 
	int		npt; 
1999/1005    
	Endpt*	ep[16];		/* active end points */ 
	Udev*	ports;	/* active ports, if hub */ 
1999/1120    
	Udev*	ports;		/* active ports, if hub */ 
1999/1005    
	Udev*	next;		/* next device on this hub */ 
}; 
 
1999/1119/sys/src/9/pc/devusb.c:200,2101999/1120/sys/src/9/pc/devusb.c:200,210
1999/1005    
}; 
 
static char *devstates[] = { 
	[Disabled]	"Disabled", 
	[Attached] "Attached", 
	[Enabled] "Enabled", 
	[Assigned] "Assigned", 
	[Configured] "Configured", 
1999/1120    
	[Disabled]		"Disabled", 
	[Attached]		"Attached", 
	[Enabled]		"Enabled", 
	[Assigned]		"Assigned", 
	[Configured]	"Configured", 
1999/1005    
}; 
 
/* 
1999/1119/sys/src/9/pc/devusb.c:1685,16911999/1120/sys/src/9/pc/devusb.c:1685,1691
1999/1005    
		l = snprint(s, READSTR, "%s %d %d %d\n", devstates[d->state], d->class, d->subclass, d->proto); 
		for(i=0; i<nelem(d->ep); i++) 
			if((e = d->ep[i]) != nil)	/* TO DO: freeze e */ 
				l += snprint(s+l, READSTR-l, "%d bytes %lud blocks %lud\n", i, e->nbytes, e->nblocks); 
1999/1120    
				l += snprint(s+l, READSTR-l, "%2d class 0x%2.2ux subclass 0x%2.2ux proto 0x%2.2ux bytes %10lud blocks %10lud\n", i, e->class, e->subclass, e->proto, e->nbytes, e->nblocks); 
1999/1005    
		n = readstr(offset, a, n, s); 
		poperror(); 
		free(s); 
1999/1119/sys/src/9/pc/devusb.c:1836,18461999/1120/sys/src/9/pc/devusb.c:1836,1858
1999/1005    
		nf = getfields(cmd, fields, nelem(fields)); 
		if(nf > 1 && strcmp(fields[0], "speed") == 0){ 
			d->ls = strtoul(fields[1], nil, 0) == 0; 
1999/1119    
		} else if(nf > 3 && strcmp(fields[0], "class") == 0){ 
1999/1005    
			/* class class subclass proto */ 
1999/1119    
			d->class = strtoul(fields[1], nil, 0); 
			d->subclass = strtoul(fields[2], nil, 0); 
			d->proto = strtoul(fields[3], nil, 0); 
1999/1120    
		} else if(nf > 5 && strcmp(fields[0], "class") == 0){ 
			i = strtoul(fields[2], nil, 0); 
			d->npt = strtoul(fields[1], nil, 0); 
			/* class config# class subclass proto */ 
			if (i < 0 || i >= nelem(d->ep) 
			 || d->npt > nelem(d->ep) || i >= d->npt) 
				error(Ebadarg); 
			if (i == 0) { 
				d->class = strtoul(fields[3], nil, 0); 
				d->subclass = strtoul(fields[4], nil, 0); 
				d->proto = strtoul(fields[5], nil, 0); 
			} 
			if(d->ep[i] == nil) 
				d->ep[i] = devendpt(d, i, 1); 
			d->ep[i]->class = strtoul(fields[3], nil, 0); 
			d->ep[i]->subclass = strtoul(fields[4], nil, 0); 
			d->ep[i]->proto = strtoul(fields[5], nil, 0); 
1999/1005    
		}else if(nf > 2 && strcmp(fields[0], "data") == 0){ 
			i = strtoul(fields[1], nil, 0); 
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) 
1999/1119/sys/src/9/pc/devusb.c:1879,18871999/1120/sys/src/9/pc/devusb.c:1891,1898
1999/1007    
				XPRINT("field 1: 0 <= %d < %d\n", i, nelem(d->ep)); 
1999/1005    
				error(Ebadarg); 
1999/1006    
			} 
1999/1005    
			if(d->ep[i] != nil) 
				error(Einuse); 
			e = devendpt(d, i, 1); 
1999/1120    
			if((e = d->ep[i]) == nil) 
				e = devendpt(d, i, 1); 
1999/1005    
			if(waserror()){ 
				freept(e); 
				nexterror(); 
1999/1120/sys/src/9/pc/devusb.c:1280,12861999/1209/sys/src/9/pc/devusb.c:1280,1291 (short | long)
1999/1006    
		delay(4); 
	} 
	outb(port+SOFMod, i); 
	// Interrupt handler 
1999/1209    
	/* 
	 * Interrupt handler. 
	 * Bail out if no IRQ assigned by the BIOS. 
	 */ 
	if(cfg->intl == 0xFF) 
		return; 
1999/1006    
	intrenable(cfg->intl, interrupt, ub, cfg->tbdf, "usb"); 
 
	ub->io = port; 
1999/1209/sys/src/9/pc/devusb.c:1389,13941999/1230/sys/src/9/pc/devusb.c:1389,1398 (short | long)
1999/1005    
	 * Top level directory contains the name of the device. 
	 */ 
	if(c->qid.path == CHDIR){ 
1999/1230    
		if(s == DEVDOTDOT){ 
			devdir(c, (Qid){CHDIR, 0}, "#U", 0, eve, 0555, dp); 
			return 1; 
		} 
1999/1005    
		if(s == 0){ 
			devdir(c, (Qid){CHDIR|Q2nd, 0}, "usb", 0, eve, 0555, dp); 
			return 1; 
1999/1209/sys/src/9/pc/devusb.c:1401,14061999/1230/sys/src/9/pc/devusb.c:1405,1414
1999/1005    
	 */ 
	t = QID(c->qid); 
	if(t < Q3rd){ 
1999/1230    
		if(s == DEVDOTDOT){ 
			devdir(c, (Qid){CHDIR, 0}, "#U", 0, eve, 0555, dp); 
			return 1; 
		} 
1999/1005    
		if(s < nelem(usbdir2)){ 
			tab = &usbdir2[s]; 
			devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); 
1999/1209/sys/src/9/pc/devusb.c:1423,14281999/1230/sys/src/9/pc/devusb.c:1431,1440
1999/1005    
	 */ 
	path = c->qid.path&~(CHDIR|QMASK);	/* slot component */ 
	q.vers = c->qid.vers; 
1999/1230    
	if(s == DEVDOTDOT){ 
		devdir(c, (Qid){CHDIR|Q2nd, 0}, "usb", 0, eve, 0555, dp); 
		return 1; 
	} 
1999/1005    
	if(s < nelem(usbdir3)){ 
		Dirtab *tab = &usbdir3[s]; 
		q.path = path | tab->qid.path; 
1999/1230/sys/src/9/pc/devusb.c:1776,18112000/0308/sys/src/9/pc/devusb.c:1776,1781 (short | long)
1999/1005    
	return p-(uchar*)a; 
} 
 
int 
getfields(char *ss, char **sp, int nptrs) 
{ 
	uchar *s = (uchar*)ss; 
	uchar **p = (uchar**)sp; 
	uint c; 
                 
	c = 0; 
	for(;;){ 
		if(--nptrs < 0) 
			break; 
		*p++ = s; 
		do { 
			c = *s++; 
			if (c == ' ') break; 
			if (c == '\t') break; 
			if (c == '\n') break; 
		} while(c); 
		if(c == 0) 
			break; 
		s[-1] = 0; 
	} 
	if(nptrs > 0) 
		*p = 0; 
	else 
	if(--s >= (uchar*)ss) 
		*s = c; 
	return p - (uchar**)sp; 
} 
                 
long 
usbwrite(Chan *c, void *a, long n, vlong) 
{ 
1999/1230/sys/src/9/pc/devusb.c:1822,18282000/0308/sys/src/9/pc/devusb.c:1792,1798
1999/1005    
			n = sizeof(cmd)-1; 
		memmove(cmd, a, n); 
		cmd[n] = 0; 
		nf = getfields(cmd, fields, nelem(fields)); 
2000/0308    
		nf = getfields(cmd, fields, nelem(fields), 0, " \t\n"); 
1999/1005    
		if(nf==1 && strcmp(fields[0], "dump")==0){ 
			dumpframe(-1, -1); 
			return n; 
1999/1230/sys/src/9/pc/devusb.c:1850,18562000/0308/sys/src/9/pc/devusb.c:1820,1826
1999/1005    
			n = sizeof(cmd)-1; 
		memmove(cmd, a, n); 
		cmd[n] = 0; 
		nf = getfields(cmd, fields, nelem(fields)); 
2000/0308    
		nf = getfields(cmd, fields, nelem(fields), 0, " \t\n"); 
1999/1005    
		if(nf > 1 && strcmp(fields[0], "speed") == 0){ 
			d->ls = strtoul(fields[1], nil, 0) == 0; 
1999/1120    
		} else if(nf > 5 && strcmp(fields[0], "class") == 0){ 
2000/0308/sys/src/9/pc/devusb.c:1284,12902000/0517/sys/src/9/pc/devusb.c:1284,1290 (short | long)
1999/1209    
	 * Interrupt handler. 
	 * Bail out if no IRQ assigned by the BIOS. 
	 */ 
	if(cfg->intl == 0xFF) 
2000/0517    
	if(cfg->intl == 0xFF || cfg->intl == 0) 
1999/1209    
		return; 
1999/1006    
	intrenable(cfg->intl, interrupt, ub, cfg->tbdf, "usb"); 
 
2000/0517/sys/src/9/pc/devusb.c:20,322000/0719/sys/src/9/pc/devusb.c:20,25 (short | long)
1999/1005    
#include	"io.h" 
#include	"../port/error.h" 
 
/* 
#ifndef Inferno 
#include	"devtab.h" 
#include	"brazilia.h" 
#endif 
*/ 
                 
#define Chatty	1 
#define DPRINT if(Chatty)print 
1999/1116    
#define XPRINT if(debug)print 
2000/0719/sys/src/9/pc/devusb.c:1247,12602000/0720/sys/src/9/pc/devusb.c:1247,1257 (short | long)
1999/1006    
	memset(&cfg, 0, sizeof(cfg)); 
	cfg = pcimatch(0, 0x8086, 0x7112);	/* Intel chipset PIIX 4*/ 
	if(cfg == nil) { 
//		cfg = pcimatch(0, 0x8086, 0x7112);	/* Intel chipset PIIX 3*/ 
//		if(cfg == nil) { 
			cfg = pcimatch(0, 0x1106, 0x0586);	/* Via chipset */ 
			if(cfg == nil) { 
				DPRINT("No USB device found\n"); 
				return; 
			} 
//		} 
2000/0720    
		cfg = pcimatch(0, 0x1106, 0x0586);	/* Via chipset */ 
		if(cfg == nil) { 
			DPRINT("No USB device found\n"); 
			return; 
		} 
1999/1006    
	} 
	port = cfg->mem[4].bar & ~0x0F; 
	if (port == 0) { 
2000/0720/sys/src/9/pc/devusb.c:1689,16982000/0722/sys/src/9/pc/devusb.c:1689,1698 (short | long)
1999/1005    
			free(s); 
			nexterror(); 
		} 
		l = snprint(s, READSTR, "%s %d %d %d\n", devstates[d->state], d->class, d->subclass, d->proto); 
2000/0722    
		l = snprint(s, READSTR, "%s %d.%d.%d\n", devstates[d->state], d->class, d->subclass, d->proto); 
1999/1005    
		for(i=0; i<nelem(d->ep); i++) 
			if((e = d->ep[i]) != nil)	/* TO DO: freeze e */ 
1999/1120    
				l += snprint(s+l, READSTR-l, "%2d class 0x%2.2ux subclass 0x%2.2ux proto 0x%2.2ux bytes %10lud blocks %10lud\n", i, e->class, e->subclass, e->proto, e->nbytes, e->nblocks); 
2000/0722    
				l += snprint(s+l, READSTR-l, "%2d %ud.%ud.%ud bytes %10lud blocks %10lud\n", i, e->class, e->subclass, e->proto, e->nbytes, e->nblocks); 
1999/1005    
		n = readstr(offset, a, n, s); 
		poperror(); 
		free(s); 
2000/0722/sys/src/9/pc/devusb.c:59,642000/0724/sys/src/9/pc/devusb.c:59,69 (short | long)
1999/1005    
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 */ 
2000/0722/sys/src/9/pc/devusb.c:168,1762000/0724/sys/src/9/pc/devusb.c:173,179
1999/1120    
	int		state; 
	int		id; 
1999/1005    
	byte	port;		/* port number on connecting hub */ 
	byte	class; 
	byte	subclass; 
	byte	proto; 
2000/0724    
	ulong	csp; 
1999/1120    
	int		ls; 
	int		npt; 
1999/1005    
	Endpt*	ep[16];		/* active end points */ 
2000/0722/sys/src/9/pc/devusb.c:211,2192000/0724/sys/src/9/pc/devusb.c:214,220
1999/1116    
	int		maxpkt;	/* maximum packet size (from endpoint descriptor) */ 
	int		data01;	/* 0=DATA0, 1=DATA1 */ 
1999/1005    
	byte	eof; 
	byte	class; 
	byte	subclass; 
	byte	proto; 
2000/0724    
	ulong	csp; 
1999/1005    
	byte	mode;	/* OREAD, OWRITE, ORDWR */ 
	byte	nbuf;	/* number of buffers allowed */ 
	byte	periodic; 
2000/0722/sys/src/9/pc/devusb.c:990,9962000/0724/sys/src/9/pc/devusb.c:991,997
1999/1005    
usbdevreset(Udev *d) 
{ 
	d->state = Disabled; 
	if(d->class == Hubclass) 
2000/0724    
	if(Class(d->csp) == Hubclass) 
1999/1005    
		for(d = d->ports; d != nil; d = d->next) 
			usbdevreset(d); 
} 
2000/0722/sys/src/9/pc/devusb.c:1689,16982000/0724/sys/src/9/pc/devusb.c:1690,1699
1999/1005    
			free(s); 
			nexterror(); 
		} 
2000/0722    
		l = snprint(s, READSTR, "%s %d.%d.%d\n", devstates[d->state], d->class, d->subclass, d->proto); 
2000/0724    
		l = snprint(s, READSTR, "%s %#6.6x\n", devstates[d->state], d->csp); 
1999/1005    
		for(i=0; i<nelem(d->ep); i++) 
			if((e = d->ep[i]) != nil)	/* TO DO: freeze e */ 
2000/0722    
				l += snprint(s+l, READSTR-l, "%2d %ud.%ud.%ud bytes %10lud blocks %10lud\n", i, e->class, e->subclass, e->proto, e->nbytes, e->nblocks); 
2000/0724    
				l += snprint(s+l, READSTR-l, "%2d %#6.6x %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks); 
1999/1005    
		n = readstr(offset, a, n, s); 
		poperror(); 
		free(s); 
2000/0722/sys/src/9/pc/devusb.c:1813,18352000/0724/sys/src/9/pc/devusb.c:1814,1832
2000/0308    
		nf = getfields(cmd, fields, nelem(fields), 0, " \t\n"); 
1999/1005    
		if(nf > 1 && strcmp(fields[0], "speed") == 0){ 
			d->ls = strtoul(fields[1], nil, 0) == 0; 
1999/1120    
		} else if(nf > 5 && strcmp(fields[0], "class") == 0){ 
2000/0724    
		} else if(nf > 3 && strcmp(fields[0], "class") == 0){ 
1999/1120    
			i = strtoul(fields[2], nil, 0); 
			d->npt = strtoul(fields[1], nil, 0); 
			/* class config# class subclass proto */ 
2000/0724    
			/* class config# csp ( == class subclass proto) */ 
1999/1120    
			if (i < 0 || i >= nelem(d->ep) 
			 || d->npt > nelem(d->ep) || i >= d->npt) 
				error(Ebadarg); 
			if (i == 0) { 
				d->class = strtoul(fields[3], nil, 0); 
				d->subclass = strtoul(fields[4], nil, 0); 
				d->proto = strtoul(fields[5], nil, 0); 
2000/0724    
				d->csp = strtoul(fields[3], nil, 0); 
1999/1120    
			} 
			if(d->ep[i] == nil) 
				d->ep[i] = devendpt(d, i, 1); 
			d->ep[i]->class = strtoul(fields[3], nil, 0); 
			d->ep[i]->subclass = strtoul(fields[4], nil, 0); 
			d->ep[i]->proto = strtoul(fields[5], nil, 0); 
2000/0724    
			d->ep[i]->csp = strtoul(fields[3], nil, 0); 
1999/1005    
		}else if(nf > 2 && strcmp(fields[0], "data") == 0){ 
			i = strtoul(fields[1], nil, 0); 
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) 
2000/0724/sys/src/9/pc/devusb.c:1690,16992000/0725/sys/src/9/pc/devusb.c:1690,1699 (short | long)
1999/1005    
			free(s); 
			nexterror(); 
		} 
2000/0724    
		l = snprint(s, READSTR, "%s %#6.6x\n", devstates[d->state], d->csp); 
2000/0725    
		l = snprint(s, READSTR, "%s %#6.6lux\n", devstates[d->state], d->csp); 
1999/1005    
		for(i=0; i<nelem(d->ep); i++) 
			if((e = d->ep[i]) != nil)	/* TO DO: freeze e */ 
2000/0724    
				l += snprint(s+l, READSTR-l, "%2d %#6.6x %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks); 
2000/0725    
				l += snprint(s+l, READSTR-l, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks); 
1999/1005    
		n = readstr(offset, a, n, s); 
		poperror(); 
		free(s); 
2000/0724/sys/src/9/pc/devusb.c:1875,18812000/0725/sys/src/9/pc/devusb.c:1875,1882
1999/1005    
			if(i < 8 || i > 1023) 
				i = 8; 
			e->maxpkt = i; 
			e->mode = strcmp(fields[3],"r")==0? OREAD: strcmp(fields[3],"w") == 0? OWRITE: ORDWR; 
2000/0725    
			e->mode = strcmp(fields[3],"r") == 0? OREAD : 
					  strcmp(fields[3],"w") == 0? OWRITE : ORDWR; 
1999/1005    
			e->periodic = 0; 
			e->sched = -1; 
1999/1117    
			if(strcmp(fields[4], "bulk") == 0){ 
2000/0724/sys/src/9/pc/devusb.c:1930,19392000/0725/sys/src/9/pc/devusb.c:1931,1943
1999/1005    
		break; 
 
	default:	/* sends DATA[01] */ 
		if((t -= Qep0) < 0 || t >= nelem(d->ep)) 
2000/0725    
		if((t -= Qep0) < 0 || t >= nelem(d->ep)) { 
			print("t = %d\n", t); 
1999/1005    
			error(Eio); 
		if((e = d->ep[t]) == nil || e->mode == OREAD) 
2000/0725    
		} 
		if((e = d->ep[t]) == nil || e->mode == OREAD) { 
1999/1005    
			error(Eio);	/* can't happen */ 
2000/0725    
		} 
1999/1005    
		n = writeusb(e, a, n, TokOUT); 
		break; 
	} 
2000/0725/sys/src/9/pc/devusb.c:1238,12662001/0126/sys/src/9/pc/devusb.c:1238,1273 (short | long)
1999/1005    
usbreset(void) 
{ 
1999/1006    
	Pcidev *cfg; 
	int i; 
	ulong port; 
2001/0126    
	int i, port; 
1999/1006    
	QTree *qt; 
	TD *t; 
1999/1117    
	Ctlr *ub; 
1999/1006    
 
	ub = &ubus; 
	memset(&cfg, 0, sizeof(cfg)); 
	cfg = pcimatch(0, 0x8086, 0x7112);	/* Intel chipset PIIX 4*/ 
	if(cfg == nil) { 
2000/0720    
		cfg = pcimatch(0, 0x1106, 0x0586);	/* Via chipset */ 
		if(cfg == nil) { 
			DPRINT("No USB device found\n"); 
			return; 
2001/0126    
	for(cfg = pcimatch(nil, 0, 0); cfg != nil; cfg = pcimatch(cfg, 0, 0)){ 
		/* 
		 * Look for devices with the correct class and 
		 * sub-class code and known device and vendor ID. 
		 */ 
		if(cfg->ccrb != 0x0C || cfg->ccru != 0x03) 
			continue; 
		switch((cfg->did<<16)|cfg->vid){ 
		default: 
			continue; 
		case (0x7112<<16)|0x8086:	/* 82371[AE]B (PIIX4[E]) */ 
		case (0x719A<<16)|0x8086:	/* 82443MX */ 
		case (0x1106<<16)|0x0586:	/* VIA 82C586 */ 
			break; 
2000/0720    
		} 
2001/0126    
		if((cfg->mem[4].bar & ~0x0F) != 0) 
			break; 
1999/1006    
	} 
	port = cfg->mem[4].bar & ~0x0F; 
	if (port == 0) { 
		print("usb: failed to map registers\n"); 
2001/0126    
	if(cfg == nil) 
1999/1006    
		return; 
	} 
 
1999/1118    
	DPRINT("USB: %x/%x port 0x%lux size 0x%x irq %d\n", 
2001/0126    
	port = cfg->mem[4].bar & ~0x0F; 
 
	DPRINT("USB: %x/%x port 0x%ux size 0x%x irq %d\n", 
1999/1006    
		cfg->vid, cfg->did, port, cfg->mem[4].size, cfg->intl); 
 
	i = inb(port+SOFMod); 
2001/0126/sys/src/9/pc/devusb.c:791,7962001/0131/sys/src/9/pc/devusb.c:791,797 (short | long)
1999/1005    
		return nil; 
	} 
	qt->root = tree; 
2001/0131    
print("tree root %8.8lux\n", tree); 
1999/1005    
	tree->head = Terminate;	/* root */ 
	tree->entries = Terminate; 
	for(i=1; i<n; i++){ 
2001/0126/sys/src/9/pc/devusb.c:798,8032001/0131/sys/src/9/pc/devusb.c:799,805
1999/1005    
		qh->head = PADDR(&tree[(i-1)/2]) | IsQH; 
		qh->entries = Terminate; 
	} 
2001/0131    
print("after loop tree root %8.8lux head %8.8lux entries %8.8lux\n", tree, tree->head, tree->entries); 
1999/1005    
	/* distribute leaves evenly round the frame list */ 
	leaf0 = n/2; 
	for(i=0; i<framesize; i++){ 
2001/0126/sys/src/9/pc/devusb.c:831,8442001/0131/sys/src/9/pc/devusb.c:833,846
1999/1005    
	if(t < 0) 
		t = 32; 
	for(i=f; i<t; i++){ 
1999/1007    
		XPRINT("F%.2d %8.8lux %8.8lux\n", i, frame[i], QFOL(frame[i])->head); 
2001/0131    
		pprint("F%.2d %8.8lux %8.8lux\n", i, frame[i], QFOL(frame[i])->head); 
1999/1005    
		for(p=frame[i]; (p & IsQH) && (p &Terminate) == 0; p = q->head){ 
			q = QFOL(p); 
			if(!(q >= tree && q < &tree[n])){ 
1999/1007    
				XPRINT("Q: p=%8.8lux out of range\n", p); 
2001/0131    
				pprint("Q: p=%8.8lux out of range\n", p); 
1999/1005    
				break; 
			} 
1999/1007    
			XPRINT("  -> %8.8lux h=%8.8lux e=%8.8lux\n", p, q->head, q->entries); 
2001/0131    
			pprint("  -> %8.8lux h=%8.8lux e=%8.8lux\n", p, q->head, q->entries); 
1999/1005    
		} 
	} 
} 
2001/0126/sys/src/9/pc/devusb.c:1310,13152001/0131/sys/src/9/pc/devusb.c:1312,1318
1999/1117    
	ub->bwsop = allocqh(ub); 
1999/1006    
	ub->bulkq = allocqh(ub); 
1999/1117    
	ub->recvq = allocqh(ub); 
2001/0131    
if(0){ /* DISABLE ERRATA FOR NOW */ 
1999/1006    
	t = alloctd(ub);	/* inactive TD, looped */ 
	t->link = PADDR(t); 
	ub->bwsop->entries = PADDR(t); 
2001/0126/sys/src/9/pc/devusb.c:1317,13222001/0131/sys/src/9/pc/devusb.c:1320,1326
1999/1117    
	ub->bwsop->head = PADDR(ub->bulkq) | IsQH; 
	ub->bulkq->head = PADDR(ub->recvq) | IsQH; 
	ub->recvq->head = PADDR(ub->bwsop) | IsQH;	/* loop back */ 
2001/0131    
} 
1999/1118    
	XPRINT("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n", 
1999/1006    
		IN(Cmd), IN(Status), IN(Usbintr), inb(port+Frnum)); 
1999/1118    
	XPRINT("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n", 
2001/0131/sys/src/9/pc/devusb.c:1312,13182001/0201/sys/src/9/pc/devusb.c:1312,1318 (short | long)
1999/1117    
	ub->bwsop = allocqh(ub); 
1999/1006    
	ub->bulkq = allocqh(ub); 
1999/1117    
	ub->recvq = allocqh(ub); 
2001/0131    
if(0){ /* DISABLE ERRATA FOR NOW */ 
2001/0201    
if(0){ 
1999/1006    
	t = alloctd(ub);	/* inactive TD, looped */ 
	t->link = PADDR(t); 
	ub->bwsop->entries = PADDR(t); 
2001/0201/sys/src/9/pc/devusb.c:791,7972001/0216/sys/src/9/pc/devusb.c:791,796 (short | long)
1999/1005    
		return nil; 
	} 
	qt->root = tree; 
2001/0131    
print("tree root %8.8lux\n", tree); 
1999/1005    
	tree->head = Terminate;	/* root */ 
	tree->entries = Terminate; 
	for(i=1; i<n; i++){ 
2001/0201/sys/src/9/pc/devusb.c:799,8052001/0216/sys/src/9/pc/devusb.c:798,803
1999/1005    
		qh->head = PADDR(&tree[(i-1)/2]) | IsQH; 
		qh->entries = Terminate; 
	} 
2001/0131    
print("after loop tree root %8.8lux head %8.8lux entries %8.8lux\n", tree, tree->head, tree->entries); 
1999/1005    
	/* distribute leaves evenly round the frame list */ 
	leaf0 = n/2; 
	for(i=0; i<framesize; i++){ 
2001/0216/sys/src/9/pc/devusb.c:1197,12032001/0503/sys/src/9/pc/devusb.c:1197,1203 (short | long)
1999/1005    
 
	d = usbdeviceofpath(c->qid.path); 
	if(d == nil || d->id != c->qid.vers || d->state == Disabled) 
		error(Ehungup); 
2001/0503    
		return nil; 
1999/1005    
	return d; 
} 
 
2001/0216/sys/src/9/pc/devusb.c:1515,15202001/0503/sys/src/9/pc/devusb.c:1515,1522
1999/1005    
	switch(QID(c->qid)){ 
	case Qctl: 
		d = usbdevice(c); 
2001/0503    
		if (d == nil) 
			error(Ehungup); 
1999/1005    
		if(0&&d->busy) 
			error(Einuse); 
		d->busy = 1; 
2001/0216/sys/src/9/pc/devusb.c:1524,15292001/0503/sys/src/9/pc/devusb.c:1526,1533
1999/1005    
 
	default: 
		d = usbdevice(c); 
2001/0503    
		if (d == nil) 
			error(Ehungup); 
1999/1005    
		s = QID(c->qid) - Qep0; 
		if(s >= 0 && s < nelem(d->ep)){ 
			Endpt *e; 
2001/0216/sys/src/9/pc/devusb.c:1572,15872001/0503/sys/src/9/pc/devusb.c:1576,1588
1999/1005    
	if(c->qid.path & CHDIR || c->qid.path < Q3rd) 
		return; 
	qlock(&usbstate); 
	if(waserror()){ 
		qunlock(&usbstate); 
		nexterror(); 
2001/0503    
	d = usbdeviceofpath(c->qid.path); 
	if (d && d->id == c->qid.vers) { 
		if(QID(c->qid) == Qctl) 
			d->busy = 0; 
		if(c->flag & COPEN) 
			freedev(d); 
1999/1005    
	} 
	d = usbdevice(c); 
	if(QID(c->qid) == Qctl) 
		d->busy = 0; 
	if(c->flag & COPEN) 
		freedev(d); 
	poperror(); 
	qunlock(&usbstate); 
} 
 
2001/0216/sys/src/9/pc/devusb.c:1672,16822001/0503/sys/src/9/pc/devusb.c:1673,1687
1999/1005    
 
	case Qctl: 
		d = usbdevice(c); 
2001/0503    
		if (d == nil) 
			error(Ehungup); 
1999/1005    
		sprint(buf, "%11d %11d ", d->x, d->id); 
		return readstr(offset, a, n, buf); 
 
	case Qsetup:	/* endpoint 0 */ 
		d = usbdevice(c); 
2001/0503    
		if (d == nil) 
			error(Ehungup); 
1999/1005    
		if((e = d->ep[0]) == nil) 
			error(Eio);	/* can't happen */ 
		e->data01 = 1; 
2001/0216/sys/src/9/pc/devusb.c:1694,16992001/0503/sys/src/9/pc/devusb.c:1699,1706
1999/1005    
 
	case Qstatus: 
		d = usbdevice(c); 
2001/0503    
		if (d == nil) 
			error(Ehungup); 
1999/1005    
		s = smalloc(READSTR); 
		if(waserror()){ 
			free(s); 
2001/0216/sys/src/9/pc/devusb.c:1710,17152001/0503/sys/src/9/pc/devusb.c:1717,1724
1999/1005    
 
	default: 
		d = usbdevice(c); 
2001/0503    
		if (d == nil) 
			error(Ehungup); 
1999/1005    
		if((t -= Qep0) < 0 || t >= nelem(d->ep)) 
			error(Eio); 
		if((e = d->ep[t]) == nil || e->mode == OWRITE) 
2001/0216/sys/src/9/pc/devusb.c:1813,18182001/0503/sys/src/9/pc/devusb.c:1822,1829
1999/1005    
		return n; 
	} 
	d = usbdevice(c); 
2001/0503    
	if (d == nil) 
		error(Ehungup); 
1999/1005    
	t = QID(c->qid); 
	switch(t){ 
	case Qctl: 
2001/0503/sys/src/9/pc/devusb.c:718,7252001/0504/sys/src/9/pc/devusb.c:718,725 (short | long)
1999/1005    
	QH *qh; 
	int vf; 
 
	t = alloctde(e, TokIN, e->maxpkt); 
	b = allocb(e->maxpkt); 
2001/0504    
	t = alloctde(e, TokIN, e->maxpkt); 
1999/1005    
	t->bp = b; 
	t->buffer = PADDR(b->wp); 
	ub = &ubus; 
2001/0503/sys/src/9/pc/devusb.c:732,7522001/0504/sys/src/9/pc/devusb.c:732,737
1999/1005    
	} 
	queuetd(ub, qh, t, vf); 
	return qh; 
} 
                 
static Block * 
usbreq(int type, int req, int value, int offset, int count) 
{ 
	Block *b; 
                 
	b = allocb(8); 
	b->wp[0] = type; 
	b->wp[1] = req; 
	PUT2(b->wp+2, value); 
	PUT2(b->wp+4, offset); 
	PUT2(b->wp+6, count); 
	b->wp += 8; 
	return b; 
} 
 
/* 
2001/0504/sys/src/9/pc/devusb.c:718,7252001/0527/sys/src/9/pc/devusb.c:718,725 (short | long)
1999/1005    
	QH *qh; 
	int vf; 
 
	b = allocb(e->maxpkt); 
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; 
2001/0504/sys/src/9/pc/devusb.c:734,7392001/0527/sys/src/9/pc/devusb.c:734,754
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 
 */ 
2001/0504/sys/src/9/pc/devusb.c:816,8292001/0527/sys/src/9/pc/devusb.c:831,844
1999/1005    
	if(t < 0) 
		t = 32; 
	for(i=f; i<t; i++){ 
2001/0131    
		pprint("F%.2d %8.8lux %8.8lux\n", i, frame[i], QFOL(frame[i])->head); 
2001/0527    
		XPRINT("F%.2d %8.8lux %8.8lux\n", i, frame[i], QFOL(frame[i])->head); 
1999/1005    
		for(p=frame[i]; (p & IsQH) && (p &Terminate) == 0; p = q->head){ 
			q = QFOL(p); 
			if(!(q >= tree && q < &tree[n])){ 
2001/0131    
				pprint("Q: p=%8.8lux out of range\n", p); 
2001/0527    
				XPRINT("Q: p=%8.8lux out of range\n", p); 
1999/1005    
				break; 
			} 
2001/0131    
			pprint("  -> %8.8lux h=%8.8lux e=%8.8lux\n", p, q->head, q->entries); 
2001/0527    
			XPRINT("  -> %8.8lux h=%8.8lux e=%8.8lux\n", p, q->head, q->entries); 
1999/1005    
		} 
	} 
} 
2001/0504/sys/src/9/pc/devusb.c:1182,11882001/0527/sys/src/9/pc/devusb.c:1197,1203
1999/1005    
 
	d = usbdeviceofpath(c->qid.path); 
	if(d == nil || d->id != c->qid.vers || d->state == Disabled) 
2001/0503    
		return nil; 
2001/0527    
		error(Ehungup); 
1999/1005    
	return d; 
} 
 
2001/0504/sys/src/9/pc/devusb.c:1223,12582001/0527/sys/src/9/pc/devusb.c:1238,1266
1999/1005    
usbreset(void) 
{ 
1999/1006    
	Pcidev *cfg; 
2001/0126    
	int i, port; 
2001/0527    
	int i; 
	ulong port; 
1999/1006    
	QTree *qt; 
	TD *t; 
1999/1117    
	Ctlr *ub; 
1999/1006    
 
	ub = &ubus; 
2001/0126    
	for(cfg = pcimatch(nil, 0, 0); cfg != nil; cfg = pcimatch(cfg, 0, 0)){ 
		/* 
		 * Look for devices with the correct class and 
		 * sub-class code and known device and vendor ID. 
		 */ 
		if(cfg->ccrb != 0x0C || cfg->ccru != 0x03) 
			continue; 
		switch((cfg->did<<16)|cfg->vid){ 
		default: 
			continue; 
		case (0x7112<<16)|0x8086:	/* 82371[AE]B (PIIX4[E]) */ 
		case (0x719A<<16)|0x8086:	/* 82443MX */ 
		case (0x1106<<16)|0x0586:	/* VIA 82C586 */ 
			break; 
2001/0527    
	memset(&cfg, 0, sizeof(cfg)); 
	cfg = pcimatch(0, 0x8086, 0x7112);	/* Intel chipset PIIX 4*/ 
	if(cfg == nil) { 
		cfg = pcimatch(0, 0x1106, 0x0586);	/* Via chipset */ 
		if(cfg == nil) { 
			DPRINT("No USB device found\n"); 
			return; 
2000/0720    
		} 
2001/0126    
		if((cfg->mem[4].bar & ~0x0F) != 0) 
			break; 
1999/1006    
	} 
2001/0126    
	if(cfg == nil) 
2001/0527    
	port = cfg->mem[4].bar & ~0x0F; 
	if (port == 0) { 
		print("usb: failed to map registers\n"); 
1999/1006    
		return; 
2001/0527    
	} 
1999/1006    
 
2001/0126    
	port = cfg->mem[4].bar & ~0x0F; 
                 
	DPRINT("USB: %x/%x port 0x%ux size 0x%x irq %d\n", 
2001/0527    
	DPRINT("USB: %x/%x port 0x%lux size 0x%x irq %d\n", 
1999/1006    
		cfg->vid, cfg->did, port, cfg->mem[4].size, cfg->intl); 
 
	i = inb(port+SOFMod); 
2001/0504/sys/src/9/pc/devusb.c:1295,13012001/0527/sys/src/9/pc/devusb.c:1303,1308
1999/1117    
	ub->bwsop = allocqh(ub); 
1999/1006    
	ub->bulkq = allocqh(ub); 
1999/1117    
	ub->recvq = allocqh(ub); 
2001/0201    
if(0){ 
1999/1006    
	t = alloctd(ub);	/* inactive TD, looped */ 
	t->link = PADDR(t); 
	ub->bwsop->entries = PADDR(t); 
2001/0504/sys/src/9/pc/devusb.c:1303,13092001/0527/sys/src/9/pc/devusb.c:1310,1315
1999/1117    
	ub->bwsop->head = PADDR(ub->bulkq) | IsQH; 
	ub->bulkq->head = PADDR(ub->recvq) | IsQH; 
	ub->recvq->head = PADDR(ub->bwsop) | IsQH;	/* loop back */ 
2001/0131    
} 
1999/1118    
	XPRINT("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n", 
1999/1006    
		IN(Cmd), IN(Status), IN(Usbintr), inb(port+Frnum)); 
1999/1118    
	XPRINT("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n", 
2001/0504/sys/src/9/pc/devusb.c:1500,15072001/0527/sys/src/9/pc/devusb.c:1506,1511
1999/1005    
	switch(QID(c->qid)){ 
	case Qctl: 
		d = usbdevice(c); 
2001/0503    
		if (d == nil) 
			error(Ehungup); 
1999/1005    
		if(0&&d->busy) 
			error(Einuse); 
		d->busy = 1; 
2001/0504/sys/src/9/pc/devusb.c:1511,15182001/0527/sys/src/9/pc/devusb.c:1515,1520
1999/1005    
 
	default: 
		d = usbdevice(c); 
2001/0503    
		if (d == nil) 
			error(Ehungup); 
1999/1005    
		s = QID(c->qid) - Qep0; 
		if(s >= 0 && s < nelem(d->ep)){ 
			Endpt *e; 
2001/0504/sys/src/9/pc/devusb.c:1561,15732001/0527/sys/src/9/pc/devusb.c:1563,1578
1999/1005    
	if(c->qid.path & CHDIR || c->qid.path < Q3rd) 
		return; 
	qlock(&usbstate); 
2001/0503    
	d = usbdeviceofpath(c->qid.path); 
	if (d && d->id == c->qid.vers) { 
		if(QID(c->qid) == Qctl) 
			d->busy = 0; 
		if(c->flag & COPEN) 
			freedev(d); 
2001/0527    
	if(waserror()){ 
		qunlock(&usbstate); 
		nexterror(); 
1999/1005    
	} 
2001/0527    
	d = usbdevice(c); 
	if(QID(c->qid) == Qctl) 
		d->busy = 0; 
	if(c->flag & COPEN) 
		freedev(d); 
	poperror(); 
1999/1005    
	qunlock(&usbstate); 
} 
 
2001/0504/sys/src/9/pc/devusb.c:1658,16722001/0527/sys/src/9/pc/devusb.c:1663,1673
1999/1005    
 
	case Qctl: 
		d = usbdevice(c); 
2001/0503    
		if (d == nil) 
			error(Ehungup); 
1999/1005    
		sprint(buf, "%11d %11d ", d->x, d->id); 
		return readstr(offset, a, n, buf); 
 
	case Qsetup:	/* endpoint 0 */ 
		d = usbdevice(c); 
2001/0503    
		if (d == nil) 
			error(Ehungup); 
1999/1005    
		if((e = d->ep[0]) == nil) 
			error(Eio);	/* can't happen */ 
		e->data01 = 1; 
2001/0504/sys/src/9/pc/devusb.c:1684,16912001/0527/sys/src/9/pc/devusb.c:1685,1690
1999/1005    
 
	case Qstatus: 
		d = usbdevice(c); 
2001/0503    
		if (d == nil) 
			error(Ehungup); 
1999/1005    
		s = smalloc(READSTR); 
		if(waserror()){ 
			free(s); 
2001/0504/sys/src/9/pc/devusb.c:1702,17092001/0527/sys/src/9/pc/devusb.c:1701,1706
1999/1005    
 
	default: 
		d = usbdevice(c); 
2001/0503    
		if (d == nil) 
			error(Ehungup); 
1999/1005    
		if((t -= Qep0) < 0 || t >= nelem(d->ep)) 
			error(Eio); 
		if((e = d->ep[t]) == nil || e->mode == OWRITE) 
2001/0504/sys/src/9/pc/devusb.c:1807,18142001/0527/sys/src/9/pc/devusb.c:1804,1809
1999/1005    
		return n; 
	} 
	d = usbdevice(c); 
2001/0503    
	if (d == nil) 
		error(Ehungup); 
1999/1005    
	t = QID(c->qid); 
	switch(t){ 
	case Qctl: 
2001/0527/sys/src/9/pc/devusb.c:879,19662001/0624/sys/src/9/pc/devusb.c:879,882 (short | long)
1999/1005    
	QH *qh; 
	int q; 
 
	if(!e->periodic || e->sched >= 0) 
		return 0; 
	ub = &ubus; 
	if(e->epq == nil){ 
		e->epq = allocqh(ub); 
		if(e->epq == nil) 
			return -1; 
	} 
	qlock(ub->tree); 
	q = pickschedq(ub->tree, e->pollms, e->bw, ~0);	/* TO DO: bus bandwidth limit */ 
	if(q < 0) 
		return -1; 
	ub->tree->bw[q] += e->bw; 
	qh = &ub->tree->root[q]; 
	e->sched = q; 
	e->epq->head = qh->entries; 
	e->epq->entries = Terminate; 
	qh->entries = PADDR(e->epq) | IsQH; 
	qunlock(ub->tree); 
	return 0; 
} 
                 
static void 
unschedendpt(Endpt *e) 
{ 
	Ctlr *ub; 
	ulong p; 
	QH *qh; 
	int q; 
                 
	ub = &ubus; 
1999/1117    
	if(!e->periodic || (q = e->sched) < 0) 
1999/1005    
		return; 
	p = PADDR(e->epq) | IsQH; 
	qlock(ub->tree); 
	ub->tree->bw[q] -= e->bw; 
	qh = &ub->tree->root[q]; 
	for(; qh->entries != p; qh = QFOL(qh->entries)) 
		if(qh->entries & Terminate || (qh->entries & IsQH) == 0){ 
			qunlock(ub->tree); 
			panic("usb: unschedendpt"); 
		} 
	qh->entries = e->epq->head; 
	qunlock(ub->tree); 
	e->epq->head = Terminate; 
} 
                 
static Endpt * 
devendpt(Udev *d, int id, int add) 
{ 
	Endpt *e, **p; 
1999/1117    
	Ctlr *ub; 
1999/1005    
                 
1999/1117    
	ub = &ubus; 
1999/1005    
	p = &d->ep[id&0xF]; 
	lock(d); 
	if((e = *p) != nil){ 
		incref(e); 
		unlock(d); 
		return e; 
	} 
	unlock(d); 
	if(!add) 
		return nil; 
	e = mallocz(sizeof(*e), 1); 
	e->ref = 1; 
	e->x = id&0xF; 
	e->id = id; 
	e->periodic = 0; 
	e->sched = -1; 
	e->maxpkt = 8; 
	e->pollms = 0; 
	e->bw = 0; 
	e->nbuf = 1; 
	e->dev = d; 
	e->active = 0; 
1999/1116    
	e->epq = allocqh(ub); 
	if(e->epq == nil) 
		panic("devendpt"); 
                 
1999/1005    
	lock(d); 
	if(*p != nil){ 
		incref(*p); 
		unlock(d); 
		free(e); 
		return *p; 
	} 
	*p = e; 
	unlock(d); 
	e->rq = qopen(8*1024, 0, nil, e); 
	e->wq = qopen(8*1024, 0, nil, e); 
	return e; 
} 
                 
static void 
freept(Endpt *e) 
{ 
	if(e != nil && decref(e) == 0){ 
		XPRINT("freept(%d,%d)\n", e->dev->x, e->x); 
		unschedendpt(e); 
		e->dev->ep[e->x] = nil; 
		eptdeactivate(e); 
		if(e->epq != nil) 
			freeqh(&ubus, e->epq); 
		free(e); 
	} 
} 
                 
static void 
usbdevreset(Udev *d) 
{ 
	d->state = Disabled; 
2000/0724    
	if(Class(d->csp) == Hubclass) 
1999/1005    
		for(d = d->ports; d != nil; d = d->next) 
			usbdevreset(d); 
} 
                 
static void 
freedev(Udev *d) 
{ 
	int i; 
                 
	if(d != nil && decref(d) == 0){ 
		for(i=0; i<nelem(d->ep); i++) 
			freept(d->ep[i]); 
		if(d->x >= 0) 
			usbdev[d->x] = nil; 
		free(d); 
	} 
} 
                 
static void 
hubportreset(Udev *h, int p) 
{ 
	USED(h, p); 
	/* reset state of each attached device? */ 
} 
                 
static	int	ioports[] = {-1, Portsc0, Portsc1}; 
                 
static void 
portreset(int port) 
{ 
	Ctlr *ub; 
	int i, p; 
                 
	/* should check that device not being configured on other port? */ 
	p = ioports[port]; 
	ub = &ubus; 
	qlock(&ub->resetl); 
	if(waserror()){ 
		qunlock(&ub->resetl); 
		nexterror(); 
	} 
	XPRINT("r: %x\n", IN(p)); 
	ilock(ub); 
	OUT(p, PortReset); 
	delay(12);	/* BUG */ 
	XPRINT("r2: %x\n", IN(p)); 
	OUT(p, IN(p) & ~PortReset); 
	XPRINT("r3: %x\n", IN(p)); 
	OUT(p, IN(p) | PortEnable); 
	microdelay(64); 
	for(i=0; i<1000 && (IN(p) & PortEnable) == 0; i++) 
		; 
	XPRINT("r': %x %d\n", IN(p), i); 
	OUT(p, (IN(p) & ~PortReset)|PortEnable); 
	iunlock(ub); 
	hubportreset(nil, port); 
	poperror(); 
	qunlock(&ub->resetl); 
} 
                 
static void 
portenable(int port, int on) 
{ 
	Ctlr *ub; 
	int w, p; 
                 
	/* should check that device not being configured on other port? */ 
	p = ioports[port]; 
	ub = &ubus; 
	qlock(&ub->resetl); 
	if(waserror()){ 
		qunlock(&ub->resetl); 
		nexterror(); 
	} 
	ilock(ub); 
	w = IN(p); 
	if(on) 
		w |= PortEnable; 
	else 
		w &= ~PortEnable; 
	OUT(p, w); 
	microdelay(64); 
	iunlock(ub); 
	XPRINT("e: %x\n", IN(p)); 
	if(!on) 
		hubportreset(nil, port); 
	poperror(); 
	qunlock(&ub->resetl); 
} 
                 
static int 
portinfo(Ctlr *ub, int *p0, int *p1) 
{ 
	int m, v; 
                 
	ilock(ub); 
	m = 0; 
	if((v = IN(Portsc0)) & PortChange){ 
		OUT(Portsc0, v); 
		m |= 1<<0; 
	} 
	*p0 = v; 
	if((v = IN(Portsc1)) & PortChange){ 
		OUT(Portsc1, v); 
		m |= 1<<1; 
	} 
	*p1 = v; 
	iunlock(ub); 
	return m; 
} 
                 
static void 
interrupt(Ureg*, void *a) 
{ 
	Ctlr *ub; 
	Endpt *e; 
	int s; 
1999/1117    
	QH *q; 
1999/1005    
                 
	ub = a; 
	s = IN(Status); 
1999/1116    
	XPRINT("usbint: #%x f%d\n", s, IN(Frnum)); 
1999/1005    
	if (s & 0x1a) { 
1999/1007    
		XPRINT("cmd #%x sofmod #%x\n", IN(Cmd), inb(ub->io+SOFMod)); 
		XPRINT("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1)); 
1999/1005    
	} 
	OUT(Status, s); 
                 
1999/1116    
	XPRINT("cleanq(ub->ctlq, 0, 0)\n"); 
	cleanq(ub->ctlq, 0, 0); 
	XPRINT("cleanq(ub->bulkq, 0, Vf)\n"); 
	cleanq(ub->bulkq, 0, Vf); 
1999/1117    
	XPRINT("clean recvq\n"); 
	for (q = ub->recvq->next; q; q = q->hlink) { 
		XPRINT("cleanq(q, 0, Vf)\n"); 
		cleanq(q, 0, Vf); 
	} 
1999/1005    
	ilock(&activends); 
	for(e = activends.f; e != nil; e = e->activef) 
		if(e->epq != nil) { 
1999/1116    
			XPRINT("cleanq(e->epq, 0, 0)\n"); 
			cleanq(e->epq, 0, 0); 
1999/1005    
		} 
	iunlock(&activends); 
} 
                 
enum 
{ 
	Qtopdir = 0, 
	Q2nd, 
	Qbusctl, 
	Qnew, 
	Qport, 
	Q3rd, 
	Qctl, 
	Qsetup, 
	Qdebug, 
	Qstatus, 
	Qep0, 
	/* other endpoint files */ 
}; 
                 
/* 
 * Qid path is: 
 *	 8 bits of file type (qids above) 
 *	10 bits of slot number +1; 0 means not attached to device 
 */ 
#define	QSHIFT	8	/* location in qid of device # */ 
#define	QMASK	((1<<QSHIFT)-1) 
                 
#define	QID(q)		((q).path&QMASK) 
#define	DEVPATH(q)	(((q)&~CHDIR)>>QSHIFT) 
                 
static Dirtab usbdir2[] = { 
1999/1118    
	"new",		{Qnew},		0,	0666, 
1999/1005    
	"ctl",		{Qbusctl},	0,	0666, 
1999/1118    
	"port",		{Qport},	0,	0444, 
1999/1005    
}; 
                 
static Dirtab usbdir3[]={ 
1999/1118    
	"ctl",		{Qctl},		0,	0666, 
1999/1005    
	"setup",	{Qsetup},	0,	0666, 
	"status",	{Qstatus},	0,	0444, 
	"debug",	{Qdebug},	1,	0666, 
	/* epNdata names are generated on demand */ 
}; 
                 
static Udev * 
usbdeviceofpath(ulong path) 
{ 
	int s; 
                 
	s = DEVPATH(path); 
	if(s == 0) 
		return nil; 
	return usbdev[s-1]; 
} 
                 
static Udev * 
usbdevice(Chan *c) 
{ 
	Udev *d; 
                 
	d = usbdeviceofpath(c->qid.path); 
	if(d == nil || d->id != c->qid.vers || d->state == Disabled) 
2001/0527    
		error(Ehungup); 
1999/1005    
	return d; 
} 
                 
static Udev * 
usbnewdevice(void) 
{ 
	Udev *d; 
	Endpt *e; 
	int i; 
                 
	d = nil; 
	qlock(&usbstate); 
	if(waserror()){ 
		qunlock(&usbstate); 
		nexterror(); 
	} 
	for(i=0; i<nelem(usbdev); i++) 
		if(usbdev[i] == nil){ 
			ubus.idgen++; 
			d = mallocz(sizeof(*d), 1); 
			d->ref = 1; 
			d->x = i; 
			d->id = (ubus.idgen << 8) | i; 
			d->state = Enabled; 
			e = devendpt(d, 0, 1);	/* always provide control endpoint 0 */ 
			e->mode = ORDWR; 
			e->periodic = 0; 
			e->sched = -1; 
			usbdev[i] = d; 
			break; 
		} 
	poperror(); 
	qunlock(&usbstate); 
	return d; 
} 
                 
static void 
usbreset(void) 
{ 
1999/1006    
	Pcidev *cfg; 
2001/0527    
	int i; 
	ulong port; 
1999/1006    
	QTree *qt; 
	TD *t; 
1999/1117    
	Ctlr *ub; 
1999/1006    
                 
	ub = &ubus; 
2001/0527    
	memset(&cfg, 0, sizeof(cfg)); 
	cfg = pcimatch(0, 0x8086, 0x7112);	/* Intel chipset PIIX 4*/ 
	if(cfg == nil) { 
		cfg = pcimatch(0, 0x1106, 0x0586);	/* Via chipset */ 
		if(cfg == nil) { 
			DPRINT("No USB device found\n"); 
			return; 
2000/0720    
		} 
1999/1006    
	} 
2001/0527    
	port = cfg->mem[4].bar & ~0x0F; 
	if (port == 0) { 
		print("usb: failed to map registers\n"); 
1999/1006    
		return; 
2001/0527    
	} 
1999/1006    
                 
2001/0527    
	DPRINT("USB: %x/%x port 0x%lux size 0x%x irq %d\n", 
1999/1006    
		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); 
1999/1209    
	/* 
	 * Interrupt handler. 
	 * Bail out if no IRQ assigned by the BIOS. 
	 */ 
2000/0517    
	if(cfg->intl == 0xFF || cfg->intl == 0) 
1999/1209    
		return; 
1999/1006    
	intrenable(cfg->intl, interrupt, ub, cfg->tbdf, "usb"); 
                 
	ub->io = port; 
	ub->tdpool = xspanalloc(128*sizeof(TD), 16, 0); 
	for(i=128; --i>=0;){ 
		ub->tdpool[i].next = ub->freetd; 
		ub->freetd = &ub->tdpool[i]; 
	} 
	ub->qhpool = xspanalloc(32*sizeof(QH), 16, 0); 
	for(i=32; --i>=0;){ 
		ub->qhpool[i].next = ub->freeqh; 
		ub->freeqh = &ub->qhpool[i]; 
	} 
                 
	/* 
	 * the root of the periodic (interrupt & isochronous) scheduling tree 
	 * points to the control queue and the bandwidth sop for bulk traffic. 
	 * this is looped following the instructions in PIIX4 errata 29773804.pdf: 
	 * a QH links to a looped but inactive TD as its sole entry, 
	 * with its head entry leading on to the bulk traffic, the last QH of which 
	 * links back to the empty QH. 
	 */ 
1999/1117    
	ub->ctlq = allocqh(ub); 
	ub->bwsop = allocqh(ub); 
1999/1006    
	ub->bulkq = allocqh(ub); 
1999/1117    
	ub->recvq = allocqh(ub); 
1999/1006    
	t = alloctd(ub);	/* inactive TD, looped */ 
	t->link = PADDR(t); 
	ub->bwsop->entries = PADDR(t); 
	ub->ctlq->head = PADDR(ub->bwsop) | IsQH; 
1999/1117    
	ub->bwsop->head = PADDR(ub->bulkq) | IsQH; 
	ub->bulkq->head = PADDR(ub->recvq) | IsQH; 
	ub->recvq->head = PADDR(ub->bwsop) | IsQH;	/* loop back */ 
1999/1118    
	XPRINT("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n", 
1999/1006    
		IN(Cmd), IN(Status), IN(Usbintr), inb(port+Frnum)); 
1999/1118    
	XPRINT("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n", 
1999/1006    
		IN(Flbaseadd), inb(port+SOFMod), IN(Portsc0), IN(Portsc1)); 
	OUT(Cmd, 0);	/* stop */ 
	ub->frames = xspanalloc(FRAMESIZE, FRAMESIZE, 0); 
	qt = mkqhtree(ub->frames, NFRAME, 32); 
	if(qt == nil){ 
		print("usb: can't allocate scheduling tree\n"); 
		ub->io = 0; 
		return; 
	} 
	qt->root->head = PADDR(ub->ctlq) | IsQH; 
	ub->tree = qt; 
1999/1118    
	XPRINT("usb tree: nel=%d depth=%d\n", qt->nel, qt->depth); 
1999/1006    
                 
	outl(port+Flbaseadd, PADDR(ub->frames)); 
	OUT(Frnum, 0); 
	OUT(Usbintr, 0xF);	/* enable all interrupts */ 
1999/1118    
	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)); 
1999/1005    
} 
                 
void 
usbinit(void) 
{ 
	Udev *d; 
                 
	if(ubus.io != 0 && usbdev[0] == nil){ 
		d = usbnewdevice();	/* reserve device 0 for configuration */ 
		incref(d); 
		d->state = Attached; 
	} 
} 
                 
Chan * 
usbattach(char *spec) 
{ 
	Ctlr *ub; 
                 
	ub = &ubus; 
	if(ub->io == 0) 
		error(Enodev); 
	if((IN(Cmd)&1)==0 || *spec) 
		OUT(Cmd, 1);	/* run */ 
//	pprint("at: c=%x s=%x c0=%x\n", IN(Cmd), IN(Status), IN(Portsc0)); 
	return devattach('U', spec); 
} 
                 
Chan * 
usbclone(Chan *c, Chan *nc) 
{ 
	return devclone(c, nc); 
} 
                 
static int 
usbgen(Chan *c, Dirtab*, int, int s, Dir *dp) 
{ 
	int t; 
	Qid q; 
	ulong path; 
	Udev *d; 
	Dirtab *tab; 
	Endpt *e; 
	char buf[NAMELEN]; 
                 
	/* 
	 * Top level directory contains the name of the device. 
	 */ 
	if(c->qid.path == CHDIR){ 
1999/1230    
		if(s == DEVDOTDOT){ 
			devdir(c, (Qid){CHDIR, 0}, "#U", 0, eve, 0555, dp); 
			return 1; 
		} 
1999/1005    
		if(s == 0){ 
			devdir(c, (Qid){CHDIR|Q2nd, 0}, "usb", 0, eve, 0555, dp); 
			return 1; 
		} 
		return -1; 
	} 
                 
	/* 
	 * Second level contains "new" plus all the clients. 
	 */ 
	t = QID(c->qid); 
	if(t < Q3rd){ 
1999/1230    
		if(s == DEVDOTDOT){ 
			devdir(c, (Qid){CHDIR, 0}, "#U", 0, eve, 0555, dp); 
			return 1; 
		} 
1999/1005    
		if(s < nelem(usbdir2)){ 
			tab = &usbdir2[s]; 
			devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); 
			return 1; 
		} 
		if((s -= nelem(usbdir2)) >= 0 && s < nelem(usbdev)){ 
			d = usbdev[s]; 
			if(d == nil) 
				return 0; 
			sprint(buf, "%d", s); 
			q = (Qid){CHDIR|((s+1)<<QSHIFT)|Q3rd, d->id}; 
			devdir(c, q, buf, 0, eve, 0555, dp); 
			return 1; 
		} 
		return -1; 
	} 
                 
	/* 
	 * Third level. 
	 */ 
	path = c->qid.path&~(CHDIR|QMASK);	/* slot component */ 
	q.vers = c->qid.vers; 
1999/1230    
	if(s == DEVDOTDOT){ 
		devdir(c, (Qid){CHDIR|Q2nd, 0}, "usb", 0, eve, 0555, dp); 
		return 1; 
	} 
1999/1005    
	if(s < nelem(usbdir3)){ 
		Dirtab *tab = &usbdir3[s]; 
		q.path = path | tab->qid.path; 
		devdir(c, q, tab->name, tab->length, eve, tab->perm, dp); 
		return 1; 
	} 
                 
	/* active endpoints */ 
	d = usbdeviceofpath(path); 
	if(d == nil) 
		return -1; 
	s -= nelem(usbdir3); 
	if(s < 0 || s >= nelem(d->ep)) 
		return -1; 
	if(s == 0 || (e = d->ep[s]) == nil)	/* ep0data is called "setup" */ 
		return 0; 
	sprint(buf, "ep%ddata", s); 
	q.path = path | (Qep0+s); 
	devdir(c, q, buf, 0, eve, e->mode==OREAD? 0444: e->mode==OWRITE? 0222: 0666, dp); 
	return 1; 
} 
                 
int 
usbwalk(Chan *c, char *name) 
{ 
	if(strcmp(name, "..") == 0){ 
		switch(QID(c->qid)){ 
		case Qtopdir: 
			return 1; 
		case Q2nd: 
			c->qid = (Qid){CHDIR|Qtopdir, 0}; 
			break; 
		case Q3rd: 
			c->qid = (Qid){CHDIR|Q2nd, 0}; 
			break; 
		default: 
			panic("usbwalk %lux", c->qid.path); 
		} 
		return 1; 
	} 
	return devwalk(c, name, nil, 0, usbgen); 
} 
                 
void 
usbstat(Chan *c, char *db) 
{ 
	devstat(c, db, nil, 0, usbgen); 
} 
                 
Chan * 
usbopen(Chan *c, int omode) 
{ 
	Udev *d; 
	int f, s; 
                 
	if(c->qid.path & CHDIR) 
		return devopen(c, omode, nil, 0, usbgen); 
                 
	f = 0; 
	if(QID(c->qid) == Qnew){ 
		d = usbnewdevice(); 
		if(d == nil) 
			error(Enodev); 
		c->qid.path = Qctl|((d->x+1)<<QSHIFT); 
		c->qid.vers = d->id; 
		f = 1; 
	} 
                 
	if(c->qid.path < Q3rd) 
		return devopen(c, omode, nil, 0, usbgen); 
                 
	qlock(&usbstate); 
	if(waserror()){ 
		qunlock(&usbstate); 
		nexterror(); 
	} 
                 
	switch(QID(c->qid)){ 
	case Qctl: 
		d = usbdevice(c); 
		if(0&&d->busy) 
			error(Einuse); 
		d->busy = 1; 
		if(!f) 
			incref(d); 
		break; 
                 
	default: 
		d = usbdevice(c); 
		s = QID(c->qid) - Qep0; 
		if(s >= 0 && s < nelem(d->ep)){ 
			Endpt *e; 
			if((e = d->ep[s]) == nil) 
				error(Enodev); 
			if(schedendpt(e) < 0) 
				error("can't schedule USB endpoint"); 
			eptactivate(e); 
		} 
		incref(d); 
		break; 
	} 
	poperror(); 
	qunlock(&usbstate); 
	c->mode = openmode(omode); 
	c->flag |= COPEN; 
	c->offset = 0; 
	return c; 
} 
                 
void 
usbcreate(Chan *c, char *name, int omode, ulong perm) 
{ 
	USED(c, name, omode, perm); 
	error(Eperm); 
} 
                 
void 
usbremove(Chan*) 
{ 
	error(Eperm); 
} 
                 
void 
usbwstat(Chan *c, char *dp) 
{ 
	USED(c, dp); 
	error(Eperm); 
} 
                 
void 
usbclose(Chan *c) 
{ 
	Udev *d; 
                 
	if(c->qid.path & CHDIR || c->qid.path < Q3rd) 
		return; 
	qlock(&usbstate); 
2001/0527    
	if(waserror()){ 
		qunlock(&usbstate); 
		nexterror(); 
1999/1005    
	} 
2001/0527    
	d = usbdevice(c); 
	if(QID(c->qid) == Qctl) 
		d->busy = 0; 
	if(c->flag & COPEN) 
		freedev(d); 
	poperror(); 
1999/1005    
	qunlock(&usbstate); 
} 
                 
static int 
eptinput(void *arg) 
{ 
	Endpt *e; 
                 
	e = arg; 
	return e->eof || e->err || qcanread(e->rq); 
} 
                 
static long 
readusb(Endpt *e, void *a, long n) 
{ 
	Block *b; 
	uchar *p; 
	long l, i; 
                 
1999/1116    
	XPRINT("qlock(%p)\n", &e->rlock); 
1999/1005    
	qlock(&e->rlock); 
1999/1116    
	XPRINT("got qlock(%p)\n", &e->rlock); 
1999/1005    
	if(waserror()){ 
		qunlock(&e->rlock); 
		eptcancel(e); 
		nexterror(); 
	} 
	p = a; 
	do { 
		if(e->eof) { 
1999/1007    
			XPRINT("e->eof\n"); 
1999/1005    
			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) { 
1999/1007    
			XPRINT("b == nil\n"); 
1999/1005    
			break; 
		} 
		if(waserror()){ 
			freeb(b); 
			nexterror(); 
		} 
		l = BLEN(b); 
		if((i = l) > n) 
			i = n; 
		if(i > 0){ 
			memmove(p, b->rp, i); 
			p += i; 
		} 
		poperror(); 
		freeb(b); 
		n -= i; 
	} while (l == e->maxpkt && n > 0); 
	poperror(); 
	qunlock(&e->rlock); 
	return p-(uchar*)a; 
} 
                 
long 
usbread(Chan *c, void *a, long n, vlong offset) 
{ 
	Endpt *e; 
	Udev *d; 
	char buf[48], *s; 
	int t, w0, w1, ps, l, i; 
                 
	if(c->qid.path & CHDIR) 
		return devdirread(c, a, n, nil, 0, usbgen); 
                 
	t = QID(c->qid); 
	switch(t){ 
	case Qbusctl: 
		snprint(buf, sizeof(buf), "%11d %11d ", 0, 0); 
		return readstr(offset, a, n, buf); 
                 
	case Qport: 
		ps = portinfo(&ubus, &w0, &w1); 
		snprint(buf, sizeof(buf), "0x%ux 0x%ux 0x%ux ", ps, w0, w1); 
		return readstr(offset, a, n, buf); 
                 
	case Qctl: 
		d = usbdevice(c); 
		sprint(buf, "%11d %11d ", d->x, d->id); 
		return readstr(offset, a, n, buf); 
                 
	case Qsetup:	/* endpoint 0 */ 
		d = usbdevice(c); 
		if((e = d->ep[0]) == nil) 
			error(Eio);	/* can't happen */ 
		e->data01 = 1; 
		n = readusb(e, a, n); 
		if(e->setin){ 
			e->setin = 0; 
			e->data01 = 1; 
			writeusb(e, "", 0, TokOUT); 
		} 
		break; 
                 
	case Qdebug: 
		n=0; 
		break; 
                 
	case Qstatus: 
		d = usbdevice(c); 
		s = smalloc(READSTR); 
		if(waserror()){ 
			free(s); 
			nexterror(); 
		} 
2000/0725    
		l = snprint(s, READSTR, "%s %#6.6lux\n", devstates[d->state], d->csp); 
1999/1005    
		for(i=0; i<nelem(d->ep); i++) 
			if((e = d->ep[i]) != nil)	/* TO DO: freeze e */ 
2000/0725    
				l += snprint(s+l, READSTR-l, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks); 
1999/1005    
		n = readstr(offset, a, n, s); 
		poperror(); 
		free(s); 
		break; 
                 
	default: 
		d = usbdevice(c); 
		if((t -= Qep0) < 0 || t >= nelem(d->ep)) 
			error(Eio); 
		if((e = d->ep[t]) == nil || e->mode == OWRITE) 
			error(Eio);	/* can't happen */ 
		n=readusb(e, a, n); 
		break; 
	} 
	return n; 
} 
                 
static int 
qisempty(void *arg) 
{ 
	return ((QH*)arg)->entries & Terminate; 
} 
                 
static long 
writeusb(Endpt *e, void *a, long n, int tok) 
{ 
	long i; 
	Block *b; 
	uchar *p; 
	QH *qh; 
                 
	p = a; 
	qlock(&e->wlock); 
	if(waserror()){ 
		qunlock(&e->wlock); 
		eptcancel(e); 
		nexterror(); 
	} 
	do { 
		int j; 
                 
		if(e->err) 
			error(e->err); 
		if((i = n) >= e->maxpkt) 
			i = e->maxpkt; 
		b = allocb(i); 
		if(waserror()){ 
			freeb(b); 
			nexterror(); 
		} 
		XPRINT("out [%ld]", i); 
		for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]); 
		XPRINT("\n"); 
		memmove(b->wp, p, i); 
		b->wp += i; 
		p += i; 
		n -= i; 
		poperror(); 
		qh = qxmit(e, b, tok); 
		tok = TokOUT; 
		if(!e->iso) 
			e->data01 ^= 1; 
		if(e->ntd >= e->nbuf) { 
			XPRINT("writeusb: sleep %lux\n", &e->wr); 
			sleep(&e->wr, qisempty, qh); 
			XPRINT("writeusb: awake\n"); 
		} 
	} while(n > 0); 
	poperror(); 
	qunlock(&e->wlock); 
	return p-(uchar*)a; 
} 
                 
long 
usbwrite(Chan *c, void *a, long n, vlong) 
{ 
	Udev *d; 
	Endpt *e; 
	int id, nw, nf, t, i; 
	char cmd[50], *fields[10]; 
                 
	if(c->qid.path & CHDIR) 
		error(Egreg); 
	t = QID(c->qid); 
	if(t == Qbusctl){ 
		if(n >= sizeof(cmd)-1) 
			n = sizeof(cmd)-1; 
		memmove(cmd, a, n); 
		cmd[n] = 0; 
2000/0308    
		nf = getfields(cmd, fields, nelem(fields), 0, " \t\n"); 
1999/1005    
		if(nf==1 && strcmp(fields[0], "dump")==0){ 
			dumpframe(-1, -1); 
			return n; 
		} 
		if(nf < 2) 
			error(Ebadarg); 
		id = strtol(fields[1], nil, 0); 
		if(id != 1 && id != 2) 
			error(Ebadarg);	/* there are two ports on the root hub */ 
		if(strcmp(fields[0], "reset") == 0) 
			portreset(id); 
		else if(strcmp(fields[0], "enable") == 0) 
			portenable(id, 1); 
		else if(strcmp(fields[0], "disable") == 0) 
			portenable(id, 0); 
		else 
			error(Ebadarg); 
		return n; 
	} 
	d = usbdevice(c); 
	t = QID(c->qid); 
	switch(t){ 
	case Qctl: 
		if(n >= sizeof(cmd)-1) 
			n = sizeof(cmd)-1; 
		memmove(cmd, a, n); 
		cmd[n] = 0; 
2000/0308    
		nf = getfields(cmd, fields, nelem(fields), 0, " \t\n"); 
1999/1005    
		if(nf > 1 && strcmp(fields[0], "speed") == 0){ 
			d->ls = strtoul(fields[1], nil, 0) == 0; 
2000/0724    
		} else if(nf > 3 && strcmp(fields[0], "class") == 0){ 
1999/1120    
			i = strtoul(fields[2], nil, 0); 
			d->npt = strtoul(fields[1], nil, 0); 
2000/0724    
			/* class config# csp ( == class subclass proto) */ 
1999/1120    
			if (i < 0 || i >= nelem(d->ep) 
			 || d->npt > nelem(d->ep) || i >= d->npt) 
				error(Ebadarg); 
			if (i == 0) { 
2000/0724    
				d->csp = strtoul(fields[3], nil, 0); 
1999/1120    
			} 
			if(d->ep[i] == nil) 
				d->ep[i] = devendpt(d, i, 1); 
2000/0724    
			d->ep[i]->csp = strtoul(fields[3], nil, 0); 
1999/1005    
		}else if(nf > 2 && strcmp(fields[0], "data") == 0){ 
			i = strtoul(fields[1], nil, 0); 
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) 
				error(Ebadarg); 
			e = d->ep[i]; 
			e->data01 = strtoul(fields[2], nil, 0) != 0; 
		}else if(nf > 2 && strcmp(fields[0], "maxpkt") == 0){ 
			i = strtoul(fields[1], nil, 0); 
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) 
				error(Ebadarg); 
			e = d->ep[i]; 
			e->maxpkt = strtoul(fields[2], nil, 0); 
			if(e->maxpkt > 1500) 
				e->maxpkt = 1500; 
		}else if(nf > 2 && strcmp(fields[0], "debug") == 0){ 
			i = strtoul(fields[1], nil, 0); 
1999/1116    
			if(i < -1 || i >= nelem(d->ep) || d->ep[i] == nil) 
1999/1005    
				error(Ebadarg); 
1999/1116    
			if (i == -1) 
				debug = 0; 
			else { 
				debug = 1; 
				e = d->ep[i]; 
				e->debug = strtoul(fields[2], nil, 0); 
			} 
1999/1005    
		}else if(nf > 1 && strcmp(fields[0], "unstall") == 0){ 
			i = strtoul(fields[1], nil, 0); 
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) 
				error(Ebadarg); 
			e = d->ep[i]; 
			e->err = nil; 
		}else if(nf == 6 && strcmp(fields[0], "ep") == 0){ 
			/* ep n maxpkt mode poll nbuf */ 
			i = strtoul(fields[1], nil, 0); 
1999/1006    
			if(i < 0 || i >= nelem(d->ep)) { 
1999/1007    
				XPRINT("field 1: 0 <= %d < %d\n", i, nelem(d->ep)); 
1999/1005    
				error(Ebadarg); 
1999/1006    
			} 
1999/1120    
			if((e = d->ep[i]) == nil) 
				e = devendpt(d, i, 1); 
1999/1005    
			if(waserror()){ 
				freept(e); 
				nexterror(); 
			} 
			i = strtoul(fields[2], nil, 0); 
			if(i < 8 || i > 1023) 
				i = 8; 
			e->maxpkt = i; 
2000/0725    
			e->mode = strcmp(fields[3],"r") == 0? OREAD : 
					  strcmp(fields[3],"w") == 0? OWRITE : ORDWR; 
1999/1005    
			e->periodic = 0; 
			e->sched = -1; 
1999/1117    
			if(strcmp(fields[4], "bulk") == 0){ 
				Ctlr *ub; 
                 
				ub = &ubus; 
				/* Each bulk device gets a queue head hanging off the 
				 * bulk queue head 
				 */ 
				if (e->epq == nil) { 
					e->epq = allocqh(ub); 
					if(e->epq == nil) 
						panic("usbwrite: allocqh"); 
				} 
				queueqh(e->epq); 
			} else { 
1999/1005    
				e->periodic = 1; 
				i = strtoul(fields[4], nil, 0); 
				if(i > 0 && i <= 1000) 
					e->pollms = i; 
1999/1006    
				else { 
1999/1007    
					XPRINT("field 4: 0 <= %d <= 1000\n", i); 
1999/1005    
					error(Ebadarg); 
1999/1006    
				} 
1999/1005    
			} 
			i = strtoul(fields[5], nil, 0); 
			if(i >= 1 && i <= 32) 
				e->nbuf = i; 
			poperror(); 
1999/1006    
		}else { 
1999/1007    
			XPRINT("command %s, fields %d\n", fields[0], nf); 
1999/1005    
			error(Ebadarg); 
1999/1006    
		} 
1999/1005    
		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] */ 
2000/0725    
		if((t -= Qep0) < 0 || t >= nelem(d->ep)) { 
			print("t = %d\n", t); 
1999/1005    
			error(Eio); 
2000/0725    
		} 
		if((e = d->ep[t]) == nil || e->mode == OREAD) { 
1999/1005    
			error(Eio);	/* can't happen */ 
2000/0725    
		} 
1999/1005    
		n = writeusb(e, a, n, TokOUT); 
		break; 
	} 
	return n; 
} 
                 
Dev usbdevtab = { 
	'U', 
	"usb", 
                 
	usbreset, 
	usbinit, 
	usbattach, 
	usbclone, 
	usbwalk, 
	usbstat, 
	usbopen, 
	devcreate, 
	usbclose, 
	usbread, 
	devbread, 
	usbwrite, 
	devbwrite, 
	devremove, 
	devwstat, 
}; 
2001/0624    
	if(!e->periodic || e->sched 
2001/0624/sys/src/9/pc/devusb.c:197,2052001/0626/sys/src/9/pc/devusb.c:197,205 (short | long)
1999/1005    
 
static char *devstates[] = { 
1999/1120    
	[Disabled]		"Disabled", 
	[Attached]		"Attached", 
2001/0626    
	[Attached]	"Attached", 
1999/1120    
	[Enabled]		"Enabled", 
	[Assigned]		"Assigned", 
2001/0626    
	[Assigned]	"Assigned", 
1999/1120    
	[Configured]	"Configured", 
1999/1005    
}; 
 
2001/0624/sys/src/9/pc/devusb.c:213,2262001/0626/sys/src/9/pc/devusb.c:213,226
1999/1116    
	int		id;		/* hardware endpoint address */ 
	int		maxpkt;	/* maximum packet size (from endpoint descriptor) */ 
	int		data01;	/* 0=DATA0, 1=DATA1 */ 
1999/1005    
	byte	eof; 
2001/0626    
	byte		eof; 
2000/0724    
	ulong	csp; 
1999/1005    
	byte	mode;	/* OREAD, OWRITE, ORDWR */ 
	byte	nbuf;	/* number of buffers allowed */ 
	byte	periodic; 
	byte	iso; 
	byte	debug; 
	byte	active;	/* listed for examination by interrupts */ 
2001/0626    
	byte		mode;	/* OREAD, OWRITE, ORDWR */ 
	byte		nbuf;	/* number of buffers allowed */ 
	byte		periodic; 
	byte		iso; 
	byte		debug; 
	byte		active;	/* listed for examination by interrupts */ 
1999/1116    
	int		sched;	/* schedule index; -1 if undefined or aperiodic */ 
	int		setin; 
1999/1005    
	ulong	bw;	/* bandwidth requirement */ 
2001/0624/sys/src/9/pc/devusb.c:369,3752001/0626/sys/src/9/pc/devusb.c:369,376
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); 
1999/1007    
		XPRINT("\ts=%s,ep=%ld,d=%ld,D=%ld\n", buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1); 
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) 
2001/0624/sys/src/9/pc/devusb.c:479,4852001/0626/sys/src/9/pc/devusb.c:480,487
1999/1005    
	Block *b; 
	int n, err; 
 
	XPRINT("cleanTD: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); 
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) 
2001/0624/sys/src/9/pc/devusb.c:487,4932001/0626/sys/src/9/pc/devusb.c:489,496
1999/1005    
	err = t->status & (AnyError&~NAKed); 
	/* TO DO: on t->status&AnyError, q->entries will not have advanced */ 
	if (err) 
1999/1007    
		XPRINT("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer); 
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){ 
2001/0624/sys/src/9/pc/devusb.c:550,5562001/0626/sys/src/9/pc/devusb.c:553,560
1999/1005    
	ilock(ub); 
1999/1116    
	tp = nil; 
1999/1005    
	for(t = q->first; t != nil;){ 
1999/1116    
		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); 
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 */ 
2001/0624/sys/src/9/pc/devusb.c:565,5732001/0626/sys/src/9/pc/devusb.c:569,577
1999/1005    
				t = t->next; 
				continue; 
			} 
1999/1116    
tp = t; 
t = t->next; 
continue; 
2001/0626    
			tp = t; 
			t = t->next; 
			continue; 
1999/1005    
			break; 
		} 
		t->status &= ~IOC; 
2001/0624/sys/src/9/pc/devusb.c:879,8822001/0626/sys/src/9/pc/devusb.c:883,1964
1999/1005    
	QH *qh; 
	int q; 
 
2001/0624    
	if(!e->periodic || e->sched 
2001/0626    
	if(!e->periodic || e->sched >= 0) 
		return 0; 
	ub = &ubus; 
	if(e->epq == nil){ 
		e->epq = allocqh(ub); 
		if(e->epq == nil) 
			return -1; 
	} 
	qlock(ub->tree); 
	q = pickschedq(ub->tree, e->pollms, e->bw, ~0);	/* TO DO: bus bandwidth limit */ 
	if(q < 0) 
		return -1; 
	ub->tree->bw[q] += e->bw; 
	qh = &ub->tree->root[q]; 
	e->sched = q; 
	e->epq->head = qh->entries; 
	e->epq->entries = Terminate; 
	qh->entries = PADDR(e->epq) | IsQH; 
	qunlock(ub->tree); 
	return 0; 
} 
 
static void 
unschedendpt(Endpt *e) 
{ 
	Ctlr *ub; 
	ulong p; 
	QH *qh; 
	int q; 
 
	ub = &ubus; 
	if(!e->periodic || (q = e->sched) < 0) 
		return; 
	p = PADDR(e->epq) | IsQH; 
	qlock(ub->tree); 
	ub->tree->bw[q] -= e->bw; 
	qh = &ub->tree->root[q]; 
	for(; qh->entries != p; qh = QFOL(qh->entries)) 
		if(qh->entries & Terminate || (qh->entries & IsQH) == 0){ 
			qunlock(ub->tree); 
			panic("usb: unschedendpt"); 
		} 
	qh->entries = e->epq->head; 
	qunlock(ub->tree); 
	e->epq->head = Terminate; 
} 
 
static Endpt * 
devendpt(Udev *d, int id, int add) 
{ 
	Endpt *e, **p; 
	Ctlr *ub; 
 
	ub = &ubus; 
	p = &d->ep[id&0xF]; 
	lock(d); 
	if((e = *p) != nil){ 
		incref(e); 
		unlock(d); 
		return e; 
	} 
	unlock(d); 
	if(!add) 
		return nil; 
	e = mallocz(sizeof(*e), 1); 
	e->ref = 1; 
	e->x = id&0xF; 
	e->id = id; 
	e->periodic = 0; 
	e->sched = -1; 
	e->maxpkt = 8; 
	e->pollms = 0; 
	e->bw = 0; 
	e->nbuf = 1; 
	e->dev = d; 
	e->active = 0; 
	e->epq = allocqh(ub); 
	if(e->epq == nil) 
		panic("devendpt"); 
 
	lock(d); 
	if(*p != nil){ 
		incref(*p); 
		unlock(d); 
		free(e); 
		return *p; 
	} 
	*p = e; 
	unlock(d); 
	e->rq = qopen(8*1024, 0, nil, e); 
	e->wq = qopen(8*1024, 0, nil, e); 
	return e; 
} 
 
static void 
freept(Endpt *e) 
{ 
	if(e != nil && decref(e) == 0){ 
		XPRINT("freept(%d,%d)\n", e->dev->x, e->x); 
		unschedendpt(e); 
		e->dev->ep[e->x] = nil; 
		eptdeactivate(e); 
		if(e->epq != nil) 
			freeqh(&ubus, e->epq); 
		free(e); 
	} 
} 
 
static void 
usbdevreset(Udev *d) 
{ 
	d->state = Disabled; 
	if(Class(d->csp) == Hubclass) 
		for(d = d->ports; d != nil; d = d->next) 
			usbdevreset(d); 
} 
 
static void 
freedev(Udev *d) 
{ 
	int i; 
 
	if(d != nil && decref(d) == 0){ 
		for(i=0; i<nelem(d->ep); i++) 
			freept(d->ep[i]); 
		if(d->x >= 0) 
			usbdev[d->x] = nil; 
		free(d); 
	} 
} 
 
static void 
hubportreset(Udev *h, int p) 
{ 
	USED(h, p); 
	/* reset state of each attached device? */ 
} 
 
static	int	ioports[] = {-1, Portsc0, Portsc1}; 
 
static void 
portreset(int port) 
{ 
	Ctlr *ub; 
	int i, p; 
 
	/* should check that device not being configured on other port? */ 
	p = ioports[port]; 
	ub = &ubus; 
	qlock(&ub->resetl); 
	if(waserror()){ 
		qunlock(&ub->resetl); 
		nexterror(); 
	} 
	XPRINT("r: %x\n", IN(p)); 
	ilock(ub); 
	OUT(p, PortReset); 
	delay(12);	/* BUG */ 
	XPRINT("r2: %x\n", IN(p)); 
	OUT(p, IN(p) & ~PortReset); 
	XPRINT("r3: %x\n", IN(p)); 
	OUT(p, IN(p) | PortEnable); 
	microdelay(64); 
	for(i=0; i<1000 && (IN(p) & PortEnable) == 0; i++) 
		; 
	XPRINT("r': %x %d\n", IN(p), i); 
	OUT(p, (IN(p) & ~PortReset)|PortEnable); 
	iunlock(ub); 
	hubportreset(nil, port); 
	poperror(); 
	qunlock(&ub->resetl); 
} 
 
static void 
portenable(int port, int on) 
{ 
	Ctlr *ub; 
	int w, p; 
 
	/* should check that device not being configured on other port? */ 
	p = ioports[port]; 
	ub = &ubus; 
	qlock(&ub->resetl); 
	if(waserror()){ 
		qunlock(&ub->resetl); 
		nexterror(); 
	} 
	ilock(ub); 
	w = IN(p); 
	if(on) 
		w |= PortEnable; 
	else 
		w &= ~PortEnable; 
	OUT(p, w); 
	microdelay(64); 
	iunlock(ub); 
	XPRINT("e: %x\n", IN(p)); 
	if(!on) 
		hubportreset(nil, port); 
	poperror(); 
	qunlock(&ub->resetl); 
} 
 
static int 
portinfo(Ctlr *ub, int *p0, int *p1) 
{ 
	int m, v; 
 
	ilock(ub); 
	m = 0; 
	if((v = IN(Portsc0)) & PortChange){ 
		OUT(Portsc0, v); 
		m |= 1<<0; 
	} 
	*p0 = v; 
	if((v = IN(Portsc1)) & PortChange){ 
		OUT(Portsc1, v); 
		m |= 1<<1; 
	} 
	*p1 = v; 
	iunlock(ub); 
	return m; 
} 
 
static void 
interrupt(Ureg*, void *a) 
{ 
	Ctlr *ub; 
	Endpt *e; 
	int s; 
	QH *q; 
 
	ub = a; 
	s = IN(Status); 
	OUT(Status, s); 
	if ((s & 0x1f) == 0) 
		return; 
	XPRINT("usbint: #%x f%d\n", s, IN(Frnum)); 
	if (s & 0x1a) { 
		XPRINT("cmd #%x sofmod #%x\n", IN(Cmd), inb(ub->io+SOFMod)); 
		XPRINT("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1)); 
	} 
 
	XPRINT("cleanq(ub->ctlq, 0, 0)\n"); 
	cleanq(ub->ctlq, 0, 0); 
	XPRINT("cleanq(ub->bulkq, 0, Vf)\n"); 
	cleanq(ub->bulkq, 0, Vf); 
	XPRINT("clean recvq\n"); 
	for (q = ub->recvq->next; q; q = q->hlink) { 
		XPRINT("cleanq(q, 0, Vf)\n"); 
		cleanq(q, 0, Vf); 
	} 
	ilock(&activends); 
	for(e = activends.f; e != nil; e = e->activef) 
		if(e->epq != nil) { 
			XPRINT("cleanq(e->epq, 0, 0)\n"); 
			cleanq(e->epq, 0, 0); 
		} 
	iunlock(&activends); 
} 
 
enum 
{ 
	Qtopdir = 0, 
	Q2nd, 
	Qbusctl, 
	Qnew, 
	Qport, 
	Q3rd, 
	Qctl, 
	Qsetup, 
	Qdebug, 
	Qstatus, 
	Qep0, 
	/* other endpoint files */ 
}; 
 
/* 
 * Qid path is: 
 *	 8 bits of file type (qids above) 
 *	10 bits of slot number +1; 0 means not attached to device 
 */ 
#define	QSHIFT	8	/* location in qid of device # */ 
#define	QMASK	((1<<QSHIFT)-1) 
 
#define	QID(q)		((ulong)(q).path&QMASK) 
#define	DEVPATH(p)	((p)>>QSHIFT) 
 
static Dirtab usbdir2[] = { 
	"new",	{Qnew},			0,	0666, 
	"ctl",		{Qbusctl},			0,	0666, 
	"port",	{Qport},			0,	0444, 
}; 
 
static Dirtab usbdir3[]={ 
	"ctl",		{Qctl},			0,	0666, 
	"setup",	{Qsetup},			0,	0666, 
	"status",	{Qstatus},			0,	0444, 
	"debug",	{Qdebug},			0,	0666, 
	/* epNdata names are generated on demand */ 
}; 
 
static Udev * 
usbdeviceofpath(ulong path) 
{ 
	int s; 
 
	s = DEVPATH(path); 
	if(s == 0) 
		return nil; 
	return usbdev[s-1]; 
} 
 
static Udev * 
usbdevice(Chan *c) 
{ 
	Udev *d; 
 
	d = usbdeviceofpath(c->qid.path); 
	if(d == nil || d->id != c->qid.vers || d->state == Disabled) 
		error(Ehungup); 
	return d; 
} 
 
static Udev * 
usbnewdevice(void) 
{ 
	Udev *d; 
	Endpt *e; 
	int i; 
 
	d = nil; 
	qlock(&usbstate); 
	if(waserror()){ 
		qunlock(&usbstate); 
		nexterror(); 
	} 
	for(i=0; i<nelem(usbdev); i++) 
		if(usbdev[i] == nil){ 
			ubus.idgen++; 
			d = mallocz(sizeof(*d), 1); 
			d->ref = 1; 
			d->x = i; 
			d->id = (ubus.idgen << 8) | i; 
			d->state = Enabled; 
			e = devendpt(d, 0, 1);	/* always provide control endpoint 0 */ 
			e->mode = ORDWR; 
			e->periodic = 0; 
			e->sched = -1; 
			usbdev[i] = d; 
			break; 
		} 
	poperror(); 
	qunlock(&usbstate); 
	return d; 
} 
 
static void 
usbreset(void) 
{ 
	Pcidev *cfg; 
	int i; 
	ulong port; 
	QTree *qt; 
	TD *t; 
	Ctlr *ub; 
	ISAConf isa; 
 
	if(isaconfig("usb", 0, &isa) == 0) { 
		XPRINT("usb not in plan9.ini\n"); 
//		return; 
	} 
	ub = &ubus; 
	memset(&cfg, 0, sizeof(cfg)); 
	cfg = pcimatch(0, 0x8086, 0x7112);	/* Intel chipset PIIX 4*/ 
	if(cfg == nil) { 
		cfg = pcimatch(0, 0x1106, 0x0586);	/* Via chipset */ 
		if(cfg == nil) { 
			DPRINT("No USB device found\n"); 
			return; 
		} 
	} 
	port = cfg->mem[4].bar & ~0x0F; 
	if (port == 0) { 
		print("usb: failed to map registers\n"); 
		return; 
	} 
 
	DPRINT("USB: %x/%x port 0x%lux size 0x%x irq %d\n", 
		cfg->vid, cfg->did, port, cfg->mem[4].size, cfg->intl); 
 
	i = inb(port+SOFMod); 
	if(1){ 
		OUT(Cmd, 4);	/* global reset */ 
		delay(15); 
		OUT(Cmd, 0);	/* end reset */ 
		delay(4); 
	} 
	outb(port+SOFMod, i); 
	/* 
	 * Interrupt handler. 
	 * Bail out if no IRQ assigned by the BIOS. 
	 */ 
	if(cfg->intl == 0xFF || cfg->intl == 0) 
		return; 
	intrenable(cfg->intl, interrupt, ub, cfg->tbdf, "usb"); 
 
	ub->io = port; 
	ub->tdpool = xspanalloc(128*sizeof(TD), 16, 0); 
	for(i=128; --i>=0;){ 
		ub->tdpool[i].next = ub->freetd; 
		ub->freetd = &ub->tdpool[i]; 
	} 
	ub->qhpool = xspanalloc(32*sizeof(QH), 16, 0); 
	for(i=32; --i>=0;){ 
		ub->qhpool[i].next = ub->freeqh; 
		ub->freeqh = &ub->qhpool[i]; 
	} 
 
	/* 
	 * the root of the periodic (interrupt & isochronous) scheduling tree 
	 * points to the control queue and the bandwidth sop for bulk traffic. 
	 * this is looped following the instructions in PIIX4 errata 29773804.pdf: 
	 * a QH links to a looped but inactive TD as its sole entry, 
	 * with its head entry leading on to the bulk traffic, the last QH of which 
	 * links back to the empty QH. 
	 */ 
	ub->ctlq = allocqh(ub); 
	ub->bwsop = allocqh(ub); 
	ub->bulkq = allocqh(ub); 
	ub->recvq = allocqh(ub); 
	t = alloctd(ub);	/* inactive TD, looped */ 
	t->link = PADDR(t); 
	ub->bwsop->entries = PADDR(t); 
	ub->ctlq->head = PADDR(ub->bwsop) | IsQH; 
	ub->bwsop->head = PADDR(ub->bulkq) | IsQH; 
	ub->bulkq->head = PADDR(ub->recvq) | IsQH; 
	ub->recvq->head = PADDR(ub->bwsop) | IsQH;	/* loop back */ 
	XPRINT("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n", 
		IN(Cmd), IN(Status), IN(Usbintr), inb(port+Frnum)); 
	XPRINT("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n", 
		IN(Flbaseadd), inb(port+SOFMod), IN(Portsc0), IN(Portsc1)); 
	OUT(Cmd, 0);	/* stop */ 
	ub->frames = xspanalloc(FRAMESIZE, FRAMESIZE, 0); 
	qt = mkqhtree(ub->frames, NFRAME, 32); 
	if(qt == nil){ 
		print("usb: can't allocate scheduling tree\n"); 
		ub->io = 0; 
		return; 
	} 
	qt->root->head = PADDR(ub->ctlq) | IsQH; 
	ub->tree = qt; 
	XPRINT("usb tree: nel=%d depth=%d\n", qt->nel, qt->depth); 
 
	outl(port+Flbaseadd, PADDR(ub->frames)); 
	OUT(Frnum, 0); 
	OUT(Usbintr, 0xF);	/* enable all interrupts */ 
	XPRINT("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(port+SOFMod)); 
	XPRINT("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1)); 
} 
 
void 
usbinit(void) 
{ 
	Udev *d; 
 
	if(ubus.io != 0 && usbdev[0] == nil){ 
		d = usbnewdevice();	/* reserve device 0 for configuration */ 
		incref(d); 
		d->state = Attached; 
	} 
} 
 
Chan * 
usbattach(char *spec) 
{ 
	Ctlr *ub; 
 
	ub = &ubus; 
	if(ub->io == 0) { 
		XPRINT("usbattach failed\n"); 
		error(Enodev); 
	} 
	if((IN(Cmd)&1)==0 || *spec) 
		OUT(Cmd, 1);	/* run */ 
//	pprint("at: c=%x s=%x c0=%x\n", IN(Cmd), IN(Status), IN(Portsc0)); 
	return devattach('U', spec); 
} 
 
static int 
usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp) 
{ 
	int t; 
	Qid q; 
	ulong path; 
	Udev *d; 
	Dirtab *tab; 
	Endpt *e; 
 
	/* 
	 * Top level directory contains the name of the device. 
	 */ 
	if(c->qid.path == Qtopdir){ 
		if(s == DEVDOTDOT){ 
			mkqid(&q, Qtopdir, 0, QTDIR); 
			devdir(c, q, "#U", 0, eve, 0555, dp); 
			return 1; 
		} 
		if(s == 0){ 
			mkqid(&q, Q2nd, 0, QTDIR); 
			devdir(c, q, "usb", 0, eve, 0555, dp); 
			return 1; 
		} 
		return -1; 
	} 
 
	/* 
	 * Second level contains "new" plus all the clients. 
	 */ 
	t = QID(c->qid); 
	if(t < Q3rd){ 
		if(s == DEVDOTDOT){ 
			mkqid(&q, Qtopdir, 0, QTDIR); 
			devdir(c, q, "#U", 0, eve, 0555, dp); 
			return 1; 
		} 
		if(s < nelem(usbdir2)){ 
			tab = &usbdir2[s]; 
			devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp); 
			return 1; 
		} 
		s -= nelem(usbdir2); 
		if(s >= 0 && s < nelem(usbdev)){ 
			d = usbdev[s]; 
			if(d == nil) 
				return -1; 
			sprint(up->genbuf, "%d", s); 
			mkqid(&q, ((s+1)<<QSHIFT)|Q3rd, d->id, QTDIR); 
			devdir(c, q, up->genbuf, 0, eve, 0555, dp); 
			return 1; 
		} 
		return -1; 
	} 
 
	/* 
	 * Third level. 
	 */ 
	path = c->qid.path & ~QMASK;	/* slot component */ 
	if(s == DEVDOTDOT){ 
		mkqid(&q, Q2nd, c->qid.vers, QTDIR); 
		devdir(c, q, "usb", 0, eve, 0555, dp); 
		return 1; 
	} 
	if(s < nelem(usbdir3)){ 
		Dirtab *tab = &usbdir3[s]; 
		mkqid(&q, path | tab->qid.path, c->qid.vers, QTFILE); 
		devdir(c, q, tab->name, tab->length, eve, tab->perm, dp); 
		return 1; 
	} 
 
	/* active endpoints */ 
	d = usbdeviceofpath(path); 
	if(d == nil) 
		return -1; 
	s -= nelem(usbdir3); 
	if(s < 0 || s >= nelem(d->ep)) 
		return -1; 
	if(s == 0 || (e = d->ep[s]) == nil)	/* ep0data is called "setup" */ 
		return 0; 
	sprint(up->genbuf, "ep%ddata", s); 
	mkqid(&q, path | (Qep0+s), c->qid.vers, QTFILE); 
	devdir(c, q, up->genbuf, 0, eve, e->mode==OREAD? 0444: e->mode==OWRITE? 0222: 0666, dp); 
	return 1; 
} 
 
static Walkqid* 
usbwalk(Chan *c, Chan *nc, char **name, int nname) 
{ 
	return devwalk(c, nc, name, nname, nil, 0, usbgen); 
} 
 
static int 
usbstat(Chan *c, uchar *db, int n) 
{ 
	return devstat(c, db, n, nil, 0, usbgen); 
} 
 
Chan * 
usbopen(Chan *c, int omode) 
{ 
	Udev *d; 
	int f, s; 
 
	if(c->qid.type == QTDIR) 
		return devopen(c, omode, nil, 0, usbgen); 
 
	f = 0; 
	if(QID(c->qid) == Qnew){ 
		d = usbnewdevice(); 
		if(d == nil) { 
			XPRINT("usbopen failed (usbnewdevice)\n"); 
			error(Enodev); 
		} 
		c->qid.path = Qctl|((d->x+1)<<QSHIFT); 
		c->qid.vers = d->id; 
		f = 1; 
	} 
 
	if(c->qid.path < Q3rd) 
		return devopen(c, omode, nil, 0, usbgen); 
 
	qlock(&usbstate); 
	if(waserror()){ 
		qunlock(&usbstate); 
		nexterror(); 
	} 
 
	switch(QID(c->qid)){ 
	case Qctl: 
		d = usbdevice(c); 
		if(0&&d->busy) 
			error(Einuse); 
		d->busy = 1; 
		if(!f) 
			incref(d); 
		break; 
 
	default: 
		d = usbdevice(c); 
		s = QID(c->qid) - Qep0; 
		if(s >= 0 && s < nelem(d->ep)){ 
			Endpt *e; 
			if((e = d->ep[s]) == nil) { 
				XPRINT("usbopen failed (endpoint)\n"); 
				error(Enodev); 
			} 
			if(schedendpt(e) < 0) 
				error("can't schedule USB endpoint"); 
			eptactivate(e); 
		} 
		incref(d); 
		break; 
	} 
	poperror(); 
	qunlock(&usbstate); 
	c->mode = openmode(omode); 
	c->flag |= COPEN; 
	c->offset = 0; 
	return c; 
} 
 
void 
usbcreate(Chan *c, char *name, int omode, ulong perm) 
{ 
	USED(c, name, omode, perm); 
	error(Eperm); 
} 
 
void 
usbremove(Chan*) 
{ 
	error(Eperm); 
} 
 
void 
usbwstat(Chan *c, char *dp) 
{ 
	USED(c, dp); 
	error(Eperm); 
} 
 
void 
usbclose(Chan *c) 
{ 
	Udev *d; 
 
	if(c->qid.type == QTDIR || c->qid.path < Q3rd) 
		return; 
	qlock(&usbstate); 
	if(waserror()){ 
		qunlock(&usbstate); 
		nexterror(); 
	} 
	d = usbdevice(c); 
	if(QID(c->qid) == Qctl) 
		d->busy = 0; 
	if(c->flag & COPEN) 
		freedev(d); 
	poperror(); 
	qunlock(&usbstate); 
} 
 
static int 
eptinput(void *arg) 
{ 
	Endpt *e; 
 
	e = arg; 
	return e->eof || e->err || qcanread(e->rq); 
} 
 
static long 
readusb(Endpt *e, void *a, long n) 
{ 
	Block *b; 
	uchar *p; 
	long l, i; 
 
	XPRINT("qlock(%p)\n", &e->rlock); 
	qlock(&e->rlock); 
	XPRINT("got qlock(%p)\n", &e->rlock); 
	if(waserror()){ 
		qunlock(&e->rlock); 
		eptcancel(e); 
		nexterror(); 
	} 
	p = a; 
	do { 
		if(e->eof) { 
			XPRINT("e->eof\n"); 
			break; 
		} 
		if(e->err) 
			error(e->err); 
		qrcv(e); 
		if(!e->iso) 
			e->data01 ^= 1; 
		sleep(&e->rr, eptinput, e); 
		if(e->err) 
			error(e->err); 
		b = qget(e->rq);	/* TO DO */ 
		if(b == nil) { 
			XPRINT("b == nil\n"); 
			break; 
		} 
		if(waserror()){ 
			freeb(b); 
			nexterror(); 
		} 
		l = BLEN(b); 
		if((i = l) > n) 
			i = n; 
		if(i > 0){ 
			memmove(p, b->rp, i); 
			p += i; 
		} 
		poperror(); 
		freeb(b); 
		n -= i; 
	} while (l == e->maxpkt && n > 0); 
	poperror(); 
	qunlock(&e->rlock); 
	return p-(uchar*)a; 
} 
 
long 
usbread(Chan *c, void *a, long n, vlong offset) 
{ 
	Endpt *e; 
	Udev *d; 
	char buf[48], *s; 
	int t, w0, w1, ps, l, i; 
 
	if(c->qid.type == QTDIR) 
		return devdirread(c, a, n, nil, 0, usbgen); 
 
	t = QID(c->qid); 
	switch(t){ 
	case Qbusctl: 
		snprint(buf, sizeof(buf), "%11d %11d ", 0, 0); 
		return readstr(offset, a, n, buf); 
 
	case Qport: 
		ps = portinfo(&ubus, &w0, &w1); 
		snprint(buf, sizeof(buf), "0x%ux 0x%ux 0x%ux ", ps, w0, w1); 
		return readstr(offset, a, n, buf); 
 
	case Qctl: 
		d = usbdevice(c); 
		sprint(buf, "%11d %11d ", d->x, d->id); 
		return readstr(offset, a, n, buf); 
 
	case Qsetup:	/* endpoint 0 */ 
		d = usbdevice(c); 
		if((e = d->ep[0]) == nil) 
			error(Eio);	/* can't happen */ 
		e->data01 = 1; 
		n = readusb(e, a, n); 
		if(e->setin){ 
			e->setin = 0; 
			e->data01 = 1; 
			writeusb(e, "", 0, TokOUT); 
		} 
		break; 
 
	case Qdebug: 
		n=0; 
		break; 
 
	case Qstatus: 
		d = usbdevice(c); 
		s = smalloc(READSTR); 
		if(waserror()){ 
			free(s); 
			nexterror(); 
		} 
		l = snprint(s, READSTR, "%s %#6.6lux\n", devstates[d->state], d->csp); 
		for(i=0; i<nelem(d->ep); i++) 
			if((e = d->ep[i]) != nil)	/* TO DO: freeze e */ 
				l += snprint(s+l, READSTR-l, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks); 
		n = readstr(offset, a, n, s); 
		poperror(); 
		free(s); 
		break; 
 
	default: 
		d = usbdevice(c); 
		if((t -= Qep0) < 0 || t >= nelem(d->ep)) 
			error(Eio); 
		if((e = d->ep[t]) == nil || e->mode == OWRITE) 
			error(Eio);	/* can't happen */ 
		n=readusb(e, a, n); 
		break; 
	} 
	return n; 
} 
 
static int 
qisempty(void *arg) 
{ 
	return ((QH*)arg)->entries & Terminate; 
} 
 
static long 
writeusb(Endpt *e, void *a, long n, int tok) 
{ 
	long i; 
	Block *b; 
	uchar *p; 
	QH *qh; 
 
	p = a; 
	qlock(&e->wlock); 
	if(waserror()){ 
		qunlock(&e->wlock); 
		eptcancel(e); 
		nexterror(); 
	} 
	do { 
		int j; 
 
		if(e->err) 
			error(e->err); 
		if((i = n) >= e->maxpkt) 
			i = e->maxpkt; 
		b = allocb(i); 
		if(waserror()){ 
			freeb(b); 
			nexterror(); 
		} 
		XPRINT("out [%ld]", i); 
		for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]); 
		XPRINT("\n"); 
		memmove(b->wp, p, i); 
		b->wp += i; 
		p += i; 
		n -= i; 
		poperror(); 
		qh = qxmit(e, b, tok); 
		tok = TokOUT; 
		if(!e->iso) 
			e->data01 ^= 1; 
		if(e->ntd >= e->nbuf) { 
			XPRINT("writeusb: sleep %lux\n", &e->wr); 
			sleep(&e->wr, qisempty, qh); 
			XPRINT("writeusb: awake\n"); 
		} 
	} while(n > 0); 
	poperror(); 
	qunlock(&e->wlock); 
	return p-(uchar*)a; 
} 
 
long 
usbwrite(Chan *c, void *a, long n, vlong) 
{ 
	Udev *d; 
	Endpt *e; 
	int id, nw, nf, t, i; 
	char cmd[50], *fields[10]; 
 
	if(c->qid.type == QTDIR) 
		error(Egreg); 
	t = QID(c->qid); 
	if(t == Qbusctl){ 
		if(n >= sizeof(cmd)-1) 
			n = sizeof(cmd)-1; 
		memmove(cmd, a, n); 
		cmd[n] = 0; 
		nf = getfields(cmd, fields, nelem(fields), 0, " \t\n"); 
		if(nf==1 && strcmp(fields[0], "dump")==0){ 
			dumpframe(-1, -1); 
			return n; 
		} 
		if(nf < 2) 
			error(Ebadarg); 
		id = strtol(fields[1], nil, 0); 
		if(id != 1 && id != 2) 
			error(Ebadarg);	/* there are two ports on the root hub */ 
		if(strcmp(fields[0], "reset") == 0) 
			portreset(id); 
		else if(strcmp(fields[0], "enable") == 0) 
			portenable(id, 1); 
		else if(strcmp(fields[0], "disable") == 0) 
			portenable(id, 0); 
		else 
			error(Ebadarg); 
		return n; 
	} 
	d = usbdevice(c); 
	t = QID(c->qid); 
	switch(t){ 
	case Qctl: 
		if(n >= sizeof(cmd)-1) 
			n = sizeof(cmd)-1; 
		memmove(cmd, a, n); 
		cmd[n] = 0; 
		nf = getfields(cmd, fields, nelem(fields), 0, " \t\n"); 
		if(nf > 1 && strcmp(fields[0], "speed") == 0){ 
			d->ls = strtoul(fields[1], nil, 0) == 0; 
		} else if(nf > 3 && strcmp(fields[0], "class") == 0){ 
			i = strtoul(fields[2], nil, 0); 
			d->npt = strtoul(fields[1], nil, 0); 
			/* class config# csp ( == class subclass proto) */ 
			if (i < 0 || i >= nelem(d->ep) 
			 || d->npt > nelem(d->ep) || i >= d->npt) 
				error(Ebadarg); 
			if (i == 0) { 
				d->csp = strtoul(fields[3], nil, 0); 
			} 
			if(d->ep[i] == nil) 
				d->ep[i] = devendpt(d, i, 1); 
			d->ep[i]->csp = strtoul(fields[3], nil, 0); 
		}else if(nf > 2 && strcmp(fields[0], "data") == 0){ 
			i = strtoul(fields[1], nil, 0); 
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) 
				error(Ebadarg); 
			e = d->ep[i]; 
			e->data01 = strtoul(fields[2], nil, 0) != 0; 
		}else if(nf > 2 && strcmp(fields[0], "maxpkt") == 0){ 
			i = strtoul(fields[1], nil, 0); 
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) 
				error(Ebadarg); 
			e = d->ep[i]; 
			e->maxpkt = strtoul(fields[2], nil, 0); 
			if(e->maxpkt > 1500) 
				e->maxpkt = 1500; 
		}else if(nf > 2 && strcmp(fields[0], "debug") == 0){ 
			i = strtoul(fields[1], nil, 0); 
			if(i < -1 || i >= nelem(d->ep) || d->ep[i] == nil) 
				error(Ebadarg); 
			if (i == -1) 
				debug = 0; 
			else { 
				debug = 1; 
				e = d->ep[i]; 
				e->debug = strtoul(fields[2], nil, 0); 
			} 
		}else if(nf > 1 && strcmp(fields[0], "unstall") == 0){ 
			i = strtoul(fields[1], nil, 0); 
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil) 
				error(Ebadarg); 
			e = d->ep[i]; 
			e->err = nil; 
		}else if(nf == 6 && strcmp(fields[0], "ep") == 0){ 
			/* ep n maxpkt mode poll nbuf */ 
			i = strtoul(fields[1], nil, 0); 
			if(i < 0 || i >= nelem(d->ep)) { 
				XPRINT("field 1: 0 <= %d < %d\n", i, nelem(d->ep)); 
				error(Ebadarg); 
			} 
			if((e = d->ep[i]) == nil) 
				e = devendpt(d, i, 1); 
			if(waserror()){ 
				freept(e); 
				nexterror(); 
			} 
			i = strtoul(fields[2], nil, 0); 
			if(i < 8 || i > 1023) 
				i = 8; 
			e->maxpkt = i; 
			e->mode = strcmp(fields[3],"r") == 0? OREAD : 
					  strcmp(fields[3],"w") == 0? OWRITE : ORDWR; 
			e->periodic = 0; 
			e->sched = -1; 
			if(strcmp(fields[4], "bulk") == 0){ 
				Ctlr *ub; 
 
				ub = &ubus; 
				/* Each bulk device gets a queue head hanging off the 
				 * bulk queue head 
				 */ 
				if (e->epq == nil) { 
					e->epq = allocqh(ub); 
					if(e->epq == nil) 
						panic("usbwrite: allocqh"); 
				} 
				queueqh(e->epq); 
			} else { 
				e->periodic = 1; 
				i = strtoul(fields[4], nil, 0); 
				if(i > 0 && i <= 1000) 
					e->pollms = i; 
				else { 
					XPRINT("field 4: 0 <= %d <= 1000\n", i); 
					error(Ebadarg); 
				} 
			} 
			i = strtoul(fields[5], nil, 0); 
			if(i >= 1 && i <= 32) 
				e->nbuf = i; 
			poperror(); 
		}else { 
			XPRINT("command %s, fields %d\n", fields[0], nf); 
			error(Ebadarg); 
		} 
		return n; 
 
	case Qsetup:	/* SETUP endpoint 0 */ 
		/* should canqlock etc */ 
		if((e = d->ep[0]) == nil) 
			error(Eio);	/* can't happen */ 
		if(n < 8 || n > 1023) 
			error(Eio); 
		nw = *(uchar*)a & RD2H; 
		e->data01 = 0; 
		n = writeusb(e, a, n, TokSETUP); 
		if(nw == 0){	/* host to device: use IN[DATA1] to ack */ 
			e->data01 = 1; 
			nw = readusb(e, cmd, 8); 
			if(nw != 0) 
				error(Eio);	/* could provide more status */ 
		}else 
			e->setin = 1;	/* two-phase */ 
		break; 
 
	default:	/* sends DATA[01] */ 
		if((t -= Qep0) < 0 || t >= nelem(d->ep)) { 
			print("t = %d\n", t); 
			error(Eio); 
		} 
		if((e = d->ep[t]) == nil || e->mode == OREAD) { 
			error(Eio);	/* can't happen */ 
		} 
		n = writeusb(e, a, n, TokOUT); 
		break; 
	} 
	return n; 
} 
 
Dev usbdevtab = { 
	'U', 
	"usb", 
 
	usbreset, 
	usbinit, 
	usbattach, 
	usbwalk, 
	usbstat, 
	usbopen, 
	devcreate, 
	usbclose, 
	usbread, 
	devbread, 
	usbwrite, 
	devbwrite, 
	devremove, 
	devwstat, 
}; 
Too many diffs (26 > 25). Stopping.


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