git: 9front

Download patch

ref: 7c8b1b6e5df48adf84c5bad640888ea4acf1d3e6
parent: 187bb7717e4b517bb6e40d993872a65a0325f2e3
author: cinap_lenrek <cinap_lenrek@gmx.de>
date: Thu Dec 6 13:53:17 EST 2012

move devusb to port

moved devusb to port, shifting the responsibility of how
to enable interrupts to the arch specific hci driver.

--- a/sys/src/9/kw/devusb.c
+++ /dev/null
@@ -1,1460 +1,0 @@
-/*
- * USB device driver framework.
- *
- * This is in charge of providing access to actual HCIs
- * and providing I/O to the various endpoints of devices.
- * A separate user program (usbd) is in charge of
- * enumerating the bus, setting up endpoints and
- * starting devices (also user programs).
- *
- * The interface provided is a violation of the standard:
- * you're welcome.
- *
- * The interface consists of a root directory with several files
- * plus a directory (epN.M) with two files per endpoint.
- * A device is represented by its first endpoint, which
- * is a control endpoint automatically allocated for each device.
- * Device control endpoints may be used to create new endpoints.
- * Devices corresponding to hubs may also allocate new devices,
- * perhaps also hubs. Initially, a hub device is allocated for
- * each controller present, to represent its root hub. Those can
- * never be removed.
- *
- * All endpoints refer to the first endpoint (epN.0) of the device,
- * which keeps per-device information, and also to the HCI used
- * to reach them. Although all endpoints cache that information.
- *
- * epN.M/data files permit I/O and are considered DMEXCL.
- * epN.M/ctl files provide status info and accept control requests.
- *
- * Endpoints may be given file names to be listed also at #u,
- * for those drivers that have nothing to do after configuring the
- * device and its endpoints.
- *
- * Drivers for different controllers are kept at usb[oue]hci.c
- * It's likely we could factor out much from controllers into
- * a generic controller driver, the problem is that details
- * regarding how to handle toggles, tokens, Tds, etc. will
- * get in the way. Thus, code is probably easier the way it is.
- */
-
-#include	"u.h"
-#include	"../port/lib.h"
-#include	"mem.h"
-#include	"dat.h"
-#include	"fns.h"
-#include	"io.h"
-#include	"../port/error.h"
-#include	"../port/usb.h"
-
-typedef struct Hcitype Hcitype;
-
-enum
-{
-	/* Qid numbers */
-	Qdir = 0,		/* #u */
-	Qusbdir,			/* #u/usb */
-	Qctl,			/* #u/usb/ctl - control requests */
-
-	Qep0dir,			/* #u/usb/ep0.0 - endpoint 0 dir */
-	Qep0io,			/* #u/usb/ep0.0/data - endpoint 0 I/O */
-	Qep0ctl,		/* #u/usb/ep0.0/ctl - endpoint 0 ctl. */
-	Qep0dummy,		/* give 4 qids to each endpoint */
-
-	Qepdir = 0,		/* (qid-qep0dir)&3 is one of these */
-	Qepio,			/* to identify which file for the endpoint */
-	Qepctl,
-
-	/* ... */
-
-	/* Usb ctls. */
-	CMdebug = 0,		/* debug on|off */
-	CMdump,			/* dump (data structures for debug) */
-
-	/* Ep. ctls */
-	CMnew = 0,		/* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */
-	CMnewdev,		/* newdev full|low|high portnb (allocate new devices) */
-	CMhub,			/* hub (set the device as a hub) */
-	CMspeed,		/* speed full|low|high|no */
-	CMmaxpkt,		/* maxpkt size */
-	CMntds,			/* ntds nb (max nb. of tds per µframe) */
-	CMclrhalt,		/* clrhalt (halt was cleared on endpoint) */
-	CMpollival,		/* pollival interval (interrupt/iso) */
-	CMhz,			/* hz n (samples/sec; iso) */
-	CMsamplesz,		/* samplesz n (sample size; iso) */
-	CMinfo,			/* info infostr (ke.ep info for humans) */
-	CMdetach,		/* detach (abort I/O forever on this ep). */
-	CMaddress,		/* address (address is assigned) */
-	CMdebugep,		/* debug n (set/clear debug for this ep) */
-	CMname,			/* name str (show up as #u/name as well) */
-	CMtmout,		/* timeout n (activate timeouts for ep) */
-	CMpreset,		/* reset the port */
-
-	/* Hub feature selectors */
-	Rportenable	= 1,
-	Rportreset	= 4,
-
-};
-
-struct Hcitype
-{
-	char*	type;
-	int	(*reset)(Hci*);
-};
-
-#define QID(q)	((int)(q).path)
-
-static char Edetach[] = "device is detached";
-static char Enotconf[] = "endpoint not configured";
-char Estalled[] = "endpoint stalled";
-
-static Cmdtab usbctls[] =
-{
-	{CMdebug,	"debug",	2},
-	{CMdump,	"dump",		1},
-};
-
-static Cmdtab epctls[] =
-{
-	{CMnew,		"new",		4},
-	{CMnewdev,	"newdev",	3},
-	{CMhub,		"hub",		1},
-	{CMspeed,	"speed",	2},
-	{CMmaxpkt,	"maxpkt",	2},
-	{CMntds,	"ntds",		2},
-	{CMpollival,	"pollival",	2},
-	{CMsamplesz,	"samplesz",	2},
-	{CMhz,		"hz",		2},
-	{CMinfo,	"info",		0},
-	{CMdetach,	"detach",	1},
-	{CMaddress,	"address",	1},
-	{CMdebugep,	"debug",	2},
-	{CMclrhalt,	"clrhalt",	1},
-	{CMname,	"name",		2},
-	{CMtmout,	"timeout",	2},
-	{CMpreset,	"reset",	1},
-};
-
-static Dirtab usbdir[] =
-{
-	"ctl",		{Qctl},		0,	0666,
-};
-
-char *usbmodename[] =
-{
-	[OREAD]	"r",
-	[OWRITE]	"w",
-	[ORDWR]	"rw",
-};
-
-static char *ttname[] =
-{
-	[Tnone]	"none",
-	[Tctl]	"control",
-	[Tiso]	"iso",
-	[Tintr]	"interrupt",
-	[Tbulk]	"bulk",
-};
-
-static char *spname[] =
-{
-	[Fullspeed]	"full",
-	[Lowspeed]	"low",
-	[Highspeed]	"high",
-	[Nospeed]	"no",
-};
-
-static int	debug;
-static Hcitype	hcitypes[Nhcis];
-static Hci*	hcis[Nhcis];
-static QLock	epslck;		/* add, del, lookup endpoints */
-static Ep*	eps[Neps];	/* all endpoints known */
-static int	epmax;		/* 1 + last endpoint index used  */
-static int	usbidgen;	/* device address generator */
-
-/*
- * Is there something like this in a library? should it be?
- */
-char*
-seprintdata(char *s, char *se, uchar *d, int n)
-{
-	int i, l;
-
-	s = seprint(s, se, " %#p[%d]: ", d, n);
-	l = n;
-	if(l > 10)
-		l = 10;
-	for(i=0; i<l; i++)
-		s = seprint(s, se, " %2.2ux", d[i]);
-	if(l < n)
-		s = seprint(s, se, "...");
-	return s;
-}
-
-static int
-name2speed(char *name)
-{
-	int i;
-
-	for(i = 0; i < nelem(spname); i++)
-		if(strcmp(name, spname[i]) == 0)
-			return i;
-	return Nospeed;
-}
-
-static int
-name2ttype(char *name)
-{
-	int i;
-
-	for(i = 0; i < nelem(ttname); i++)
-		if(strcmp(name, ttname[i]) == 0)
-			return i;
-	/* may be a std. USB ep. type */
-	i = strtol(name, nil, 0);
-	switch(i+1){
-	case Tctl:
-	case Tiso:
-	case Tbulk:
-	case Tintr:
-		return i+1;
-	default:
-		return Tnone;
-	}
-}
-
-static int
-name2mode(char *mode)
-{
-	int i;
-
-	for(i = 0; i < nelem(usbmodename); i++)
-		if(strcmp(mode, usbmodename[i]) == 0)
-			return i;
-	return -1;
-}
-
-static int
-qid2epidx(int q)
-{
-	q = (q-Qep0dir)/4;
-	if(q < 0 || q >= epmax || eps[q] == nil)
-		return -1;
-	return q;
-}
-
-static int
-isqtype(int q, int type)
-{
-	if(q < Qep0dir)
-		return 0;
-	q -= Qep0dir;
-	return (q & 3) == type;
-}
-
-void
-addhcitype(char* t, int (*r)(Hci*))
-{
-	static int ntype;
-
-	if(ntype == Nhcis)
-		panic("too many USB host interface types");
-	hcitypes[ntype].type = t;
-	hcitypes[ntype].reset = r;
-	ntype++;
-}
-
-static char*
-seprintep(char *s, char *se, Ep *ep, int all)
-{
-	static char* dsnames[] = { "config", "enabled", "detached", "reset" };
-	Udev *d;
-	int i;
-	int di;
-
-	d = ep->dev;
-
-	qlock(ep);
-	if(waserror()){
-		qunlock(ep);
-		nexterror();
-	}
-	di = ep->dev->nb;
-	if(all)
-		s = seprint(s, se, "dev %d ep %d ", di, ep->nb);
-	s = seprint(s, se, "%s", dsnames[ep->dev->state]);
-	s = seprint(s, se, " %s", ttname[ep->ttype]);
-	assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR);
-	s = seprint(s, se, " %s", usbmodename[ep->mode]);
-	s = seprint(s, se, " speed %s", spname[d->speed]);
-	s = seprint(s, se, " maxpkt %ld", ep->maxpkt);
-	s = seprint(s, se, " pollival %ld", ep->pollival);
-	s = seprint(s, se, " samplesz %ld", ep->samplesz);
-	s = seprint(s, se, " hz %ld", ep->hz);
-	s = seprint(s, se, " hub %d", ep->dev->hub);
-	s = seprint(s, se, " port %d", ep->dev->port);
-	if(ep->inuse)
-		s = seprint(s, se, " busy");
-	else
-		s = seprint(s, se, " idle");
-	if(all){
-		s = seprint(s, se, " load %uld", ep->load);
-		s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep);
-		s = seprint(s, se, " idx %d", ep->idx);
-		if(ep->name != nil)
-			s = seprint(s, se, " name '%s'", ep->name);
-		if(ep->tmout != 0)
-			s = seprint(s, se, " tmout");
-		if(ep == ep->ep0){
-			s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno);
-			s = seprint(s, se, " eps:");
-			for(i = 0; i < nelem(d->eps); i++)
-				if(d->eps[i] != nil)
-					s = seprint(s, se, " ep%d.%d", di, i);
-		}
-	}
-	if(ep->info != nil)
-		s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type);
-	else
-		s = seprint(s, se, "\n");
-	qunlock(ep);
-	poperror();
-	return s;
-}
-
-static Ep*
-epalloc(Hci *hp)
-{
-	Ep *ep;
-	int i;
-
-	ep = smalloc(sizeof(Ep));
-	ep->ref = 1;
-	qlock(&epslck);
-	for(i = 0; i < Neps; i++)
-		if(eps[i] == nil)
-			break;
-	if(i == Neps){
-		qunlock(&epslck);
-		free(ep);
-		print("usb: bug: too few endpoints.\n");
-		return nil;
-	}
-	ep->idx = i;
-	if(epmax <= i)
-		epmax = i+1;
-	eps[i] = ep;
-	ep->hp = hp;
-	ep->maxpkt = 8;
-	ep->ntds = 1;
-	ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */
-	qunlock(&epslck);
-	return ep;
-}
-
-static Ep*
-getep(int i)
-{
-	Ep *ep;
-
-	if(i < 0 || i >= epmax || eps[i] == nil)
-		return nil;
-	qlock(&epslck);
-	ep = eps[i];
-	if(ep != nil)
-		incref(ep);
-	qunlock(&epslck);
-	return ep;
-}
-
-static void
-putep(Ep *ep)
-{
-	Udev *d;
-
-	if(ep != nil && decref(ep) == 0){
-		d = ep->dev;
-		deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep);
-		qlock(&epslck);
-		eps[ep->idx] = nil;
-		if(ep->idx == epmax-1)
-			epmax--;
-		if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen)
-			usbidgen--;
-		qunlock(&epslck);
-		if(d != nil){
-			qlock(ep->ep0);
-			d->eps[ep->nb] = nil;
-			qunlock(ep->ep0);
-		}
-		if(ep->ep0 != ep){
-			putep(ep->ep0);
-			ep->ep0 = nil;
-		}
-		free(ep->info);
-		free(ep->name);
-		free(ep);
-	}
-}
-
-static void
-dumpeps(void)
-{
-	int i;
-	static char buf[512];
-	char *s;
-	char *e;
-	Ep *ep;
-
-	print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps);
-	for(i = 0; i < epmax; i++){
-		s = buf;
-		e = buf+sizeof(buf);
-		ep = getep(i);
-		if(ep != nil){
-			if(waserror()){
-				putep(ep);
-				nexterror();
-			}
-			s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb);
-			seprintep(s, e, ep, 1);
-			print("%s", buf);
-			ep->hp->seprintep(buf, e, ep);
-			print("%s", buf);
-			poperror();
-			putep(ep);
-		}
-	}
-	print("usb dump hcis:\n");
-	for(i = 0; i < Nhcis; i++)
-		if(hcis[i] != nil)
-			hcis[i]->dump(hcis[i]);
-}
-
-static int
-newusbid(Hci *)
-{
-	int id;
-
-	qlock(&epslck);
-	id = ++usbidgen;
-	if(id >= 0x7F)
-		print("#u: too many device addresses; reuse them more\n");
-	qunlock(&epslck);
-	return id;
-}
-
-/*
- * Create endpoint 0 for a new device
- */
-static Ep*
-newdev(Hci *hp, int ishub, int isroot)
-{
-	Ep *ep;
-	Udev *d;
-
-	ep = epalloc(hp);
-	d = ep->dev = smalloc(sizeof(Udev));
-	d->nb = newusbid(hp);
-	d->eps[0] = ep;
-	ep->nb = 0;
-	ep->toggle[0] = ep->toggle[1] = 0;
-	d->ishub = ishub;
-	d->isroot = isroot;
-	if(hp->highspeed != 0)
-		d->speed = Highspeed;
-	else
-		d->speed = Fullspeed;
-	d->state = Dconfig;		/* address not yet set */
-	ep->dev = d;
-	ep->ep0 = ep;			/* no ref counted here */
-	ep->ttype = Tctl;
-	ep->tmout = Xfertmout;
-	ep->mode = ORDWR;
-	dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep);
-	return ep;
-}
-
-/*
- * Create a new endpoint for the device
- * accessed via the given endpoint 0.
- */
-static Ep*
-newdevep(Ep *ep, int i, int tt, int mode)
-{
-	Ep *nep;
-	Udev *d;
-
-	d = ep->dev;
-	if(d->eps[i] != nil)
-		error("endpoint already in use");
-	nep = epalloc(ep->hp);
-	incref(ep);
-	d->eps[i] = nep;
-	nep->nb = i;
-	nep->toggle[0] = nep->toggle[1] = 0;
-	nep->ep0 = ep;
-	nep->dev = ep->dev;
-	nep->mode = mode;
-	nep->ttype = tt;
-	nep->debug = ep->debug;
-	/* set defaults */
-	switch(tt){
-	case Tctl:
-		nep->tmout = Xfertmout;
-		break;
-	case Tintr:
-		nep->pollival = 10;
-		break;
-	case Tiso:
-		nep->tmout = Xfertmout;
-		nep->pollival = 10;
-		nep->samplesz = 4;
-		nep->hz = 44100;
-		break;
-	}
-	deprint("newdevep ep%d.%d %#p\n", d->nb, nep->nb, nep);
-	return ep;
-}
-
-static int
-epdataperm(int mode)
-{
-
-	switch(mode){
-	case OREAD:
-		return 0440|DMEXCL;
-		break;
-	case OWRITE:
-		return 0220|DMEXCL;
-		break;
-	default:
-		return 0660|DMEXCL;
-	}
-}
-
-static int
-usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
-{
-	Qid q;
-	Dirtab *dir;
-	int perm;
-	char *se;
-	Ep *ep;
-	int nb;
-	int mode;
-
-	if(0)ddprint("usbgen q %#x s %d...", QID(c->qid), s);
-	if(s == DEVDOTDOT){
-		if(QID(c->qid) <= Qusbdir){
-			mkqid(&q, Qdir, 0, QTDIR);
-			devdir(c, q, "#u", 0, eve, 0555, dp);
-		}else{
-			mkqid(&q, Qusbdir, 0, QTDIR);
-			devdir(c, q, "usb", 0, eve, 0555, dp);
-		}
-		if(0)ddprint("ok\n");
-		return 1;
-	}
-
-	switch(QID(c->qid)){
-	case Qdir:				/* list #u */
-		if(s == 0){
-			mkqid(&q, Qusbdir, 0, QTDIR);
-			devdir(c, q, "usb", 0, eve, 0555, dp);
-			if(0)ddprint("ok\n");
-			return 1;
-		}
-		s--;
-		if(s < 0 || s >= epmax)
-			goto Fail;
-		ep = getep(s);
-		if(ep == nil || ep->name == nil){
-			if(ep != nil)
-				putep(ep);
-			if(0)ddprint("skip\n");
-			return 0;
-		}
-		if(waserror()){
-			putep(ep);
-			nexterror();
-		}
-		mkqid(&q, Qep0io+s*4, 0, QTFILE);
-		devdir(c, q, ep->name, 0, eve, epdataperm(ep->mode), dp);
-		putep(ep);
-		poperror();
-		if(0)ddprint("ok\n");
-		return 1;
-
-	case Qusbdir:				/* list #u/usb */
-	Usbdir:
-		if(s < nelem(usbdir)){
-			dir = &usbdir[s];
-			mkqid(&q, dir->qid.path, 0, QTFILE);
-			devdir(c, q, dir->name, dir->length, eve, dir->perm, dp);
-			if(0)ddprint("ok\n");
-			return 1;
-		}
-		s -= nelem(usbdir);
-		if(s < 0 || s >= epmax)
-			goto Fail;
-		ep = getep(s);
-		if(ep == nil){
-			if(0)ddprint("skip\n");
-			return 0;
-		}
-		if(waserror()){
-			putep(ep);
-			nexterror();
-		}
-		se = up->genbuf+sizeof(up->genbuf);
-		seprint(up->genbuf, se, "ep%d.%d", ep->dev->nb, ep->nb);
-		mkqid(&q, Qep0dir+4*s, 0, QTDIR);
-		putep(ep);
-		poperror();
-		devdir(c, q, up->genbuf, 0, eve, 0755, dp);
-		if(0)ddprint("ok\n");
-		return 1;
-
-	case Qctl:
-		s = 0;
-		goto Usbdir;
-
-	default:				/* list #u/usb/epN.M */
-		nb = qid2epidx(QID(c->qid));
-		ep = getep(nb);
-		if(ep == nil)
-			goto Fail;
-		mode = ep->mode;
-		putep(ep);
-		if(isqtype(QID(c->qid), Qepdir)){
-		Epdir:
-			switch(s){
-			case 0:
-				mkqid(&q, Qep0io+nb*4, 0, QTFILE);
-				perm = epdataperm(mode);
-				devdir(c, q, "data", 0, eve, perm, dp);
-				break;
-			case 1:
-				mkqid(&q, Qep0ctl+nb*4, 0, QTFILE);
-				devdir(c, q, "ctl", 0, eve, 0664, dp);
-				break;
-			default:
-				goto Fail;
-			}
-		}else if(isqtype(QID(c->qid), Qepctl)){
-			s = 1;
-			goto Epdir;
-		}else{
-			s = 0;
-			goto Epdir;
-		}
-		if(0)ddprint("ok\n");
-		return 1;
-	}
-Fail:
-	if(0)ddprint("fail\n");
-	return -1;
-}
-
-static Hci*
-hciprobe(int cardno, int ctlrno)
-{
-	Hci *hp;
-	char *type;
-	char name[64];
-	static int epnb = 1;	/* guess the endpoint nb. for the controller */
-
-	ddprint("hciprobe %d %d\n", cardno, ctlrno);
-	hp = smalloc(sizeof(Hci));
-	hp->ctlrno = ctlrno;
-	hp->tbdf = BUSUNKNOWN;
-
-	if(cardno < 0)
-		for(cardno = 0; cardno < Nhcis; cardno++){
-			if(hcitypes[cardno].type == nil)
-				break;
-			type = hp->type;
-			if(type==nil || *type==0)
-				type = "uhci";
-			if(cistrcmp(hcitypes[cardno].type, type) == 0)
-				break;
-		}
-
-	if(cardno >= Nhcis || hcitypes[cardno].type == nil){
-		free(hp);
-		return nil;
-	}
-	dprint("%s...", hcitypes[cardno].type);
-	if(hcitypes[cardno].reset(hp) < 0){
-		free(hp);
-		return nil;
-	}
-
-	snprint(name, sizeof(name), "usb%s", hcitypes[cardno].type);
-	intrenable(Irqlo, hp->irq, hp->interrupt, hp, name);
-	print("#u/usb/ep%d.0: %s: port %#luX irq %d\n",
-		epnb, hcitypes[cardno].type, hp->port, hp->irq);
-	epnb++;
-	return hp;
-}
-
-static void
-usbreset(void)
-{
-	int cardno, ctlrno;
-	Hci *hp;
-
-	dprint("usbreset\n");
-
-	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++)
-		if((hp = hciprobe(-1, ctlrno)) != nil)
-			hcis[ctlrno] = hp;
-	cardno = ctlrno = 0;
-	while(cardno < Nhcis && ctlrno < Nhcis && hcitypes[cardno].type != nil)
-		if(hcis[ctlrno] != nil)
-			ctlrno++;
-		else{
-			hp = hciprobe(cardno, ctlrno);
-			if(hp == nil)
-				cardno++;
-			hcis[ctlrno++] = hp;
-		}
-	if(hcis[Nhcis-1] != nil)
-		print("usbreset: bug: Nhcis too small\n");
-}
-
-static void
-usbinit(void)
-{
-	Hci *hp;
-	int ctlrno;
-	Ep *d;
-	char info[40];
-
-	dprint("usbinit\n");
-	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){
-		hp = hcis[ctlrno];
-		if(hp != nil){
-			if(hp->init != nil)
-				hp->init(hp);
-			d = newdev(hp, 1, 1);		/* new root hub */
-			d->dev->state = Denabled;	/* although addr == 0 */
-			d->maxpkt = 64;
-			snprint(info, sizeof(info), "ports %d", hp->nports);
-			kstrdup(&d->info, info);
-		}
-	}
-}
-
-static Chan*
-usbattach(char *spec)
-{
-	return devattach(L'u', spec);
-}
-
-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);
-}
-
-/*
- * µs for the given transfer, for bandwidth allocation.
- * This is a very rough worst case for what 5.11.3
- * of the usb 2.0 spec says.
- * Also, we are using maxpkt and not actual transfer sizes.
- * Only when we are sure we
- * are not exceeding b/w might we consider adjusting it.
- */
-static ulong
-usbload(int speed, int maxpkt)
-{
-	enum{ Hostns = 1000, Hubns = 333 };
-	ulong l;
-	ulong bs;
-
-	l = 0;
-	bs = 10UL * maxpkt;
-	switch(speed){
-	case Highspeed:
-		l = 55*8*2 + 2 * (3 + bs) + Hostns;
-		break;
-	case Fullspeed:
-		l = 9107 + 84 * (4 + bs) + Hostns;
-		break;
-	case Lowspeed:
-		l = 64107 + 2 * Hubns + 667 * (3 + bs) + Hostns;
-		break;
-	default:
-		print("usbload: bad speed %d\n", speed);
-		/* let it run */
-	}
-	return l / 1000UL;	/* in µs */
-}
-
-static Chan*
-usbopen(Chan *c, int omode)
-{
-	int q;
-	Ep *ep;
-	int mode;
-
-	mode = openmode(omode);
-	q = QID(c->qid);
-
-	if(q >= Qep0dir && qid2epidx(q) < 0)
-		error(Eio);
-	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
-		return devopen(c, omode, nil, 0, usbgen);
-
-	ep = getep(qid2epidx(q));
-	if(ep == nil)
-		error(Eio);
-	deprint("usbopen q %#x fid %d omode %d\n", q, c->fid, mode);
-	if(waserror()){
-		putep(ep);
-		nexterror();
-	}
-	qlock(ep);
-	if(ep->inuse){
-		qunlock(ep);
-		error(Einuse);
-	}
-	ep->inuse = 1;
-	qunlock(ep);
-	if(waserror()){
-		ep->inuse = 0;
-		nexterror();
-	}
-	if(mode != OREAD && ep->mode == OREAD)
-		error(Eperm);
-	if(mode != OWRITE && ep->mode == OWRITE)
-		error(Eperm);
-	if(ep->ttype == Tnone)
-		error(Enotconf);
-	ep->clrhalt = 0;
-	ep->rhrepl = -1;
-	if(ep->load == 0)
-		ep->load = usbload(ep->dev->speed, ep->maxpkt);
-	ep->hp->epopen(ep);
-
-	poperror();	/* ep->inuse */
-	poperror();	/* don't putep(): ref kept for fid using the ep. */
-
-	c->mode = mode;
-	c->flag |= COPEN;
-	c->offset = 0;
-	c->aux = nil;	/* paranoia */
-	return c;
-}
-
-static void
-epclose(Ep *ep)
-{
-	qlock(ep);
-	if(waserror()){
-		qunlock(ep);
-		nexterror();
-	}
-	if(ep->inuse){
-		ep->hp->epclose(ep);
-		ep->inuse = 0;
-	}
-	qunlock(ep);
-	poperror();
-}
-
-static void
-usbclose(Chan *c)
-{
-	int q;
-	Ep *ep;
-
-	q = QID(c->qid);
-	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
-		return;
-
-	ep = getep(qid2epidx(q));
-	if(ep == nil)
-		return;
-	deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref);
-	if(waserror()){
-		putep(ep);
-		nexterror();
-	}
-	if(c->flag & COPEN){
-		free(c->aux);
-		c->aux = nil;
-		epclose(ep);
-		putep(ep);	/* release ref kept since usbopen */
-		c->flag &= ~COPEN;
-	}
-	poperror();
-	putep(ep);
-}
-
-static long
-ctlread(Chan *c, void *a, long n, vlong offset)
-{
-	int q;
-	char *s;
-	char *us;
-	char *se;
-	Ep *ep;
-	int i;
-
-	q = QID(c->qid);
-	us = s = smalloc(READSTR);
-	se = s + READSTR;
-	if(waserror()){
-		free(us);
-		nexterror();
-	}
-	if(q == Qctl)
-		for(i = 0; i < epmax; i++){
-			ep = getep(i);
-			if(ep != nil){
-				if(waserror()){
-					putep(ep);
-					nexterror();
-				}
-				s = seprint(s, se, "ep%d.%d ", ep->dev->nb, ep->nb);
-				s = seprintep(s, se, ep, 0);
-				poperror();
-			}
-			putep(ep);
-		}
-	else{
-		ep = getep(qid2epidx(q));
-		if(ep == nil)
-			error(Eio);
-		if(waserror()){
-			putep(ep);
-			nexterror();
-		}
-		if(c->aux != nil){
-			/* After a new endpoint request we read
-			 * the new endpoint name back.
-			 */
-			strecpy(s, se, c->aux);
-			free(c->aux);
-			c->aux = nil;
-		}else
-			seprintep(s, se, ep, 0);
-		poperror();
-		putep(ep);
-	}
-	n = readstr(offset, a, n, us);
-	poperror();
-	free(us);
-	return n;
-}
-
-/*
- * Fake root hub emulation.
- */
-static long
-rhubread(Ep *ep, void *a, long n)
-{
-	char *b;
-
-	if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2)
-		return -1;
-	if(ep->rhrepl < 0)
-		return -1;
-
-	b = a;
-	memset(b, 0, n);
-	PUT2(b, ep->rhrepl);
-	ep->rhrepl = -1;
-	return n;
-}
-
-static long
-rhubwrite(Ep *ep, void *a, long n)
-{
-	uchar *s;
-	int cmd;
-	int feature;
-	int port;
-	Hci *hp;
-
-	if(ep->dev == nil || ep->dev->isroot == 0 || ep->nb != 0)
-		return -1;
-	if(n != Rsetuplen)
-		error("root hub is a toy hub");
-	ep->rhrepl = -1;
-	s = a;
-	if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother))
-		error("root hub is a toy hub");
-	hp = ep->hp;
-	cmd = s[Rreq];
-	feature = GET2(s+Rvalue);
-	port = GET2(s+Rindex);
-	if(port < 1 || port > hp->nports)
-		error("bad hub port number");
-	switch(feature){
-	case Rportenable:
-		ep->rhrepl = hp->portenable(hp, port, cmd == Rsetfeature);
-		break;
-	case Rportreset:
-		ep->rhrepl = hp->portreset(hp, port, cmd == Rsetfeature);
-		break;
-	case Rgetstatus:
-		ep->rhrepl = hp->portstatus(hp, port);
-		break;
-	default:
-		ep->rhrepl = 0;
-	}
-	return n;
-}
-
-static long
-usbread(Chan *c, void *a, long n, vlong offset)
-{
-	int q;
-	Ep *ep;
-	int nr;
-
-	q = QID(c->qid);
-
-	if(c->qid.type == QTDIR)
-		return devdirread(c, a, n, nil, 0, usbgen);
-
-	if(q == Qctl || isqtype(q, Qepctl))
-		return ctlread(c, a, n, offset);
-
-	ep = getep(qid2epidx(q));
-	if(ep == nil)
-		error(Eio);
-	if(waserror()){
-		putep(ep);
-		nexterror();
-	}
-	if(ep->dev->state == Ddetach)
-		error(Edetach);
-	if(ep->mode == OWRITE || ep->inuse == 0)
-		error(Ebadusefd);
-	switch(ep->ttype){
-	case Tnone:
-		error("endpoint not configured");
-	case Tctl:
-		nr = rhubread(ep, a, n);
-		if(nr >= 0){
-			n = nr;
-			break;
-		}
-		/* else fall */
-	default:
-		ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",q,c->fid,n,offset);
-		n = ep->hp->epread(ep, a, n);
-		break;
-	}
-	poperror();
-	putep(ep);
-	return n;
-}
-
-static long
-pow2(int n)
-{
-	return 1 << n;
-}
-
-static void
-setmaxpkt(Ep *ep, char* s)
-{
-	long spp;	/* samples per packet */
-
-	if(ep->dev->speed == Highspeed)
-		spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000;
-	else
-		spp = (ep->hz * ep->pollival + 999) / 1000;
-	ep->maxpkt = spp * ep->samplesz;
-	deprint("usb: %s: setmaxpkt: hz %ld poll %ld"
-		" ntds %d %s speed -> spp %ld maxpkt %ld\n", s,
-		ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed],
-		spp, ep->maxpkt);
-	if(ep->maxpkt > 1024){
-		print("usb: %s: maxpkt %ld > 1024. truncating\n", s, ep->maxpkt);
-		ep->maxpkt = 1024;
-	}
-}
-
-/*
- * Many endpoint ctls. simply update the portable representation
- * of the endpoint. The actual controller driver will look
- * at them to setup the endpoints as dictated.
- */
-static long
-epctl(Ep *ep, Chan *c, void *a, long n)
-{
-	int i, l, mode, nb, tt;
-	char *b, *s;
-	Cmdbuf *cb;
-	Cmdtab *ct;
-	Ep *nep;
-	Udev *d;
-	static char *Info = "info ";
-
-	d = ep->dev;
-
-	cb = parsecmd(a, n);
-	if(waserror()){
-		free(cb);
-		nexterror();
-	}
-	ct = lookupcmd(cb, epctls, nelem(epctls));
-	if(ct == nil)
-		error(Ebadctl);
-	i = ct->index;
-	if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset)
-		if(ep != ep->ep0)
-			error("allowed only on a setup endpoint");
-	if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname)
-		if(ep != ep->ep0 && ep->inuse != 0)
-			error("must configure before using");
-	switch(i){
-	case CMnew:
-		deprint("usb epctl %s\n", cb->f[0]);
-		nb = strtol(cb->f[1], nil, 0);
-		if(nb < 0 || nb >= Ndeveps)
-			error("bad endpoint number");
-		tt = name2ttype(cb->f[2]);
-		if(tt == Tnone)
-			error("unknown endpoint type");
-		mode = name2mode(cb->f[3]);
-		if(mode < 0)
-			error("unknown i/o mode");
-		newdevep(ep, nb, tt, mode);
-		break;
-	case CMnewdev:
-		deprint("usb epctl %s\n", cb->f[0]);
-		if(ep != ep->ep0 || d->ishub == 0)
-			error("not a hub setup endpoint");
-		l = name2speed(cb->f[1]);
-		if(l == Nospeed)
-			error("speed must be full|low|high");
-		nep = newdev(ep->hp, 0, 0);
-		nep->dev->speed = l;
-		if(nep->dev->speed  != Lowspeed)
-			nep->maxpkt = 64;	/* assume full speed */
-		nep->dev->hub = d->nb;
-		nep->dev->port = atoi(cb->f[2]);
-		/* next read request will read
-		 * the name for the new endpoint
-		 */
-		l = sizeof(up->genbuf);
-		snprint(up->genbuf, l, "ep%d.%d", nep->dev->nb, nep->nb);
-		kstrdup(&c->aux, up->genbuf);
-		break;
-	case CMhub:
-		deprint("usb epctl %s\n", cb->f[0]);
-		d->ishub = 1;
-		break;
-	case CMspeed:
-		l = name2speed(cb->f[1]);
-		deprint("usb epctl %s %d\n", cb->f[0], l);
-		if(l == Nospeed)
-			error("speed must be full|low|high");
-		qlock(ep->ep0);
-		d->speed = l;
-		qunlock(ep->ep0);
-		break;
-	case CMmaxpkt:
-		l = strtoul(cb->f[1], nil, 0);
-		deprint("usb epctl %s %d\n", cb->f[0], l);
-		if(l < 1 || l > 1024)
-			error("maxpkt not in [1:1024]");
-		qlock(ep);
-		ep->maxpkt = l;
-		qunlock(ep);
-		break;
-	case CMntds:
-		l = strtoul(cb->f[1], nil, 0);
-		deprint("usb epctl %s %d\n", cb->f[0], l);
-		if(l < 1 || l > 3)
-			error("ntds not in [1:3]");
-		qlock(ep);
-		ep->ntds = l;
-		qunlock(ep);
-		break;
-	case CMpollival:
-		if(ep->ttype != Tintr && ep->ttype != Tiso)
-			error("not an intr or iso endpoint");
-		l = strtoul(cb->f[1], nil, 0);
-		deprint("usb epctl %s %d\n", cb->f[0], l);
-		if(ep->ttype == Tiso ||
-		   (ep->ttype == Tintr && ep->dev->speed == Highspeed)){
-			if(l < 1 || l > 16)
-				error("pollival power not in [1:16]");
-			l = pow2(l-1);
-		}else
-			if(l < 1 || l > 255)
-				error("pollival not in [1:255]");
-		qlock(ep);
-		ep->pollival = l;
-		if(ep->ttype == Tiso)
-			setmaxpkt(ep, "pollival");
-		qunlock(ep);
-		break;
-	case CMsamplesz:
-		if(ep->ttype != Tiso)
-			error("not an iso endpoint");
-		l = strtoul(cb->f[1], nil, 0);
-		deprint("usb epctl %s %d\n", cb->f[0], l);
-		if(l <= 0 || l > 8)
-			error("samplesz not in [1:8]");
-		qlock(ep);
-		ep->samplesz = l;
-		setmaxpkt(ep, "samplesz");
-		qunlock(ep);
-		break;
-	case CMhz:
-		if(ep->ttype != Tiso)
-			error("not an iso endpoint");
-		l = strtoul(cb->f[1], nil, 0);
-		deprint("usb epctl %s %d\n", cb->f[0], l);
-		if(l <= 0 || l > 100000)
-			error("hz not in [1:100000]");
-		qlock(ep);
-		ep->hz = l;
-		setmaxpkt(ep, "hz");
-		qunlock(ep);
-		break;
-	case CMclrhalt:
-		qlock(ep);
-		deprint("usb epctl %s\n", cb->f[0]);
-		ep->clrhalt = 1;
-		qunlock(ep);
-		break;
-	case CMinfo:
-		deprint("usb epctl %s\n", cb->f[0]);
-		l = strlen(Info);
-		s = a;
-		if(n < l+2 || strncmp(Info, s, l) != 0)
-			error(Ebadctl);
-		if(n > 1024)
-			n = 1024;
-		b = smalloc(n);
-		memmove(b, s+l, n-l);
-		b[n-l] = 0;
-		if(b[n-l-1] == '\n')
-			b[n-l-1] = 0;
-		qlock(ep);
-		free(ep->info);
-		ep->info = b;
-		qunlock(ep);
-		break;
-	case CMaddress:
-		deprint("usb epctl %s\n", cb->f[0]);
-		ep->dev->state = Denabled;
-		break;
-	case CMdetach:
-		if(ep->dev->isroot != 0)
-			error("can't detach a root hub");
-		deprint("usb epctl %s ep%d.%d\n",
-			cb->f[0], ep->dev->nb, ep->nb);
-		ep->dev->state = Ddetach;
-		/* Release file system ref. for its endpoints */
-		for(i = 0; i < nelem(ep->dev->eps); i++)
-			putep(ep->dev->eps[i]);
-		break;
-	case CMdebugep:
-		if(strcmp(cb->f[1], "on") == 0)
-			ep->debug = 1;
-		else if(strcmp(cb->f[1], "off") == 0)
-			ep->debug = 0;
-		else
-			ep->debug = strtoul(cb->f[1], nil, 0);
-		print("usb: ep%d.%d debug %d\n",
-			ep->dev->nb, ep->nb, ep->debug);
-		break;
-	case CMname:
-		deprint("usb epctl %s %s\n", cb->f[0], cb->f[1]);
-		validname(cb->f[1], 0);
-		kstrdup(&ep->name, cb->f[1]);
-		break;
-	case CMtmout:
-		deprint("usb epctl %s\n", cb->f[0]);
-		if(ep->ttype == Tiso || ep->ttype == Tctl)
-			error("ctl ignored for this endpoint type");
-		ep->tmout = strtoul(cb->f[1], nil, 0);
-		if(ep->tmout != 0 && ep->tmout < Xfertmout)
-			ep->tmout = Xfertmout;
-		break;
-	case CMpreset:
-		deprint("usb epctl %s\n", cb->f[0]);
-		if(ep->ttype != Tctl)
-			error("not a control endpoint");
-		if(ep->dev->state != Denabled)
-			error("forbidden on devices not enabled");
-		ep->dev->state = Dreset;
-		break;
-	default:
-		panic("usb: unknown epctl %d", ct->index);
-	}
-	free(cb);
-	poperror();
-	return n;
-}
-
-static long
-usbctl(void *a, long n)
-{
-	Cmdtab *ct;
-	Cmdbuf *cb;
-	Ep *ep;
-	int i;
-
-	cb = parsecmd(a, n);
-	if(waserror()){
-		free(cb);
-		nexterror();
-	}
-	ct = lookupcmd(cb, usbctls, nelem(usbctls));
-	dprint("usb ctl %s\n", cb->f[0]);
-	switch(ct->index){
-	case CMdebug:
-		if(strcmp(cb->f[1], "on") == 0)
-			debug = 1;
-		else if(strcmp(cb->f[1], "off") == 0)
-			debug = 0;
-		else
-			debug = strtol(cb->f[1], nil, 0);
-		print("usb: debug %d\n", debug);
-		for(i = 0; i < epmax; i++)
-			if((ep = getep(i)) != nil){
-				ep->hp->debug(ep->hp, debug);
-				putep(ep);
-			}
-		break;
-	case CMdump:
-		dumpeps();
-		break;
-	}
-	free(cb);
-	poperror();
-	return n;
-}
-
-static long
-ctlwrite(Chan *c, void *a, long n)
-{
-	int q;
-	Ep *ep;
-
-	q = QID(c->qid);
-	if(q == Qctl)
-		return usbctl(a, n);
-
-	ep = getep(qid2epidx(q));
-	if(ep == nil)
-		error(Eio);
-	if(waserror()){
-		putep(ep);
-		nexterror();
-	}
-	if(ep->dev->state == Ddetach)
-		error(Edetach);
-	if(isqtype(q, Qepctl) && c->aux != nil){
-		/* Be sure we don't keep a cloned ep name */
-		free(c->aux);
-		c->aux = nil;
-		error("read, not write, expected");
-	}
-	n = epctl(ep, c, a, n);
-	putep(ep);
-	poperror();
-	return n;
-}
-
-static long
-usbwrite(Chan *c, void *a, long n, vlong off)
-{
-	int nr, q;
-	Ep *ep;
-
-	if(c->qid.type == QTDIR)
-		error(Eisdir);
-
-	q = QID(c->qid);
-
-	if(q == Qctl || isqtype(q, Qepctl))
-		return ctlwrite(c, a, n);
-
-	ep = getep(qid2epidx(q));
-	if(ep == nil)
-		error(Eio);
-	if(waserror()){
-		putep(ep);
-		nexterror();
-	}
-	if(ep->dev->state == Ddetach)
-		error(Edetach);
-	if(ep->mode == OREAD || ep->inuse == 0)
-		error(Ebadusefd);
-
-	switch(ep->ttype){
-	case Tnone:
-		error("endpoint not configured");
-	case Tctl:
-		nr = rhubwrite(ep, a, n);
-		if(nr >= 0){
-			n = nr;
-			break;
-		}
-		/* else fall */
-	default:
-		ddeprint("\nusbwrite q %#x fid %d cnt %ld off %lld\n",q, c->fid, n, off);
-		ep->hp->epwrite(ep, a, n);
-	}
-	putep(ep);
-	poperror();
-	return n;
-}
-
-void
-usbshutdown(void)
-{
-	Hci *hp;
-	int i;
-
-	for(i = 0; i < Nhcis; i++){
-		hp = hcis[i];
-		if(hp == nil)
-			continue;
-		if(hp->shutdown == nil)
-			print("#u: no shutdown function for %s\n", hp->type);
-		else
-			hp->shutdown(hp);
-	}
-}
-
-Dev usbdevtab = {
-	L'u',
-	"usb",
-
-	usbreset,
-	usbinit,
-	usbshutdown,
-	usbattach,
-	usbwalk,
-	usbstat,
-	usbopen,
-	devcreate,
-	usbclose,
-	usbread,
-	devbread,
-	usbwrite,
-	devbwrite,
-	devremove,
-	devwstat,
-};
--- a/sys/src/9/kw/fns.h
+++ b/sys/src/9/kw/fns.h
@@ -131,6 +131,7 @@
 extern int splfhi(void);
 extern int splflo(void);
 extern void sysprocsetup(Proc*);
