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

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

2001/0624/sys/src/9/pc/devusb.c:197,2052001/0626/sys/src/9/pc/devusb.c:197,205 (short | long | prev | next)
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, 
}; 


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