xref: /plan9/sys/src/9/omap/devusb.c (revision 305b51b870592dafc6b4483fa85b31f528e7ab21)
18e32b400SDavid du Colombier /*
2add6b5c5SDavid du Colombier  * USB device driver framework.
38e32b400SDavid du Colombier  *
48e32b400SDavid du Colombier  * This is in charge of providing access to actual HCIs
58e32b400SDavid du Colombier  * and providing I/O to the various endpoints of devices.
68e32b400SDavid du Colombier  * A separate user program (usbd) is in charge of
78e32b400SDavid du Colombier  * enumerating the bus, setting up endpoints and
88e32b400SDavid du Colombier  * starting devices (also user programs).
98e32b400SDavid du Colombier  *
108e32b400SDavid du Colombier  * The interface provided is a violation of the standard:
118e32b400SDavid du Colombier  * you're welcome.
128e32b400SDavid du Colombier  *
138e32b400SDavid du Colombier  * The interface consists of a root directory with several files
148e32b400SDavid du Colombier  * plus a directory (epN.M) with two files per endpoint.
158e32b400SDavid du Colombier  * A device is represented by its first endpoint, which
168e32b400SDavid du Colombier  * is a control endpoint automatically allocated for each device.
178e32b400SDavid du Colombier  * Device control endpoints may be used to create new endpoints.
188e32b400SDavid du Colombier  * Devices corresponding to hubs may also allocate new devices,
198e32b400SDavid du Colombier  * perhaps also hubs. Initially, a hub device is allocated for
208e32b400SDavid du Colombier  * each controller present, to represent its root hub. Those can
218e32b400SDavid du Colombier  * never be removed.
228e32b400SDavid du Colombier  *
238e32b400SDavid du Colombier  * All endpoints refer to the first endpoint (epN.0) of the device,
248e32b400SDavid du Colombier  * which keeps per-device information, and also to the HCI used
258e32b400SDavid du Colombier  * to reach them. Although all endpoints cache that information.
268e32b400SDavid du Colombier  *
278e32b400SDavid du Colombier  * epN.M/data files permit I/O and are considered DMEXCL.
288e32b400SDavid du Colombier  * epN.M/ctl files provide status info and accept control requests.
298e32b400SDavid du Colombier  *
308e32b400SDavid du Colombier  * Endpoints may be given file names to be listed also at #u,
318e32b400SDavid du Colombier  * for those drivers that have nothing to do after configuring the
328e32b400SDavid du Colombier  * device and its endpoints.
338e32b400SDavid du Colombier  *
348e32b400SDavid du Colombier  * Drivers for different controllers are kept at usb[oue]hci.c
358e32b400SDavid du Colombier  * It's likely we could factor out much from controllers into
368e32b400SDavid du Colombier  * a generic controller driver, the problem is that details
378e32b400SDavid du Colombier  * regarding how to handle toggles, tokens, Tds, etc. will
388e32b400SDavid du Colombier  * get in the way. Thus, code is probably easier the way it is.
398e32b400SDavid du Colombier  */
408e32b400SDavid du Colombier 
418e32b400SDavid du Colombier #include	"u.h"
428e32b400SDavid du Colombier #include	"../port/lib.h"
438e32b400SDavid du Colombier #include	"mem.h"
448e32b400SDavid du Colombier #include	"dat.h"
458e32b400SDavid du Colombier #include	"fns.h"
468e32b400SDavid du Colombier #include	"io.h"
478e32b400SDavid du Colombier #include	"../port/error.h"
4884860c5dSDavid du Colombier #include	"../port/usb.h"
498e32b400SDavid du Colombier 
508e32b400SDavid du Colombier typedef struct Hcitype Hcitype;
518e32b400SDavid du Colombier 
528e32b400SDavid du Colombier enum
538e32b400SDavid du Colombier {
548e32b400SDavid du Colombier 	/* Qid numbers */
558e32b400SDavid du Colombier 	Qdir = 0,		/* #u */
568e32b400SDavid du Colombier 	Qusbdir,			/* #u/usb */
578e32b400SDavid du Colombier 	Qctl,			/* #u/usb/ctl - control requests */
588e32b400SDavid du Colombier 
598e32b400SDavid du Colombier 	Qep0dir,			/* #u/usb/ep0.0 - endpoint 0 dir */
608e32b400SDavid du Colombier 	Qep0io,			/* #u/usb/ep0.0/data - endpoint 0 I/O */
618e32b400SDavid du Colombier 	Qep0ctl,		/* #u/usb/ep0.0/ctl - endpoint 0 ctl. */
628e32b400SDavid du Colombier 	Qep0dummy,		/* give 4 qids to each endpoint */
638e32b400SDavid du Colombier 
648e32b400SDavid du Colombier 	Qepdir = 0,		/* (qid-qep0dir)&3 is one of these */
658e32b400SDavid du Colombier 	Qepio,			/* to identify which file for the endpoint */
668e32b400SDavid du Colombier 	Qepctl,
678e32b400SDavid du Colombier 
688e32b400SDavid du Colombier 	/* ... */
698e32b400SDavid du Colombier 
708e32b400SDavid du Colombier 	/* Usb ctls. */
718e32b400SDavid du Colombier 	CMdebug = 0,		/* debug on|off */
728e32b400SDavid du Colombier 	CMdump,			/* dump (data structures for debug) */
738e32b400SDavid du Colombier 
748e32b400SDavid du Colombier 	/* Ep. ctls */
758e32b400SDavid du Colombier 	CMnew = 0,		/* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */
768e32b400SDavid du Colombier 	CMnewdev,		/* newdev full|low|high portnb (allocate new devices) */
778e32b400SDavid du Colombier 	CMhub,			/* hub (set the device as a hub) */
788e32b400SDavid du Colombier 	CMspeed,		/* speed full|low|high|no */
798e32b400SDavid du Colombier 	CMmaxpkt,		/* maxpkt size */
808e32b400SDavid du Colombier 	CMntds,			/* ntds nb (max nb. of tds per µframe) */
818e32b400SDavid du Colombier 	CMclrhalt,		/* clrhalt (halt was cleared on endpoint) */
828e32b400SDavid du Colombier 	CMpollival,		/* pollival interval (interrupt/iso) */
838e32b400SDavid du Colombier 	CMhz,			/* hz n (samples/sec; iso) */
848e32b400SDavid du Colombier 	CMsamplesz,		/* samplesz n (sample size; iso) */
858e32b400SDavid du Colombier 	CMinfo,			/* info infostr (ke.ep info for humans) */
868e32b400SDavid du Colombier 	CMdetach,		/* detach (abort I/O forever on this ep). */
878e32b400SDavid du Colombier 	CMaddress,		/* address (address is assigned) */
888e32b400SDavid du Colombier 	CMdebugep,		/* debug n (set/clear debug for this ep) */
898e32b400SDavid du Colombier 	CMname,			/* name str (show up as #u/name as well) */
908e32b400SDavid du Colombier 	CMtmout,		/* timeout n (activate timeouts for ep) */
918e32b400SDavid du Colombier 	CMpreset,		/* reset the port */
928e32b400SDavid du Colombier 
938e32b400SDavid du Colombier 	/* Hub feature selectors */
948e32b400SDavid du Colombier 	Rportenable	= 1,
958e32b400SDavid du Colombier 	Rportreset	= 4,
968e32b400SDavid du Colombier 
978e32b400SDavid du Colombier };
988e32b400SDavid du Colombier 
998e32b400SDavid du Colombier struct Hcitype
1008e32b400SDavid du Colombier {
1018e32b400SDavid du Colombier 	char*	type;
1028e32b400SDavid du Colombier 	int	(*reset)(Hci*);
1038e32b400SDavid du Colombier };
1048e32b400SDavid du Colombier 
1058e32b400SDavid du Colombier #define QID(q)	((int)(q).path)
1068e32b400SDavid du Colombier 
1078e32b400SDavid du Colombier static char Edetach[] = "device is detached";
1088e32b400SDavid du Colombier static char Enotconf[] = "endpoint not configured";
1098e32b400SDavid du Colombier char Estalled[] = "endpoint stalled";
1108e32b400SDavid du Colombier 
1118e32b400SDavid du Colombier static Cmdtab usbctls[] =
1128e32b400SDavid du Colombier {
1138e32b400SDavid du Colombier 	{CMdebug,	"debug",	2},
1148e32b400SDavid du Colombier 	{CMdump,	"dump",		1},
1158e32b400SDavid du Colombier };
1168e32b400SDavid du Colombier 
1178e32b400SDavid du Colombier static Cmdtab epctls[] =
1188e32b400SDavid du Colombier {
1198e32b400SDavid du Colombier 	{CMnew,		"new",		4},
1208e32b400SDavid du Colombier 	{CMnewdev,	"newdev",	3},
1218e32b400SDavid du Colombier 	{CMhub,		"hub",		1},
1228e32b400SDavid du Colombier 	{CMspeed,	"speed",	2},
1238e32b400SDavid du Colombier 	{CMmaxpkt,	"maxpkt",	2},
1248e32b400SDavid du Colombier 	{CMntds,	"ntds",		2},
1258e32b400SDavid du Colombier 	{CMpollival,	"pollival",	2},
1268e32b400SDavid du Colombier 	{CMsamplesz,	"samplesz",	2},
1278e32b400SDavid du Colombier 	{CMhz,		"hz",		2},
1288e32b400SDavid du Colombier 	{CMinfo,	"info",		0},
1298e32b400SDavid du Colombier 	{CMdetach,	"detach",	1},
1308e32b400SDavid du Colombier 	{CMaddress,	"address",	1},
1318e32b400SDavid du Colombier 	{CMdebugep,	"debug",	2},
1328e32b400SDavid du Colombier 	{CMclrhalt,	"clrhalt",	1},
1338e32b400SDavid du Colombier 	{CMname,	"name",		2},
1348e32b400SDavid du Colombier 	{CMtmout,	"timeout",	2},
1358e32b400SDavid du Colombier 	{CMpreset,	"reset",	1},
1368e32b400SDavid du Colombier };
1378e32b400SDavid du Colombier 
1388e32b400SDavid du Colombier static Dirtab usbdir[] =
1398e32b400SDavid du Colombier {
1408e32b400SDavid du Colombier 	"ctl",		{Qctl},		0,	0666,
1418e32b400SDavid du Colombier };
1428e32b400SDavid du Colombier 
1438e32b400SDavid du Colombier char *usbmodename[] =
1448e32b400SDavid du Colombier {
1458e32b400SDavid du Colombier 	[OREAD]	"r",
1468e32b400SDavid du Colombier 	[OWRITE]	"w",
1478e32b400SDavid du Colombier 	[ORDWR]	"rw",
1488e32b400SDavid du Colombier };
1498e32b400SDavid du Colombier 
1508e32b400SDavid du Colombier static char *ttname[] =
1518e32b400SDavid du Colombier {
1528e32b400SDavid du Colombier 	[Tnone]	"none",
1538e32b400SDavid du Colombier 	[Tctl]	"control",
1548e32b400SDavid du Colombier 	[Tiso]	"iso",
1558e32b400SDavid du Colombier 	[Tintr]	"interrupt",
1568e32b400SDavid du Colombier 	[Tbulk]	"bulk",
1578e32b400SDavid du Colombier };
1588e32b400SDavid du Colombier 
1598e32b400SDavid du Colombier static char *spname[] =
1608e32b400SDavid du Colombier {
1618e32b400SDavid du Colombier 	[Fullspeed]	"full",
1628e32b400SDavid du Colombier 	[Lowspeed]	"low",
1638e32b400SDavid du Colombier 	[Highspeed]	"high",
1648e32b400SDavid du Colombier 	[Nospeed]	"no",
1658e32b400SDavid du Colombier };
1668e32b400SDavid du Colombier 
167c8a340cdSDavid du Colombier static int	debug;
1688e32b400SDavid du Colombier static Hcitype	hcitypes[Nhcis];
1698e32b400SDavid du Colombier static Hci*	hcis[Nhcis];
1708e32b400SDavid du Colombier static QLock	epslck;		/* add, del, lookup endpoints */
1718e32b400SDavid du Colombier static Ep*	eps[Neps];	/* all endpoints known */
1728e32b400SDavid du Colombier static int	epmax;		/* 1 + last endpoint index used  */
1738e32b400SDavid du Colombier static int	usbidgen;	/* device address generator */
1748e32b400SDavid du Colombier 
1758e32b400SDavid du Colombier /*
1768e32b400SDavid du Colombier  * Is there something like this in a library? should it be?
1778e32b400SDavid du Colombier  */
1788e32b400SDavid du Colombier char*
1798e32b400SDavid du Colombier seprintdata(char *s, char *se, uchar *d, int n)
1808e32b400SDavid du Colombier {
1818e32b400SDavid du Colombier 	int i, l;
1828e32b400SDavid du Colombier 
1838e32b400SDavid du Colombier 	s = seprint(s, se, " %#p[%d]: ", d, n);
1848e32b400SDavid du Colombier 	l = n;
1858e32b400SDavid du Colombier 	if(l > 10)
1868e32b400SDavid du Colombier 		l = 10;
1878e32b400SDavid du Colombier 	for(i=0; i<l; i++)
1888e32b400SDavid du Colombier 		s = seprint(s, se, " %2.2ux", d[i]);
1898e32b400SDavid du Colombier 	if(l < n)
1908e32b400SDavid du Colombier 		s = seprint(s, se, "...");
1918e32b400SDavid du Colombier 	return s;
1928e32b400SDavid du Colombier }
1938e32b400SDavid du Colombier 
1948e32b400SDavid du Colombier static int
1958e32b400SDavid du Colombier name2speed(char *name)
1968e32b400SDavid du Colombier {
1978e32b400SDavid du Colombier 	int i;
1988e32b400SDavid du Colombier 
1998e32b400SDavid du Colombier 	for(i = 0; i < nelem(spname); i++)
2008e32b400SDavid du Colombier 		if(strcmp(name, spname[i]) == 0)
2018e32b400SDavid du Colombier 			return i;
2028e32b400SDavid du Colombier 	return Nospeed;
2038e32b400SDavid du Colombier }
2048e32b400SDavid du Colombier 
2058e32b400SDavid du Colombier static int
2068e32b400SDavid du Colombier name2ttype(char *name)
2078e32b400SDavid du Colombier {
2088e32b400SDavid du Colombier 	int i;
2098e32b400SDavid du Colombier 
2108e32b400SDavid du Colombier 	for(i = 0; i < nelem(ttname); i++)
2118e32b400SDavid du Colombier 		if(strcmp(name, ttname[i]) == 0)
2128e32b400SDavid du Colombier 			return i;
2138e32b400SDavid du Colombier 	/* may be a std. USB ep. type */
2148e32b400SDavid du Colombier 	i = strtol(name, nil, 0);
2158e32b400SDavid du Colombier 	switch(i+1){
2168e32b400SDavid du Colombier 	case Tctl:
2178e32b400SDavid du Colombier 	case Tiso:
2188e32b400SDavid du Colombier 	case Tbulk:
2198e32b400SDavid du Colombier 	case Tintr:
2208e32b400SDavid du Colombier 		return i+1;
2218e32b400SDavid du Colombier 	default:
2228e32b400SDavid du Colombier 		return Tnone;
2238e32b400SDavid du Colombier 	}
2248e32b400SDavid du Colombier }
2258e32b400SDavid du Colombier 
2268e32b400SDavid du Colombier static int
2278e32b400SDavid du Colombier name2mode(char *mode)
2288e32b400SDavid du Colombier {
2298e32b400SDavid du Colombier 	int i;
2308e32b400SDavid du Colombier 
2318e32b400SDavid du Colombier 	for(i = 0; i < nelem(usbmodename); i++)
2328e32b400SDavid du Colombier 		if(strcmp(mode, usbmodename[i]) == 0)
2338e32b400SDavid du Colombier 			return i;
2348e32b400SDavid du Colombier 	return -1;
2358e32b400SDavid du Colombier }
2368e32b400SDavid du Colombier 
2378e32b400SDavid du Colombier static int
2388e32b400SDavid du Colombier qid2epidx(int q)
2398e32b400SDavid du Colombier {
2408e32b400SDavid du Colombier 	q = (q-Qep0dir)/4;
2418e32b400SDavid du Colombier 	if(q < 0 || q >= epmax || eps[q] == nil)
2428e32b400SDavid du Colombier 		return -1;
2438e32b400SDavid du Colombier 	return q;
2448e32b400SDavid du Colombier }
2458e32b400SDavid du Colombier 
2468e32b400SDavid du Colombier static int
2478e32b400SDavid du Colombier isqtype(int q, int type)
2488e32b400SDavid du Colombier {
2498e32b400SDavid du Colombier 	if(q < Qep0dir)
2508e32b400SDavid du Colombier 		return 0;
2518e32b400SDavid du Colombier 	q -= Qep0dir;
2528e32b400SDavid du Colombier 	return (q & 3) == type;
2538e32b400SDavid du Colombier }
2548e32b400SDavid du Colombier 
2558e32b400SDavid du Colombier void
2568e32b400SDavid du Colombier addhcitype(char* t, int (*r)(Hci*))
2578e32b400SDavid du Colombier {
2588e32b400SDavid du Colombier 	static int ntype;
2598e32b400SDavid du Colombier 
2608e32b400SDavid du Colombier 	if(ntype == Nhcis)
2618e32b400SDavid du Colombier 		panic("too many USB host interface types");
2628e32b400SDavid du Colombier 	hcitypes[ntype].type = t;
2638e32b400SDavid du Colombier 	hcitypes[ntype].reset = r;
2648e32b400SDavid du Colombier 	ntype++;
2658e32b400SDavid du Colombier }
2668e32b400SDavid du Colombier 
2678e32b400SDavid du Colombier static char*
2688e32b400SDavid du Colombier seprintep(char *s, char *se, Ep *ep, int all)
2698e32b400SDavid du Colombier {
2708e32b400SDavid du Colombier 	static char* dsnames[] = { "config", "enabled", "detached", "reset" };
2718e32b400SDavid du Colombier 	Udev *d;
2728e32b400SDavid du Colombier 	int i;
2738e32b400SDavid du Colombier 	int di;
2748e32b400SDavid du Colombier 
2758e32b400SDavid du Colombier 	d = ep->dev;
2768e32b400SDavid du Colombier 
2778e32b400SDavid du Colombier 	qlock(ep);
2788e32b400SDavid du Colombier 	if(waserror()){
2798e32b400SDavid du Colombier 		qunlock(ep);
2808e32b400SDavid du Colombier 		nexterror();
2818e32b400SDavid du Colombier 	}
2828e32b400SDavid du Colombier 	di = ep->dev->nb;
2838e32b400SDavid du Colombier 	if(all)
2848e32b400SDavid du Colombier 		s = seprint(s, se, "dev %d ep %d ", di, ep->nb);
2858e32b400SDavid du Colombier 	s = seprint(s, se, "%s", dsnames[ep->dev->state]);
2868e32b400SDavid du Colombier 	s = seprint(s, se, " %s", ttname[ep->ttype]);
2878e32b400SDavid du Colombier 	assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR);
2888e32b400SDavid du Colombier 	s = seprint(s, se, " %s", usbmodename[ep->mode]);
2898e32b400SDavid du Colombier 	s = seprint(s, se, " speed %s", spname[d->speed]);
2908e32b400SDavid du Colombier 	s = seprint(s, se, " maxpkt %ld", ep->maxpkt);
2918e32b400SDavid du Colombier 	s = seprint(s, se, " pollival %ld", ep->pollival);
2928e32b400SDavid du Colombier 	s = seprint(s, se, " samplesz %ld", ep->samplesz);
2938e32b400SDavid du Colombier 	s = seprint(s, se, " hz %ld", ep->hz);
2948e32b400SDavid du Colombier 	s = seprint(s, se, " hub %d", ep->dev->hub);
2958e32b400SDavid du Colombier 	s = seprint(s, se, " port %d", ep->dev->port);
2968e32b400SDavid du Colombier 	if(ep->inuse)
2978e32b400SDavid du Colombier 		s = seprint(s, se, " busy");
2988e32b400SDavid du Colombier 	else
2998e32b400SDavid du Colombier 		s = seprint(s, se, " idle");
3008e32b400SDavid du Colombier 	if(all){
3018e32b400SDavid du Colombier 		s = seprint(s, se, " load %uld", ep->load);
3028e32b400SDavid du Colombier 		s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep);
3038e32b400SDavid du Colombier 		s = seprint(s, se, " idx %d", ep->idx);
3048e32b400SDavid du Colombier 		if(ep->name != nil)
3058e32b400SDavid du Colombier 			s = seprint(s, se, " name '%s'", ep->name);
3068e32b400SDavid du Colombier 		if(ep->tmout != 0)
3078e32b400SDavid du Colombier 			s = seprint(s, se, " tmout");
3088e32b400SDavid du Colombier 		if(ep == ep->ep0){
3098e32b400SDavid du Colombier 			s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno);
3108e32b400SDavid du Colombier 			s = seprint(s, se, " eps:");
3118e32b400SDavid du Colombier 			for(i = 0; i < nelem(d->eps); i++)
3128e32b400SDavid du Colombier 				if(d->eps[i] != nil)
3138e32b400SDavid du Colombier 					s = seprint(s, se, " ep%d.%d", di, i);
3148e32b400SDavid du Colombier 		}
3158e32b400SDavid du Colombier 	}
3168e32b400SDavid du Colombier 	if(ep->info != nil)
3178e32b400SDavid du Colombier 		s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type);
3188e32b400SDavid du Colombier 	else
3198e32b400SDavid du Colombier 		s = seprint(s, se, "\n");
3208e32b400SDavid du Colombier 	qunlock(ep);
3218e32b400SDavid du Colombier 	poperror();
3228e32b400SDavid du Colombier 	return s;
3238e32b400SDavid du Colombier }
3248e32b400SDavid du Colombier 
3258e32b400SDavid du Colombier static Ep*
3268e32b400SDavid du Colombier epalloc(Hci *hp)
3278e32b400SDavid du Colombier {
3288e32b400SDavid du Colombier 	Ep *ep;
3298e32b400SDavid du Colombier 	int i;
3308e32b400SDavid du Colombier 
331*305b51b8SDavid du Colombier 	ep = smalloc(sizeof(Ep));
3328e32b400SDavid du Colombier 	ep->ref = 1;
3338e32b400SDavid du Colombier 	qlock(&epslck);
3348e32b400SDavid du Colombier 	for(i = 0; i < Neps; i++)
3358e32b400SDavid du Colombier 		if(eps[i] == nil)
3368e32b400SDavid du Colombier 			break;
3378e32b400SDavid du Colombier 	if(i == Neps){
3388e32b400SDavid du Colombier 		qunlock(&epslck);
3398e32b400SDavid du Colombier 		free(ep);
3408e32b400SDavid du Colombier 		print("usb: bug: too few endpoints.\n");
3418e32b400SDavid du Colombier 		return nil;
3428e32b400SDavid du Colombier 	}
3438e32b400SDavid du Colombier 	ep->idx = i;
3448e32b400SDavid du Colombier 	if(epmax <= i)
3458e32b400SDavid du Colombier 		epmax = i+1;
3468e32b400SDavid du Colombier 	eps[i] = ep;
3478e32b400SDavid du Colombier 	ep->hp = hp;
3488e32b400SDavid du Colombier 	ep->maxpkt = 8;
3498e32b400SDavid du Colombier 	ep->ntds = 1;
3508e32b400SDavid du Colombier 	ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */
3518e32b400SDavid du Colombier 	qunlock(&epslck);
3528e32b400SDavid du Colombier 	return ep;
3538e32b400SDavid du Colombier }
3548e32b400SDavid du Colombier 
3558e32b400SDavid du Colombier static Ep*
3568e32b400SDavid du Colombier getep(int i)
3578e32b400SDavid du Colombier {
3588e32b400SDavid du Colombier 	Ep *ep;
3598e32b400SDavid du Colombier 
3608e32b400SDavid du Colombier 	if(i < 0 || i >= epmax || eps[i] == nil)
3618e32b400SDavid du Colombier 		return nil;
3628e32b400SDavid du Colombier 	qlock(&epslck);
3638e32b400SDavid du Colombier 	ep = eps[i];
3648e32b400SDavid du Colombier 	if(ep != nil)
3658e32b400SDavid du Colombier 		incref(ep);
3668e32b400SDavid du Colombier 	qunlock(&epslck);
3678e32b400SDavid du Colombier 	return ep;
3688e32b400SDavid du Colombier }
3698e32b400SDavid du Colombier 
3708e32b400SDavid du Colombier static void
3718e32b400SDavid du Colombier putep(Ep *ep)
3728e32b400SDavid du Colombier {
3738e32b400SDavid du Colombier 	Udev *d;
3748e32b400SDavid du Colombier 
3758e32b400SDavid du Colombier 	if(ep != nil && decref(ep) == 0){
3768e32b400SDavid du Colombier 		d = ep->dev;
3778e32b400SDavid du Colombier 		deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep);
3788e32b400SDavid du Colombier 		qlock(&epslck);
3798e32b400SDavid du Colombier 		eps[ep->idx] = nil;
3808e32b400SDavid du Colombier 		if(ep->idx == epmax-1)
3818e32b400SDavid du Colombier 			epmax--;
3828e32b400SDavid du Colombier 		if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen)
3838e32b400SDavid du Colombier 			usbidgen--;
3848e32b400SDavid du Colombier 		qunlock(&epslck);
3858e32b400SDavid du Colombier 		if(d != nil){
3868e32b400SDavid du Colombier 			qlock(ep->ep0);
3878e32b400SDavid du Colombier 			d->eps[ep->nb] = nil;
3888e32b400SDavid du Colombier 			qunlock(ep->ep0);
3898e32b400SDavid du Colombier 		}
3908e32b400SDavid du Colombier 		if(ep->ep0 != ep){
3918e32b400SDavid du Colombier 			putep(ep->ep0);
3928e32b400SDavid du Colombier 			ep->ep0 = nil;
3938e32b400SDavid du Colombier 		}
3948e32b400SDavid du Colombier 		free(ep->info);
3958e32b400SDavid du Colombier 		free(ep->name);
3968e32b400SDavid du Colombier 		free(ep);
3978e32b400SDavid du Colombier 	}
3988e32b400SDavid du Colombier }
3998e32b400SDavid du Colombier 
4008e32b400SDavid du Colombier static void
4018e32b400SDavid du Colombier dumpeps(void)
4028e32b400SDavid du Colombier {
4038e32b400SDavid du Colombier 	int i;
4048e32b400SDavid du Colombier 	static char buf[512];
4058e32b400SDavid du Colombier 	char *s;
4068e32b400SDavid du Colombier 	char *e;
4078e32b400SDavid du Colombier 	Ep *ep;
4088e32b400SDavid du Colombier 
4098e32b400SDavid du Colombier 	print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps);
4108e32b400SDavid du Colombier 	for(i = 0; i < epmax; i++){
4118e32b400SDavid du Colombier 		s = buf;
4128e32b400SDavid du Colombier 		e = buf+sizeof(buf);
4138e32b400SDavid du Colombier 		ep = getep(i);
4148e32b400SDavid du Colombier 		if(ep != nil){
4158e32b400SDavid du Colombier 			if(waserror()){
4168e32b400SDavid du Colombier 				putep(ep);
4178e32b400SDavid du Colombier 				nexterror();
4188e32b400SDavid du Colombier 			}
4198e32b400SDavid du Colombier 			s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb);
4208e32b400SDavid du Colombier 			seprintep(s, e, ep, 1);
4218e32b400SDavid du Colombier 			print("%s", buf);
4228e32b400SDavid du Colombier 			ep->hp->seprintep(buf, e, ep);
4238e32b400SDavid du Colombier 			print("%s", buf);
4248e32b400SDavid du Colombier 			poperror();
4258e32b400SDavid du Colombier 			putep(ep);
4268e32b400SDavid du Colombier 		}
4278e32b400SDavid du Colombier 	}
4288e32b400SDavid du Colombier 	print("usb dump hcis:\n");
4298e32b400SDavid du Colombier 	for(i = 0; i < Nhcis; i++)
4308e32b400SDavid du Colombier 		if(hcis[i] != nil)
4318e32b400SDavid du Colombier 			hcis[i]->dump(hcis[i]);
4328e32b400SDavid du Colombier }
4338e32b400SDavid du Colombier 
4348e32b400SDavid du Colombier static int
4358e32b400SDavid du Colombier newusbid(Hci *)
4368e32b400SDavid du Colombier {
4378e32b400SDavid du Colombier 	int id;
4388e32b400SDavid du Colombier 
4398e32b400SDavid du Colombier 	qlock(&epslck);
4408e32b400SDavid du Colombier 	id = ++usbidgen;
4418e32b400SDavid du Colombier 	if(id >= 0x7F)
4428e32b400SDavid du Colombier 		print("#u: too many device addresses; reuse them more\n");
4438e32b400SDavid du Colombier 	qunlock(&epslck);
4448e32b400SDavid du Colombier 	return id;
4458e32b400SDavid du Colombier }
4468e32b400SDavid du Colombier 
4478e32b400SDavid du Colombier /*
4488e32b400SDavid du Colombier  * Create endpoint 0 for a new device
4498e32b400SDavid du Colombier  */
4508e32b400SDavid du Colombier static Ep*
4518e32b400SDavid du Colombier newdev(Hci *hp, int ishub, int isroot)
4528e32b400SDavid du Colombier {
4538e32b400SDavid du Colombier 	Ep *ep;
4548e32b400SDavid du Colombier 	Udev *d;
4558e32b400SDavid du Colombier 
4568e32b400SDavid du Colombier 	ep = epalloc(hp);
457*305b51b8SDavid du Colombier 	d = ep->dev = smalloc(sizeof(Udev));
4588e32b400SDavid du Colombier 	d->nb = newusbid(hp);
4598e32b400SDavid du Colombier 	d->eps[0] = ep;
4608e32b400SDavid du Colombier 	ep->nb = 0;
4618e32b400SDavid du Colombier 	ep->toggle[0] = ep->toggle[1] = 0;
4628e32b400SDavid du Colombier 	d->ishub = ishub;
4638e32b400SDavid du Colombier 	d->isroot = isroot;
4648e32b400SDavid du Colombier 	if(hp->highspeed != 0)
4658e32b400SDavid du Colombier 		d->speed = Highspeed;
4668e32b400SDavid du Colombier 	else
4678e32b400SDavid du Colombier 		d->speed = Fullspeed;
4688e32b400SDavid du Colombier 	d->state = Dconfig;		/* address not yet set */
4698e32b400SDavid du Colombier 	ep->dev = d;
4708e32b400SDavid du Colombier 	ep->ep0 = ep;			/* no ref counted here */
4718e32b400SDavid du Colombier 	ep->ttype = Tctl;
4728e32b400SDavid du Colombier 	ep->tmout = Xfertmout;
4738e32b400SDavid du Colombier 	ep->mode = ORDWR;
4748e32b400SDavid du Colombier 	dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep);
4758e32b400SDavid du Colombier 	return ep;
4768e32b400SDavid du Colombier }
4778e32b400SDavid du Colombier 
4788e32b400SDavid du Colombier /*
4798e32b400SDavid du Colombier  * Create a new endpoint for the device
4808e32b400SDavid du Colombier  * accessed via the given endpoint 0.
4818e32b400SDavid du Colombier  */
4828e32b400SDavid du Colombier static Ep*
4838e32b400SDavid du Colombier newdevep(Ep *ep, int i, int tt, int mode)
4848e32b400SDavid du Colombier {
4858e32b400SDavid du Colombier 	Ep *nep;
4868e32b400SDavid du Colombier 	Udev *d;
4878e32b400SDavid du Colombier 
4888e32b400SDavid du Colombier 	d = ep->dev;
4898e32b400SDavid du Colombier 	if(d->eps[i] != nil)
4908e32b400SDavid du Colombier 		error("endpoint already in use");
4918e32b400SDavid du Colombier 	nep = epalloc(ep->hp);
4928e32b400SDavid du Colombier 	incref(ep);
4938e32b400SDavid du Colombier 	d->eps[i] = nep;
4948e32b400SDavid du Colombier 	nep->nb = i;
4958e32b400SDavid du Colombier 	nep->toggle[0] = nep->toggle[1] = 0;
4968e32b400SDavid du Colombier 	nep->ep0 = ep;
4978e32b400SDavid du Colombier 	nep->dev = ep->dev;
4988e32b400SDavid du Colombier 	nep->mode = mode;
4998e32b400SDavid du Colombier 	nep->ttype = tt;
5008e32b400SDavid du Colombier 	nep->debug = ep->debug;
5018e32b400SDavid du Colombier 	/* set defaults */
5028e32b400SDavid du Colombier 	switch(tt){
5038e32b400SDavid du Colombier 	case Tctl:
5048e32b400SDavid du Colombier 		nep->tmout = Xfertmout;
5058e32b400SDavid du Colombier 		break;
5068e32b400SDavid du Colombier 	case Tintr:
5078e32b400SDavid du Colombier 		nep->pollival = 10;
5088e32b400SDavid du Colombier 		break;
5098e32b400SDavid du Colombier 	case Tiso:
5108e32b400SDavid du Colombier 		nep->tmout = Xfertmout;
5118e32b400SDavid du Colombier 		nep->pollival = 10;
5128e32b400SDavid du Colombier 		nep->samplesz = 4;
5138e32b400SDavid du Colombier 		nep->hz = 44100;
5148e32b400SDavid du Colombier 		break;
5158e32b400SDavid du Colombier 	}
5168e32b400SDavid du Colombier 	deprint("newdevep ep%d.%d %#p\n", d->nb, nep->nb, nep);
5178e32b400SDavid du Colombier 	return ep;
5188e32b400SDavid du Colombier }
5198e32b400SDavid du Colombier 
5208e32b400SDavid du Colombier static int
5218e32b400SDavid du Colombier epdataperm(int mode)
5228e32b400SDavid du Colombier {
5238e32b400SDavid du Colombier 
5248e32b400SDavid du Colombier 	switch(mode){
5258e32b400SDavid du Colombier 	case OREAD:
5268e32b400SDavid du Colombier 		return 0440|DMEXCL;
5278e32b400SDavid du Colombier 		break;
5288e32b400SDavid du Colombier 	case OWRITE:
5298e32b400SDavid du Colombier 		return 0220|DMEXCL;
5308e32b400SDavid du Colombier 		break;
5318e32b400SDavid du Colombier 	default:
5328e32b400SDavid du Colombier 		return 0660|DMEXCL;
5338e32b400SDavid du Colombier 	}
5348e32b400SDavid du Colombier }
5358e32b400SDavid du Colombier 
5368e32b400SDavid du Colombier static int
5378e32b400SDavid du Colombier usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
5388e32b400SDavid du Colombier {
5398e32b400SDavid du Colombier 	Qid q;
5408e32b400SDavid du Colombier 	Dirtab *dir;
5418e32b400SDavid du Colombier 	int perm;
5428e32b400SDavid du Colombier 	char *se;
5438e32b400SDavid du Colombier 	Ep *ep;
5448e32b400SDavid du Colombier 	int nb;
5458e32b400SDavid du Colombier 	int mode;
5468e32b400SDavid du Colombier 
5478e32b400SDavid du Colombier 	if(0)ddprint("usbgen q %#x s %d...", QID(c->qid), s);
5488e32b400SDavid du Colombier 	if(s == DEVDOTDOT){
5498e32b400SDavid du Colombier 		if(QID(c->qid) <= Qusbdir){
5508e32b400SDavid du Colombier 			mkqid(&q, Qdir, 0, QTDIR);
5518e32b400SDavid du Colombier 			devdir(c, q, "#u", 0, eve, 0555, dp);
5528e32b400SDavid du Colombier 		}else{
5538e32b400SDavid du Colombier 			mkqid(&q, Qusbdir, 0, QTDIR);
5548e32b400SDavid du Colombier 			devdir(c, q, "usb", 0, eve, 0555, dp);
5558e32b400SDavid du Colombier 		}
5568e32b400SDavid du Colombier 		if(0)ddprint("ok\n");
5578e32b400SDavid du Colombier 		return 1;
5588e32b400SDavid du Colombier 	}
5598e32b400SDavid du Colombier 
5608e32b400SDavid du Colombier 	switch(QID(c->qid)){
5618e32b400SDavid du Colombier 	case Qdir:				/* list #u */
5628e32b400SDavid du Colombier 		if(s == 0){
5638e32b400SDavid du Colombier 			mkqid(&q, Qusbdir, 0, QTDIR);
5648e32b400SDavid du Colombier 			devdir(c, q, "usb", 0, eve, 0555, dp);
5658e32b400SDavid du Colombier 			if(0)ddprint("ok\n");
5668e32b400SDavid du Colombier 			return 1;
5678e32b400SDavid du Colombier 		}
5688e32b400SDavid du Colombier 		s--;
5698e32b400SDavid du Colombier 		if(s < 0 || s >= epmax)
5708e32b400SDavid du Colombier 			goto Fail;
5718e32b400SDavid du Colombier 		ep = getep(s);
5728e32b400SDavid du Colombier 		if(ep == nil || ep->name == nil){
5738e32b400SDavid du Colombier 			if(ep != nil)
5748e32b400SDavid du Colombier 				putep(ep);
5758e32b400SDavid du Colombier 			if(0)ddprint("skip\n");
5768e32b400SDavid du Colombier 			return 0;
5778e32b400SDavid du Colombier 		}
5788e32b400SDavid du Colombier 		if(waserror()){
5798e32b400SDavid du Colombier 			putep(ep);
5808e32b400SDavid du Colombier 			nexterror();
5818e32b400SDavid du Colombier 		}
5828e32b400SDavid du Colombier 		mkqid(&q, Qep0io+s*4, 0, QTFILE);
5838e32b400SDavid du Colombier 		devdir(c, q, ep->name, 0, eve, epdataperm(ep->mode), dp);
5848e32b400SDavid du Colombier 		putep(ep);
5858e32b400SDavid du Colombier 		poperror();
5868e32b400SDavid du Colombier 		if(0)ddprint("ok\n");
5878e32b400SDavid du Colombier 		return 1;
5888e32b400SDavid du Colombier 
5898e32b400SDavid du Colombier 	case Qusbdir:				/* list #u/usb */
5908e32b400SDavid du Colombier 	Usbdir:
5918e32b400SDavid du Colombier 		if(s < nelem(usbdir)){
5928e32b400SDavid du Colombier 			dir = &usbdir[s];
5938e32b400SDavid du Colombier 			mkqid(&q, dir->qid.path, 0, QTFILE);
5948e32b400SDavid du Colombier 			devdir(c, q, dir->name, dir->length, eve, dir->perm, dp);
5958e32b400SDavid du Colombier 			if(0)ddprint("ok\n");
5968e32b400SDavid du Colombier 			return 1;
5978e32b400SDavid du Colombier 		}
5988e32b400SDavid du Colombier 		s -= nelem(usbdir);
5998e32b400SDavid du Colombier 		if(s < 0 || s >= epmax)
6008e32b400SDavid du Colombier 			goto Fail;
6018e32b400SDavid du Colombier 		ep = getep(s);
6028e32b400SDavid du Colombier 		if(ep == nil){
6038e32b400SDavid du Colombier 			if(0)ddprint("skip\n");
6048e32b400SDavid du Colombier 			return 0;
6058e32b400SDavid du Colombier 		}
6068e32b400SDavid du Colombier 		if(waserror()){
6078e32b400SDavid du Colombier 			putep(ep);
6088e32b400SDavid du Colombier 			nexterror();
6098e32b400SDavid du Colombier 		}
6108e32b400SDavid du Colombier 		se = up->genbuf+sizeof(up->genbuf);
6118e32b400SDavid du Colombier 		seprint(up->genbuf, se, "ep%d.%d", ep->dev->nb, ep->nb);
6128e32b400SDavid du Colombier 		mkqid(&q, Qep0dir+4*s, 0, QTDIR);
6138e32b400SDavid du Colombier 		putep(ep);
6148e32b400SDavid du Colombier 		poperror();
6158e32b400SDavid du Colombier 		devdir(c, q, up->genbuf, 0, eve, 0755, dp);
6168e32b400SDavid du Colombier 		if(0)ddprint("ok\n");
6178e32b400SDavid du Colombier 		return 1;
6188e32b400SDavid du Colombier 
6198e32b400SDavid du Colombier 	case Qctl:
6208e32b400SDavid du Colombier 		s = 0;
6218e32b400SDavid du Colombier 		goto Usbdir;
6228e32b400SDavid du Colombier 
6238e32b400SDavid du Colombier 	default:				/* list #u/usb/epN.M */
6248e32b400SDavid du Colombier 		nb = qid2epidx(QID(c->qid));
6258e32b400SDavid du Colombier 		ep = getep(nb);
6268e32b400SDavid du Colombier 		if(ep == nil)
6278e32b400SDavid du Colombier 			goto Fail;
6288e32b400SDavid du Colombier 		mode = ep->mode;
6298e32b400SDavid du Colombier 		putep(ep);
6308e32b400SDavid du Colombier 		if(isqtype(QID(c->qid), Qepdir)){
6318e32b400SDavid du Colombier 		Epdir:
6328e32b400SDavid du Colombier 			switch(s){
6338e32b400SDavid du Colombier 			case 0:
6348e32b400SDavid du Colombier 				mkqid(&q, Qep0io+nb*4, 0, QTFILE);
6358e32b400SDavid du Colombier 				perm = epdataperm(mode);
6368e32b400SDavid du Colombier 				devdir(c, q, "data", 0, eve, perm, dp);
6378e32b400SDavid du Colombier 				break;
6388e32b400SDavid du Colombier 			case 1:
6398e32b400SDavid du Colombier 				mkqid(&q, Qep0ctl+nb*4, 0, QTFILE);
6408e32b400SDavid du Colombier 				devdir(c, q, "ctl", 0, eve, 0664, dp);
6418e32b400SDavid du Colombier 				break;
6428e32b400SDavid du Colombier 			default:
6438e32b400SDavid du Colombier 				goto Fail;
6448e32b400SDavid du Colombier 			}
6458e32b400SDavid du Colombier 		}else if(isqtype(QID(c->qid), Qepctl)){
6468e32b400SDavid du Colombier 			s = 1;
6478e32b400SDavid du Colombier 			goto Epdir;
6488e32b400SDavid du Colombier 		}else{
6498e32b400SDavid du Colombier 			s = 0;
6508e32b400SDavid du Colombier 			goto Epdir;
6518e32b400SDavid du Colombier 		}
6528e32b400SDavid du Colombier 		if(0)ddprint("ok\n");
6538e32b400SDavid du Colombier 		return 1;
6548e32b400SDavid du Colombier 	}
6558e32b400SDavid du Colombier Fail:
6568e32b400SDavid du Colombier 	if(0)ddprint("fail\n");
6578e32b400SDavid du Colombier 	return -1;
6588e32b400SDavid du Colombier }
6598e32b400SDavid du Colombier 
6608e32b400SDavid du Colombier static Hci*
6618e32b400SDavid du Colombier hciprobe(int cardno, int ctlrno)
6628e32b400SDavid du Colombier {
6638e32b400SDavid du Colombier 	Hci *hp;
6648e32b400SDavid du Colombier 	char *type;
6658e32b400SDavid du Colombier 	char name[64];
6668e32b400SDavid du Colombier 	static int epnb = 1;	/* guess the endpoint nb. for the controller */
6678e32b400SDavid du Colombier 
6688e32b400SDavid du Colombier 	ddprint("hciprobe %d %d\n", cardno, ctlrno);
669*305b51b8SDavid du Colombier 	hp = smalloc(sizeof(Hci));
6708e32b400SDavid du Colombier 	hp->ctlrno = ctlrno;
6718e32b400SDavid du Colombier 
672c8a340cdSDavid du Colombier 	if(cardno < 0)
6738e32b400SDavid du Colombier 		for(cardno = 0; cardno < Nhcis; cardno++){
6748e32b400SDavid du Colombier 			if(hcitypes[cardno].type == nil)
6758e32b400SDavid du Colombier 				break;
6768e32b400SDavid du Colombier 			type = hp->type;
6778e32b400SDavid du Colombier 			if(type==nil || *type==0)
6788e32b400SDavid du Colombier 				type = "uhci";
6798e32b400SDavid du Colombier 			if(cistrcmp(hcitypes[cardno].type, type) == 0)
6808e32b400SDavid du Colombier 				break;
6818e32b400SDavid du Colombier 		}
6828e32b400SDavid du Colombier 
6838e32b400SDavid du Colombier 	if(cardno >= Nhcis || hcitypes[cardno].type == nil){
6848e32b400SDavid du Colombier 		free(hp);
6858e32b400SDavid du Colombier 		return nil;
6868e32b400SDavid du Colombier 	}
6878e32b400SDavid du Colombier 	dprint("%s...", hcitypes[cardno].type);
6888e32b400SDavid du Colombier 	if(hcitypes[cardno].reset(hp) < 0){
6898e32b400SDavid du Colombier 		free(hp);
6908e32b400SDavid du Colombier 		return nil;
6918e32b400SDavid du Colombier 	}
6928e32b400SDavid du Colombier 
6938e32b400SDavid du Colombier 	snprint(name, sizeof(name), "usb%s", hcitypes[cardno].type);
6948e32b400SDavid du Colombier 	intrenable(hp->irq, hp->interrupt, hp, UNKNOWN, name);
6958e32b400SDavid du Colombier 
696fe25a076SDavid du Colombier 	print("#u/usb/ep%d.0: %s: port %#luX irq %d\n",
6978e32b400SDavid du Colombier 		epnb, hcitypes[cardno].type, hp->port, hp->irq);
6988e32b400SDavid du Colombier 	epnb++;
6998e32b400SDavid du Colombier 
7008e32b400SDavid du Colombier 	return hp;
7018e32b400SDavid du Colombier }
7028e32b400SDavid du Colombier 
7038e32b400SDavid du Colombier static void
7048e32b400SDavid du Colombier usbreset(void)
7058e32b400SDavid du Colombier {
7068e32b400SDavid du Colombier 	int cardno, ctlrno;
7078e32b400SDavid du Colombier 	Hci *hp;
7088e32b400SDavid du Colombier 
7098e32b400SDavid du Colombier 	dprint("usbreset\n");
7108e32b400SDavid du Colombier 
7118e32b400SDavid du Colombier 	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++)
7128e32b400SDavid du Colombier 		if((hp = hciprobe(-1, ctlrno)) != nil)
7138e32b400SDavid du Colombier 			hcis[ctlrno] = hp;
7148e32b400SDavid du Colombier 	cardno = ctlrno = 0;
7158e32b400SDavid du Colombier 	while(cardno < Nhcis && ctlrno < Nhcis && hcitypes[cardno].type != nil)
7168e32b400SDavid du Colombier 		if(hcis[ctlrno] != nil)
7178e32b400SDavid du Colombier 			ctlrno++;
7188e32b400SDavid du Colombier 		else{
7198e32b400SDavid du Colombier 			hp = hciprobe(cardno, ctlrno);
7208e32b400SDavid du Colombier 			if(hp == nil)
7218e32b400SDavid du Colombier 				cardno++;
7228e32b400SDavid du Colombier 			hcis[ctlrno++] = hp;
7238e32b400SDavid du Colombier 		}
7248e32b400SDavid du Colombier 	if(hcis[Nhcis-1] != nil)
7258e32b400SDavid du Colombier 		print("usbreset: bug: Nhcis too small\n");
7268e32b400SDavid du Colombier }
7278e32b400SDavid du Colombier 
7288e32b400SDavid du Colombier static void
7298e32b400SDavid du Colombier usbinit(void)
7308e32b400SDavid du Colombier {
7318e32b400SDavid du Colombier 	Hci *hp;
7328e32b400SDavid du Colombier 	int ctlrno;
7338e32b400SDavid du Colombier 	Ep *d;
7348e32b400SDavid du Colombier 	char info[40];
7358e32b400SDavid du Colombier 
7368e32b400SDavid du Colombier 	dprint("usbinit\n");
7378e32b400SDavid du Colombier 	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){
7388e32b400SDavid du Colombier 		hp = hcis[ctlrno];
7398e32b400SDavid du Colombier 		if(hp != nil){
7408e32b400SDavid du Colombier 			if(hp->init != nil)
7418e32b400SDavid du Colombier 				hp->init(hp);
7428e32b400SDavid du Colombier 			d = newdev(hp, 1, 1);		/* new root hub */
7438e32b400SDavid du Colombier 			d->dev->state = Denabled;	/* although addr == 0 */
7448e32b400SDavid du Colombier 			d->maxpkt = 64;
7458e32b400SDavid du Colombier 			snprint(info, sizeof(info), "ports %d", hp->nports);
7468e32b400SDavid du Colombier 			kstrdup(&d->info, info);
7478e32b400SDavid du Colombier 		}
7488e32b400SDavid du Colombier 	}
7498e32b400SDavid du Colombier }
7508e32b400SDavid du Colombier 
7518e32b400SDavid du Colombier static Chan*
7528e32b400SDavid du Colombier usbattach(char *spec)
7538e32b400SDavid du Colombier {
7548e32b400SDavid du Colombier 	return devattach(L'u', spec);
7558e32b400SDavid du Colombier }
7568e32b400SDavid du Colombier 
7578e32b400SDavid du Colombier static Walkqid*
7588e32b400SDavid du Colombier usbwalk(Chan *c, Chan *nc, char **name, int nname)
7598e32b400SDavid du Colombier {
7608e32b400SDavid du Colombier 	return devwalk(c, nc, name, nname, nil, 0, usbgen);
7618e32b400SDavid du Colombier }
7628e32b400SDavid du Colombier 
7638e32b400SDavid du Colombier static int
7648e32b400SDavid du Colombier usbstat(Chan *c, uchar *db, int n)
7658e32b400SDavid du Colombier {
7668e32b400SDavid du Colombier 	return devstat(c, db, n, nil, 0, usbgen);
7678e32b400SDavid du Colombier }
7688e32b400SDavid du Colombier 
7698e32b400SDavid du Colombier /*
7708e32b400SDavid du Colombier  * µs for the given transfer, for bandwidth allocation.
7718e32b400SDavid du Colombier  * This is a very rough worst case for what 5.11.3
7728e32b400SDavid du Colombier  * of the usb 2.0 spec says.
7738e32b400SDavid du Colombier  * Also, we are using maxpkt and not actual transfer sizes.
7748e32b400SDavid du Colombier  * Only when we are sure we
7758e32b400SDavid du Colombier  * are not exceeding b/w might we consider adjusting it.
7768e32b400SDavid du Colombier  */
7778e32b400SDavid du Colombier static ulong
7788e32b400SDavid du Colombier usbload(int speed, int maxpkt)
7798e32b400SDavid du Colombier {
7808e32b400SDavid du Colombier 	enum{ Hostns = 1000, Hubns = 333 };
7818e32b400SDavid du Colombier 	ulong l;
7828e32b400SDavid du Colombier 	ulong bs;
7838e32b400SDavid du Colombier 
7848e32b400SDavid du Colombier 	l = 0;
7858e32b400SDavid du Colombier 	bs = 10UL * maxpkt;
7868e32b400SDavid du Colombier 	switch(speed){
7878e32b400SDavid du Colombier 	case Highspeed:
7888e32b400SDavid du Colombier 		l = 55*8*2 + 2 * (3 + bs) + Hostns;
7898e32b400SDavid du Colombier 		break;
7908e32b400SDavid du Colombier 	case Fullspeed:
7918e32b400SDavid du Colombier 		l = 9107 + 84 * (4 + bs) + Hostns;
7928e32b400SDavid du Colombier 		break;
7938e32b400SDavid du Colombier 	case Lowspeed:
7948e32b400SDavid du Colombier 		l = 64107 + 2 * Hubns + 667 * (3 + bs) + Hostns;
7958e32b400SDavid du Colombier 		break;
7968e32b400SDavid du Colombier 	default:
7978e32b400SDavid du Colombier 		print("usbload: bad speed %d\n", speed);
7988e32b400SDavid du Colombier 		/* let it run */
7998e32b400SDavid du Colombier 	}
8008e32b400SDavid du Colombier 	return l / 1000UL;	/* in µs */
8018e32b400SDavid du Colombier }
8028e32b400SDavid du Colombier 
8038e32b400SDavid du Colombier static Chan*
8048e32b400SDavid du Colombier usbopen(Chan *c, int omode)
8058e32b400SDavid du Colombier {
8068e32b400SDavid du Colombier 	int q;
8078e32b400SDavid du Colombier 	Ep *ep;
8088e32b400SDavid du Colombier 	int mode;
8098e32b400SDavid du Colombier 
8108e32b400SDavid du Colombier 	mode = openmode(omode);
8118e32b400SDavid du Colombier 	q = QID(c->qid);
8128e32b400SDavid du Colombier 
8138e32b400SDavid du Colombier 	if(q >= Qep0dir && qid2epidx(q) < 0)
8148e32b400SDavid du Colombier 		error(Eio);
8158e32b400SDavid du Colombier 	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
8168e32b400SDavid du Colombier 		return devopen(c, omode, nil, 0, usbgen);
8178e32b400SDavid du Colombier 
8188e32b400SDavid du Colombier 	ep = getep(qid2epidx(q));
8198e32b400SDavid du Colombier 	if(ep == nil)
8208e32b400SDavid du Colombier 		error(Eio);
8218e32b400SDavid du Colombier 	deprint("usbopen q %#x fid %d omode %d\n", q, c->fid, mode);
8228e32b400SDavid du Colombier 	if(waserror()){
8238e32b400SDavid du Colombier 		putep(ep);
8248e32b400SDavid du Colombier 		nexterror();
8258e32b400SDavid du Colombier 	}
8268e32b400SDavid du Colombier 	qlock(ep);
8278e32b400SDavid du Colombier 	if(ep->inuse){
8288e32b400SDavid du Colombier 		qunlock(ep);
8298e32b400SDavid du Colombier 		error(Einuse);
8308e32b400SDavid du Colombier 	}
8318e32b400SDavid du Colombier 	ep->inuse = 1;
8328e32b400SDavid du Colombier 	qunlock(ep);
8338e32b400SDavid du Colombier 	if(waserror()){
8348e32b400SDavid du Colombier 		ep->inuse = 0;
8358e32b400SDavid du Colombier 		nexterror();
8368e32b400SDavid du Colombier 	}
8378e32b400SDavid du Colombier 	if(mode != OREAD && ep->mode == OREAD)
8388e32b400SDavid du Colombier 		error(Eperm);
8398e32b400SDavid du Colombier 	if(mode != OWRITE && ep->mode == OWRITE)
8408e32b400SDavid du Colombier 		error(Eperm);
8418e32b400SDavid du Colombier 	if(ep->ttype == Tnone)
8428e32b400SDavid du Colombier 		error(Enotconf);
8438e32b400SDavid du Colombier 	ep->clrhalt = 0;
8448e32b400SDavid du Colombier 	ep->rhrepl = -1;
8458e32b400SDavid du Colombier 	if(ep->load == 0)
8468e32b400SDavid du Colombier 		ep->load = usbload(ep->dev->speed, ep->maxpkt);
8478e32b400SDavid du Colombier 	ep->hp->epopen(ep);
8488e32b400SDavid du Colombier 
8498e32b400SDavid du Colombier 	poperror();	/* ep->inuse */
8508e32b400SDavid du Colombier 	poperror();	/* don't putep(): ref kept for fid using the ep. */
8518e32b400SDavid du Colombier 
8528e32b400SDavid du Colombier 	c->mode = mode;
8538e32b400SDavid du Colombier 	c->flag |= COPEN;
8548e32b400SDavid du Colombier 	c->offset = 0;
8558e32b400SDavid du Colombier 	c->aux = nil;	/* paranoia */
8568e32b400SDavid du Colombier 	return c;
8578e32b400SDavid du Colombier }
8588e32b400SDavid du Colombier 
8598e32b400SDavid du Colombier static void
8608e32b400SDavid du Colombier epclose(Ep *ep)
8618e32b400SDavid du Colombier {
8628e32b400SDavid du Colombier 	qlock(ep);
8638e32b400SDavid du Colombier 	if(waserror()){
8648e32b400SDavid du Colombier 		qunlock(ep);
8658e32b400SDavid du Colombier 		nexterror();
8668e32b400SDavid du Colombier 	}
8678e32b400SDavid du Colombier 	if(ep->inuse){
8688e32b400SDavid du Colombier 		ep->hp->epclose(ep);
8698e32b400SDavid du Colombier 		ep->inuse = 0;
8708e32b400SDavid du Colombier 	}
8718e32b400SDavid du Colombier 	qunlock(ep);
8728e32b400SDavid du Colombier 	poperror();
8738e32b400SDavid du Colombier }
8748e32b400SDavid du Colombier 
8758e32b400SDavid du Colombier static void
8768e32b400SDavid du Colombier usbclose(Chan *c)
8778e32b400SDavid du Colombier {
8788e32b400SDavid du Colombier 	int q;
8798e32b400SDavid du Colombier 	Ep *ep;
8808e32b400SDavid du Colombier 
8818e32b400SDavid du Colombier 	q = QID(c->qid);
8828e32b400SDavid du Colombier 	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
8838e32b400SDavid du Colombier 		return;
8848e32b400SDavid du Colombier 
8858e32b400SDavid du Colombier 	ep = getep(qid2epidx(q));
8868e32b400SDavid du Colombier 	if(ep == nil)
8878e32b400SDavid du Colombier 		return;
8888e32b400SDavid du Colombier 	deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref);
8898e32b400SDavid du Colombier 	if(waserror()){
8908e32b400SDavid du Colombier 		putep(ep);
8918e32b400SDavid du Colombier 		nexterror();
8928e32b400SDavid du Colombier 	}
8938e32b400SDavid du Colombier 	if(c->flag & COPEN){
8948e32b400SDavid du Colombier 		free(c->aux);
8958e32b400SDavid du Colombier 		c->aux = nil;
8968e32b400SDavid du Colombier 		epclose(ep);
8978e32b400SDavid du Colombier 		putep(ep);	/* release ref kept since usbopen */
8988e32b400SDavid du Colombier 		c->flag &= ~COPEN;
8998e32b400SDavid du Colombier 	}
9008e32b400SDavid du Colombier 	poperror();
9018e32b400SDavid du Colombier 	putep(ep);
9028e32b400SDavid du Colombier }
9038e32b400SDavid du Colombier 
9048e32b400SDavid du Colombier static long
9058e32b400SDavid du Colombier ctlread(Chan *c, void *a, long n, vlong offset)
9068e32b400SDavid du Colombier {
9078e32b400SDavid du Colombier 	int q;
9088e32b400SDavid du Colombier 	char *s;
9098e32b400SDavid du Colombier 	char *us;
9108e32b400SDavid du Colombier 	char *se;
9118e32b400SDavid du Colombier 	Ep *ep;
9128e32b400SDavid du Colombier 	int i;
9138e32b400SDavid du Colombier 
9148e32b400SDavid du Colombier 	q = QID(c->qid);
9158e32b400SDavid du Colombier 	us = s = smalloc(READSTR);
9168e32b400SDavid du Colombier 	se = s + READSTR;
9178e32b400SDavid du Colombier 	if(waserror()){
9188e32b400SDavid du Colombier 		free(us);
9198e32b400SDavid du Colombier 		nexterror();
9208e32b400SDavid du Colombier 	}
9218e32b400SDavid du Colombier 	if(q == Qctl)
9228e32b400SDavid du Colombier 		for(i = 0; i < epmax; i++){
9238e32b400SDavid du Colombier 			ep = getep(i);
9248e32b400SDavid du Colombier 			if(ep != nil){
9258e32b400SDavid du Colombier 				if(waserror()){
9268e32b400SDavid du Colombier 					putep(ep);
9278e32b400SDavid du Colombier 					nexterror();
9288e32b400SDavid du Colombier 				}
9298e32b400SDavid du Colombier 				s = seprint(s, se, "ep%d.%d ", ep->dev->nb, ep->nb);
9308e32b400SDavid du Colombier 				s = seprintep(s, se, ep, 0);
9318e32b400SDavid du Colombier 				poperror();
9328e32b400SDavid du Colombier 			}
9338e32b400SDavid du Colombier 			putep(ep);
9348e32b400SDavid du Colombier 		}
9358e32b400SDavid du Colombier 	else{
9368e32b400SDavid du Colombier 		ep = getep(qid2epidx(q));
9378e32b400SDavid du Colombier 		if(ep == nil)
9388e32b400SDavid du Colombier 			error(Eio);
9398e32b400SDavid du Colombier 		if(waserror()){
9408e32b400SDavid du Colombier 			putep(ep);
9418e32b400SDavid du Colombier 			nexterror();
9428e32b400SDavid du Colombier 		}
9438e32b400SDavid du Colombier 		if(c->aux != nil){
9448e32b400SDavid du Colombier 			/* After a new endpoint request we read
9458e32b400SDavid du Colombier 			 * the new endpoint name back.
9468e32b400SDavid du Colombier 			 */
9478e32b400SDavid du Colombier 			strecpy(s, se, c->aux);
9488e32b400SDavid du Colombier 			free(c->aux);
9498e32b400SDavid du Colombier 			c->aux = nil;
9508e32b400SDavid du Colombier 		}else
9518e32b400SDavid du Colombier 			seprintep(s, se, ep, 0);
9528e32b400SDavid du Colombier 		poperror();
9538e32b400SDavid du Colombier 		putep(ep);
9548e32b400SDavid du Colombier 	}
9558e32b400SDavid du Colombier 	n = readstr(offset, a, n, us);
9568e32b400SDavid du Colombier 	poperror();
9578e32b400SDavid du Colombier 	free(us);
9588e32b400SDavid du Colombier 	return n;
9598e32b400SDavid du Colombier }
9608e32b400SDavid du Colombier 
9618e32b400SDavid du Colombier /*
9628e32b400SDavid du Colombier  * Fake root hub emulation.
9638e32b400SDavid du Colombier  */
9648e32b400SDavid du Colombier static long
9658e32b400SDavid du Colombier rhubread(Ep *ep, void *a, long n)
9668e32b400SDavid du Colombier {
9678e32b400SDavid du Colombier 	char *b;
9688e32b400SDavid du Colombier 
9698e32b400SDavid du Colombier 	if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2)
9708e32b400SDavid du Colombier 		return -1;
9718e32b400SDavid du Colombier 	if(ep->rhrepl < 0)
9728e32b400SDavid du Colombier 		return -1;
9738e32b400SDavid du Colombier 
9748e32b400SDavid du Colombier 	b = a;
9758e32b400SDavid du Colombier 	memset(b, 0, n);
9768e32b400SDavid du Colombier 	PUT2(b, ep->rhrepl);
9778e32b400SDavid du Colombier 	ep->rhrepl = -1;
9788e32b400SDavid du Colombier 	return n;
9798e32b400SDavid du Colombier }
9808e32b400SDavid du Colombier 
9818e32b400SDavid du Colombier static long
9828e32b400SDavid du Colombier rhubwrite(Ep *ep, void *a, long n)
9838e32b400SDavid du Colombier {
9848e32b400SDavid du Colombier 	uchar *s;
9858e32b400SDavid du Colombier 	int cmd;
9868e32b400SDavid du Colombier 	int feature;
9878e32b400SDavid du Colombier 	int port;
9888e32b400SDavid du Colombier 	Hci *hp;
9898e32b400SDavid du Colombier 
9908e32b400SDavid du Colombier 	if(ep->dev == nil || ep->dev->isroot == 0 || ep->nb != 0)
9918e32b400SDavid du Colombier 		return -1;
9928e32b400SDavid du Colombier 	if(n != Rsetuplen)
9938e32b400SDavid du Colombier 		error("root hub is a toy hub");
9948e32b400SDavid du Colombier 	ep->rhrepl = -1;
9958e32b400SDavid du Colombier 	s = a;
9968e32b400SDavid du Colombier 	if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother))
9978e32b400SDavid du Colombier 		error("root hub is a toy hub");
9988e32b400SDavid du Colombier 	hp = ep->hp;
9998e32b400SDavid du Colombier 	cmd = s[Rreq];
10008e32b400SDavid du Colombier 	feature = GET2(s+Rvalue);
10018e32b400SDavid du Colombier 	port = GET2(s+Rindex);
10028e32b400SDavid du Colombier 	if(port < 1 || port > hp->nports)
10038e32b400SDavid du Colombier 		error("bad hub port number");
10048e32b400SDavid du Colombier 	switch(feature){
10058e32b400SDavid du Colombier 	case Rportenable:
10068e32b400SDavid du Colombier 		ep->rhrepl = hp->portenable(hp, port, cmd == Rsetfeature);
10078e32b400SDavid du Colombier 		break;
10088e32b400SDavid du Colombier 	case Rportreset:
10098e32b400SDavid du Colombier 		ep->rhrepl = hp->portreset(hp, port, cmd == Rsetfeature);
10108e32b400SDavid du Colombier 		break;
10118e32b400SDavid du Colombier 	case Rgetstatus:
10128e32b400SDavid du Colombier 		ep->rhrepl = hp->portstatus(hp, port);
10138e32b400SDavid du Colombier 		break;
10148e32b400SDavid du Colombier 	default:
10158e32b400SDavid du Colombier 		ep->rhrepl = 0;
10168e32b400SDavid du Colombier 	}
10178e32b400SDavid du Colombier 	return n;
10188e32b400SDavid du Colombier }
10198e32b400SDavid du Colombier 
10208e32b400SDavid du Colombier static long
10218e32b400SDavid du Colombier usbread(Chan *c, void *a, long n, vlong offset)
10228e32b400SDavid du Colombier {
10238e32b400SDavid du Colombier 	int q;
10248e32b400SDavid du Colombier 	Ep *ep;
10258e32b400SDavid du Colombier 	int nr;
10268e32b400SDavid du Colombier 
10278e32b400SDavid du Colombier 	q = QID(c->qid);
10288e32b400SDavid du Colombier 
10298e32b400SDavid du Colombier 	if(c->qid.type == QTDIR)
10308e32b400SDavid du Colombier 		return devdirread(c, a, n, nil, 0, usbgen);
10318e32b400SDavid du Colombier 
10328e32b400SDavid du Colombier 	if(q == Qctl || isqtype(q, Qepctl))
10338e32b400SDavid du Colombier 		return ctlread(c, a, n, offset);
10348e32b400SDavid du Colombier 
10358e32b400SDavid du Colombier 	ep = getep(qid2epidx(q));
10368e32b400SDavid du Colombier 	if(ep == nil)
10378e32b400SDavid du Colombier 		error(Eio);
10388e32b400SDavid du Colombier 	if(waserror()){
10398e32b400SDavid du Colombier 		putep(ep);
10408e32b400SDavid du Colombier 		nexterror();
10418e32b400SDavid du Colombier 	}
10428e32b400SDavid du Colombier 	if(ep->dev->state == Ddetach)
10438e32b400SDavid du Colombier 		error(Edetach);
10448e32b400SDavid du Colombier 	if(ep->mode == OWRITE || ep->inuse == 0)
10458e32b400SDavid du Colombier 		error(Ebadusefd);
10468e32b400SDavid du Colombier 	switch(ep->ttype){
10478e32b400SDavid du Colombier 	case Tnone:
10488e32b400SDavid du Colombier 		error("endpoint not configured");
10498e32b400SDavid du Colombier 	case Tctl:
10508e32b400SDavid du Colombier 		nr = rhubread(ep, a, n);
10518e32b400SDavid du Colombier 		if(nr >= 0){
10528e32b400SDavid du Colombier 			n = nr;
10538e32b400SDavid du Colombier 			break;
10548e32b400SDavid du Colombier 		}
10558e32b400SDavid du Colombier 		/* else fall */
10568e32b400SDavid du Colombier 	default:
10578e32b400SDavid du Colombier 		ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",q,c->fid,n,offset);
10588e32b400SDavid du Colombier 		n = ep->hp->epread(ep, a, n);
10598e32b400SDavid du Colombier 		break;
10608e32b400SDavid du Colombier 	}
10618e32b400SDavid du Colombier 	poperror();
10628e32b400SDavid du Colombier 	putep(ep);
10638e32b400SDavid du Colombier 	return n;
10648e32b400SDavid du Colombier }
10658e32b400SDavid du Colombier 
10668e32b400SDavid du Colombier static long
10678e32b400SDavid du Colombier pow2(int n)
10688e32b400SDavid du Colombier {
1069add6b5c5SDavid du Colombier 	return 1 << n;
10708e32b400SDavid du Colombier }
10718e32b400SDavid du Colombier 
10728e32b400SDavid du Colombier static void
10738e32b400SDavid du Colombier setmaxpkt(Ep *ep, char* s)
10748e32b400SDavid du Colombier {
10758e32b400SDavid du Colombier 	long spp;	/* samples per packet */
10768e32b400SDavid du Colombier 
10778e32b400SDavid du Colombier 	if(ep->dev->speed == Highspeed)
10788e32b400SDavid du Colombier 		spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000;
10798e32b400SDavid du Colombier 	else
10808e32b400SDavid du Colombier 		spp = (ep->hz * ep->pollival + 999) / 1000;
10818e32b400SDavid du Colombier 	ep->maxpkt = spp * ep->samplesz;
10828e32b400SDavid du Colombier 	deprint("usb: %s: setmaxpkt: hz %ld poll %ld"
10838e32b400SDavid du Colombier 		" ntds %d %s speed -> spp %ld maxpkt %ld\n", s,
10848e32b400SDavid du Colombier 		ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed],
10858e32b400SDavid du Colombier 		spp, ep->maxpkt);
10868e32b400SDavid du Colombier 	if(ep->maxpkt > 1024){
10878e32b400SDavid du Colombier 		print("usb: %s: maxpkt %ld > 1024. truncating\n", s, ep->maxpkt);
10888e32b400SDavid du Colombier 		ep->maxpkt = 1024;
10898e32b400SDavid du Colombier 	}
10908e32b400SDavid du Colombier }
10918e32b400SDavid du Colombier 
10928e32b400SDavid du Colombier /*
10938e32b400SDavid du Colombier  * Many endpoint ctls. simply update the portable representation
10948e32b400SDavid du Colombier  * of the endpoint. The actual controller driver will look
10958e32b400SDavid du Colombier  * at them to setup the endpoints as dictated.
10968e32b400SDavid du Colombier  */
10978e32b400SDavid du Colombier static long
10988e32b400SDavid du Colombier epctl(Ep *ep, Chan *c, void *a, long n)
10998e32b400SDavid du Colombier {
1100add6b5c5SDavid du Colombier 	int i, l, mode, nb, tt;
1101add6b5c5SDavid du Colombier 	char *b, *s;
1102add6b5c5SDavid du Colombier 	Cmdbuf *cb;
1103add6b5c5SDavid du Colombier 	Cmdtab *ct;
11048e32b400SDavid du Colombier 	Ep *nep;
11058e32b400SDavid du Colombier 	Udev *d;
1106add6b5c5SDavid du Colombier 	static char *Info = "info ";
11078e32b400SDavid du Colombier 
11088e32b400SDavid du Colombier 	d = ep->dev;
11098e32b400SDavid du Colombier 
11108e32b400SDavid du Colombier 	cb = parsecmd(a, n);
11118e32b400SDavid du Colombier 	if(waserror()){
11128e32b400SDavid du Colombier 		free(cb);
11138e32b400SDavid du Colombier 		nexterror();
11148e32b400SDavid du Colombier 	}
11158e32b400SDavid du Colombier 	ct = lookupcmd(cb, epctls, nelem(epctls));
11168e32b400SDavid du Colombier 	if(ct == nil)
11178e32b400SDavid du Colombier 		error(Ebadctl);
11188e32b400SDavid du Colombier 	i = ct->index;
11198e32b400SDavid du Colombier 	if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset)
11208e32b400SDavid du Colombier 		if(ep != ep->ep0)
11218e32b400SDavid du Colombier 			error("allowed only on a setup endpoint");
11228e32b400SDavid du Colombier 	if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname)
11238e32b400SDavid du Colombier 		if(ep != ep->ep0 && ep->inuse != 0)
11248e32b400SDavid du Colombier 			error("must configure before using");
11258e32b400SDavid du Colombier 	switch(i){
11268e32b400SDavid du Colombier 	case CMnew:
11278e32b400SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
11288e32b400SDavid du Colombier 		nb = strtol(cb->f[1], nil, 0);
11298e32b400SDavid du Colombier 		if(nb < 0 || nb >= Ndeveps)
11308e32b400SDavid du Colombier 			error("bad endpoint number");
11318e32b400SDavid du Colombier 		tt = name2ttype(cb->f[2]);
11328e32b400SDavid du Colombier 		if(tt == Tnone)
11338e32b400SDavid du Colombier 			error("unknown endpoint type");
11348e32b400SDavid du Colombier 		mode = name2mode(cb->f[3]);
11358e32b400SDavid du Colombier 		if(mode < 0)
11368e32b400SDavid du Colombier 			error("unknown i/o mode");
11378e32b400SDavid du Colombier 		newdevep(ep, nb, tt, mode);
11388e32b400SDavid du Colombier 		break;
11398e32b400SDavid du Colombier 	case CMnewdev:
11408e32b400SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
11418e32b400SDavid du Colombier 		if(ep != ep->ep0 || d->ishub == 0)
11428e32b400SDavid du Colombier 			error("not a hub setup endpoint");
11438e32b400SDavid du Colombier 		l = name2speed(cb->f[1]);
11448e32b400SDavid du Colombier 		if(l == Nospeed)
11458e32b400SDavid du Colombier 			error("speed must be full|low|high");
11468e32b400SDavid du Colombier 		nep = newdev(ep->hp, 0, 0);
11478e32b400SDavid du Colombier 		nep->dev->speed = l;
11488e32b400SDavid du Colombier 		if(nep->dev->speed  != Lowspeed)
11498e32b400SDavid du Colombier 			nep->maxpkt = 64;	/* assume full speed */
11508e32b400SDavid du Colombier 		nep->dev->hub = d->nb;
11518e32b400SDavid du Colombier 		nep->dev->port = atoi(cb->f[2]);
11528e32b400SDavid du Colombier 		/* next read request will read
11538e32b400SDavid du Colombier 		 * the name for the new endpoint
11548e32b400SDavid du Colombier 		 */
11558e32b400SDavid du Colombier 		l = sizeof(up->genbuf);
11568e32b400SDavid du Colombier 		snprint(up->genbuf, l, "ep%d.%d", nep->dev->nb, nep->nb);
11578e32b400SDavid du Colombier 		kstrdup(&c->aux, up->genbuf);
11588e32b400SDavid du Colombier 		break;
11598e32b400SDavid du Colombier 	case CMhub:
11608e32b400SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
11618e32b400SDavid du Colombier 		d->ishub = 1;
11628e32b400SDavid du Colombier 		break;
11638e32b400SDavid du Colombier 	case CMspeed:
11648e32b400SDavid du Colombier 		l = name2speed(cb->f[1]);
11658e32b400SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
11668e32b400SDavid du Colombier 		if(l == Nospeed)
11678e32b400SDavid du Colombier 			error("speed must be full|low|high");
11688e32b400SDavid du Colombier 		qlock(ep->ep0);
11698e32b400SDavid du Colombier 		d->speed = l;
11708e32b400SDavid du Colombier 		qunlock(ep->ep0);
11718e32b400SDavid du Colombier 		break;
11728e32b400SDavid du Colombier 	case CMmaxpkt:
11738e32b400SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
11748e32b400SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
11758e32b400SDavid du Colombier 		if(l < 1 || l > 1024)
11768e32b400SDavid du Colombier 			error("maxpkt not in [1:1024]");
11778e32b400SDavid du Colombier 		qlock(ep);
11788e32b400SDavid du Colombier 		ep->maxpkt = l;
11798e32b400SDavid du Colombier 		qunlock(ep);
11808e32b400SDavid du Colombier 		break;
11818e32b400SDavid du Colombier 	case CMntds:
11828e32b400SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
11838e32b400SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
11848e32b400SDavid du Colombier 		if(l < 1 || l > 3)
11858e32b400SDavid du Colombier 			error("ntds not in [1:3]");
11868e32b400SDavid du Colombier 		qlock(ep);
11878e32b400SDavid du Colombier 		ep->ntds = l;
11888e32b400SDavid du Colombier 		qunlock(ep);
11898e32b400SDavid du Colombier 		break;
11908e32b400SDavid du Colombier 	case CMpollival:
11918e32b400SDavid du Colombier 		if(ep->ttype != Tintr && ep->ttype != Tiso)
11928e32b400SDavid du Colombier 			error("not an intr or iso endpoint");
11938e32b400SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
11948e32b400SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
11958e32b400SDavid du Colombier 		if(ep->ttype == Tiso ||
11968e32b400SDavid du Colombier 		   (ep->ttype == Tintr && ep->dev->speed == Highspeed)){
11978e32b400SDavid du Colombier 			if(l < 1 || l > 16)
11988e32b400SDavid du Colombier 				error("pollival power not in [1:16]");
11998e32b400SDavid du Colombier 			l = pow2(l-1);
12008e32b400SDavid du Colombier 		}else
12018e32b400SDavid du Colombier 			if(l < 1 || l > 255)
12028e32b400SDavid du Colombier 				error("pollival not in [1:255]");
12038e32b400SDavid du Colombier 		qlock(ep);
12048e32b400SDavid du Colombier 		ep->pollival = l;
12058e32b400SDavid du Colombier 		if(ep->ttype == Tiso)
12068e32b400SDavid du Colombier 			setmaxpkt(ep, "pollival");
12078e32b400SDavid du Colombier 		qunlock(ep);
12088e32b400SDavid du Colombier 		break;
12098e32b400SDavid du Colombier 	case CMsamplesz:
12108e32b400SDavid du Colombier 		if(ep->ttype != Tiso)
12118e32b400SDavid du Colombier 			error("not an iso endpoint");
12128e32b400SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
12138e32b400SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
12148e32b400SDavid du Colombier 		if(l <= 0 || l > 8)
12158e32b400SDavid du Colombier 			error("samplesz not in [1:8]");
12168e32b400SDavid du Colombier 		qlock(ep);
12178e32b400SDavid du Colombier 		ep->samplesz = l;
12188e32b400SDavid du Colombier 		setmaxpkt(ep, "samplesz");
12198e32b400SDavid du Colombier 		qunlock(ep);
12208e32b400SDavid du Colombier 		break;
12218e32b400SDavid du Colombier 	case CMhz:
12228e32b400SDavid du Colombier 		if(ep->ttype != Tiso)
12238e32b400SDavid du Colombier 			error("not an iso endpoint");
12248e32b400SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
12258e32b400SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
12268e32b400SDavid du Colombier 		if(l <= 0 || l > 100000)
12278e32b400SDavid du Colombier 			error("hz not in [1:100000]");
12288e32b400SDavid du Colombier 		qlock(ep);
12298e32b400SDavid du Colombier 		ep->hz = l;
12308e32b400SDavid du Colombier 		setmaxpkt(ep, "hz");
12318e32b400SDavid du Colombier 		qunlock(ep);
12328e32b400SDavid du Colombier 		break;
12338e32b400SDavid du Colombier 	case CMclrhalt:
12348e32b400SDavid du Colombier 		qlock(ep);
12358e32b400SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
12368e32b400SDavid du Colombier 		ep->clrhalt = 1;
12378e32b400SDavid du Colombier 		qunlock(ep);
12388e32b400SDavid du Colombier 		break;
12398e32b400SDavid du Colombier 	case CMinfo:
12408e32b400SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
12418e32b400SDavid du Colombier 		l = strlen(Info);
12428e32b400SDavid du Colombier 		s = a;
12438e32b400SDavid du Colombier 		if(n < l+2 || strncmp(Info, s, l) != 0)
12448e32b400SDavid du Colombier 			error(Ebadctl);
12458e32b400SDavid du Colombier 		if(n > 1024)
12468e32b400SDavid du Colombier 			n = 1024;
12478e32b400SDavid du Colombier 		b = smalloc(n);
12488e32b400SDavid du Colombier 		memmove(b, s+l, n-l);
12498e32b400SDavid du Colombier 		b[n-l] = 0;
12508e32b400SDavid du Colombier 		if(b[n-l-1] == '\n')
12518e32b400SDavid du Colombier 			b[n-l-1] = 0;
12528e32b400SDavid du Colombier 		qlock(ep);
12538e32b400SDavid du Colombier 		free(ep->info);
12548e32b400SDavid du Colombier 		ep->info = b;
12558e32b400SDavid du Colombier 		qunlock(ep);
12568e32b400SDavid du Colombier 		break;
12578e32b400SDavid du Colombier 	case CMaddress:
12588e32b400SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
12598e32b400SDavid du Colombier 		ep->dev->state = Denabled;
12608e32b400SDavid du Colombier 		break;
12618e32b400SDavid du Colombier 	case CMdetach:
12628e32b400SDavid du Colombier 		if(ep->dev->isroot != 0)
12638e32b400SDavid du Colombier 			error("can't detach a root hub");
12648e32b400SDavid du Colombier 		deprint("usb epctl %s ep%d.%d\n",
12658e32b400SDavid du Colombier 			cb->f[0], ep->dev->nb, ep->nb);
12668e32b400SDavid du Colombier 		ep->dev->state = Ddetach;
12678e32b400SDavid du Colombier 		/* Release file system ref. for its endpoints */
12688e32b400SDavid du Colombier 		for(i = 0; i < nelem(ep->dev->eps); i++)
12698e32b400SDavid du Colombier 			putep(ep->dev->eps[i]);
12708e32b400SDavid du Colombier 		break;
12718e32b400SDavid du Colombier 	case CMdebugep:
12728e32b400SDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0)
12738e32b400SDavid du Colombier 			ep->debug = 1;
12748e32b400SDavid du Colombier 		else if(strcmp(cb->f[1], "off") == 0)
12758e32b400SDavid du Colombier 			ep->debug = 0;
12768e32b400SDavid du Colombier 		else
12778e32b400SDavid du Colombier 			ep->debug = strtoul(cb->f[1], nil, 0);
12788e32b400SDavid du Colombier 		print("usb: ep%d.%d debug %d\n",
12798e32b400SDavid du Colombier 			ep->dev->nb, ep->nb, ep->debug);
12808e32b400SDavid du Colombier 		break;
12818e32b400SDavid du Colombier 	case CMname:
12828e32b400SDavid du Colombier 		deprint("usb epctl %s %s\n", cb->f[0], cb->f[1]);
12838e32b400SDavid du Colombier 		validname(cb->f[1], 0);
12848e32b400SDavid du Colombier 		kstrdup(&ep->name, cb->f[1]);
12858e32b400SDavid du Colombier 		break;
12868e32b400SDavid du Colombier 	case CMtmout:
12878e32b400SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
12888e32b400SDavid du Colombier 		if(ep->ttype == Tiso || ep->ttype == Tctl)
12898e32b400SDavid du Colombier 			error("ctl ignored for this endpoint type");
12908e32b400SDavid du Colombier 		ep->tmout = strtoul(cb->f[1], nil, 0);
12918e32b400SDavid du Colombier 		if(ep->tmout != 0 && ep->tmout < Xfertmout)
12928e32b400SDavid du Colombier 			ep->tmout = Xfertmout;
12938e32b400SDavid du Colombier 		break;
12948e32b400SDavid du Colombier 	case CMpreset:
12958e32b400SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
12968e32b400SDavid du Colombier 		if(ep->ttype != Tctl)
12978e32b400SDavid du Colombier 			error("not a control endpoint");
12988e32b400SDavid du Colombier 		if(ep->dev->state != Denabled)
12998e32b400SDavid du Colombier 			error("forbidden on devices not enabled");
13008e32b400SDavid du Colombier 		ep->dev->state = Dreset;
13018e32b400SDavid du Colombier 		break;
13028e32b400SDavid du Colombier 	default:
13038e32b400SDavid du Colombier 		panic("usb: unknown epctl %d", ct->index);
13048e32b400SDavid du Colombier 	}
13058e32b400SDavid du Colombier 	free(cb);
13068e32b400SDavid du Colombier 	poperror();
13078e32b400SDavid du Colombier 	return n;
13088e32b400SDavid du Colombier }
13098e32b400SDavid du Colombier 
13108e32b400SDavid du Colombier static long
13118e32b400SDavid du Colombier usbctl(void *a, long n)
13128e32b400SDavid du Colombier {
13138e32b400SDavid du Colombier 	Cmdtab *ct;
13148e32b400SDavid du Colombier 	Cmdbuf *cb;
13158e32b400SDavid du Colombier 	Ep *ep;
13168e32b400SDavid du Colombier 	int i;
13178e32b400SDavid du Colombier 
13188e32b400SDavid du Colombier 	cb = parsecmd(a, n);
13198e32b400SDavid du Colombier 	if(waserror()){
13208e32b400SDavid du Colombier 		free(cb);
13218e32b400SDavid du Colombier 		nexterror();
13228e32b400SDavid du Colombier 	}
13238e32b400SDavid du Colombier 	ct = lookupcmd(cb, usbctls, nelem(usbctls));
13248e32b400SDavid du Colombier 	dprint("usb ctl %s\n", cb->f[0]);
13258e32b400SDavid du Colombier 	switch(ct->index){
13268e32b400SDavid du Colombier 	case CMdebug:
13278e32b400SDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0)
13288e32b400SDavid du Colombier 			debug = 1;
13298e32b400SDavid du Colombier 		else if(strcmp(cb->f[1], "off") == 0)
13308e32b400SDavid du Colombier 			debug = 0;
13318e32b400SDavid du Colombier 		else
13328e32b400SDavid du Colombier 			debug = strtol(cb->f[1], nil, 0);
13338e32b400SDavid du Colombier 		print("usb: debug %d\n", debug);
13348e32b400SDavid du Colombier 		for(i = 0; i < epmax; i++)
13358e32b400SDavid du Colombier 			if((ep = getep(i)) != nil){
13368e32b400SDavid du Colombier 				ep->hp->debug(ep->hp, debug);
13378e32b400SDavid du Colombier 				putep(ep);
13388e32b400SDavid du Colombier 			}
13398e32b400SDavid du Colombier 		break;
13408e32b400SDavid du Colombier 	case CMdump:
13418e32b400SDavid du Colombier 		dumpeps();
13428e32b400SDavid du Colombier 		break;
13438e32b400SDavid du Colombier 	}
13448e32b400SDavid du Colombier 	free(cb);
13458e32b400SDavid du Colombier 	poperror();
13468e32b400SDavid du Colombier 	return n;
13478e32b400SDavid du Colombier }
13488e32b400SDavid du Colombier 
13498e32b400SDavid du Colombier static long
13508e32b400SDavid du Colombier ctlwrite(Chan *c, void *a, long n)
13518e32b400SDavid du Colombier {
13528e32b400SDavid du Colombier 	int q;
13538e32b400SDavid du Colombier 	Ep *ep;
13548e32b400SDavid du Colombier 
13558e32b400SDavid du Colombier 	q = QID(c->qid);
13568e32b400SDavid du Colombier 	if(q == Qctl)
13578e32b400SDavid du Colombier 		return usbctl(a, n);
13588e32b400SDavid du Colombier 
13598e32b400SDavid du Colombier 	ep = getep(qid2epidx(q));
13608e32b400SDavid du Colombier 	if(ep == nil)
13618e32b400SDavid du Colombier 		error(Eio);
13628e32b400SDavid du Colombier 	if(waserror()){
13638e32b400SDavid du Colombier 		putep(ep);
13648e32b400SDavid du Colombier 		nexterror();
13658e32b400SDavid du Colombier 	}
13668e32b400SDavid du Colombier 	if(ep->dev->state == Ddetach)
13678e32b400SDavid du Colombier 		error(Edetach);
13688e32b400SDavid du Colombier 	if(isqtype(q, Qepctl) && c->aux != nil){
13698e32b400SDavid du Colombier 		/* Be sure we don't keep a cloned ep name */
13708e32b400SDavid du Colombier 		free(c->aux);
13718e32b400SDavid du Colombier 		c->aux = nil;
13728e32b400SDavid du Colombier 		error("read, not write, expected");
13738e32b400SDavid du Colombier 	}
13748e32b400SDavid du Colombier 	n = epctl(ep, c, a, n);
13758e32b400SDavid du Colombier 	putep(ep);
13768e32b400SDavid du Colombier 	poperror();
13778e32b400SDavid du Colombier 	return n;
13788e32b400SDavid du Colombier }
13798e32b400SDavid du Colombier 
13808e32b400SDavid du Colombier static long
13818e32b400SDavid du Colombier usbwrite(Chan *c, void *a, long n, vlong off)
13828e32b400SDavid du Colombier {
1383add6b5c5SDavid du Colombier 	int nr, q;
13848e32b400SDavid du Colombier 	Ep *ep;
13858e32b400SDavid du Colombier 
13868e32b400SDavid du Colombier 	if(c->qid.type == QTDIR)
13878e32b400SDavid du Colombier 		error(Eisdir);
13888e32b400SDavid du Colombier 
13898e32b400SDavid du Colombier 	q = QID(c->qid);
13908e32b400SDavid du Colombier 
13918e32b400SDavid du Colombier 	if(q == Qctl || isqtype(q, Qepctl))
13928e32b400SDavid du Colombier 		return ctlwrite(c, a, n);
13938e32b400SDavid du Colombier 
13948e32b400SDavid du Colombier 	ep = getep(qid2epidx(q));
13958e32b400SDavid du Colombier 	if(ep == nil)
13968e32b400SDavid du Colombier 		error(Eio);
13978e32b400SDavid du Colombier 	if(waserror()){
13988e32b400SDavid du Colombier 		putep(ep);
13998e32b400SDavid du Colombier 		nexterror();
14008e32b400SDavid du Colombier 	}
14018e32b400SDavid du Colombier 	if(ep->dev->state == Ddetach)
14028e32b400SDavid du Colombier 		error(Edetach);
14038e32b400SDavid du Colombier 	if(ep->mode == OREAD || ep->inuse == 0)
14048e32b400SDavid du Colombier 		error(Ebadusefd);
14058e32b400SDavid du Colombier 
14068e32b400SDavid du Colombier 	switch(ep->ttype){
14078e32b400SDavid du Colombier 	case Tnone:
14088e32b400SDavid du Colombier 		error("endpoint not configured");
14098e32b400SDavid du Colombier 	case Tctl:
14108e32b400SDavid du Colombier 		nr = rhubwrite(ep, a, n);
14118e32b400SDavid du Colombier 		if(nr >= 0){
14128e32b400SDavid du Colombier 			n = nr;
14138e32b400SDavid du Colombier 			break;
14148e32b400SDavid du Colombier 		}
14158e32b400SDavid du Colombier 		/* else fall */
14168e32b400SDavid du Colombier 	default:
14178e32b400SDavid du Colombier 		ddeprint("\nusbwrite q %#x fid %d cnt %ld off %lld\n",q, c->fid, n, off);
14188e32b400SDavid du Colombier 		ep->hp->epwrite(ep, a, n);
14198e32b400SDavid du Colombier 	}
14208e32b400SDavid du Colombier 	putep(ep);
14218e32b400SDavid du Colombier 	poperror();
14228e32b400SDavid du Colombier 	return n;
14238e32b400SDavid du Colombier }
14248e32b400SDavid du Colombier 
14258e32b400SDavid du Colombier void
14268e32b400SDavid du Colombier usbshutdown(void)
14278e32b400SDavid du Colombier {
14288e32b400SDavid du Colombier 	Hci *hp;
14298e32b400SDavid du Colombier 	int i;
14308e32b400SDavid du Colombier 
14318e32b400SDavid du Colombier 	for(i = 0; i < Nhcis; i++){
14328e32b400SDavid du Colombier 		hp = hcis[i];
14338e32b400SDavid du Colombier 		if(hp == nil)
14348e32b400SDavid du Colombier 			continue;
14358e32b400SDavid du Colombier 		if(hp->shutdown == nil)
14368e32b400SDavid du Colombier 			print("#u: no shutdown function for %s\n", hp->type);
14378e32b400SDavid du Colombier 		else
14388e32b400SDavid du Colombier 			hp->shutdown(hp);
14398e32b400SDavid du Colombier 	}
14408e32b400SDavid du Colombier }
14418e32b400SDavid du Colombier 
14428e32b400SDavid du Colombier Dev usbdevtab = {
14438e32b400SDavid du Colombier 	L'u',
14448e32b400SDavid du Colombier 	"usb",
14458e32b400SDavid du Colombier 
14468e32b400SDavid du Colombier 	usbreset,
14478e32b400SDavid du Colombier 	usbinit,
14488e32b400SDavid du Colombier 	usbshutdown,
14498e32b400SDavid du Colombier 	usbattach,
14508e32b400SDavid du Colombier 	usbwalk,
14518e32b400SDavid du Colombier 	usbstat,
14528e32b400SDavid du Colombier 	usbopen,
14538e32b400SDavid du Colombier 	devcreate,
14548e32b400SDavid du Colombier 	usbclose,
14558e32b400SDavid du Colombier 	usbread,
14568e32b400SDavid du Colombier 	devbread,
14578e32b400SDavid du Colombier 	usbwrite,
14588e32b400SDavid du Colombier 	devbwrite,
14598e32b400SDavid du Colombier 	devremove,
14608e32b400SDavid du Colombier 	devwstat,
14618e32b400SDavid du Colombier };
1462