+extern int isaconfig(char*, int, ISAConf*);	/* only devusb.c */
 
 /*
  * PCI
--- a/sys/src/9/kw/main.c
+++ b/sys/src/9/kw/main.c
@@ -237,6 +237,13 @@
 
 void	archconsole(void);
 
+/* dummy for usb */
+int
+isaconfig(char *, int, ISAConf *)
+{
+	return 0;
+}
+
 /*
  * entered from l.s with mmu enabled.
  *
--- a/sys/src/9/kw/usbehcikw.c
+++ b/sys/src/9/kw/usbehcikw.c
@@ -340,6 +340,9 @@
 	ehcilinkage(hp);
 	hp->shutdown = shutdown;
 	hp->debug = setdebug;
+
+	intrenable(Irqlo, hp->irq, hp->interrupt, hp, hp->type);
+
 	return 0;
 }
 
--- a/sys/src/9/omap/devusb.c
+++ /dev/null
@@ -1,1461 +1,0 @@
-/*
- * USB device driver framework.
- *
- * This is in charge of providing access to actual HCIs
- * and providing I/O to the various endpoints of devices.
- * A separate user program (usbd) is in charge of
- * enumerating the bus, setting up endpoints and
- * starting devices (also user programs).
- *
- * The interface provided is a violation of the standard:
- * you're welcome.
- *
- * The interface consists of a root directory with several files
- * plus a directory (epN.M) with two files per endpoint.
- * A device is represented by its first endpoint, which
- * is a control endpoint automatically allocated for each device.
- * Device control endpoints may be used to create new endpoints.
- * Devices corresponding to hubs may also allocate new devices,
- * perhaps also hubs. Initially, a hub device is allocated for
- * each controller present, to represent its root hub. Those can
- * never be removed.
- *
- * All endpoints refer to the first endpoint (epN.0) of the device,
- * which keeps per-device information, and also to the HCI used
- * to reach them. Although all endpoints cache that information.
- *
- * epN.M/data files permit I/O and are considered DMEXCL.
- * epN.M/ctl files provide status info and accept control requests.
- *
- * Endpoints may be given file names to be listed also at #u,
- * for those drivers that have nothing to do after configuring the
- * device and its endpoints.
- *
- * Drivers for different controllers are kept at usb[oue]hci.c
- * It's likely we could factor out much from controllers into
- * a generic controller driver, the problem is that details
- * regarding how to handle toggles, tokens, Tds, etc. will
- * get in the way. Thus, code is probably easier the way it is.
- */
-
-#include	"u.h"
-#include	"../port/lib.h"
-#include	"mem.h"
-#include	"dat.h"
-#include	"fns.h"
-#include	"io.h"
-#include	"../port/error.h"
-#include	"../port/usb.h"
-
-typedef struct Hcitype Hcitype;
-
-enum
-{
-	/* Qid numbers */
-	Qdir = 0,		/* #u */
-	Qusbdir,			/* #u/usb */
-	Qctl,			/* #u/usb/ctl - control requests */
-
-	Qep0dir,			/* #u/usb/ep0.0 - endpoint 0 dir */
-	Qep0io,			/* #u/usb/ep0.0/data - endpoint 0 I/O */
-	Qep0ctl,		/* #u/usb/ep0.0/ctl - endpoint 0 ctl. */
-	Qep0dummy,		/* give 4 qids to each endpoint */
-
-	Qepdir = 0,		/* (qid-qep0dir)&3 is one of these */
-	Qepio,			/* to identify which file for the endpoint */
-	Qepctl,
-
-	/* ... */
-
-	/* Usb ctls. */
-	CMdebug = 0,		/* debug on|off */
-	CMdump,			/* dump (data structures for debug) */
-
-	/* Ep. ctls */
-	CMnew = 0,		/* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */
-	CMnewdev,		/* newdev full|low|high portnb (allocate new devices) */
-	CMhub,			/* hub (set the device as a hub) */
-	CMspeed,		/* speed full|low|high|no */
-	CMmaxpkt,		/* maxpkt size */
-	CMntds,			/* ntds nb (max nb. of tds per µframe) */
-	CMclrhalt,		/* clrhalt (halt was cleared on endpoint) */
-	CMpollival,		/* pollival interval (interrupt/iso) */
-	CMhz,			/* hz n (samples/sec; iso) */
-	CMsamplesz,		/* samplesz n (sample size; iso) */
-	CMinfo,			/* info infostr (ke.ep info for humans) */
-	CMdetach,		/* detach (abort I/O forever on this ep). */
-	CMaddress,		/* address (address is assigned) */
-	CMdebugep,		/* debug n (set/clear debug for this ep) */
-	CMname,			/* name str (show up as #u/name as well) */
-	CMtmout,		/* timeout n (activate timeouts for ep) */
-	CMpreset,		/* reset the port */
-
-	/* Hub feature selectors */
-	Rportenable	= 1,
-	Rportreset	= 4,
-
-};
-
-struct Hcitype
-{
-	char*	type;
-	int	(*reset)(Hci*);
-};
-
-#define QID(q)	((int)(q).path)
-
-static char Edetach[] = "device is detached";
-static char Enotconf[] = "endpoint not configured";
-char Estalled[] = "endpoint stalled";
-
-static Cmdtab usbctls[] =
-{
-	{CMdebug,	"debug",	2},
-	{CMdump,	"dump",		1},
-};
-
-static Cmdtab epctls[] =
-{
-	{CMnew,		"new",		4},
-	{CMnewdev,	"newdev",	3},
-	{CMhub,		"hub",		1},
-	{CMspeed,	"speed",	2},
-	{CMmaxpkt,	"maxpkt",	2},
-	{CMntds,	"ntds",		2},
-	{CMpollival,	"pollival",	2},
-	{CMsamplesz,	"samplesz",	2},
-	{CMhz,		"hz",		2},
-	{CMinfo,	"info",		0},
-	{CMdetach,	"detach",	1},
-	{CMaddress,	"address",	1},
-	{CMdebugep,	"debug",	2},
-	{CMclrhalt,	"clrhalt",	1},
-	{CMname,	"name",		2},
-	{CMtmout,	"timeout",	2},
-	{CMpreset,	"reset",	1},
-};
-
-static Dirtab usbdir[] =
-{
-	"ctl",		{Qctl},		0,	0666,
-};
-
-char *usbmodename[] =
-{
-	[OREAD]	"r",
-	[OWRITE]	"w",
-	[ORDWR]	"rw",
-};
-
-static char *ttname[] =
-{
-	[Tnone]	"none",
-	[Tctl]	"control",
-	[Tiso]	"iso",
-	[Tintr]	"interrupt",
-	[Tbulk]	"bulk",
-};
-
-static char *spname[] =
-{
-	[Fullspeed]	"full",
-	[Lowspeed]	"low",
-	[Highspeed]	"high",
-	[Nospeed]	"no",
-};
-
-static int	debug;
-static Hcitype	hcitypes[Nhcis];
-static Hci*	hcis[Nhcis];
-static QLock	epslck;		/* add, del, lookup endpoints */
-static Ep*	eps[Neps];	/* all endpoints known */
-static int	epmax;		/* 1 + last endpoint index used  */
-static int	usbidgen;	/* device address generator */
-
-/*
- * Is there something like this in a library? should it be?
- */
-char*
-seprintdata(char *s, char *se, uchar *d, int n)
-{
-	int i, l;
-
-	s = seprint(s, se, " %#p[%d]: ", d, n);
-	l = n;
-	if(l > 10)
-		l = 10;
-	for(i=0; i<l; i++)
-		s = seprint(s, se, " %2.2ux", d[i]);
-	if(l < n)
-		s = seprint(s, se, "...");
-	return s;
-}
-
-static int
-name2speed(char *name)
-{
-	int i;
-
-	for(i = 0; i < nelem(spname); i++)
-		if(strcmp(name, spname[i]) == 0)
-			return i;
-	return Nospeed;
-}
-
-static int
-name2ttype(char *name)
-{
-	int i;
-
-	for(i = 0; i < nelem(ttname); i++)
-		if(strcmp(name, ttname[i]) == 0)
-			return i;
-	/* may be a std. USB ep. type */
-	i = strtol(name, nil, 0);
-	switch(i+1){
-	case Tctl:
-	case Tiso:
-	case Tbulk:
-	case Tintr:
-		return i+1;
-	default:
-		return Tnone;
-	}
-}
-
-static int
-name2mode(char *mode)
-{
-	int i;
-
-	for(i = 0; i < nelem(usbmodename); i++)
-		if(strcmp(mode, usbmodename[i]) == 0)
-			return i;
-	return -1;
-}
-
-static int
-qid2epidx(int q)
-{
-	q = (q-Qep0dir)/4;
-	if(q < 0 || q >= epmax || eps[q] == nil)
-		return -1;
-	return q;
-}
-
-static int
-isqtype(int q, int type)
-{
-	if(q < Qep0dir)
-		return 0;
-	q -= Qep0dir;
-	return (q & 3) == type;
-}
-
-void
-addhcitype(char* t, int (*r)(Hci*))
-{
-	static int ntype;
-
-	if(ntype == Nhcis)
-		panic("too many USB host interface types");
-	hcitypes[ntype].type = t;
-	hcitypes[ntype].reset = r;
-	ntype++;
-}
-
-static char*
-seprintep(char *s, char *se, Ep *ep, int all)
-{
-	static char* dsnames[] = { "config", "enabled", "detached", "reset" };
-	Udev *d;
-	int i;
-	int di;
-
-	d = ep->dev;
-
-	qlock(ep);
-	if(waserror()){
-		qunlock(ep);
-		nexterror();
-	}
-	di = ep->dev->nb;
-	if(all)
-		s = seprint(s, se, "dev %d ep %d ", di, ep->nb);
-	s = seprint(s, se, "%s", dsnames[ep->dev->state]);
-	s = seprint(s, se, " %s", ttname[ep->ttype]);
-	assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR);
-	s = seprint(s, se, " %s", usbmodename[ep->mode]);
-	s = seprint(s, se, " speed %s", spname[d->speed]);
-	s = seprint(s, se, " maxpkt %ld", ep->maxpkt);
-	s = seprint(s, se, " pollival %ld", ep->pollival);
-	s = seprint(s, se, " samplesz %ld", ep->samplesz);
-	s = seprint(s, se, " hz %ld", ep->hz);
-	s = seprint(s, se, " hub %d", ep->dev->hub);
-	s = seprint(s, se, " port %d", ep->dev->port);
-	if(ep->inuse)
-		s = seprint(s, se, " busy");
-	else
-		s = seprint(s, se, " idle");
-	if(all){
-		s = seprint(s, se, " load %uld", ep->load);
-		s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep);
-		s = seprint(s, se, " idx %d", ep->idx);
-		if(ep->name != nil)
-			s = seprint(s, se, " name '%s'", ep->name);
-		if(ep->tmout != 0)
-			s = seprint(s, se, " tmout");
-		if(ep == ep->ep0){
-			s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno);
-			s = seprint(s, se, " eps:");
-			for(i = 0; i < nelem(d->eps); i++)
-				if(d->eps[i] != nil)
-					s = seprint(s, se, " ep%d.%d", di, i);
-		}
-	}
-	if(ep->info != nil)
-		s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type);
-	else
-		s = seprint(s, se, "\n");
-	qunlock(ep);
-	poperror();
-	return s;
-}
-
-static Ep*
-epalloc(Hci *hp)
-{
-	Ep *ep;
-	int i;
-
-	ep = smalloc(sizeof(Ep));
-	ep->ref = 1;
-	qlock(&epslck);
-	for(i = 0; i < Neps; i++)
-		if(eps[i] == nil)
-			break;
-	if(i == Neps){
-		qunlock(&epslck);
-		free(ep);
-		print("usb: bug: too few endpoints.\n");
-		return nil;
-	}
-	ep->idx = i;
-	if(epmax <= i)
-		epmax = i+1;
-	eps[i] = ep;
-	ep->hp = hp;
-	ep->maxpkt = 8;
-	ep->ntds = 1;
-	ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */
-	qunlock(&epslck);
-	return ep;
-}
-
-static Ep*
-getep(int i)
-{
-	Ep *ep;
-
-	if(i < 0 || i >= epmax || eps[i] == nil)
-		return nil;
-	qlock(&epslck);
-	ep = eps[i];
-	if(ep != nil)
-		incref(ep);
-	qunlock(&epslck);
-	return ep;
-}
-
-static void
-putep(Ep *ep)
-{
-	Udev *d;
-
-	if(ep != nil && decref(ep) == 0){
-		d = ep->dev;
-		deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep);
-		qlock(&epslck);
-		eps[ep->idx] = nil;
-		if(ep->idx == epmax-1)
-			epmax--;
-		if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen)
-			usbidgen--;
-		qunlock(&epslck);
-		if(d != nil){
-			qlock(ep->ep0);
-			d->eps[ep->nb] = nil;
-			qunlock(ep->ep0);
-		}
-		if(ep->ep0 != ep){
-			putep(ep->ep0);
-			ep->ep0 = nil;
-		}
-		free(ep->info);
-		free(ep->name);
-		free(ep);
-	}
-}
-
-static void
-dumpeps(void)
-{
-	int i;
-	static char buf[512];
-	char *s;
-	char *e;
-	Ep *ep;
-
-	print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps);
-	for(i = 0; i < epmax; i++){
-		s = buf;
-		e = buf+sizeof(buf);
-		ep = getep(i);
-		if(ep != nil){
-			if(waserror()){
-				putep(ep);
-				nexterror();
-			}
-			s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb);
-			seprintep(s, e, ep, 1);
-			print("%s", buf);
-			ep->hp->seprintep(buf, e, ep);
-			print("%s", buf);
-			poperror();
-			putep(ep);
-		}
-	}
-	print("usb dump hcis:\n");
-	for(i = 0; i < Nhcis; i++)
-		if(hcis[i] != nil)
-			hcis[i]->dump(hcis[i]);
-}
-
-static int
-newusbid(Hci *)
-{
-	int id;
-
-	qlock(&epslck);
-	id = ++usbidgen;
-	if(id >= 0x7F)
-		print("#u: too many device addresses; reuse them more\n");
-	qunlock(&epslck);
-	return id;
-}
-
-/*
- * Create endpoint 0 for a new device
- */
-static Ep*
-newdev(Hci *hp, int ishub, int isroot)
-{
-	Ep *ep;
-	Udev *d;
-
-	ep = epalloc(hp);
-	d = ep->dev = smalloc(sizeof(Udev));
-	d->nb = newusbid(hp);
-	d->eps[0] = ep;
-	ep->nb = 0;
-	ep->toggle[0] = ep->toggle[1] = 0;
-	d->ishub = ishub;
-	d->isroot = isroot;
-	if(hp->highspeed != 0)
-		d->speed = Highspeed;
-	else
-		d->speed = Fullspeed;
-	d->state = Dconfig;		/* address not yet set */
-	ep->dev = d;
-	ep->ep0 = ep;			/* no ref counted here */
-	ep->ttype = Tctl;
-	ep->tmout = Xfertmout;
-	ep->mode = ORDWR;
-	dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep);
-	return ep;
-}
-
-/*
- * Create a new endpoint for the device
- * accessed via the given endpoint 0.
- */
-static Ep*
-newdevep(Ep *ep, int i, int tt, int mode)
-{
-	Ep *nep;
-	Udev *d;
-
-	d = ep->dev;
-	if(d->eps[i] != nil)
-		error("endpoint already in use");
-	nep = epalloc(ep->hp);
-	incref(ep);
-	d->eps[i] = nep;
-	nep->nb = i;
-	nep->toggle[0] = nep->toggle[1] = 0;
-	nep->ep0 = ep;
-	nep->dev = ep->dev;
-	nep->mode = mode;
-	nep->ttype = tt;
-	nep->debug = ep->debug;
-	/* set defaults */
-	switch(tt){
-	case Tctl:
-		nep->tmout = Xfertmout;
-		break;
-	case Tintr:
-		nep->pollival = 10;
-		break;
-	case Tiso:
-		nep->tmout = Xfertmout;
-		nep->pollival = 10;
-		nep->samplesz = 4;
-		nep->hz = 44100;
-		break;
-	}
-	deprint("newdevep ep%d.%d %#p\n", d->nb, nep->nb, nep);
-	return ep;
-}
-
-static int
-epdataperm(int mode)
-{
-
-	switch(mode){
-	case OREAD:
-		return 0440|DMEXCL;
-		break;
-	case OWRITE:
-		return 0220|DMEXCL;
-		break;
-	default:
-		return 0660|DMEXCL;
-	}
-}
-
-static int
-usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
-{
-	Qid q;
-	Dirtab *dir;
-	int perm;
-	char *se;
-	Ep *ep;
-	int nb;
-	int mode;
-
-	if(0)ddprint("usbgen q %#x s %d...", QID(c->qid), s);
-	if(s == DEVDOTDOT){
-		if(QID(c->qid) <= Qusbdir){
-			mkqid(&q, Qdir, 0, QTDIR);
-			devdir(c, q, "#u", 0, eve, 0555, dp);
-		}else{
-			mkqid(&q, Qusbdir, 0, QTDIR);
-			devdir(c, q, "usb", 0, eve, 0555, dp);
-		}
-		if(0)ddprint("ok\n");
-		return 1;
-	}
-
-	switch(QID(c->qid)){
-	case Qdir:				/* list #u */
-		if(s == 0){
-			mkqid(&q, Qusbdir, 0, QTDIR);
-			devdir(c, q, "usb", 0, eve, 0555, dp);
-			if(0)ddprint("ok\n");
-			return 1;
-		}
-		s--;
-		if(s < 0 || s >= epmax)
-			goto Fail;
-		ep = getep(s);
-		if(ep == nil || ep->name == nil){
-			if(ep != nil)
-				putep(ep);
-			if(0)ddprint("skip\n");
-			return 0;
-		}
-		if(waserror()){
-			putep(ep);
-			nexterror();
-		}
-		mkqid(&q, Qep0io+s*4, 0, QTFILE);
-		devdir(c, q, ep->name, 0, eve, epdataperm(ep->mode), dp);
-		putep(ep);
-		poperror();
-		if(0)ddprint("ok\n");
-		return 1;
-
-	case Qusbdir:				/* list #u/usb */
-	Usbdir:
-		if(s < nelem(usbdir)){
-			dir = &usbdir[s];
-			mkqid(&q, dir->qid.path, 0, QTFILE);
-			devdir(c, q, dir->name, dir->length, eve, dir->perm, dp);
-			if(0)ddprint("ok\n");
-			return 1;
-		}
-		s -= nelem(usbdir);
-		if(s < 0 || s >= epmax)
-			goto Fail;
-		ep = getep(s);
-		if(ep == nil){
-			if(0)ddprint("skip\n");
-			return 0;
-		}
-		if(waserror()){
-			putep(ep);
-			nexterror();
-		}
-		se = up->genbuf+sizeof(up->genbuf);
-		seprint(up->genbuf, se, "ep%d.%d", ep->dev->nb, ep->nb);
-		mkqid(&q, Qep0dir+4*s, 0, QTDIR);
-		putep(ep);
-		poperror();
-		devdir(c, q, up->genbuf, 0, eve, 0755, dp);
-		if(0)ddprint("ok\n");
-		return 1;
-
-	case Qctl:
-		s = 0;
-		goto Usbdir;
-
-	default:				/* list #u/usb/epN.M */
-		nb = qid2epidx(QID(c->qid));
-		ep = getep(nb);
-		if(ep == nil)
-			goto Fail;
-		mode = ep->mode;
-		putep(ep);
-		if(isqtype(QID(c->qid), Qepdir)){
-		Epdir:
-			switch(s){
-			case 0:
-				mkqid(&q, Qep0io+nb*4, 0, QTFILE);
-				perm = epdataperm(mode);
-				devdir(c, q, "data", 0, eve, perm, dp);
-				break;
-			case 1:
-				mkqid(&q, Qep0ctl+nb*4, 0, QTFILE);
-				devdir(c, q, "ctl", 0, eve, 0664, dp);
-				break;
-			default:
-				goto Fail;
-			}
-		}else if(isqtype(QID(c->qid), Qepctl)){
-			s = 1;
-			goto Epdir;
-		}else{
-			s = 0;
-			goto Epdir;
-		}
-		if(0)ddprint("ok\n");
-		return 1;
-	}
-Fail:
-	if(0)ddprint("fail\n");
-	return -1;
-}
-
-static Hci*
-hciprobe(int cardno, int ctlrno)
-{
-	Hci *hp;
-	char *type;
-	char name[64];
-	static int epnb = 1;	/* guess the endpoint nb. for the controller */
-
-	ddprint("hciprobe %d %d\n", cardno, ctlrno);
-	hp = smalloc(sizeof(Hci));
-	hp->ctlrno = ctlrno;
-
-	if(cardno < 0)
-		for(cardno = 0; cardno < Nhcis; cardno++){
-			if(hcitypes[cardno].type == nil)
-				break;
-			type = hp->type;
-			if(type==nil || *type==0)
-				type = "uhci";
-			if(cistrcmp(hcitypes[cardno].type, type) == 0)
-				break;
-		}
-
-	if(cardno >= Nhcis || hcitypes[cardno].type == nil){
-		free(hp);
-		return nil;
-	}
-	dprint("%s...", hcitypes[cardno].type);
-	if(hcitypes[cardno].reset(hp) < 0){
-		free(hp);
-		return nil;
-	}
-
-	snprint(name, sizeof(name), "usb%s", hcitypes[cardno].type);
-	intrenable(hp->irq, hp->interrupt, hp, UNKNOWN, name);
-
-	print("#u/usb/ep%d.0: %s: port %#luX irq %d\n",
-		epnb, hcitypes[cardno].type, hp->port, hp->irq);
-	epnb++;
-
-	return hp;
-}
-
-static void
-usbreset(void)
-{
-	int cardno, ctlrno;
-	Hci *hp;
-
-	dprint("usbreset\n");
-
-	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++)
-		if((hp = hciprobe(-1, ctlrno)) != nil)
-			hcis[ctlrno] = hp;
-	cardno = ctlrno = 0;
-	while(cardno < Nhcis && ctlrno < Nhcis && hcitypes[cardno].type != nil)
-		if(hcis[ctlrno] != nil)
-			ctlrno++;
-		else{
-			hp = hciprobe(cardno, ctlrno);
-			if(hp == nil)
-				cardno++;
-			hcis[ctlrno++] = hp;
-		}
-	if(hcis[Nhcis-1] != nil)
-		print("usbreset: bug: Nhcis too small\n");
-}
-
-static void
-usbinit(void)
-{
-	Hci *hp;
-	int ctlrno;
-	Ep *d;
-	char info[40];
-
-	dprint("usbinit\n");
-	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){
-		hp = hcis[ctlrno];
-		if(hp != nil){
-			if(hp->init != nil)
-				hp->init(hp);
-			d = newdev(hp, 1, 1);		/* new root hub */
-			d->dev->state = Denabled;	/* although addr == 0 */
-			d->maxpkt = 64;
-			snprint(info, sizeof(info), "ports %d", hp->nports);
-			kstrdup(&d->info, info);
-		}
-	}
-}
-
-static Chan*
-usbattach(char *spec)
-{
-	return devattach(L'u', spec);
-}
-
-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);
-}
-
-/*
- * µs for the given transfer, for bandwidth allocation.
- * This is a very rough worst case for what 5.11.3
- * of the usb 2.0 spec says.
- * Also, we are using maxpkt and not actual transfer sizes.
- * Only when we are sure we
- * are not exceeding b/w might we consider adjusting it.
- */
-static ulong
-usbload(int speed, int maxpkt)
-{
-	enum{ Hostns = 1000, Hubns = 333 };
-	ulong l;
-	ulong bs;
-
-	l = 0;
-	bs = 10UL * maxpkt;
-	switch(speed){
-	case Highspeed:
-		l = 55*8*2 + 2 * (3 + bs) + Hostns;
-		break;
-	case Fullspeed:
-		l = 9107 + 84 * (4 + bs) + Hostns;
-		break;
-	case Lowspeed:
-		l = 64107 + 2 * Hubns + 667 * (3 + bs) + Hostns;
-		break;
-	default:
-		print("usbload: bad speed %d\n", speed);
-		/* let it run */
-	}
-	return l / 1000UL;	/* in µs */
-}
-
-static Chan*
-usbopen(Chan *c, int omode)
-{
-	int q;
-	Ep *ep;
-	int mode;
-
-	mode = openmode(omode);
-	q = QID(c->qid);
-
-	if(q >= Qep0dir && qid2epidx(q) < 0)
-		error(Eio);
-	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
-		return devopen(c, omode, nil, 0, usbgen);
-
-	ep = getep(qid2epidx(q));
-	if(ep == nil)
-		error(Eio);
-	deprint("usbopen q %#x fid %d omode %d\n", q, c->fid, mode);
-	if(waserror()){
-		putep(ep);
-		nexterror();
-	}
-	qlock(ep);
-	if(ep->inuse){
-		qunlock(ep);
-		error(Einuse);
-	}
-	ep->inuse = 1;
-	qunlock(ep);
-	if(waserror()){
-		ep->inuse = 0;
-		nexterror();
-	}
-	if(mode != OREAD && ep->mode == OREAD)
-		error(Eperm);
-	if(mode != OWRITE && ep->mode == OWRITE)
-		error(Eperm);
-	if(ep->ttype == Tnone)
-		error(Enotconf);
-	ep->clrhalt = 0;
-	ep->rhrepl = -1;
-	if(ep->load == 0)
-		ep->load = usbload(ep->dev->speed, ep->maxpkt);
-	ep->hp->epopen(ep);
-
-	poperror();	/* ep->inuse */
-	poperror();	/* don't putep(): ref kept for fid using the ep. */
-
-	c->mode = mode;
-	c->flag |= COPEN;
-	c->offset = 0;
-	c->aux = nil;	/* paranoia */
-	return c;
-}
-
-static void
-epclose(Ep *ep)
-{
-	qlock(ep);
-	if(waserror()){
-		qunlock(ep);
-		nexterror();
-	}
-	if(ep->inuse){
-		ep->hp->epclose(ep);
-		ep->inuse = 0;
-	}
-	qunlock(ep);
-	poperror();
-}
-
-static void
-usbclose(Chan *c)
-{
-	int q;
-	Ep *ep;
-
-	q = QID(c->qid);
-	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
-		return;
-
-	ep = getep(qid2epidx(q));
-	if(ep == nil)
-		return;
-	deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref);
-	if(waserror()){
-		putep(ep);
-		nexterror();
-	}
-	if(c->flag & COPEN){
-		free(c->aux);
-		c->aux = nil;
-		epclose(ep);
-		putep(ep);	/* release ref kept since usbopen */
-		c->flag &= ~COPEN;
-	}
-	poperror();
-	putep(ep);
-}
-
-static long
-ctlread(Chan *c, void *a, long n, vlong offset)
-{
-	int q;
-	char *s;
-	char *us;
-	char *se;
-	Ep *ep;
-	int i;
-
-	q = QID(c->qid);
-	us = s = smalloc(READSTR);
-	se = s + READSTR;
-	if(waserror()){
-		free(us);
-		nexterror();
-	}
-	if(q == Qctl)
-		for(i = 0; i < epmax; i++){
-			ep = getep(i);
-			if(ep != nil){
-				if(waserror()){
-					putep(ep);
-					nexterror();
-				}
-				s = seprint(s, se, "ep%d.%d ", ep->dev->nb, ep->nb);
-				s = seprintep(s, se, ep, 0);
-				poperror();
-			}
-			putep(ep);
-		}
-	else{
-		ep = getep(qid2epidx(q));
-		if(ep == nil)
-			error(Eio);
-		if(waserror()){
-			putep(ep);
-			nexterror();
-		}
-		if(c->aux != nil){
-			/* After a new endpoint request we read
-			 * the new endpoint name back.
-			 */
-			strecpy(s, se, c->aux);
-			free(c->aux);
-			c->aux = nil;
-		}else
-			seprintep(s, se, ep, 0);
-		poperror();
-		putep(ep);
-	}
-	n = readstr(offset, a, n, us);
-	poperror();
-	free(us);
-	return n;
-}
-
-/*
- * Fake root hub emulation.
- */
-static long
-rhubread(Ep *ep, void *a, long n)
-{
-	char *b;
-
-	if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2)
-		return -1;
-	if(ep->rhrepl < 0)
-		return -1;
-
-	b = a;
-	memset(b, 0, n);
-	PUT2(b, ep->rhrepl);
-	ep->rhrepl = -1;
-	return n;
-}
-
-static long
-rhubwrite(Ep *ep, void *a, long n)
-{
-	uchar *s;
-	int cmd;
-	int feature;
-	int port;
-	Hci *hp;
-
-	if(ep->dev == nil || ep->dev->isroot == 0 || ep->nb != 0)
-		return -1;
-	if(n != Rsetuplen)
-		error("root hub is a toy hub");
-	ep->rhrepl = -1;
-	s = a;
-	if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother))
-		error("root hub is a toy hub");
-	hp = ep->hp;
-	cmd = s[Rreq];
-	feature = GET2(s+Rvalue);
-	port = GET2(s+Rindex);
-	if(port < 1 || port > hp->nports)
-		error("bad hub port number");
-	switch(feature){
-	case Rportenable:
-		ep->rhrepl = hp->portenable(hp, port, cmd == Rsetfeature);
-		break;
-	case Rportreset:
-		ep->rhrepl = hp->portreset(hp, port, cmd == Rsetfeature);
-		break;
-	case Rgetstatus:
-		ep->rhrepl = hp->portstatus(hp, port);
-		break;
-	default:
-		ep->rhrepl = 0;
-	}
-	return n;
-}
-
-static long
-usbread(Chan *c, void *a, long n, vlong offset)
-{
-	int q;
-	Ep *ep;
-	int nr;
-
-	q = QID(c->qid);
-
-	if(c->qid.type == QTDIR)
-		return devdirread(c, a, n, nil, 0, usbgen);
-
-	if(q == Qctl || isqtype(q, Qepctl))
-		return ctlread(c, a, n, offset);
-
-	ep = getep(qid2epidx(q));
-	if(ep == nil)
-		error(Eio);
-	if(waserror()){
-		putep(ep);
-		nexterror();
-	}
-	if(ep->dev->state == Ddetach)
-		error(Edetach);
-	if(ep->mode == OWRITE || ep->inuse == 0)
-		error(Ebadusefd);
-	switch(ep->ttype){
-	case Tnone:
-		error("endpoint not configured");
-	case Tctl:
-		nr = rhubread(ep, a, n);
-		if(nr >= 0){
-			n = nr;
-			break;
-		}
-		/* else fall */
-	default:
-		ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",q,c->fid,n,offset);
-		n = ep->hp->epread(ep, a, n);
-		break;
-	}
-	poperror();
-	putep(ep);
-	return n;
-}
-
-static long
-pow2(int n)
-{
-	return 1 << n;
-}
-
-static void
-setmaxpkt(Ep *ep, char* s)
-{
-	long spp;	/* samples per packet */
-
-	if(ep->dev->speed == Highspeed)
-		spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000;
-	else
-		spp = (ep->hz * ep->pollival + 999) / 1000;
-	ep->maxpkt = spp * ep->samplesz;
-	deprint("usb: %s: setmaxpkt: hz %ld poll %ld"
-		" ntds %d %s speed -> spp %ld maxpkt %ld\n", s,
-		ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed],
-		spp, ep->maxpkt);
-	if(ep->maxpkt > 1024){
-		print("usb: %s: maxpkt %ld > 1024. truncating\n", s, ep->maxpkt);
-		ep->maxpkt = 1024;
-	}
-}
-
-/*
- * Many endpoint ctls. simply update the portable representation
- * of the endpoint. The actual controller driver will look
- * at them to setup the endpoints as dictated.
- */
-static long
-epctl(Ep *ep, Chan *c, void *a, long n)
-{
-	int i, l, mode, nb, tt;
-	char *b, *s;
-	Cmdbuf *cb;
-	Cmdtab *ct;
-	Ep *nep;
-	Udev *d;
-	static char *Info = "info ";
-
-	d = ep->dev;
-
-	cb = parsecmd(a, n);
-	if(waserror()){
-		free(cb);
-		nexterror();
-	}
-	ct = lookupcmd(cb, epctls, nelem(epctls));
-	if(ct == nil)
-		error(Ebadctl);
-	i = ct->index;
-	if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset)
-		if(ep != ep->ep0)
-			error("allowed only on a setup endpoint");
-	if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname)
-		if(ep != ep->ep0 && ep->inuse != 0)
-			error("must configure before using");
-	switch(i){
-	case CMnew:
-		deprint("usb epctl %s\n", cb->f[0]);
-		nb = strtol(cb->f[1], nil, 0);
-		if(nb < 0 || nb >= Ndeveps)
-			error("bad endpoint number");
-		tt = name2ttype(cb->f[2]);
-		if(tt == Tnone)
-			error("unknown endpoint type");
-		mode = name2mode(cb->f[3]);
-		if(mode < 0)
-			error("unknown i/o mode");
-		newdevep(ep, nb, tt, mode);
-		break;
-	case CMnewdev:
-		deprint("usb epctl %s\n", cb->f[0]);
-		if(ep != ep->ep0 || d->ishub == 0)
-			error("not a hub setup endpoint");
-		l = name2speed(cb->f[1]);
-		if(l == Nospeed)
-			error("speed must be full|low|high");
-		nep = newdev(ep->hp, 0, 0);
-		nep->dev->speed = l;
-		if(nep->dev->speed  != Lowspeed)
-			nep->maxpkt = 64;	/* assume full speed */
-		nep->dev->hub = d->nb;
-		nep->dev->port = atoi(cb->f[2]);
-		/* next read request will read
-		 * the name for the new endpoint
-		 */
-		l = sizeof(up->genbuf);
-		snprint(up->genbuf, l, "ep%d.%d", nep->dev->nb, nep->nb);
-		kstrdup(&c->aux, up->genbuf);
-		break;
-	case CMhub:
-		deprint("usb epctl %s\n", cb->f[0]);
-		d->ishub = 1;
-		break;
-	case CMspeed:
-		l = name2speed(cb->f[1]);
-		deprint("usb epctl %s %d\n", cb->f[0], l);
-		if(l == Nospeed)
-			error("speed must be full|low|high");
-		qlock(ep->ep0);
-		d->speed = l;
-		qunlock(ep->ep0);
-		break;
-	case CMmaxpkt:
-		l = strtoul(cb->f[1], nil, 0);
-		deprint("usb epctl %s %d\n", cb->f[0], l);
-		if(l < 1 || l > 1024)
-			error("maxpkt not in [1:1024]");
-		qlock(ep);
-		ep->maxpkt = l;
-		qunlock(ep);
-		break;
-	case CMntds:
-		l = strtoul(cb->f[1], nil, 0);
-		deprint("usb epctl %s %d\n", cb->f[0], l);
-		if(l < 1 || l > 3)
-			error("ntds not in [1:3]");
-		qlock(ep);
-		ep->ntds = l;
-		qunlock(ep);
-		break;
-	case CMpollival:
-		if(ep->ttype != Tintr && ep->ttype != Tiso)
-			error("not an intr or iso endpoint");
-		l = strtoul(cb->f[1], nil, 0);
-		deprint("usb epctl %s %d\n", cb->f[0], l);
-		if(ep->ttype == Tiso ||
-		   (ep->ttype == Tintr && ep->dev->speed == Highspeed)){
-			if(l < 1 || l > 16)
-				error("pollival power not in [1:16]");
-			l = pow2(l-1);
-		}else
-			if(l < 1 || l > 255)
-				error("pollival not in [1:255]");
-		qlock(ep);
-		ep->pollival = l;
-		if(ep->ttype == Tiso)
-			setmaxpkt(ep, "pollival");
-		qunlock(ep);
-		break;
-	case CMsamplesz:
-		if(ep->ttype != Tiso)
-			error("not an iso endpoint");
-		l = strtoul(cb->f[1], nil, 0);
-		deprint("usb epctl %s %d\n", cb->f[0], l);
-		if(l <= 0 || l > 8)
-			error("samplesz not in [1:8]");
-		qlock(ep);
-		ep->samplesz = l;
-		setmaxpkt(ep, "samplesz");
-		qunlock(ep);
-		break;
-	case CMhz:
-		if(ep->ttype != Tiso)
-			error("not an iso endpoint");
-		l = strtoul(cb->f[1], nil, 0);
-		deprint("usb epctl %s %d\n", cb->f[0], l);
-		if(l <= 0 || l > 100000)
-			error("hz not in [1:100000]");
-		qlock(ep);
-		ep->hz = l;
-		setmaxpkt(ep, "hz");
-		qunlock(ep);
-		break;
-	case CMclrhalt:
-		qlock(ep);
-		deprint("usb epctl %s\n", cb->f[0]);
-		ep->clrhalt = 1;
-		qunlock(ep);
-		break;
-	case CMinfo:
-		deprint("usb epctl %s\n", cb->f[0]);
-		l = strlen(Info);
-		s = a;
-		if(n < l+2 || strncmp(Info, s, l) != 0)
-			error(Ebadctl);
-		if(n > 1024)
-			n = 1024;
-		b = smalloc(n);
-		memmove(b, s+l, n-l);
-		b[n-l] = 0;
-		if(b[n-l-1] == '\n')
-			b[n-l-1] = 0;
-		qlock(ep);
-		free(ep->info);
-		ep->info = b;
-		qunlock(ep);
-		break;
-	case CMaddress:
-		deprint("usb epctl %s\n", cb->f[0]);
-		ep->dev->state = Denabled;
-		break;
-	case CMdetach:
-		if(ep->dev->isroot != 0)
-			error("can't detach a root hub");
-		deprint("usb epctl %s ep%d.%d\n",
-			cb->f[0], ep->dev->nb, ep->nb);
-		ep->dev->state = Ddetach;
-		/* Release file system ref. for its endpoints */
-		for(i = 0; i < nelem(ep->dev->eps); i++)
-			putep(ep->dev->eps[i]);
-		break;
-	case CMdebugep:
-		if(strcmp(cb->f[1], "on") == 0)
-			ep->debug = 1;
-		else if(strcmp(cb->f[1], "off") == 0)
-			ep->debug = 0;
-		else
-			ep->debug = strtoul(cb->f[1], nil, 0);
-		print("usb: ep%d.%d debug %d\n",
-			ep->dev->nb, ep->nb, ep->debug);
-		break;
-	case CMname:
-		deprint("usb epctl %s %s\n", cb->f[0], cb->f[1]);
-		validname(cb->f[1], 0);
-		kstrdup(&ep->name, cb->f[1]);
-		break;
-	case CMtmout:
-		deprint("usb epctl %s\n", cb->f[0]);
-		if(ep->ttype == Tiso || ep->ttype == Tctl)
-			error("ctl ignored for this endpoint type");
-		ep->tmout = strtoul(cb->f[1], nil, 0);
-		if(ep->tmout != 0 && ep->tmout < Xfertmout)
-			ep->tmout = Xfertmout;
-		break;
-	case CMpreset:
-		deprint("usb epctl %s\n", cb->f[0]);
-		if(ep->ttype != Tctl)
-			error("not a control endpoint");
-		if(ep->dev->state != Denabled)
-			error("forbidden on devices not enabled");
-		ep->dev->state = Dreset;
-		break;
-	default:
-		panic("usb: unknown epctl %d", ct->index);
-	}
-	free(cb);
-	poperror();
-	return n;
-}
-
-static long
-usbctl(void *a, long n)
-{
-	Cmdtab *ct;
-	Cmdbuf *cb;
-	Ep *ep;
-	int i;
-
-	cb = parsecmd(a, n);
-	if(waserror()){
-		free(cb);
-		nexterror();
-	}
-	ct = lookupcmd(cb, usbctls, nelem(usbctls));
-	dprint("usb ctl %s\n", cb->f[0]);
-	switch(ct->index){
-	case CMdebug:
-		if(strcmp(cb->f[1], "on") == 0)
-			debug = 1;
-		else if(strcmp(cb->f[1], "off") == 0)
-			debug = 0;
-		else
-			debug = strtol(cb->f[1], nil, 0);
-		print("usb: debug %d\n", debug);
-		for(i = 0; i < epmax; i++)
-			if((ep = getep(i)) != nil){
-				ep->hp->debug(ep->hp, debug);
-				putep(ep);
-			}
-		break;
-	case CMdump:
-		dumpeps();
-		break;
-	}
-	free(cb);
-	poperror();
-	return n;
-}
-
-static long
-ctlwrite(Chan *c, void *a, long n)
-{
-	int q;
-	Ep *ep;
-
-	q = QID(c->qid);
-	if(q == Qctl)
-		return usbctl(a, n);
-
-	ep = getep(qid2epidx(q));
-	if(ep == nil)
-		error(Eio);
-	if(waserror()){
-		putep(ep);
-		nexterror();
-	}
-	if(ep->dev->state == Ddetach)
-		error(Edetach);
-	if(isqtype(q, Qepctl) && c->aux != nil){
-		/* Be sure we don't keep a cloned ep name */
-		free(c->aux);
-		c->aux = nil;
-		error("read, not write, expected");
-	}
-	n = epctl(ep, c, a, n);
-	putep(ep);
-	poperror();
-	return n;
-}
-
-static long
-usbwrite(Chan *c, void *a, long n, vlong off)
-{
-	int nr, q;
-	Ep *ep;
-
-	if(c->qid.type == QTDIR)
-		error(Eisdir);
-
-	q = QID(c->qid);
-
-	if(q == Qctl || isqtype(q, Qepctl))
-		return ctlwrite(c, a, n);
-
-	ep = getep(qid2epidx(q));
-	if(ep == nil)
-		error(Eio);
-	if(waserror()){
-		putep(ep);
-		nexterror();
-	}
-	if(ep->dev->state == Ddetach)
-		error(Edetach);
-	if(ep->mode == OREAD || ep->inuse == 0)
-		error(Ebadusefd);
-
-	switch(ep->ttype){
-	case Tnone:
-		error("endpoint not configured");
-	case Tctl:
-		nr = rhubwrite(ep, a, n);
-		if(nr >= 0){
-			n = nr;
-			break;
-		}
-		/* else fall */
-	default:
-		ddeprint("\nusbwrite q %#x fid %d cnt %ld off %lld\n",q, c->fid, n, off);
-		ep->hp->epwrite(ep, a, n);
-	}
-	putep(ep);
-	poperror();
-	return n;
-}
-
-void
-usbshutdown(void)
-{
-	Hci *hp;
-	int i;
-
-	for(i = 0; i < Nhcis; i++){
-		hp = hcis[i];
-		if(hp == nil)
-			continue;
-		if(hp->shutdown == nil)
-			print("#u: no shutdown function for %s\n", hp->type);
-		else
-			hp->shutdown(hp);
-	}
-}
-
-Dev usbdevtab = {
-	L'u',
-	"usb",
-
-	usbreset,
-	usbinit,
-	usbshutdown,
-	usbattach,
-	usbwalk,
-	usbstat,
-	usbopen,
-	devcreate,
-	usbclose,
-	usbread,
-	devbread,
-	usbwrite,
-	devbwrite,
-	devremove,
-	devwstat,
-};
--- a/sys/src/9/omap/usbehciomap.c
+++ b/sys/src/9/omap/usbehciomap.c
@@ -214,6 +214,9 @@
 	intrenable(78, hp->interrupt, hp, UNKNOWN, "usbtll");
 	intrenable(92, hp->interrupt, hp, UNKNOWN, "usb otg");
 	intrenable(93, hp->interrupt, hp, UNKNOWN, "usb otg dma");
