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