+
+	intrenable(hp->irq, hp->interrupt, hp, UNKNOWN, hp->type);
+
 	return 0;
 }
 
--- a/sys/src/9/pc/devusb.c
+++ /dev/null
@@ -1,1486 +1,0 @@
-/*
- * USB device driver framework.
- *
- * This is in charge of providing access to actual HCIs
- * and providing I/O to the various endpoints of devices.
- * A separate user program (usbd) is in charge of
- * enumerating the bus, setting up endpoints and
- * starting devices (also user programs).
- *
- * The interface provided is a violation of the standard:
- * you're welcome.
- *
- * The interface consists of a root directory with several files
- * plus a directory (epN.M) with two files per endpoint.
- * A device is represented by its first endpoint, which
- * is a control endpoint automatically allocated for each device.
- * Device control endpoints may be used to create new endpoints.
- * Devices corresponding to hubs may also allocate new devices,
- * perhaps also hubs. Initially, a hub device is allocated for
- * each controller present, to represent its root hub. Those can
- * never be removed.
- *
- * All endpoints refer to the first endpoint (epN.0) of the device,
- * which keeps per-device information, and also to the HCI used
- * to reach them. Although all endpoints cache that information.
- *
- * epN.M/data files permit I/O and are considered DMEXCL.
- * epN.M/ctl files provide status info and accept control requests.
- *
- * Endpoints may be given file names to be listed also at #u,
- * for those drivers that have nothing to do after configuring the
- * device and its endpoints.
- *
- * Drivers for different controllers are kept at usb[oue]hci.c
- * It's likely we could factor out much from controllers into
- * a generic controller driver, the problem is that details
- * regarding how to handle toggles, tokens, Tds, etc. will
- * get in the way. Thus, code is probably easier the way it is.
- */
-
-#include	"u.h"
-#include	"../port/lib.h"
-#include	"mem.h"
-#include	"dat.h"
-#include	"fns.h"
-#include	"io.h"
-#include	"../port/error.h"
-#include	"../port/usb.h"
-
-typedef struct Hcitype Hcitype;
-
-enum
-{
-	/* Qid numbers */
-	Qdir = 0,		/* #u */
-	Qusbdir,			/* #u/usb */
-	Qctl,			/* #u/usb/ctl - control requests */
-
-	Qep0dir,			/* #u/usb/ep0.0 - endpoint 0 dir */
-	Qep0io,			/* #u/usb/ep0.0/data - endpoint 0 I/O */
-	Qep0ctl,		/* #u/usb/ep0.0/ctl - endpoint 0 ctl. */
-	Qep0dummy,		/* give 4 qids to each endpoint */
-
-	Qepdir = 0,		/* (qid-qep0dir)&3 is one of these */
-	Qepio,			/* to identify which file for the endpoint */
-	Qepctl,
-
-	/* ... */
-
-	/* Usb ctls. */
-	CMdebug = 0,		/* debug on|off */
-	CMdump,			/* dump (data structures for debug) */
-
-	/* Ep. ctls */
-	CMnew = 0,		/* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */
-	CMnewdev,		/* newdev full|low|high portnb (allocate new devices) */
-	CMhub,			/* hub (set the device as a hub) */
-	CMspeed,		/* speed full|low|high|no */
-	CMmaxpkt,		/* maxpkt size */
-	CMntds,			/* ntds nb (max nb. of tds per µframe) */
-	CMclrhalt,		/* clrhalt (halt was cleared on endpoint) */
-	CMpollival,		/* pollival interval (interrupt/iso) */
-	CMhz,			/* hz n (samples/sec; iso) */
-	CMsamplesz,		/* samplesz n (sample size; iso) */
-	CMinfo,			/* info infostr (ke.ep info for humans) */
-	CMdetach,		/* detach (abort I/O forever on this ep). */
-	CMaddress,		/* address (address is assigned) */
-	CMdebugep,		/* debug n (set/clear debug for this ep) */
-	CMname,			/* name str (show up as #u/name as well) */
-	CMtmout,		/* timeout n (activate timeouts for ep) */
-	CMsampledelay,		/* maximum delay introduced by buffering (iso) */
-	CMpreset,		/* reset the port */
-
-	/* Hub feature selectors */
-	Rportenable	= 1,
-	Rportreset	= 4,
-
-};
-
-struct Hcitype
-{
-	char*	type;
-	int	(*reset)(Hci*);
-};
-
-#define QID(q)	((int)(q).path)
-
-static char Edetach[] = "device is detached";
-static char Enotconf[] = "endpoint not configured";
-char Estalled[] = "endpoint stalled";
-
-static Cmdtab usbctls[] =
-{
-	{CMdebug,	"debug",	2},
-	{CMdump,	"dump",		1},
-};
-
-static Cmdtab epctls[] =
-{
-	{CMnew,		"new",		4},
-	{CMnewdev,	"newdev",	3},
-	{CMhub,		"hub",		1},
-	{CMspeed,	"speed",	2},
-	{CMmaxpkt,	"maxpkt",	2},
-	{CMntds,	"ntds",		2},
-	{CMpollival,	"pollival",	2},
-	{CMsamplesz,	"samplesz",	2},
-	{CMhz,		"hz",		2},
-	{CMinfo,	"info",		0},
-	{CMdetach,	"detach",	1},
-	{CMaddress,	"address",	1},
-	{CMdebugep,	"debug",	2},
-	{CMclrhalt,	"clrhalt",	1},
-	{CMname,	"name",		2},
-	{CMtmout,	"timeout",	2},
-	{CMsampledelay,	"sampledelay",	2},
-	{CMpreset,	"reset",	1},
-};
-
-static Dirtab usbdir[] =
-{
-	"ctl",		{Qctl},		0,	0666,
-};
-
-char *usbmodename[] =
-{
-	[OREAD]	"r",
-	[OWRITE]	"w",
-	[ORDWR]	"rw",
-};
-
-static char *ttname[] =
-{
-	[Tnone]	"none",
-	[Tctl]	"control",
-	[Tiso]	"iso",
-	[Tintr]	"interrupt",
-	[Tbulk]	"bulk",
-};
-
-static char *spname[] =
-{
-	[Fullspeed]	"full",
-	[Lowspeed]	"low",
-	[Highspeed]	"high",
-	[Nospeed]	"no",
-};
-
-static int	debug;
-static Hcitype	hcitypes[Nhcis];
-static Hci*	hcis[Nhcis];
-static QLock	epslck;		/* add, del, lookup endpoints */
-static Ep*	eps[Neps];	/* all endpoints known */
-static int	epmax;		/* 1 + last endpoint index used  */
-static int	usbidgen;	/* device address generator */
-
-/*
- * Is there something like this in a library? should it be?
- */
-char*
-seprintdata(char *s, char *se, uchar *d, int n)
-{
-	int i, l;
-
-	s = seprint(s, se, " %#p[%d]: ", d, n);
-	l = n;
-	if(l > 10)
-		l = 10;
-	for(i=0; i<l; i++)
-		s = seprint(s, se, " %2.2ux", d[i]);
-	if(l < n)
-		s = seprint(s, se, "...");
-	return s;
-}
-
-static int
-name2speed(char *name)
-{
-	int i;
-
-	for(i = 0; i < nelem(spname); i++)
-		if(strcmp(name, spname[i]) == 0)
-			return i;
-	return Nospeed;
-}
-
-static int
-name2ttype(char *name)
-{
-	int i;
-
-	for(i = 0; i < nelem(ttname); i++)
-		if(strcmp(name, ttname[i]) == 0)
-			return i;
-	/* may be a std. USB ep. type */
-	i = strtol(name, nil, 0);
-	switch(i+1){
-	case Tctl:
-	case Tiso:
-	case Tbulk:
-	case Tintr:
-		return i+1;
-	default:
-		return Tnone;
-	}
-}
-
-static int
-name2mode(char *mode)
-{
-	int i;
-
-	for(i = 0; i < nelem(usbmodename); i++)
-		if(strcmp(mode, usbmodename[i]) == 0)
-			return i;
-	return -1;
-}
-
-static int
-qid2epidx(int q)
-{
-	q = (q-Qep0dir)/4;
-	if(q < 0 || q >= epmax || eps[q] == nil)
-		return -1;
-	return q;
-}
-
-static int
-isqtype(int q, int type)
-{
-	if(q < Qep0dir)
-		return 0;
-	q -= Qep0dir;
-	return (q & 3) == type;
-}
-
-void
-addhcitype(char* t, int (*r)(Hci*))
-{
-	static int ntype;
-
-	if(ntype == Nhcis)
-		panic("too many USB host interface types");
-	hcitypes[ntype].type = t;
-	hcitypes[ntype].reset = r;
-	ntype++;
-}
-
-static char*
-seprintep(char *s, char *se, Ep *ep, int all)
-{
-	static char* dsnames[] = { "config", "enabled", "detached", "reset" };
-	Udev *d;
-	int i;
-	int di;
-
-	d = ep->dev;
-
-	qlock(ep);
-	if(waserror()){
-		qunlock(ep);
-		nexterror();
-	}
-	di = ep->dev->nb;
-	if(all)
-		s = seprint(s, se, "dev %d ep %d ", di, ep->nb);
-	s = seprint(s, se, "%s", dsnames[ep->dev->state]);
-	s = seprint(s, se, " %s", ttname[ep->ttype]);
-	assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR);
-	s = seprint(s, se, " %s", usbmodename[ep->mode]);
-	s = seprint(s, se, " speed %s", spname[d->speed]);
-	s = seprint(s, se, " maxpkt %ld", ep->maxpkt);
-	s = seprint(s, se, " pollival %ld", ep->pollival);
-	s = seprint(s, se, " samplesz %ld", ep->samplesz);
-	s = seprint(s, se, " hz %ld", ep->hz);
-	s = seprint(s, se, " hub %d", ep->dev->hub);
-	s = seprint(s, se, " port %d", ep->dev->port);
-	if(ep->inuse)
-		s = seprint(s, se, " busy");
-	else
-		s = seprint(s, se, " idle");
-	if(all){
-		s = seprint(s, se, " load %uld", ep->load);
-		s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep);
-		s = seprint(s, se, " idx %d", ep->idx);
-		if(ep->name != nil)
-			s = seprint(s, se, " name '%s'", ep->name);
-		if(ep->tmout != 0)
-			s = seprint(s, se, " tmout");
-		if(ep == ep->ep0){
-			s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno);
-			s = seprint(s, se, " eps:");
-			for(i = 0; i < nelem(d->eps); i++)
-				if(d->eps[i] != nil)
-					s = seprint(s, se, " ep%d.%d", di, i);
-		}
-	}
-	if(ep->info != nil)
-		s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type);
-	else
-		s = seprint(s, se, "\n");
-	qunlock(ep);
-	poperror();
-	return s;
-}
-
-static Ep*
-epalloc(Hci *hp)
-{
-	Ep *ep;
-	int i;
-
-	ep = smalloc(sizeof(Ep));
-	ep->ref = 1;
-	qlock(&epslck);
-	for(i = 0; i < Neps; i++)
-		if(eps[i] == nil)
-			break;
-	if(i == Neps){
-		qunlock(&epslck);
-		free(ep);
-		print("usb: bug: too few endpoints.\n");
-		return nil;
-	}
-	ep->idx = i;
-	if(epmax <= i)
-		epmax = i+1;
-	eps[i] = ep;
-	ep->hp = hp;
-	ep->maxpkt = 8;
-	ep->ntds = 1;
-	ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */
-	qunlock(&epslck);
-	return ep;
-}
-
-static Ep*
-getep(int i)
-{
-	Ep *ep;
-
-	if(i < 0 || i >= epmax || eps[i] == nil)
-		return nil;
-	qlock(&epslck);
-	ep = eps[i];
-	if(ep != nil)
-		incref(ep);
-	qunlock(&epslck);
-	return ep;
-}
-
-static void
-putep(Ep *ep)
-{
-	Udev *d;
-
-	if(ep != nil && decref(ep) == 0){
-		d = ep->dev;
-		deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep);
-		qlock(&epslck);
-		eps[ep->idx] = nil;
-		if(ep->idx == epmax-1)
-			epmax--;
-		if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen)
-			usbidgen--;
-		qunlock(&epslck);
-		if(d != nil){
-			qlock(ep->ep0);
-			d->eps[ep->nb] = nil;
-			qunlock(ep->ep0);
-		}
-		if(ep->ep0 != ep){
-			putep(ep->ep0);
-			ep->ep0 = nil;
-		}
-		free(ep->info);
-		free(ep->name);
-		free(ep);
-	}
-}
-
-static void
-dumpeps(void)
-{
-	int i;
-	static char buf[512];
-	char *s;
-	char *e;
-	Ep *ep;
-
-	print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps);
-	for(i = 0; i < epmax; i++){
-		s = buf;
-		e = buf+sizeof(buf);
-		ep = getep(i);
-		if(ep != nil){
-			if(waserror()){
-				putep(ep);
-				nexterror();
-			}
-			s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb);
-			seprintep(s, e, ep, 1);
-			print("%s", buf);
-			ep->hp->seprintep(buf, e, ep);
-			print("%s", buf);
-			poperror();
-			putep(ep);
-		}
-	}
-	print("usb dump hcis:\n");
-	for(i = 0; i < Nhcis; i++)
-		if(hcis[i] != nil)
-			hcis[i]->dump(hcis[i]);
-}
-
-static int
-newusbid(Hci *)
-{
-	int id;
-
-	qlock(&epslck);
-	id = ++usbidgen;
-	if(id >= 0x7F)
-		print("#u: too many device addresses; reuse them more\n");
-	qunlock(&epslck);
-	return id;
-}
-
-/*
- * Create endpoint 0 for a new device
- */
-static Ep*
-newdev(Hci *hp, int ishub, int isroot)
-{
-	Ep *ep;
-	Udev *d;
-
-	ep = epalloc(hp);
-	d = ep->dev = smalloc(sizeof(Udev));
-	d->nb = newusbid(hp);
-	d->eps[0] = ep;
-	ep->nb = 0;
-	ep->toggle[0] = ep->toggle[1] = 0;
-	d->ishub = ishub;
-	d->isroot = isroot;
-	if(hp->highspeed != 0)
-		d->speed = Highspeed;
-	else
-		d->speed = Fullspeed;
-	d->state = Dconfig;		/* address not yet set */
-	ep->dev = d;
-	ep->ep0 = ep;			/* no ref counted here */
-	ep->ttype = Tctl;
-	ep->tmout = Xfertmout;
-	ep->mode = ORDWR;
-	dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep);
-	return ep;
-}
-
-/*
- * Create a new endpoint for the device
- * accessed via the given endpoint 0.
- */
-static Ep*
-newdevep(Ep *ep, int i, int tt, int mode)
-{
-	Ep *nep;
-	Udev *d;
-
-	d = ep->dev;
-	if(d->eps[i] != nil)
-		error("endpoint already in use");
-	nep = epalloc(ep->hp);
-	incref(ep);
-	d->eps[i] = nep;
-	nep->nb = i;
-	nep->toggle[0] = nep->toggle[1] = 0;
-	nep->ep0 = ep;
-	nep->dev = ep->dev;
-	nep->mode = mode;
-	nep->ttype = tt;
-	nep->debug = ep->debug;
-	/* set defaults */
-	switch(tt){
-	case Tctl:
-		nep->tmout = Xfertmout;
-		break;
-	case Tintr:
-		nep->pollival = 10;
-		break;
-	case Tiso:
-		nep->tmout = Xfertmout;
-		nep->pollival = 10;
-		nep->samplesz = 4;
-		nep->hz = 44100;
-		break;
-	}
-	deprint("newdevep ep%d.%d %#p\n", d->nb, nep->nb, nep);
-	return ep;
-}
-
-static int
-epdataperm(int mode)
-{
-
-	switch(mode){
-	case OREAD:
-		return 0440|DMEXCL;
-		break;
-	case OWRITE:
-		return 0220|DMEXCL;
-		break;
-	default:
-		return 0660|DMEXCL;
-	}
-}
-
-static int
-usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
-{
-	Qid q;
-	Dirtab *dir;
-	int perm;
-	char *se;
-	Ep *ep;
-	int nb;
-	int mode;
-
-	if(0)ddprint("usbgen q %#x s %d...", QID(c->qid), s);
-	if(s == DEVDOTDOT){
-		if(QID(c->qid) <= Qusbdir){
-			mkqid(&q, Qdir, 0, QTDIR);
-			devdir(c, q, "#u", 0, eve, 0555, dp);
-		}else{
-			mkqid(&q, Qusbdir, 0, QTDIR);
-			devdir(c, q, "usb", 0, eve, 0555, dp);
-		}
-		if(0)ddprint("ok\n");
-		return 1;
-	}
-
-	switch(QID(c->qid)){
-	case Qdir:				/* list #u */
-		if(s == 0){
-			mkqid(&q, Qusbdir, 0, QTDIR);
-			devdir(c, q, "usb", 0, eve, 0555, dp);
-			if(0)ddprint("ok\n");
-			return 1;
-		}
-		s--;
-		if(s < 0 || s >= epmax)
-			goto Fail;
-		ep = getep(s);
-		if(ep == nil || ep->name == nil){
-			if(ep != nil)
-				putep(ep);
-			if(0)ddprint("skip\n");
-			return 0;
-		}
-		if(waserror()){
-			putep(ep);
-			nexterror();
-		}
-		mkqid(&q, Qep0io+s*4, 0, QTFILE);
-		devdir(c, q, ep->name, 0, eve, epdataperm(ep->mode), dp);
-		putep(ep);
-		poperror();
-		if(0)ddprint("ok\n");
-		return 1;
-
-	case Qusbdir:				/* list #u/usb */
-	Usbdir:
-		if(s < nelem(usbdir)){
-			dir = &usbdir[s];
-			mkqid(&q, dir->qid.path, 0, QTFILE);
-			devdir(c, q, dir->name, dir->length, eve, dir->perm, dp);
-			if(0)ddprint("ok\n");
-			return 1;
-		}
-		s -= nelem(usbdir);
-		if(s < 0 || s >= epmax)
-			goto Fail;
-		ep = getep(s);
-		if(ep == nil){
-			if(0)ddprint("skip\n");
-			return 0;
-		}
-		if(waserror()){
-			putep(ep);
-			nexterror();
-		}
-		se = up->genbuf+sizeof(up->genbuf);
-		seprint(up->genbuf, se, "ep%d.%d", ep->dev->nb, ep->nb);
-		mkqid(&q, Qep0dir+4*s, 0, QTDIR);
-		putep(ep);
-		poperror();
-		devdir(c, q, up->genbuf, 0, eve, 0755, dp);
-		if(0)ddprint("ok\n");
-		return 1;
-
-	case Qctl:
-		s = 0;
-		goto Usbdir;
-
-	default:				/* list #u/usb/epN.M */
-		nb = qid2epidx(QID(c->qid));
-		ep = getep(nb);
-		if(ep == nil)
-			goto Fail;
-		mode = ep->mode;
-		putep(ep);
-		if(isqtype(QID(c->qid), Qepdir)){
-		Epdir:
-			switch(s){
-			case 0:
-				mkqid(&q, Qep0io+nb*4, 0, QTFILE);
-				perm = epdataperm(mode);
-				devdir(c, q, "data", 0, eve, perm, dp);
-				break;
-			case 1:
-				mkqid(&q, Qep0ctl+nb*4, 0, QTFILE);
-				devdir(c, q, "ctl", 0, eve, 0664, dp);
-				break;
-			default:
-				goto Fail;
-			}
-		}else if(isqtype(QID(c->qid), Qepctl)){
-			s = 1;
-			goto Epdir;
-		}else{
-			s = 0;
-			goto Epdir;
-		}
-		if(0)ddprint("ok\n");
-		return 1;
-	}
-Fail:
-	if(0)ddprint("fail\n");
-	return -1;
-}
-
-static Hci*
-hciprobe(int cardno, int ctlrno)
-{
-	Hci *hp;
-	char *type;
-	char name[64];
-	static int epnb = 1;	/* guess the endpoint nb. for the controller */
-
-	ddprint("hciprobe %d %d\n", cardno, ctlrno);
-	hp = smalloc(sizeof(Hci));
-	hp->ctlrno = ctlrno;
-	hp->tbdf = BUSUNKNOWN;
-
-	if(cardno < 0){
-		if(isaconfig("usb", ctlrno, hp) == 0){
-			free(hp);
-			return nil;
-		}
-		for(cardno = 0; cardno < Nhcis; cardno++){
-			if(hcitypes[cardno].type == nil)
-				break;
-			type = hp->type;
-			if(type==nil || *type==0)
-				type = "uhci";
-			if(cistrcmp(hcitypes[cardno].type, type) == 0)
-				break;
-		}
-	}
-
-	if(cardno >= Nhcis || hcitypes[cardno].type == nil){
-		free(hp);
-		return nil;
-	}
-	dprint("%s...", hcitypes[cardno].type);
-	if(hcitypes[cardno].reset(hp) < 0){
-		free(hp);
-		return nil;
-	}
-
-	/*
-	 * IRQ2 doesn't really exist, it's used to gang the interrupt
-	 * controllers together. A device set to IRQ2 will appear on
-	 * the second interrupt controller as IRQ9.
-	 */
-	if(hp->irq == 2)
-		hp->irq = 9;
-	snprint(name, sizeof(name), "usb%s", hcitypes[cardno].type);
-	intrenable(hp->irq, hp->interrupt, hp, hp->tbdf, name);
-
-	/*
-	 * modern machines have too many usb controllers to list on
-	 * the console.
-	 */
-	dprint("#u/usb/ep%d.0: %s: port 0x%luX irq %d\n",
-		epnb, hcitypes[cardno].type, hp->port, hp->irq);
-	epnb++;
-	return hp;
-}
-
-static void
-usbreset(void)
-{
-	int cardno, ctlrno;
-	Hci *hp;
-
-	if(getconf("*nousbprobe"))
-		return;
-	dprint("usbreset\n");
-
-	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++)
-		if((hp = hciprobe(-1, ctlrno)) != nil)
-			hcis[ctlrno] = hp;
-	cardno = ctlrno = 0;
-	while(cardno < Nhcis && ctlrno < Nhcis && hcitypes[cardno].type != nil)
-		if(hcis[ctlrno] != nil)
-			ctlrno++;
-		else{
-			hp = hciprobe(cardno, ctlrno);
-			if(hp == nil)
-				cardno++;
-			hcis[ctlrno++] = hp;
-		}
-	if(hcis[Nhcis-1] != nil)
-		print("usbreset: bug: Nhcis (%d) too small\n", Nhcis);
-}
-
-static void
-usbinit(void)
-{
-	Hci *hp;
-	int ctlrno;
-	Ep *d;
-	char info[40];
-
-	dprint("usbinit\n");
-	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){
-		hp = hcis[ctlrno];
-		if(hp != nil){
-			if(hp->init != nil)
-				hp->init(hp);
-			d = newdev(hp, 1, 1);		/* new root hub */
-			d->dev->state = Denabled;	/* although addr == 0 */
-			d->maxpkt = 64;
-			snprint(info, sizeof(info), "ports %d", hp->nports);
-			kstrdup(&d->info, info);
-		}
-	}
-}
-
-static Chan*
-usbattach(char *spec)
-{
-	return devattach(L'u', spec);
-}
-
-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);
-}
-
-/*
- * µs for the given transfer, for bandwidth allocation.
- * This is a very rough worst case for what 5.11.3
- * of the usb 2.0 spec says.
- * Also, we are using maxpkt and not actual transfer sizes.
- * Only when we are sure we
- * are not exceeding b/w might we consider adjusting it.
- */
-static ulong
-usbload(int speed, int maxpkt)
-{
-	enum{ Hostns = 1000, Hubns = 333 };
-	ulong l;
-	ulong bs;
-
-	l = 0;
-	bs = 10UL * maxpkt;
-	switch(speed){
-	case Highspeed:
-		l = 55*8*2 + 2 * (3 + bs) + Hostns;
-		break;
-	case Fullspeed:
-		l = 9107 + 84 * (4 + bs) + Hostns;
-		break;
-	case Lowspeed:
-		l = 64107 + 2 * Hubns + 667 * (3 + bs) + Hostns;
-		break;
-	default:
-		print("usbload: bad speed %d\n", speed);
-		/* let it run */
-	}
-	return l / 1000UL;	/* in µs */
-}
-
-static Chan*
-usbopen(Chan *c, int omode)
-{
-	int q;
-	Ep *ep;
-	int mode;
-
-	mode = openmode(omode);
-	q = QID(c->qid);
-
-	if(q >= Qep0dir && qid2epidx(q) < 0)
-		error(Eio);
-	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
-		return devopen(c, omode, nil, 0, usbgen);
-
-	ep = getep(qid2epidx(q));
-	if(ep == nil)
-		error(Eio);
-	deprint("usbopen q %#x fid %d omode %d\n", q, c->fid, mode);
-	if(waserror()){
-		putep(ep);
-		nexterror();
-	}
-	qlock(ep);
-	if(ep->inuse){
-		qunlock(ep);
-		error(Einuse);
-	}
-	ep->inuse = 1;
-	qunlock(ep);
-	if(waserror()){
-		ep->inuse = 0;
-		nexterror();
-	}
-	if(mode != OREAD && ep->mode == OREAD)
-		error(Eperm);
-	if(mode != OWRITE && ep->mode == OWRITE)
-		error(Eperm);
-	if(ep->ttype == Tnone)
-		error(Enotconf);
-	ep->clrhalt = 0;
-	ep->rhrepl = -1;
-	if(ep->load == 0)
-		ep->load = usbload(ep->dev->speed, ep->maxpkt);
-	ep->hp->epopen(ep);
-
-	poperror();	/* ep->inuse */
-	poperror();	/* don't putep(): ref kept for fid using the ep. */
-
-	c->mode = mode;
-	c->flag |= COPEN;
-	c->offset = 0;
-	c->aux = nil;	/* paranoia */
-	return c;
-}
-
-static void
-epclose(Ep *ep)
-{
-	qlock(ep);
-	if(waserror()){
-		qunlock(ep);
-		nexterror();
-	}
-	if(ep->inuse){
-		ep->hp->epclose(ep);
-		ep->inuse = 0;
-	}
-	qunlock(ep);
-	poperror();
-}
-
-static void
-usbclose(Chan *c)
-{
-	int q;
-	Ep *ep;
-
-	q = QID(c->qid);
-	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
-		return;
-
-	ep = getep(qid2epidx(q));
-	if(ep == nil)
-		return;
-	deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref);
-	if(waserror()){
-		putep(ep);
-		nexterror();
-	}
-	if(c->flag & COPEN){
-		free(c->aux);
-		c->aux = nil;
-		epclose(ep);
-		putep(ep);	/* release ref kept since usbopen */
-		c->flag &= ~COPEN;
-	}
-	poperror();
-	putep(ep);
-}
-
-static long
-ctlread(Chan *c, void *a, long n, vlong offset)
-{
-	int q;
-	char *s;
-	char *us;
-	char *se;
-	Ep *ep;
-	int i;
-
-	q = QID(c->qid);
-	us = s = smalloc(READSTR);
-	se = s + READSTR;
-	if(waserror()){
-		free(us);
-		nexterror();
-	}
-	if(q == Qctl)
-		for(i = 0; i < epmax; i++){
-			ep = getep(i);
-			if(ep != nil){
-				if(waserror()){
-					putep(ep);
-					nexterror();
-				}
-				s = seprint(s, se, "ep%d.%d ", ep->dev->nb, ep->nb);
-				s = seprintep(s, se, ep, 0);
-				poperror();
-			}
-			putep(ep);
-		}
-	else{
-		ep = getep(qid2epidx(q));
-		if(ep == nil)
-			error(Eio);
-		if(waserror()){
-			putep(ep);
-			nexterror();
-		}
-		if(c->aux != nil){
-			/* After a new endpoint request we read
-			 * the new endpoint name back.
-			 */
-			strecpy(s, se, c->aux);
-			free(c->aux);
-			c->aux = nil;
-		}else
-			seprintep(s, se, ep, 0);
-		poperror();
-		putep(ep);
-	}
-	n = readstr(offset, a, n, us);
-	poperror();
-	free(us);
-	return n;
-}
-
-/*
- * Fake root hub emulation.
- */
-static long
-rhubread(Ep *ep, void *a, long n)
-{
-	char *b;
-
-	if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2)
-		return -1;
-	if(ep->rhrepl < 0)
-		return -1;
-
-	b = a;
-	memset(b, 0, n);
-	PUT2(b, ep->rhrepl);
-	ep->rhrepl = -1;
-	return n;
-}
-
-static long
-rhubwrite(Ep *ep, void *a, long n)
-{
-	uchar *s;
-	int cmd;
-	int feature;
-	int port;
-	Hci *hp;
-
-	if(ep->dev == nil || ep->dev->isroot == 0 || ep->nb != 0)
-		return -1;
-	if(n != Rsetuplen)
-		error("root hub is a toy hub");
-	ep->rhrepl = -1;
-	s = a;
-	if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother))
-		error("root hub is a toy hub");
-	hp = ep->hp;
-	cmd = s[Rreq];
-	feature = GET2(s+Rvalue);
-	port = GET2(s+Rindex);
-	if(port < 1 || port > hp->nports)
-		error("bad hub port number");
-	switch(feature){
-	case Rportenable:
-		ep->rhrepl = hp->portenable(hp, port, cmd == Rsetfeature);
-		break;
-	case Rportreset:
-		ep->rhrepl = hp->portreset(hp, port, cmd == Rsetfeature);
-		break;
-	case Rgetstatus:
-		ep->rhrepl = hp->portstatus(hp, port);
-		break;
-	default:
-		ep->rhrepl = 0;
-	}
-	return n;
-}
-
-static long
-usbread(Chan *c, void *a, long n, vlong offset)
-{
-	int q;
-	Ep *ep;
-	int nr;
-
-	q = QID(c->qid);
-
-	if(c->qid.type == QTDIR)
-		return devdirread(c, a, n, nil, 0, usbgen);
-
-	if(q == Qctl || isqtype(q, Qepctl))
-		return ctlread(c, a, n, offset);
-
-	ep = getep(qid2epidx(q));
-	if(ep == nil)
-		error(Eio);
-	if(waserror()){
-		putep(ep);
-		nexterror();
-	}
-	if(ep->dev->state == Ddetach)
-		error(Edetach);
-	if(ep->mode == OWRITE || ep->inuse == 0)
-		error(Ebadusefd);
-	switch(ep->ttype){
-	case Tnone:
-		error("endpoint not configured");
-	case Tctl:
-		nr = rhubread(ep, a, n);
-		if(nr >= 0){
-			n = nr;
-			break;
-		}
-		/* else fall */
-	default:
-		ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",q,c->fid,n,offset);
-		n = ep->hp->epread(ep, a, n);
-		break;
-	}
-	poperror();
-	putep(ep);
-	return n;
-}
-
-static long
-pow2(int n)
-{
-	return 1 << n;
-}
-
-static void
-setmaxpkt(Ep *ep, char* s)
-{
-	long spp;	/* samples per packet */
-
-	if(ep->dev->speed == Highspeed)
-		spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000;
-	else
-		spp = (ep->hz * ep->pollival + 999) / 1000;
-	ep->maxpkt = spp * ep->samplesz;
-	deprint("usb: %s: setmaxpkt: hz %ld poll %ld"
-		" ntds %d %s speed -> spp %ld maxpkt %ld\n", s,
-		ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed],
-		spp, ep->maxpkt);
-	if(ep->maxpkt > 1024){
-		print("usb: %s: maxpkt %ld > 1024. truncating\n", s, ep->maxpkt);
-		ep->maxpkt = 1024;
-	}
-}
-
-/*
- * Many endpoint ctls. simply update the portable representation
- * of the endpoint. The actual controller driver will look
- * at them to setup the endpoints as dictated.
- */
-static long
-epctl(Ep *ep, Chan *c, void *a, long n)
-{
-	int i, l, mode, nb, tt;
-	char *b, *s;
-	Cmdbuf *cb;
-	Cmdtab *ct;
-	Ep *nep;
-	Udev *d;
-	static char *Info = "info ";
-
-	d = ep->dev;
-
-	cb = parsecmd(a, n);
-	if(waserror()){
-		free(cb);
-		nexterror();
-	}
-	ct = lookupcmd(cb, epctls, nelem(epctls));
-	if(ct == nil)
-		error(Ebadctl);
-	i = ct->index;
-	if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset)
-		if(ep != ep->ep0)
-			error("allowed only on a setup endpoint");
-	if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname)
-		if(ep != ep->ep0 && ep->inuse != 0)
-			error("must configure before using");
-	switch(i){
-	case CMnew:
-		deprint("usb epctl %s\n", cb->f[0]);
-		nb = strtol(cb->f[1], nil, 0);
-		if(nb < 0 || nb >= Ndeveps)
-			error("bad endpoint number");
-		tt = name2ttype(cb->f[2]);
-		if(tt == Tnone)
-			error("unknown endpoint type");
-		mode = name2mode(cb->f[3]);
-		if(mode < 0)
-			error("unknown i/o mode");
-		newdevep(ep, nb, tt, mode);
-		break;
-	case CMnewdev:
-		deprint("usb epctl %s\n", cb->f[0]);
-		if(ep != ep->ep0 || d->ishub == 0)
-			error("not a hub setup endpoint");
-		l = name2speed(cb->f[1]);
-		if(l == Nospeed)
-			error("speed must be full|low|high");
-		nep = newdev(ep->hp, 0, 0);
-		nep->dev->speed = l;
-		if(nep->dev->speed  != Lowspeed)
-			nep->maxpkt = 64;	/* assume full speed */
-		nep->dev->hub = d->nb;
-		nep->dev->port = atoi(cb->f[2]);
-		/* next read request will read
-		 * the name for the new endpoint
-		 */
-		l = sizeof(up->genbuf);
-		snprint(up->genbuf, l, "ep%d.%d", nep->dev->nb, nep->nb);
-		kstrdup(&c->aux, up->genbuf);
-		break;
-	case CMhub:
-		deprint("usb epctl %s\n", cb->f[0]);
-		d->ishub = 1;
-		break;
-	case CMspeed:
-		l = name2speed(cb->f[1]);
-		deprint("usb epctl %s %d\n", cb->f[0], l);
-		if(l == Nospeed)
-			error("speed must be full|low|high");
-		qlock(ep->ep0);
-		d->speed = l;
-		qunlock(ep->ep0);
-		break;
-	case CMmaxpkt:
-		l = strtoul(cb->f[1], nil, 0);
-		deprint("usb epctl %s %d\n", cb->f[0], l);
-		if(l < 1 || l > 1024)
-			error("maxpkt not in [1:1024]");
-		qlock(ep);
-		ep->maxpkt = l;
-		qunlock(ep);
-		break;
-	case CMntds:
-		l = strtoul(cb->f[1], nil, 0);
-		deprint("usb epctl %s %d\n", cb->f[0], l);
-		if(l < 1 || l > 3)
-			error("ntds not in [1:3]");
-		qlock(ep);
-		ep->ntds = l;
-		qunlock(ep);
-		break;
-	case CMpollival:
-		if(ep->ttype != Tintr && ep->ttype != Tiso)
-			error("not an intr or iso endpoint");
-		l = strtoul(cb->f[1], nil, 0);
-		deprint("usb epctl %s %d\n", cb->f[0], l);
-		if(ep->ttype == Tiso ||
-		   (ep->ttype == Tintr && ep->dev->speed == Highspeed)){
-			if(l < 1 || l > 16)
-				error("pollival power not in [1:16]");
-			l = pow2(l-1);
-		}else
-			if(l < 1 || l > 255)
-				error("pollival not in [1:255]");
-		qlock(ep);
-		ep->pollival = l;
-		if(ep->ttype == Tiso)
-			setmaxpkt(ep, "pollival");
-		qunlock(ep);
-		break;
-	case CMsamplesz:
-		if(ep->ttype != Tiso)
-			error("not an iso endpoint");
-		l = strtoul(cb->f[1], nil, 0);
-		deprint("usb epctl %s %d\n", cb->f[0], l);
-		if(l <= 0 || l > 8)
-			error("samplesz not in [1:8]");
-		qlock(ep);
-		ep->samplesz = l;
-		setmaxpkt(ep, "samplesz");
-		qunlock(ep);
-		break;
-	case CMhz:
-		if(ep->ttype != Tiso)
-			error("not an iso endpoint");
-		l = strtoul(cb->f[1], nil, 0);
-		deprint("usb epctl %s %d\n", cb->f[0], l);
-		if(l <= 0 || l > 100000)
-			error("hz not in [1:100000]");
-		qlock(ep);
-		ep->hz = l;
-		setmaxpkt(ep, "hz");
-		qunlock(ep);
-		break;
-	case CMclrhalt:
-		qlock(ep);
-		deprint("usb epctl %s\n", cb->f[0]);
-		ep->clrhalt = 1;
-		qunlock(ep);
-		break;
-	case CMinfo:
-		deprint("usb epctl %s\n", cb->f[0]);
-		l = strlen(Info);
-		s = a;
-		if(n < l+2 || strncmp(Info, s, l) != 0)
-			error(Ebadctl);
-		if(n > 1024)
-			n = 1024;
-		b = smalloc(n);
-		memmove(b, s+l, n-l);
-		b[n-l] = 0;
-		if(b[n-l-1] == '\n')
-			b[n-l-1] = 0;
-		qlock(ep);
-		free(ep->info);
-		ep->info = b;
-		qunlock(ep);
-		break;
-	case CMaddress:
-		deprint("usb epctl %s\n", cb->f[0]);
-		ep->dev->state = Denabled;
-		break;
-	case CMdetach:
-		if(ep->dev->isroot != 0)
-			error("can't detach a root hub");
-		deprint("usb epctl %s ep%d.%d\n",
-			cb->f[0], ep->dev->nb, ep->nb);
-		ep->dev->state = Ddetach;
-		/* Release file system ref. for its endpoints */
-		for(i = 0; i < nelem(ep->dev->eps); i++)
-			putep(ep->dev->eps[i]);
-		break;
-	case CMdebugep:
-		if(strcmp(cb->f[1], "on") == 0)
-			ep->debug = 1;
-		else if(strcmp(cb->f[1], "off") == 0)
-			ep->debug = 0;
-		else
-			ep->debug = strtoul(cb->f[1], nil, 0);
-		print("usb: ep%d.%d debug %d\n",
-			ep->dev->nb, ep->nb, ep->debug);
-		break;
-	case CMname:
-		deprint("usb epctl %s %s\n", cb->f[0], cb->f[1]);
-		validname(cb->f[1], 0);
-		kstrdup(&ep->name, cb->f[1]);
-		break;
-	case CMtmout:
-		deprint("usb epctl %s\n", cb->f[0]);
-		if(ep->ttype == Tiso || ep->ttype == Tctl)
-			error("ctl ignored for this endpoint type");
-		ep->tmout = strtoul(cb->f[1], nil, 0);
-		if(ep->tmout != 0 && ep->tmout < Xfertmout)
-			ep->tmout = Xfertmout;
-		break;
-	case CMsampledelay:
-		if(ep->ttype != Tiso)
-			error("ctl ignored for this endpoint type");
-		ep->sampledelay = strtoul(cb->f[1], nil, 0);
-		break;
-	case CMpreset:
-		deprint("usb epctl %s\n", cb->f[0]);
-		if(ep->ttype != Tctl)
-			error("not a control endpoint");
-		if(ep->dev->state != Denabled)
-			error("forbidden on devices not enabled");
-		ep->dev->state = Dreset;
-		break;
-	default:
-		panic("usb: unknown epctl %d", ct->index);
-	}
-	free(cb);
-	poperror();
-	return n;
-}
-
-static long
-usbctl(void *a, long n)
-{
-	Cmdtab *ct;
-	Cmdbuf *cb;
-	Ep *ep;
-	int i;
-
-	cb = parsecmd(a, n);
-	if(waserror()){
-		free(cb);
-		nexterror();
-	}
-	ct = lookupcmd(cb, usbctls, nelem(usbctls));
-	dprint("usb ctl %s\n", cb->f[0]);
-	switch(ct->index){
-	case CMdebug:
-		if(strcmp(cb->f[1], "on") == 0)
-			debug = 1;
-		else if(strcmp(cb->f[1], "off") == 0)
-			debug = 0;
-		else
-			debug = strtol(cb->f[1], nil, 0);
-		print("usb: debug %d\n", debug);
-		for(i = 0; i < epmax; i++)
-			if((ep = getep(i)) != nil){
-				ep->hp->debug(ep->hp, debug);
-				putep(ep);
-			}
-		break;
-	case CMdump:
-		dumpeps();
-		break;
-	}
-	free(cb);
-	poperror();
-	return n;
-}
-
-static long
-ctlwrite(Chan *c, void *a, long n)
-{
-	int q;
-	Ep *ep;
-
-	q = QID(c->qid);
-	if(q == Qctl)
-		return usbctl(a, n);
-
-	ep = getep(qid2epidx(q));
-	if(ep == nil)
-		error(Eio);
-	if(waserror()){
-		putep(ep);
-		nexterror();
-	}
-	if(ep->dev->state == Ddetach)
-		error(Edetach);
-	if(isqtype(q, Qepctl) && c->aux != nil){
-		/* Be sure we don't keep a cloned ep name */
-		free(c->aux);
-		c->aux = nil;
-		error("read, not write, expected");
-	}
-	n = epctl(ep, c, a, n);
-	putep(ep);
-	poperror();
-	return n;
-}
-
-static long
-usbwrite(Chan *c, void *a, long n, vlong off)
-{
-	int nr, q;
-	Ep *ep;
-
-	if(c->qid.type == QTDIR)
-		error(Eisdir);
-
-	q = QID(c->qid);
-
-	if(q == Qctl || isqtype(q, Qepctl))
-		return ctlwrite(c, a, n);
-
-	ep = getep(qid2epidx(q));
-	if(ep == nil)
-		error(Eio);
-	if(waserror()){
-		putep(ep);
-		nexterror();
-	}
-	if(ep->dev->state == Ddetach)
-		error(Edetach);
-	if(ep->mode == OREAD || ep->inuse == 0)
-		error(Ebadusefd);
-
-	switch(ep->ttype){
-	case Tnone:
-		error("endpoint not configured");
-	case Tctl:
-		nr = rhubwrite(ep, a, n);
-		if(nr >= 0){
-			n = nr;
-			break;
-		}
-		/* else fall */
-	default:
-		ddeprint("\nusbwrite q %#x fid %d cnt %ld off %lld\n",q, c->fid, n, off);
-		ep->hp->epwrite(ep, a, n);
-	}
-	putep(ep);
-	poperror();
-	return n;
-}
-
-void
-usbshutdown(void)
-{
-	Hci *hp;
-	int i;
-
-	for(i = 0; i < Nhcis; i++){
-		hp = hcis[i];
-		if(hp == nil)
-			continue;
-		if(hp->shutdown == nil)
-			print("#u: no shutdown function for %s\n", hp->type);
-		else
-			hp->shutdown(hp);
-	}
-}
-
-Dev usbdevtab = {
-	L'u',
-	"usb",
-
-	usbreset,
-	usbinit,
-	usbshutdown,
-	usbattach,
-	usbwalk,
-	usbstat,
-	usbopen,
-	devcreate,
-	usbclose,
-	usbread,
-	devbread,
-	usbwrite,
-	devbwrite,
-	devremove,
-	devwstat,
-};
--- a/sys/src/9/pc/usbehcipc.c
+++ b/sys/src/9/pc/usbehcipc.c
@@ -273,6 +273,18 @@
 	ehcilinkage(hp);
 	hp->shutdown = shutdown;
 	hp->debug = setdebug;
+	if(hp->interrupt == nil)
+		return 0;
+
+	/*
+	 * IRQ2 doesn't really exist, it's used to gang the interrupt
+	 * controllers together. A device set to IRQ2 will appear on
+	 * the second interrupt controller as IRQ9.
+	 */
+	if(hp->irq == 2)
+		hp->irq = 9;
+	intrenable(hp->irq, hp->interrupt, hp, hp->tbdf, hp->type);
+
 	return 0;
 }
 
--- a/sys/src/9/pc/usbohci.c
+++ b/sys/src/9/pc/usbohci.c
@@ -2608,6 +2608,16 @@
 	hp->shutdown = shutdown;
 	hp->debug = usbdebug;
 	hp->type = "ohci";
+
+	/*
+	 * IRQ2 doesn't really exist, it's used to gang the interrupt
+	 * controllers together. A device set to IRQ2 will appear on
+	 * the second interrupt controller as IRQ9.
+	 */
+	if(hp->irq == 2)
+		hp->irq = 9;
+	intrenable(hp->irq, hp->interrupt, hp, hp->tbdf, hp->type);
+
 	return 0;
 }
 
--- a/sys/src/9/pc/usbuhci.c
+++ b/sys/src/9/pc/usbuhci.c
@@ -2339,6 +2339,16 @@
 	hp->shutdown = shutdown;
 	hp->debug = setdebug;
 	hp->type = "uhci";
+
+	/*
+	 * IRQ2 doesn't really exist, it's used to gang the interrupt
+	 * controllers together. A device set to IRQ2 will appear on
+	 * the second interrupt controller as IRQ9.
+	 */
+	if(hp->irq == 2)
+		hp->irq = 9;
+	intrenable(hp->irq, hp->interrupt, hp, hp->tbdf, hp->type);
+
 	return 0;
 }
 
--- /dev/null
+++ b/sys/src/9/port/devusb.c
@@ -1,0 +1,1475 @@
+/*
+ * USB device driver framework.
+ *
+ * This is in charge of providing access to actual HCIs
+ * and providing I/O to the various endpoints of devices.
+ * A separate user program (usbd) is in charge of
+ * enumerating the bus, setting up endpoints and
+ * starting devices (also user programs).
+ *
+ * The interface provided is a violation of the standard:
+ * you're welcome.
+ *
+ * The interface consists of a root directory with several files
+ * plus a directory (epN.M) with two files per endpoint.
+ * A device is represented by its first endpoint, which
+ * is a control endpoint automatically allocated for each device.
+ * Device control endpoints may be used to create new endpoints.
+ * Devices corresponding to hubs may also allocate new devices,
+ * perhaps also hubs. Initially, a hub device is allocated for
+ * each controller present, to represent its root hub. Those can
+ * never be removed.
+ *
+ * All endpoints refer to the first endpoint (epN.0) of the device,
+ * which keeps per-device information, and also to the HCI used
+ * to reach them. Although all endpoints cache that information.
+ *
+ * epN.M/data files permit I/O and are considered DMEXCL.
+ * epN.M/ctl files provide status info and accept control requests.
+ *
+ * Endpoints may be given file names to be listed also at #u,
+ * for those drivers that have nothing to do after configuring the
+ * device and its endpoints.
+ *
+ * Drivers for different controllers are kept at usb[oue]hci.c
+ * It's likely we could factor out much from controllers into
+ * a generic controller driver, the problem is that details
+ * regarding how to handle toggles, tokens, Tds, etc. will
+ * get in the way. Thus, code is probably easier the way it is.
+ */
+
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+#include	"../port/usb.h"
+
+typedef struct Hcitype Hcitype;
+
+enum
+{
+	/* Qid numbers */
+	Qdir = 0,		/* #u */
+	Qusbdir,			/* #u/usb */
+	Qctl,			/* #u/usb/ctl - control requests */
+
+	Qep0dir,			/* #u/usb/ep0.0 - endpoint 0 dir */
+	Qep0io,			/* #u/usb/ep0.0/data - endpoint 0 I/O */
+	Qep0ctl,		/* #u/usb/ep0.0/ctl - endpoint 0 ctl. */
+	Qep0dummy,		/* give 4 qids to each endpoint */
+
+	Qepdir = 0,		/* (qid-qep0dir)&3 is one of these */
+	Qepio,			/* to identify which file for the endpoint */
+	Qepctl,
+
+	/* ... */
+
+	/* Usb ctls. */
+	CMdebug = 0,		/* debug on|off */
+	CMdump,			/* dump (data structures for debug) */
+
+	/* Ep. ctls */
+	CMnew = 0,		/* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */
+	CMnewdev,		/* newdev full|low|high portnb (allocate new devices) */
+	CMhub,			/* hub (set the device as a hub) */
+	CMspeed,		/* speed full|low|high|no */
+	CMmaxpkt,		/* maxpkt size */
+	CMntds,			/* ntds nb (max nb. of tds per µframe) */
+	CMclrhalt,		/* clrhalt (halt was cleared on endpoint) */
+	CMpollival,		/* pollival interval (interrupt/iso) */
+	CMhz,			/* hz n (samples/sec; iso) */
+	CMsamplesz,		/* samplesz n (sample size; iso) */
+	CMinfo,			/* info infostr (ke.ep info for humans) */
+	CMdetach,		/* detach (abort I/O forever on this ep). */
+	CMaddress,		/* address (address is assigned) */
+	CMdebugep,		/* debug n (set/clear debug for this ep) */
+	CMname,			/* name str (show up as #u/name as well) */
+	CMtmout,		/* timeout n (activate timeouts for ep) */
+	CMsampledelay,		/* maximum delay introduced by buffering (iso) */
+	CMpreset,		/* reset the port */
+
+	/* Hub feature selectors */
+	Rportenable	= 1,
+	Rportreset	= 4,
+
+};
+
+struct Hcitype
+{
+	char*	type;
+	int	(*reset)(Hci*);
+};
+
+#define QID(q)	((int)(q).path)
+
+static char Edetach[] = "device is detached";
+static char Enotconf[] = "endpoint not configured";
+char Estalled[] = "endpoint stalled";
+
+static Cmdtab usbctls[] =
+{
+	{CMdebug,	"debug",	2},
+	{CMdump,	"dump",		1},
+};
+
+static Cmdtab epctls[] =
+{
+	{CMnew,		"new",		4},
+	{CMnewdev,	"newdev",	3},
+	{CMhub,		"hub",		1},
+	{CMspeed,	"speed",	2},
+	{CMmaxpkt,	"maxpkt",	2},
+	{CMntds,	"ntds",		2},
+	{CMpollival,	"pollival",	2},
+	{CMsamplesz,	"samplesz",	2},
+	{CMhz,		"hz",		2},
+	{CMinfo,	"info",		0},
+	{CMdetach,	"detach",	1},
+	{CMaddress,	"address",	1},
+	{CMdebugep,	"debug",	2},
+	{CMclrhalt,	"clrhalt",	1},
+	{CMname,	"name",		2},
+	{CMtmout,	"timeout",	2},
+	{CMsampledelay,	"sampledelay",	2},
+	{CMpreset,	"reset",	1},
+};
+
+static Dirtab usbdir[] =
+{
+	"ctl",		{Qctl},		0,	0666,
+};
+
+char *usbmodename[] =
+{
+	[OREAD]	"r",
+	[OWRITE]	"w",
+	[ORDWR]	"rw",
+};
+
+static char *ttname[] =
+{
+	[Tnone]	"none",
+	[Tctl]	"control",
+	[Tiso]	"iso",
+	[Tintr]	"interrupt",
+	[Tbulk]	"bulk",
+};
+
+static char *spname[] =
+{
+	[Fullspeed]	"full",
+	[Lowspeed]	"low",
+	[Highspeed]	"high",
+	[Nospeed]	"no",
+};
+
+static int	debug;
+static Hcitype	hcitypes[Nhcis];
+static Hci*	hcis[Nhcis];
+static QLock	epslck;		/* add, del, lookup endpoints */
+static Ep*	eps[Neps];	/* all endpoints known */
+static int	epmax;		/* 1 + last endpoint index used  */
+static int	usbidgen;	/* device address generator */
+
+/*
+ * Is there something like this in a library? should it be?
+ */
+char*
+seprintdata(char *s, char *se, uchar *d, int n)
+{
+	int i, l;
+
+	s = seprint(s, se, " %#p[%d]: ", d, n);
+	l = n;
+	if(l > 10)
+		l = 10;
+	for(i=0; i<l; i++)
+		s = seprint(s, se, " %2.2ux", d[i]);
+	if(l < n)
+		s = seprint(s, se, "...");
+	return s;
+}
+
+static int
+name2speed(char *name)
+{
+	int i;
+
+	for(i = 0; i < nelem(spname); i++)
+		if(strcmp(name, spname[i]) == 0)
+			return i;
+	return Nospeed;
+}
+
+static int
+name2ttype(char *name)
+{
+	int i;
+
+	for(i = 0; i < nelem(ttname); i++)
+		if(strcmp(name, ttname[i]) == 0)
+			return i;
+	/* may be a std. USB ep. type */
+	i = strtol(name, nil, 0);
+	switch(i+1){
+	case Tctl:
+	case Tiso:
+	case Tbulk:
+	case Tintr:
+		return i+1;
+	default:
+		return Tnone;
+	}
+}
+
+static int
+name2mode(char *mode)
+{
+	int i;
+
+	for(i = 0; i < nelem(usbmodename); i++)
+		if(strcmp(mode, usbmodename[i]) == 0)
+			return i;
+	return -1;
+}
+
+static int
+qid2epidx(int q)
+{
+	q = (q-Qep0dir)/4;
+	if(q < 0 || q >= epmax || eps[q] == nil)
+		return -1;
+	return q;
+}
+
+static int
+isqtype(int q, int type)
+{
+	if(q < Qep0dir)
+		return 0;
+	q -= Qep0dir;
+	return (q & 3) == type;
+}
+
+void
+addhcitype(char* t, int (*r)(Hci*))
+{
+	static int ntype;
+
+	if(ntype == Nhcis)
+		panic("too many USB host interface types");
+	hcitypes[ntype].type = t;
+	hcitypes[ntype].reset = r;
+	ntype++;
+}
+
+static char*
+seprintep(char *s, char *se, Ep *ep, int all)
+{
+	static char* dsnames[] = { "config", "enabled", "detached", "reset" };
+	Udev *d;
+	int i;
+	int di;
+
+	d = ep->dev;
+
+	qlock(ep);
+	if(waserror()){
+		qunlock(ep);
+		nexterror();
+	}
+	di = ep->dev->nb;
+	if(all)
+		s = seprint(s, se, "dev %d ep %d ", di, ep->nb);
+	s = seprint(s, se, "%s", dsnames[ep->dev->state]);
+	s = seprint(s, se, " %s", ttname[ep->ttype]);
+	assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR);
+	s = seprint(s, se, " %s", usbmodename[ep->mode]);
+	s = seprint(s, se, " speed %s", spname[d->speed]);
+	s = seprint(s, se, " maxpkt %ld", ep->maxpkt);
+	s = seprint(s, se, " pollival %ld", ep->pollival);
+	s = seprint(s, se, " samplesz %ld", ep->samplesz);
+	s = seprint(s, se, " hz %ld", ep->hz);
+	s = seprint(s, se, " hub %d", ep->dev->hub);
+	s = seprint(s, se, " port %d", ep->dev->port);
+	if(ep->inuse)
+		s = seprint(s, se, " busy");
+	else
+		s = seprint(s, se, " idle");
+	if(all){
+		s = seprint(s, se, " load %uld", ep->load);
+		s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep);
+		s = seprint(s, se, " idx %d", ep->idx);
+		if(ep->name != nil)
+			s = seprint(s, se, " name '%s'", ep->name);
+		if(ep->tmout != 0)
+			s = seprint(s, se, " tmout");
+		if(ep == ep->ep0){
+			s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno);
+			s = seprint(s, se, " eps:");
+			for(i = 0; i < nelem(d->eps); i++)
+				if(d->eps[i] != nil)
+					s = seprint(s, se, " ep%d.%d", di, i);
+		}
+	}
+	if(ep->info != nil)
+		s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type);
+	else
+		s = seprint(s, se, "\n");
+	qunlock(ep);
+	poperror();
+	return s;
+}
+
+static Ep*
+epalloc(Hci *hp)
+{
+	Ep *ep;
+	int i;
+
+	ep = smalloc(sizeof(Ep));
+	ep->ref = 1;
+	qlock(&epslck);
+	for(i = 0; i < Neps; i++)
+		if(eps[i] == nil)
+			break;
+	if(i == Neps){
+		qunlock(&epslck);
+		free(ep);
+		print("usb: bug: too few endpoints.\n");
+		return nil;
+	}
+	ep->idx = i;
+	if(epmax <= i)
+		epmax = i+1;
+	eps[i] = ep;
+	ep->hp = hp;
+	ep->maxpkt = 8;
+	ep->ntds = 1;
+	ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */
+	qunlock(&epslck);
+	return ep;
+}
+
+static Ep*
+getep(int i)
+{
+	Ep *ep;
+
+	if(i < 0 || i >= epmax || eps[i] == nil)
+		return nil;
+	qlock(&epslck);
+	ep = eps[i];
+	if(ep != nil)
+		incref(ep);
+	qunlock(&epslck);
+	return ep;
+}
+
+static void
+putep(Ep *ep)
+{
+	Udev *d;
+
+	if(ep != nil && decref(ep) == 0){
+		d = ep->dev;
+		deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep);
+		qlock(&epslck);
+		eps[ep->idx] = nil;
+		if(ep->idx == epmax-1)
+			epmax--;
+		if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen)
+			usbidgen--;
+		qunlock(&epslck);
+		if(d != nil){
+			qlock(ep->ep0);
+			d->eps[ep->nb] = nil;
+			qunlock(ep->ep0);
+		}
+		if(ep->ep0 != ep){
+			putep(ep->ep0);
+			ep->ep0 = nil;
+		}
+		free(ep->info);
+		free(ep->name);
+		free(ep);
+	}
+}
+
+static void
+dumpeps(void)
+{
+	int i;
+	static char buf[512];
+	char *s;
+	char *e;
+	Ep *ep;
+
+	print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps);
+	for(i = 0; i < epmax; i++){
+		s = buf;
+		e = buf+sizeof(buf);
+		ep = getep(i);
+		if(ep != nil){
+			if(waserror()){
+				putep(ep);
+				nexterror();
+			}
+			s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb);
+			seprintep(s, e, ep, 1);
+			print("%s", buf);
+			ep->hp->seprintep(buf, e, ep);
+			print("%s", buf);
+			poperror();
+			putep(ep);
+		}
+	}
+	print("usb dump hcis:\n");
+	for(i = 0; i < Nhcis; i++)
+		if(hcis[i] != nil)
+			hcis[i]->dump(hcis[i]);
+}
+
+static int
+newusbid(Hci *)
+{
+	int id;
+
+	qlock(&epslck);
+	id = ++usbidgen;
+	if(id >= 0x7F)
+		print("#u: too many device addresses; reuse them more\n");
+	qunlock(&epslck);
+	return id;
+}
+
+/*
+ * Create endpoint 0 for a new device
+ */
+static Ep*
+newdev(Hci *hp, int ishub, int isroot)
+{
+	Ep *ep;
+	Udev *d;
+
+	ep = epalloc(hp);
+	d = ep->dev = smalloc(sizeof(Udev));
+	d->nb = newusbid(hp);
+	d->eps[0] = ep;
+	ep->nb = 0;
+	ep->toggle[0] = ep->toggle[1] = 0;
+	d->ishub = ishub;
+	d->isroot = isroot;
+	if(hp->highspeed != 0)
+		d->speed = Highspeed;
+	else
+		d->speed = Fullspeed;
+	d->state = Dconfig;		/* address not yet set */
+	ep->dev = d;
+	ep->ep0 = ep;			/* no ref counted here */
+	ep->ttype = Tctl;
+	ep->tmout = Xfertmout;
+	ep->mode = ORDWR;
+	dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep);
+	return ep;
+}
+
+/*
+ * Create a new endpoint for the device
+ * accessed via the given endpoint 0.
+ */
+static Ep*
+newdevep(Ep *ep, int i, int tt, int mode)
+{
+	Ep *nep;
+	Udev *d;
+
+	d = ep->dev;
+	if(d->eps[i] != nil)
+		error("endpoint already in use");
+	nep = epalloc(ep->hp);
+	incref(ep);
+	d->eps[i] = nep;
+	nep->nb = i;
+	nep->toggle[0] = nep->toggle[1] = 0;
+	nep->ep0 = ep;
+	nep->dev = ep->dev;
+	nep->mode = mode;
+	nep->ttype = tt;
+	nep->debug = ep->debug;
+	/* set defaults */
+	switch(tt){
+	case Tctl:
+		nep->tmout = Xfertmout;
+		break;
+	case Tintr:
+		nep->pollival = 10;
+		break;
+	case Tiso:
+		nep->tmout = Xfertmout;
+		nep->pollival = 10;
+		nep->samplesz = 4;
+		nep->hz = 44100;
+		break;
+	}
+	deprint("newdevep ep%d.%d %#p\n", d->nb, nep->nb, nep);
+	return ep;
+}
+
+static int
+epdataperm(int mode)
+{
+
+	switch(mode){
+	case OREAD:
+		return 0440|DMEXCL;
+		break;
+	case OWRITE:
+		return 0220|DMEXCL;
+		break;
+	default:
+		return 0660|DMEXCL;
+	}
+}
+
+static int
+usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
+{
+	Qid q;
+	Dirtab *dir;
+	int perm;
+	char *se;
+	Ep *ep;
+	int nb;
+	int mode;
+
+	if(0)ddprint("usbgen q %#x s %d...", QID(c->qid), s);
+	if(s == DEVDOTDOT){
+		if(QID(c->qid) <= Qusbdir){
+			mkqid(&q, Qdir, 0, QTDIR);
+			devdir(c, q, "#u", 0, eve, 0555, dp);
+		}else{
+			mkqid(&q, Qusbdir, 0, QTDIR);
+			devdir(c, q, "usb", 0, eve, 0555, dp);
+		}
+		if(0)ddprint("ok\n");
+		return 1;
+	}
+
+	switch(QID(c->qid)){
+	case Qdir:				/* list #u */
+		if(s == 0){
+			mkqid(&q, Qusbdir, 0, QTDIR);
+			devdir(c, q, "usb", 0, eve, 0555, dp);
+			if(0)ddprint("ok\n");
+			return 1;
+		}
+		s--;
+		if(s < 0 || s >= epmax)
+			goto Fail;
+		ep = getep(s);
+		if(ep == nil || ep->name == nil){
+			if(ep != nil)
+				putep(ep);
+			if(0)ddprint("skip\n");
+			return 0;
+		}
+		if(waserror()){
+			putep(ep);
+			nexterror();
+		}
+		mkqid(&q, Qep0io+s*4, 0, QTFILE);
+		devdir(c, q, ep->name, 0, eve, epdataperm(ep->mode), dp);
+		putep(ep);
+		poperror();
+		if(0)ddprint("ok\n");
+		return 1;
+
+	case Qusbdir:				/* list #u/usb */
+	Usbdir:
+		if(s < nelem(usbdir)){
+			dir = &usbdir[s];
+			mkqid(&q, dir->qid.path, 0, QTFILE);
+			devdir(c, q, dir->name, dir->length, eve, dir->perm, dp);
+			if(0)ddprint("ok\n");
+			return 1;
+		}
+		s -= nelem(usbdir);
+		if(s < 0 || s >= epmax)
+			goto Fail;
+		ep = getep(s);
+		if(ep == nil){
+			if(0)ddprint("skip\n");
+			return 0;
+		}
+		if(waserror()){
+			putep(ep);
+			nexterror();
+		}
+		se = up->genbuf+sizeof(up->genbuf);
+		seprint(up->genbuf, se, "ep%d.%d", ep->dev->nb, ep->nb);
+		mkqid(&q, Qep0dir+4*s, 0, QTDIR);
+		putep(ep);
+		poperror();
+		devdir(c, q, up->genbuf, 0, eve, 0755, dp);
+		if(0)ddprint("ok\n");
+		return 1;
+
+	case Qctl:
+		s = 0;
+		goto Usbdir;
+
+	default:				/* list #u/usb/epN.M */
+		nb = qid2epidx(QID(c->qid));
+		ep = getep(nb);
+		if(ep == nil)
+			goto Fail;
+		mode = ep->mode;
+		putep(ep);
+		if(isqtype(QID(c->qid), Qepdir)){
+		Epdir:
+			switch(s){
+			case 0:
+				mkqid(&q, Qep0io+nb*4, 0, QTFILE);
+				perm = epdataperm(mode);
+				devdir(c, q, "data", 0, eve, perm, dp);
+				break;
+			case 1:
+				mkqid(&q, Qep0ctl+nb*4, 0, QTFILE);
+				devdir(c, q, "ctl", 0, eve, 0664, dp);
+				break;
+			default:
+				goto Fail;
+			}
+		}else if(isqtype(QID(c->qid), Qepctl)){
+			s = 1;
+			goto Epdir;
+		}else{
+			s = 0;
+			goto Epdir;
+		}
+		if(0)ddprint("ok\n");
+		return 1;
+	}
+Fail:
+	if(0)ddprint("fail\n");
+	return -1;
+}
+
+static Hci*
+hciprobe(int cardno, int ctlrno)
+{
+	Hci *hp;
+	char *type;
+	static int epnb = 1;	/* guess the endpoint nb. for the controller */
+
+	ddprint("hciprobe %d %d\n", cardno, ctlrno);
+	hp = smalloc(sizeof(Hci));
+	hp->ctlrno = ctlrno;
+	hp->tbdf = BUSUNKNOWN;
+
+	if(cardno < 0){
+		if(isaconfig("usb", ctlrno, hp) == 0){
+			free(hp);
+			return nil;
+		}
+		for(cardno = 0; cardno < Nhcis; cardno++){
+			if(hcitypes[cardno].type == nil)
+				break;
+			type = hp->type;
+			if(type==nil || *type==0)
+				type = "uhci";
+			if(cistrcmp(hcitypes[cardno].type, type) == 0)
+				break;
+		}
+	}
+
+	if(cardno >= Nhcis || hcitypes[cardno].type == nil){
+		free(hp);
+		return nil;
+	}
+	dprint("%s...", hcitypes[cardno].type);
+	if(hcitypes[cardno].reset(hp) < 0){
+		free(hp);
+		return nil;
+	}
+
+	/*
+	 * modern machines have too many usb controllers to list on
+	 * the console.
+	 */
+	dprint("#u/usb/ep%d.0: %s: port 0x%luX irq %d\n",
+		epnb, hcitypes[cardno].type, hp->port, hp->irq);
+	epnb++;
+	return hp;
+}
+
+static void
+usbreset(void)
+{
+	int cardno, ctlrno;
+	Hci *hp;
+
+	if(getconf("*nousbprobe"))
+		return;
+	dprint("usbreset\n");
+
+	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++)
+		if((hp = hciprobe(-1, ctlrno)) != nil)
+			hcis[ctlrno] = hp;
+	cardno = ctlrno = 0;
+	while(cardno < Nhcis && ctlrno < Nhcis && hcitypes[cardno].type != nil)
+		if(hcis[ctlrno] != nil)
+			ctlrno++;
+		else{
+			hp = hciprobe(cardno, ctlrno);
+			if(hp == nil)
+				cardno++;
+			hcis[ctlrno++] = hp;
+		}
+	if(hcis[Nhcis-1] != nil)
+		print("usbreset: bug: Nhcis (%d) too small\n", Nhcis);
+}
+
+static void
+usbinit(void)
+{
+	Hci *hp;
+	int ctlrno;
+	Ep *d;
+	char info[40];
+
+	dprint("usbinit\n");
+	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){
+		hp = hcis[ctlrno];
+		if(hp != nil){
+			if(hp->init != nil)
+				hp->init(hp);
+			d = newdev(hp, 1, 1);		/* new root hub */
+			d->dev->state = Denabled;	/* although addr == 0 */
+			d->maxpkt = 64;
+			snprint(info, sizeof(info), "ports %d", hp->nports);
+			kstrdup(&d->info, info);
+		}
+	}
+}
+
+static Chan*
+usbattach(char *spec)
+{
+	return devattach(L'u', spec);
+}
+
+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);
+}
+
+/*
+ * µs for the given transfer, for bandwidth allocation.
+ * This is a very rough worst case for what 5.11.3
+ * of the usb 2.0 spec says.
+ * Also, we are using maxpkt and not actual transfer sizes.
+ * Only when we are sure we
+ * are not exceeding b/w might we consider adjusting it.
+ */
+static ulong
+usbload(int speed, int maxpkt)
+{
+	enum{ Hostns = 1000, Hubns = 333 };
+	ulong l;
+	ulong bs;
+
+	l = 0;
+	bs = 10UL * maxpkt;
+	switch(speed){
+	case Highspeed:
+		l = 55*8*2 + 2 * (3 + bs) + Hostns;
+		break;
+	case Fullspeed:
+		l = 9107 + 84 * (4 + bs) + Hostns;
+		break;
+	case Lowspeed:
+		l = 64107 + 2 * Hubns + 667 * (3 + bs) + Hostns;
+		break;
+	default:
+		print("usbload: bad speed %d\n", speed);
+		/* let it run */
+	}
+	return l / 1000UL;	/* in µs */
+}
+
+static Chan*
+usbopen(Chan *c, int omode)
+{
+	int q;
+	Ep *ep;
+	int mode;
+
+	mode = openmode(omode);
+	q = QID(c->qid);
+
+	if(q >= Qep0dir && qid2epidx(q) < 0)
+		error(Eio);
+	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
+		return devopen(c, omode, nil, 0, usbgen);
+
+	ep = getep(qid2epidx(q));
+	if(ep == nil)
+		error(Eio);
+	deprint("usbopen q %#x fid %d omode %d\n", q, c->fid, mode);
+	if(waserror()){
+		putep(ep);
+		nexterror();
+	}
+	qlock(ep);
+	if(ep->inuse){
+		qunlock(ep);
+		error(Einuse);
+	}
+	ep->inuse = 1;
+	qunlock(ep);
+	if(waserror()){
+		ep->inuse = 0;
+		nexterror();
+	}
+	if(mode != OREAD && ep->mode == OREAD)
+		error(Eperm);
+	if(mode != OWRITE && ep->mode == OWRITE)
+		error(Eperm);
+	if(ep->ttype == Tnone)
+		error(Enotconf);
+	ep->clrhalt = 0;
+	ep->rhrepl = -1;
+	if(ep->load == 0)
+		ep->load = usbload(ep->dev->speed, ep->maxpkt);
+	ep->hp->epopen(ep);
+
+	poperror();	/* ep->inuse */
+	poperror();	/* don't putep(): ref kept for fid using the ep. */
+
+	c->mode = mode;
+	c->flag |= COPEN;
+	c->offset = 0;
+	c->aux = nil;	/* paranoia */
+	return c;
+}
+
+static void
+epclose(Ep *ep)
+{
+	qlock(ep);
+	if(waserror()){
+		qunlock(ep);
+		nexterror();
+	}
+	if(ep->inuse){
+		ep->hp->epclose(ep);
+		ep->inuse = 0;
+	}
+	qunlock(ep);
+	poperror();
+}
+
+static void
+usbclose(Chan *c)
+{
+	int q;
+	Ep *ep;
+
+	q = QID(c->qid);
+	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
+		return;
+
+	ep = getep(qid2epidx(q));
+	if(ep == nil)
+		return;
+	deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref);
+	if(waserror()){
+		putep(ep);
+		nexterror();
+	}
+	if(c->flag & COPEN){
+		free(c->aux);
+		c->aux = nil;
+		epclose(ep);
+		putep(ep);	/* release ref kept since usbopen */
+		c->flag &= ~COPEN;
+	}
+	poperror();
+	putep(ep);
+}
+
+static long
+ctlread(Chan *c, void *a, long n, vlong offset)
+{
+	int q;
+	char *s;
+	char *us;
+	char *se;
+	Ep *ep;
+	int i;
+
+	q = QID(c->qid);
+	us = s = smalloc(READSTR);
+	se = s + READSTR;
+	if(waserror()){
+		free(us);
+		nexterror();
+	}
+	if(q == Qctl)
+		for(i = 0; i < epmax; i++){
+			ep = getep(i);
+			if(ep != nil){
+				if(waserror()){
+					putep(ep);
+					nexterror();
+				}
+				s = seprint(s, se, "ep%d.%d ", ep->dev->nb, ep->nb);
+				s = seprintep(s, se, ep, 0);
+				poperror();
+			}
+			putep(ep);
+		}
+	else{
+		ep = getep(qid2epidx(q));
+		if(ep == nil)
+			error(Eio);
+		if(waserror()){
+			putep(ep);
+			nexterror();
+		}
+		if(c->aux != nil){
+			/* After a new endpoint request we read
+			 * the new endpoint name back.
+			 */
+			strecpy(s, se, c->aux);
+			free(c->aux);
+			c->aux = nil;
+		}else
+			seprintep(s, se, ep, 0);
+		poperror();
+		putep(ep);
+	}
+	n = readstr(offset, a, n, us);
+	poperror();
+	free(us);
+	return n;
+}
+
+/*
+ * Fake root hub emulation.
+ */
+static long
+rhubread(Ep *ep, void *a, long n)
+{
+	char *b;
+
+	if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2)
+		return -1;
+	if(ep->rhrepl < 0)
+		return -1;
+
+	b = a;
+	memset(b, 0, n);
+	PUT2(b, ep->rhrepl);
+	ep->rhrepl = -1;
+	return n;
+}
+
+static long
+rhubwrite(Ep *ep, void *a, long n)
+{
+	uchar *s;
+	int cmd;
+	int feature;
+	int port;
+	Hci *hp;
+
+	if(ep->dev == nil || ep->dev->isroot == 0 || ep->nb != 0)
+		return -1;
+	if(n != Rsetuplen)
+		error("root hub is a toy hub");
+	ep->rhrepl = -1;
+	s = a;
+	if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother))
+		error("root hub is a toy hub");
+	hp = ep->hp;
+	cmd = s[Rreq];
+	feature = GET2(s+Rvalue);
+	port = GET2(s+Rindex);
+	if(port < 1 || port > hp->nports)
+		error("bad hub port number");
+	switch(feature){
+	case Rportenable:
+		ep->rhrepl = hp->portenable(hp, port, cmd == Rsetfeature);
+		break;
+	case Rportreset:
+		ep->rhrepl = hp->portreset(hp, port, cmd == Rsetfeature);
+		break;
+	case Rgetstatus:
+		ep->rhrepl = hp->portstatus(hp, port);
+		break;
+	default:
+		ep->rhrepl = 0;
+	}
+	return n;
+}
+
+static long
+usbread(Chan *c, void *a, long n, vlong offset)
+{
+	int q;
+	Ep *ep;
+	int nr;
+
+	q = QID(c->qid);
+
+	if(c->qid.type == QTDIR)
+		return devdirread(c, a, n, nil, 0, usbgen);
+
+	if(q == Qctl || isqtype(q, Qepctl))
+		return ctlread(c, a, n, offset);
+
+	ep = getep(qid2epidx(q));
+	if(ep == nil)
+		error(Eio);
+	if(waserror()){
+		putep(ep);
+		nexterror();
+	}
+	if(ep->dev->state == Ddetach)
+		error(Edetach);
+	if(ep->mode == OWRITE || ep->inuse == 0)
+		error(Ebadusefd);
+	switch(ep->ttype){
+	case Tnone:
+		error("endpoint not configured");
+	case Tctl:
+		nr = rhubread(ep, a, n);
+		if(nr >= 0){
+			n = nr;
+			break;
+		}
+		/* else fall */
+	default:
+		ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",q,c->fid,n,offset);
+		n = ep->hp->epread(ep, a, n);
+		break;
+	}
+	poperror();
+	putep(ep);
+	return n;
+}
+
+static long
+pow2(int n)
+{
+	return 1 << n;
+}
+
+static void
+setmaxpkt(Ep *ep, char* s)
+{
+	long spp;	/* samples per packet */
+
+	if(ep->dev->speed == Highspeed)
+		spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000;
+	else
+		spp = (ep->hz * ep->pollival + 999) / 1000;
+	ep->maxpkt = spp * ep->samplesz;
+	deprint("usb: %s: setmaxpkt: hz %ld poll %ld"
+		" ntds %d %s speed -> spp %ld maxpkt %ld\n", s,
+		ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed],
+		spp, ep->maxpkt);
+	if(ep->maxpkt > 1024){
+		print("usb: %s: maxpkt %ld > 1024. truncating\n", s, ep->maxpkt);
+		ep->maxpkt = 1024;
+	}
+}
+
+/*
+ * Many endpoint ctls. simply update the portable representation
+ * of the endpoint. The actual controller driver will look
+ * at them to setup the endpoints as dictated.
+ */
+static long
+epctl(Ep *ep, Chan *c, void *a, long n)
+{
+	int i, l, mode, nb, tt;
+	char *b, *s;
+	Cmdbuf *cb;
+	Cmdtab *ct;
+	Ep *nep;
+	Udev *d;
+	static char *Info = "info ";
+
+	d = ep->dev;
+
+	cb = parsecmd(a, n);
+	if(waserror()){
+		free(cb);
+		nexterror();
+	}
+	ct = lookupcmd(cb, epctls, nelem(epctls));
+	if(ct == nil)
+		error(Ebadctl);
+	i = ct->index;
+	if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset)
+		if(ep != ep->ep0)
+			error("allowed only on a setup endpoint");
+	if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname)
+		if(ep != ep->ep0 && ep->inuse != 0)
+			error("must configure before using");
+	switch(i){
+	case CMnew:
+		deprint("usb epctl %s\n", cb->f[0]);
+		nb = strtol(cb->f[1], nil, 0);
+		if(nb < 0 || nb >= Ndeveps)
+			error("bad endpoint number");
+		tt = name2ttype(cb->f[2]);
+		if(tt == Tnone)
+			error("unknown endpoint type");
+		mode = name2mode(cb->f[3]);
+		if(mode < 0)
+			error("unknown i/o mode");
+		newdevep(ep, nb, tt, mode);
+		break;
+	case CMnewdev:
+		deprint("usb epctl %s\n", cb->f[0]);
+		if(ep != ep->ep0 || d->ishub == 0)
+			error("not a hub setup endpoint");
+		l = name2speed(cb->f[1]);
+		if(l == Nospeed)
+			error("speed must be full|low|high");
+		nep = newdev(ep->hp, 0, 0);
+		nep->dev->speed = l;
+		if(nep->dev->speed  != Lowspeed)
+			nep->maxpkt = 64;	/* assume full speed */
+		nep->dev->hub = d->nb;
+		nep->dev->port = atoi(cb->f[2]);
+		/* next read request will read
+		 * the name for the new endpoint
+		 */
+		l = sizeof(up->genbuf);
+		snprint(up->genbuf, l, "ep%d.%d", nep->dev->nb, nep->nb);
+		kstrdup(&c->aux, up->genbuf);
+		break;
+	case CMhub:
+		deprint("usb epctl %s\n", cb->f[0]);
+		d->ishub = 1;
+		break;
+	case CMspeed:
+		l = name2speed(cb->f[1]);
+		deprint("usb epctl %s %d\n", cb->f[0], l);
+		if(l == Nospeed)
+			error("speed must be full|low|high");
+		qlock(ep->ep0);
+		d->speed = l;
+		qunlock(ep->ep0);
+		break;
+	case CMmaxpkt:
+		l = strtoul(cb->f[1], nil, 0);
+		deprint("usb epctl %s %d\n", cb->f[0], l);
+		if(l < 1 || l > 1024)
+			error("maxpkt not in [1:1024]");
+		qlock(ep);
+		ep->maxpkt = l;
+		qunlock(ep);
+		break;
+	case CMntds:
+		l = strtoul(cb->f[1], nil, 0);
+		deprint("usb epctl %s %d\n", cb->f[0], l);
+		if(l < 1 || l > 3)
+			error("ntds not in [1:3]");
+		qlock(ep);
+		ep->ntds = l;
+		qunlock(ep);
+		break;
+	case CMpollival:
+		if(ep->ttype != Tintr && ep->ttype != Tiso)
+			error("not an intr or iso endpoint");
+		l = strtoul(cb->f[1], nil, 0);
+		deprint("usb epctl %s %d\n", cb->f[0], l);
+		if(ep->ttype == Tiso ||
+		   (ep->ttype == Tintr && ep->dev->speed == Highspeed)){
+			if(l < 1 || l > 16)
+				error("pollival power not in [1:16]");
+			l = pow2(l-1);
+		}else
+			if(l < 1 || l > 255)
+				error("pollival not in [1:255]");
+		qlock(ep);
+		ep->pollival = l;
+		if(ep->ttype == Tiso)
+			setmaxpkt(ep, "pollival");
+		qunlock(ep);
+		break;
+	case CMsamplesz:
+		if(ep->ttype != Tiso)
+			error("not an iso endpoint");
+		l = strtoul(cb->f[1], nil, 0);
+		deprint("usb epctl %s %d\n", cb->f[0], l);
+		if(l <= 0 || l > 8)
+			error("samplesz not in [1:8]");
+		qlock(ep);
+		ep->samplesz = l;
+		setmaxpkt(ep, "samplesz");
+		qunlock(ep);
+		break;
+	case CMhz:
+		if(ep->ttype != Tiso)
+			error("not an iso endpoint");
+		l = strtoul(cb->f[1], nil, 0);
+		deprint("usb epctl %s %d\n", cb->f[0], l);
+		if(l <= 0 || l > 100000)
+			error("hz not in [1:100000]");
+		qlock(ep);
+		ep->hz = l;
+		setmaxpkt(ep, "hz");
+		qunlock(ep);
+		break;
+	case CMclrhalt:
+		qlock(ep);
+		deprint("usb epctl %s\n", cb->f[0]);
+		ep->clrhalt = 1;
+		qunlock(ep);
+		break;
+	case CMinfo:
+		deprint("usb epctl %s\n", cb->f[0]);
+		l = strlen(Info);
+		s = a;
+		if(n < l+2 || strncmp(Info, s, l) != 0)
+			error(Ebadctl);
+		if(n > 1024)
+			n = 1024;
+		b = smalloc(n);
+		memmove(b, s+l, n-l);
+		b[n-l] = 0;
+		if(b[n-l-1] == '\n')
+			b[n-l-1] = 0;
+		qlock(ep);
+		free(ep->info);
+		ep->info = b;
+		qunlock(ep);
+		break;
+	case CMaddress:
+		deprint("usb epctl %s\n", cb->f[0]);
+		ep->dev->state = Denabled;
+		break;
+	case CMdetach:
+		if(ep->dev->isroot != 0)
+			error("can't detach a root hub");
+		deprint("usb epctl %s ep%d.%d\n",
+			cb->f[0], ep->dev->nb, ep->nb);
+		ep->dev->state = Ddetach;
+		/* Release file system ref. for its endpoints */
+		for(i = 0; i < nelem(ep->dev->eps); i++)
+			putep(ep->dev->eps[i]);
+		break;
+	case CMdebugep:
+		if(strcmp(cb->f[1], "on") == 0)
+			ep->debug = 1;
+		else if(strcmp(cb->f[1], "off") == 0)
+			ep->debug = 0;
+		else
+			ep->debug = strtoul(cb->f[1], nil, 0);
+		print("usb: ep%d.%d debug %d\n",
+			ep->dev->nb, ep->nb, ep->debug);
+		break;
+	case CMname:
+		deprint("usb epctl %s %s\n", cb->f[0], cb->f[1]);
+		validname(cb->f[1], 0);
+		kstrdup(&ep->name, cb->f[1]);
+		break;
+	case CMtmout:
+		deprint("usb epctl %s\n", cb->f[0]);
+		if(ep->ttype == Tiso || ep->ttype == Tctl)
+			error("ctl ignored for this endpoint type");
+		ep->tmout = strtoul(cb->f[1], nil, 0);
+		if(ep->tmout != 0 && ep->tmout < Xfertmout)
+			ep->tmout = Xfertmout;
+		break;
+	case CMsampledelay:
+		if(ep->ttype != Tiso)
+			error("ctl ignored for this endpoint type");
+		ep->sampledelay = strtoul(cb->f[1], nil, 0);
+		break;
+	case CMpreset:
+		deprint("usb epctl %s\n", cb->f[0]);
+		if(ep->ttype != Tctl)
+			error("not a control endpoint");
+		if(ep->dev->state != Denabled)
+			error("forbidden on devices not enabled");
+		ep->dev->state = Dreset;
+		break;
+	default:
+		panic("usb: unknown epctl %d", ct->index);
+	}
+	free(cb);
+	poperror();
+	return n;
+}
+
+static long
+usbctl(void *a, long n)
+{
+	Cmdtab *ct;
+	Cmdbuf *cb;
+	Ep *ep;
+	int i;
+
+	cb = parsecmd(a, n);
+	if(waserror()){
+		free(cb);
+		nexterror();
+	}
+	ct = lookupcmd(cb, usbctls, nelem(usbctls));
+	dprint("usb ctl %s\n", cb->f[0]);
+	switch(ct->index){
+	case CMdebug:
+		if(strcmp(cb->f[1], "on") == 0)
+			debug = 1;
+		else if(strcmp(cb->f[1], "off") == 0)
+			debug = 0;
+		else
+			debug = strtol(cb->f[1], nil, 0);
+		print("usb: debug %d\n", debug);
+		for(i = 0; i < epmax; i++)
+			if((ep = getep(i)) != nil){
+				ep->hp->debug(ep->hp, debug);
+				putep(ep);
+			}
+		break;
+	case CMdump:
+		dumpeps();
+		break;
+	}
+	free(cb);
+	poperror();
+	return n;
+}
+
+static long
+ctlwrite(Chan *c, void *a, long n)
+{
+	int q;
+	Ep *ep;
+
+	q = QID(c->qid);
+	if(q == Qctl)
+		return usbctl(a, n);
+
+	ep = getep(qid2epidx(q));
+	if(ep == nil)
+		error(Eio);
+	if(waserror()){
+		putep(ep);
+		nexterror();
+	}
+	if(ep->dev->state == Ddetach)
+		error(Edetach);
+	if(isqtype(q, Qepctl) && c->aux != nil){
+		/* Be sure we don't keep a cloned ep name */
+		free(c->aux);
+		c->aux = nil;
+		error("read, not write, expected");
+	}
+	n = epctl(ep, c, a, n);
+	putep(ep);
+	poperror();
+	return n;
+}
+
+static long
+usbwrite(Chan *c, void *a, long n, vlong off)
+{
+	int nr, q;
+	Ep *ep;
+
+	if(c->qid.type == QTDIR)
+		error(Eisdir);
+
+	q = QID(c->qid);
+
+	if(q == Qctl || isqtype(q, Qepctl))
+		return ctlwrite(c, a, n);
+
+	ep = getep(qid2epidx(q));
+	if(ep == nil)
+		error(Eio);
+	if(waserror()){
+		putep(ep);
+		nexterror();
+	}
+	if(ep->dev->state == Ddetach)
+		error(Edetach);
+	if(ep->mode == OREAD || ep->inuse == 0)
+		error(Ebadusefd);
+
+	switch(ep->ttype){
+	case Tnone:
+		error("endpoint not configured");
+	case Tctl:
+		nr = rhubwrite(ep, a, n);
+		if(nr >= 0){
+			n = nr;
+			break;
+		}
+		/* else fall */
+	default:
+		ddeprint("\nusbwrite q %#x fid %d cnt %ld off %lld\n",q, c->fid, n, off);
+		ep->hp->epwrite(ep, a, n);
+	}
+	putep(ep);
+	poperror();
+	return n;
+}
+
+void
+usbshutdown(void)
+{
+	Hci *hp;
+	int i;
+
+	for(i = 0; i < Nhcis; i++){
+		hp = hcis[i];
+		if(hp == nil)
+			continue;
+		if(hp->shutdown == nil)
+			print("#u: no shutdown function for %s\n", hp->type);
+		else
+			hp->shutdown(hp);
+	}
+}
+
+Dev usbdevtab = {
+	L'u',
+	"usb",
+
+	usbreset,
+	usbinit,
+	usbshutdown,
+	usbattach,
+	usbwalk,
+	usbstat,
+	usbopen,
+	devcreate,
+	usbclose,
+	usbread,
+	devbread,
+	usbwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
--