xref: /plan9-contrib/sys/src/9/bcm/devusb.c (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
15d9de2d3SDavid du Colombier /*
25d9de2d3SDavid du Colombier  * USB device driver framework.
35d9de2d3SDavid du Colombier  *
45d9de2d3SDavid du Colombier  * This is in charge of providing access to actual HCIs
55d9de2d3SDavid du Colombier  * and providing I/O to the various endpoints of devices.
65d9de2d3SDavid du Colombier  * A separate user program (usbd) is in charge of
75d9de2d3SDavid du Colombier  * enumerating the bus, setting up endpoints and
85d9de2d3SDavid du Colombier  * starting devices (also user programs).
95d9de2d3SDavid du Colombier  *
105d9de2d3SDavid du Colombier  * The interface provided is a violation of the standard:
115d9de2d3SDavid du Colombier  * you're welcome.
125d9de2d3SDavid du Colombier  *
135d9de2d3SDavid du Colombier  * The interface consists of a root directory with several files
145d9de2d3SDavid du Colombier  * plus a directory (epN.M) with two files per endpoint.
155d9de2d3SDavid du Colombier  * A device is represented by its first endpoint, which
165d9de2d3SDavid du Colombier  * is a control endpoint automatically allocated for each device.
175d9de2d3SDavid du Colombier  * Device control endpoints may be used to create new endpoints.
185d9de2d3SDavid du Colombier  * Devices corresponding to hubs may also allocate new devices,
195d9de2d3SDavid du Colombier  * perhaps also hubs. Initially, a hub device is allocated for
205d9de2d3SDavid du Colombier  * each controller present, to represent its root hub. Those can
215d9de2d3SDavid du Colombier  * never be removed.
225d9de2d3SDavid du Colombier  *
235d9de2d3SDavid du Colombier  * All endpoints refer to the first endpoint (epN.0) of the device,
245d9de2d3SDavid du Colombier  * which keeps per-device information, and also to the HCI used
255d9de2d3SDavid du Colombier  * to reach them. Although all endpoints cache that information.
265d9de2d3SDavid du Colombier  *
275d9de2d3SDavid du Colombier  * epN.M/data files permit I/O and are considered DMEXCL.
285d9de2d3SDavid du Colombier  * epN.M/ctl files provide status info and accept control requests.
295d9de2d3SDavid du Colombier  *
305d9de2d3SDavid du Colombier  * Endpoints may be given file names to be listed also at #u,
315d9de2d3SDavid du Colombier  * for those drivers that have nothing to do after configuring the
325d9de2d3SDavid du Colombier  * device and its endpoints.
335d9de2d3SDavid du Colombier  *
345d9de2d3SDavid du Colombier  * Drivers for different controllers are kept at usb[oue]hci.c
355d9de2d3SDavid du Colombier  * It's likely we could factor out much from controllers into
365d9de2d3SDavid du Colombier  * a generic controller driver, the problem is that details
375d9de2d3SDavid du Colombier  * regarding how to handle toggles, tokens, Tds, etc. will
385d9de2d3SDavid du Colombier  * get in the way. Thus, code is probably easier the way it is.
395d9de2d3SDavid du Colombier  */
405d9de2d3SDavid du Colombier 
415d9de2d3SDavid du Colombier #include	"u.h"
425d9de2d3SDavid du Colombier #include	"../port/lib.h"
435d9de2d3SDavid du Colombier #include	"mem.h"
445d9de2d3SDavid du Colombier #include	"dat.h"
455d9de2d3SDavid du Colombier #include	"fns.h"
465d9de2d3SDavid du Colombier #include	"io.h"
475d9de2d3SDavid du Colombier #include	"../port/error.h"
48*5c47fe09SDavid du Colombier #include	"usb.h"
495d9de2d3SDavid du Colombier 
505d9de2d3SDavid du Colombier typedef struct Hcitype Hcitype;
515d9de2d3SDavid du Colombier 
525d9de2d3SDavid du Colombier enum
535d9de2d3SDavid du Colombier {
545d9de2d3SDavid du Colombier 	/* Qid numbers */
555d9de2d3SDavid du Colombier 	Qdir = 0,		/* #u */
565d9de2d3SDavid du Colombier 	Qusbdir,			/* #u/usb */
575d9de2d3SDavid du Colombier 	Qctl,			/* #u/usb/ctl - control requests */
585d9de2d3SDavid du Colombier 
595d9de2d3SDavid du Colombier 	Qep0dir,			/* #u/usb/ep0.0 - endpoint 0 dir */
605d9de2d3SDavid du Colombier 	Qep0io,			/* #u/usb/ep0.0/data - endpoint 0 I/O */
615d9de2d3SDavid du Colombier 	Qep0ctl,		/* #u/usb/ep0.0/ctl - endpoint 0 ctl. */
625d9de2d3SDavid du Colombier 	Qep0dummy,		/* give 4 qids to each endpoint */
635d9de2d3SDavid du Colombier 
645d9de2d3SDavid du Colombier 	Qepdir = 0,		/* (qid-qep0dir)&3 is one of these */
655d9de2d3SDavid du Colombier 	Qepio,			/* to identify which file for the endpoint */
665d9de2d3SDavid du Colombier 	Qepctl,
675d9de2d3SDavid du Colombier 
685d9de2d3SDavid du Colombier 	/* ... */
695d9de2d3SDavid du Colombier 
705d9de2d3SDavid du Colombier 	/* Usb ctls. */
715d9de2d3SDavid du Colombier 	CMdebug = 0,		/* debug on|off */
725d9de2d3SDavid du Colombier 	CMdump,			/* dump (data structures for debug) */
735d9de2d3SDavid du Colombier 
745d9de2d3SDavid du Colombier 	/* Ep. ctls */
755d9de2d3SDavid du Colombier 	CMnew = 0,		/* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */
76*5c47fe09SDavid du Colombier 	CMnewdev,		/* newdev full|low|high|super portnb (allocate new devices) */
775d9de2d3SDavid du Colombier 	CMhub,			/* hub (set the device as a hub) */
785d9de2d3SDavid du Colombier 	CMspeed,		/* speed full|low|high|no */
795d9de2d3SDavid du Colombier 	CMmaxpkt,		/* maxpkt size */
805d9de2d3SDavid du Colombier 	CMntds,			/* ntds nb (max nb. of tds per µframe) */
815d9de2d3SDavid du Colombier 	CMclrhalt,		/* clrhalt (halt was cleared on endpoint) */
825d9de2d3SDavid du Colombier 	CMpollival,		/* pollival interval (interrupt/iso) */
835d9de2d3SDavid du Colombier 	CMhz,			/* hz n (samples/sec; iso) */
845d9de2d3SDavid du Colombier 	CMsamplesz,		/* samplesz n (sample size; iso) */
855d9de2d3SDavid du Colombier 	CMinfo,			/* info infostr (ke.ep info for humans) */
865d9de2d3SDavid du Colombier 	CMdetach,		/* detach (abort I/O forever on this ep). */
875d9de2d3SDavid du Colombier 	CMaddress,		/* address (address is assigned) */
885d9de2d3SDavid du Colombier 	CMdebugep,		/* debug n (set/clear debug for this ep) */
895d9de2d3SDavid du Colombier 	CMname,			/* name str (show up as #u/name as well) */
905d9de2d3SDavid du Colombier 	CMtmout,		/* timeout n (activate timeouts for ep) */
915d9de2d3SDavid du Colombier 	CMpreset,		/* reset the port */
925d9de2d3SDavid du Colombier 
935d9de2d3SDavid du Colombier 	/* Hub feature selectors */
945d9de2d3SDavid du Colombier 	Rportenable	= 1,
955d9de2d3SDavid du Colombier 	Rportreset	= 4,
965d9de2d3SDavid du Colombier 
975d9de2d3SDavid du Colombier };
985d9de2d3SDavid du Colombier 
995d9de2d3SDavid du Colombier struct Hcitype
1005d9de2d3SDavid du Colombier {
1015d9de2d3SDavid du Colombier 	char*	type;
1025d9de2d3SDavid du Colombier 	int	(*reset)(Hci*);
1035d9de2d3SDavid du Colombier };
1045d9de2d3SDavid du Colombier 
1055d9de2d3SDavid du Colombier #define QID(q)	((int)(q).path)
1065d9de2d3SDavid du Colombier 
1075d9de2d3SDavid du Colombier static Cmdtab usbctls[] =
1085d9de2d3SDavid du Colombier {
1095d9de2d3SDavid du Colombier 	{CMdebug,	"debug",	2},
1105d9de2d3SDavid du Colombier 	{CMdump,	"dump",		1},
1115d9de2d3SDavid du Colombier };
1125d9de2d3SDavid du Colombier 
1135d9de2d3SDavid du Colombier static Cmdtab epctls[] =
1145d9de2d3SDavid du Colombier {
1155d9de2d3SDavid du Colombier 	{CMnew,		"new",		4},
1165d9de2d3SDavid du Colombier 	{CMnewdev,	"newdev",	3},
1175d9de2d3SDavid du Colombier 	{CMhub,		"hub",		1},
1185d9de2d3SDavid du Colombier 	{CMspeed,	"speed",	2},
1195d9de2d3SDavid du Colombier 	{CMmaxpkt,	"maxpkt",	2},
1205d9de2d3SDavid du Colombier 	{CMntds,	"ntds",		2},
1215d9de2d3SDavid du Colombier 	{CMpollival,	"pollival",	2},
1225d9de2d3SDavid du Colombier 	{CMsamplesz,	"samplesz",	2},
1235d9de2d3SDavid du Colombier 	{CMhz,		"hz",		2},
1245d9de2d3SDavid du Colombier 	{CMinfo,	"info",		0},
1255d9de2d3SDavid du Colombier 	{CMdetach,	"detach",	1},
1265d9de2d3SDavid du Colombier 	{CMaddress,	"address",	1},
1275d9de2d3SDavid du Colombier 	{CMdebugep,	"debug",	2},
1285d9de2d3SDavid du Colombier 	{CMclrhalt,	"clrhalt",	1},
1295d9de2d3SDavid du Colombier 	{CMname,	"name",		2},
1305d9de2d3SDavid du Colombier 	{CMtmout,	"timeout",	2},
1315d9de2d3SDavid du Colombier 	{CMpreset,	"reset",	1},
1325d9de2d3SDavid du Colombier };
1335d9de2d3SDavid du Colombier 
1345d9de2d3SDavid du Colombier static Dirtab usbdir[] =
1355d9de2d3SDavid du Colombier {
1365d9de2d3SDavid du Colombier 	"ctl",		{Qctl},		0,	0666,
1375d9de2d3SDavid du Colombier };
1385d9de2d3SDavid du Colombier 
1395d9de2d3SDavid du Colombier char *usbmodename[] =
1405d9de2d3SDavid du Colombier {
1415d9de2d3SDavid du Colombier 	[OREAD]	"r",
1425d9de2d3SDavid du Colombier 	[OWRITE]	"w",
1435d9de2d3SDavid du Colombier 	[ORDWR]	"rw",
1445d9de2d3SDavid du Colombier };
1455d9de2d3SDavid du Colombier 
1465d9de2d3SDavid du Colombier static char *ttname[] =
1475d9de2d3SDavid du Colombier {
1485d9de2d3SDavid du Colombier 	[Tnone]	"none",
1495d9de2d3SDavid du Colombier 	[Tctl]	"control",
1505d9de2d3SDavid du Colombier 	[Tiso]	"iso",
1515d9de2d3SDavid du Colombier 	[Tintr]	"interrupt",
1525d9de2d3SDavid du Colombier 	[Tbulk]	"bulk",
1535d9de2d3SDavid du Colombier };
1545d9de2d3SDavid du Colombier 
1555d9de2d3SDavid du Colombier static char *spname[] =
1565d9de2d3SDavid du Colombier {
157*5c47fe09SDavid du Colombier 	[Superspeed]	"super",
1585d9de2d3SDavid du Colombier 	[Fullspeed]	"full",
1595d9de2d3SDavid du Colombier 	[Lowspeed]	"low",
1605d9de2d3SDavid du Colombier 	[Highspeed]	"high",
1615d9de2d3SDavid du Colombier 	[Nospeed]	"no",
1625d9de2d3SDavid du Colombier };
1635d9de2d3SDavid du Colombier 
1645d9de2d3SDavid du Colombier static int	debug;
1655d9de2d3SDavid du Colombier static Hcitype	hcitypes[Nhcis];
1665d9de2d3SDavid du Colombier static Hci*	hcis[Nhcis];
1675d9de2d3SDavid du Colombier static QLock	epslck;		/* add, del, lookup endpoints */
1685d9de2d3SDavid du Colombier static Ep*	eps[Neps];	/* all endpoints known */
1695d9de2d3SDavid du Colombier static int	epmax;		/* 1 + last endpoint index used  */
1705d9de2d3SDavid du Colombier static int	usbidgen;	/* device address generator */
1715d9de2d3SDavid du Colombier 
1725d9de2d3SDavid du Colombier /*
1735d9de2d3SDavid du Colombier  * Is there something like this in a library? should it be?
1745d9de2d3SDavid du Colombier  */
1755d9de2d3SDavid du Colombier char*
seprintdata(char * s,char * se,uchar * d,int n)1765d9de2d3SDavid du Colombier seprintdata(char *s, char *se, uchar *d, int n)
1775d9de2d3SDavid du Colombier {
1785d9de2d3SDavid du Colombier 	int i, l;
1795d9de2d3SDavid du Colombier 
1805d9de2d3SDavid du Colombier 	s = seprint(s, se, " %#p[%d]: ", d, n);
1815d9de2d3SDavid du Colombier 	l = n;
1825d9de2d3SDavid du Colombier 	if(l > 10)
1835d9de2d3SDavid du Colombier 		l = 10;
1845d9de2d3SDavid du Colombier 	for(i=0; i<l; i++)
1855d9de2d3SDavid du Colombier 		s = seprint(s, se, " %2.2ux", d[i]);
1865d9de2d3SDavid du Colombier 	if(l < n)
1875d9de2d3SDavid du Colombier 		s = seprint(s, se, "...");
1885d9de2d3SDavid du Colombier 	return s;
1895d9de2d3SDavid du Colombier }
1905d9de2d3SDavid du Colombier 
1915d9de2d3SDavid du Colombier static int
name2speed(char * name)1925d9de2d3SDavid du Colombier name2speed(char *name)
1935d9de2d3SDavid du Colombier {
1945d9de2d3SDavid du Colombier 	int i;
1955d9de2d3SDavid du Colombier 
1965d9de2d3SDavid du Colombier 	for(i = 0; i < nelem(spname); i++)
1975d9de2d3SDavid du Colombier 		if(strcmp(name, spname[i]) == 0)
1985d9de2d3SDavid du Colombier 			return i;
1995d9de2d3SDavid du Colombier 	return Nospeed;
2005d9de2d3SDavid du Colombier }
2015d9de2d3SDavid du Colombier 
2025d9de2d3SDavid du Colombier static int
name2ttype(char * name)2035d9de2d3SDavid du Colombier name2ttype(char *name)
2045d9de2d3SDavid du Colombier {
2055d9de2d3SDavid du Colombier 	int i;
2065d9de2d3SDavid du Colombier 
2075d9de2d3SDavid du Colombier 	for(i = 0; i < nelem(ttname); i++)
2085d9de2d3SDavid du Colombier 		if(strcmp(name, ttname[i]) == 0)
2095d9de2d3SDavid du Colombier 			return i;
2105d9de2d3SDavid du Colombier 	/* may be a std. USB ep. type */
2115d9de2d3SDavid du Colombier 	i = strtol(name, nil, 0);
2125d9de2d3SDavid du Colombier 	switch(i+1){
2135d9de2d3SDavid du Colombier 	case Tctl:
2145d9de2d3SDavid du Colombier 	case Tiso:
2155d9de2d3SDavid du Colombier 	case Tbulk:
2165d9de2d3SDavid du Colombier 	case Tintr:
2175d9de2d3SDavid du Colombier 		return i+1;
2185d9de2d3SDavid du Colombier 	default:
2195d9de2d3SDavid du Colombier 		return Tnone;
2205d9de2d3SDavid du Colombier 	}
2215d9de2d3SDavid du Colombier }
2225d9de2d3SDavid du Colombier 
2235d9de2d3SDavid du Colombier static int
name2mode(char * mode)2245d9de2d3SDavid du Colombier name2mode(char *mode)
2255d9de2d3SDavid du Colombier {
2265d9de2d3SDavid du Colombier 	int i;
2275d9de2d3SDavid du Colombier 
2285d9de2d3SDavid du Colombier 	for(i = 0; i < nelem(usbmodename); i++)
2295d9de2d3SDavid du Colombier 		if(strcmp(mode, usbmodename[i]) == 0)
2305d9de2d3SDavid du Colombier 			return i;
2315d9de2d3SDavid du Colombier 	return -1;
2325d9de2d3SDavid du Colombier }
2335d9de2d3SDavid du Colombier 
2345d9de2d3SDavid du Colombier static int
qid2epidx(int q)2355d9de2d3SDavid du Colombier qid2epidx(int q)
2365d9de2d3SDavid du Colombier {
2375d9de2d3SDavid du Colombier 	q = (q-Qep0dir)/4;
2385d9de2d3SDavid du Colombier 	if(q < 0 || q >= epmax || eps[q] == nil)
2395d9de2d3SDavid du Colombier 		return -1;
2405d9de2d3SDavid du Colombier 	return q;
2415d9de2d3SDavid du Colombier }
2425d9de2d3SDavid du Colombier 
2435d9de2d3SDavid du Colombier static int
isqtype(int q,int type)2445d9de2d3SDavid du Colombier isqtype(int q, int type)
2455d9de2d3SDavid du Colombier {
2465d9de2d3SDavid du Colombier 	if(q < Qep0dir)
2475d9de2d3SDavid du Colombier 		return 0;
2485d9de2d3SDavid du Colombier 	q -= Qep0dir;
2495d9de2d3SDavid du Colombier 	return (q & 3) == type;
2505d9de2d3SDavid du Colombier }
2515d9de2d3SDavid du Colombier 
2525d9de2d3SDavid du Colombier void
addhcitype(char * t,int (* r)(Hci *))2535d9de2d3SDavid du Colombier addhcitype(char* t, int (*r)(Hci*))
2545d9de2d3SDavid du Colombier {
2555d9de2d3SDavid du Colombier 	static int ntype;
2565d9de2d3SDavid du Colombier 
2575d9de2d3SDavid du Colombier 	if(ntype == Nhcis)
2585d9de2d3SDavid du Colombier 		panic("too many USB host interface types");
2595d9de2d3SDavid du Colombier 	hcitypes[ntype].type = t;
2605d9de2d3SDavid du Colombier 	hcitypes[ntype].reset = r;
2615d9de2d3SDavid du Colombier 	ntype++;
2625d9de2d3SDavid du Colombier }
2635d9de2d3SDavid du Colombier 
2645d9de2d3SDavid du Colombier static char*
seprintep(char * s,char * se,Ep * ep,int all)2655d9de2d3SDavid du Colombier seprintep(char *s, char *se, Ep *ep, int all)
2665d9de2d3SDavid du Colombier {
2675d9de2d3SDavid du Colombier 	static char* dsnames[] = { "config", "enabled", "detached", "reset" };
268b4d1cf41SDavid du Colombier 	Udev *d;
269b4d1cf41SDavid du Colombier 	int i;
270b4d1cf41SDavid du Colombier 	int di;
2715d9de2d3SDavid du Colombier 
2725d9de2d3SDavid du Colombier 	d = ep->dev;
2735d9de2d3SDavid du Colombier 
2745d9de2d3SDavid du Colombier 	qlock(ep);
2755d9de2d3SDavid du Colombier 	if(waserror()){
2765d9de2d3SDavid du Colombier 		qunlock(ep);
2775d9de2d3SDavid du Colombier 		nexterror();
2785d9de2d3SDavid du Colombier 	}
2795d9de2d3SDavid du Colombier 	di = ep->dev->nb;
2805d9de2d3SDavid du Colombier 	if(all)
2815d9de2d3SDavid du Colombier 		s = seprint(s, se, "dev %d ep %d ", di, ep->nb);
2825d9de2d3SDavid du Colombier 	s = seprint(s, se, "%s", dsnames[ep->dev->state]);
2835d9de2d3SDavid du Colombier 	s = seprint(s, se, " %s", ttname[ep->ttype]);
2845d9de2d3SDavid du Colombier 	assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR);
2855d9de2d3SDavid du Colombier 	s = seprint(s, se, " %s", usbmodename[ep->mode]);
2865d9de2d3SDavid du Colombier 	s = seprint(s, se, " speed %s", spname[d->speed]);
2875d9de2d3SDavid du Colombier 	s = seprint(s, se, " maxpkt %ld", ep->maxpkt);
2885d9de2d3SDavid du Colombier 	s = seprint(s, se, " pollival %ld", ep->pollival);
2895d9de2d3SDavid du Colombier 	s = seprint(s, se, " samplesz %ld", ep->samplesz);
2905d9de2d3SDavid du Colombier 	s = seprint(s, se, " hz %ld", ep->hz);
2915d9de2d3SDavid du Colombier 	s = seprint(s, se, " hub %d", ep->dev->hub);
2925d9de2d3SDavid du Colombier 	s = seprint(s, se, " port %d", ep->dev->port);
293*5c47fe09SDavid du Colombier 	s = seprint(s, se, " rootport %d", ep->dev->rootport);
294*5c47fe09SDavid du Colombier 	s = seprint(s, se, " addr %d", ep->dev->addr);
2955d9de2d3SDavid du Colombier 	if(ep->inuse)
2965d9de2d3SDavid du Colombier 		s = seprint(s, se, " busy");
2975d9de2d3SDavid du Colombier 	else
2985d9de2d3SDavid du Colombier 		s = seprint(s, se, " idle");
2995d9de2d3SDavid du Colombier 	if(all){
3005d9de2d3SDavid du Colombier 		s = seprint(s, se, " load %uld", ep->load);
3015d9de2d3SDavid du Colombier 		s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep);
3025d9de2d3SDavid du Colombier 		s = seprint(s, se, " idx %d", ep->idx);
3035d9de2d3SDavid du Colombier 		if(ep->name != nil)
3045d9de2d3SDavid du Colombier 			s = seprint(s, se, " name '%s'", ep->name);
3055d9de2d3SDavid du Colombier 		if(ep->tmout != 0)
3065d9de2d3SDavid du Colombier 			s = seprint(s, se, " tmout");
3075d9de2d3SDavid du Colombier 		if(ep == ep->ep0){
3085d9de2d3SDavid du Colombier 			s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno);
3095d9de2d3SDavid du Colombier 			s = seprint(s, se, " eps:");
3105d9de2d3SDavid du Colombier 			for(i = 0; i < nelem(d->eps); i++)
3115d9de2d3SDavid du Colombier 				if(d->eps[i] != nil)
3125d9de2d3SDavid du Colombier 					s = seprint(s, se, " ep%d.%d", di, i);
3135d9de2d3SDavid du Colombier 		}
3145d9de2d3SDavid du Colombier 	}
3155d9de2d3SDavid du Colombier 	if(ep->info != nil)
3165d9de2d3SDavid du Colombier 		s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type);
3175d9de2d3SDavid du Colombier 	else
3185d9de2d3SDavid du Colombier 		s = seprint(s, se, "\n");
3195d9de2d3SDavid du Colombier 	qunlock(ep);
3205d9de2d3SDavid du Colombier 	poperror();
3215d9de2d3SDavid du Colombier 	return s;
3225d9de2d3SDavid du Colombier }
3235d9de2d3SDavid du Colombier 
3245d9de2d3SDavid du Colombier static Ep*
epalloc(Hci * hp)3255d9de2d3SDavid du Colombier epalloc(Hci *hp)
3265d9de2d3SDavid du Colombier {
3275d9de2d3SDavid du Colombier 	Ep *ep;
3285d9de2d3SDavid du Colombier 	int i;
3295d9de2d3SDavid du Colombier 
3305d9de2d3SDavid du Colombier 	ep = smalloc(sizeof(Ep));
3315d9de2d3SDavid du Colombier 	ep->ref = 1;
3325d9de2d3SDavid du Colombier 	qlock(&epslck);
3335d9de2d3SDavid du Colombier 	for(i = 0; i < Neps; i++)
3345d9de2d3SDavid du Colombier 		if(eps[i] == nil)
3355d9de2d3SDavid du Colombier 			break;
3365d9de2d3SDavid du Colombier 	if(i == Neps){
3375d9de2d3SDavid du Colombier 		qunlock(&epslck);
3385d9de2d3SDavid du Colombier 		free(ep);
3395d9de2d3SDavid du Colombier 		print("usb: bug: too few endpoints.\n");
3405d9de2d3SDavid du Colombier 		return nil;
3415d9de2d3SDavid du Colombier 	}
3425d9de2d3SDavid du Colombier 	ep->idx = i;
3435d9de2d3SDavid du Colombier 	if(epmax <= i)
3445d9de2d3SDavid du Colombier 		epmax = i+1;
3455d9de2d3SDavid du Colombier 	eps[i] = ep;
3465d9de2d3SDavid du Colombier 	ep->hp = hp;
3475d9de2d3SDavid du Colombier 	ep->maxpkt = 8;
3485d9de2d3SDavid du Colombier 	ep->ntds = 1;
3495d9de2d3SDavid du Colombier 	ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */
3505d9de2d3SDavid du Colombier 	qunlock(&epslck);
3515d9de2d3SDavid du Colombier 	return ep;
3525d9de2d3SDavid du Colombier }
3535d9de2d3SDavid du Colombier 
3545d9de2d3SDavid du Colombier static Ep*
getep(int i)3555d9de2d3SDavid du Colombier getep(int i)
3565d9de2d3SDavid du Colombier {
3575d9de2d3SDavid du Colombier 	Ep *ep;
3585d9de2d3SDavid du Colombier 
3595d9de2d3SDavid du Colombier 	if(i < 0 || i >= epmax || eps[i] == nil)
3605d9de2d3SDavid du Colombier 		return nil;
3615d9de2d3SDavid du Colombier 	qlock(&epslck);
3625d9de2d3SDavid du Colombier 	ep = eps[i];
3635d9de2d3SDavid du Colombier 	if(ep != nil)
3645d9de2d3SDavid du Colombier 		incref(ep);
3655d9de2d3SDavid du Colombier 	qunlock(&epslck);
3665d9de2d3SDavid du Colombier 	return ep;
3675d9de2d3SDavid du Colombier }
3685d9de2d3SDavid du Colombier 
3695d9de2d3SDavid du Colombier static void
putep(Ep * ep)3705d9de2d3SDavid du Colombier putep(Ep *ep)
3715d9de2d3SDavid du Colombier {
3725d9de2d3SDavid du Colombier 	Udev *d;
3735d9de2d3SDavid du Colombier 
3745d9de2d3SDavid du Colombier 	if(ep != nil && decref(ep) == 0){
3755d9de2d3SDavid du Colombier 		d = ep->dev;
3765d9de2d3SDavid du Colombier 		deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep);
3775d9de2d3SDavid du Colombier 		qlock(&epslck);
3785d9de2d3SDavid du Colombier 		eps[ep->idx] = nil;
3795d9de2d3SDavid du Colombier 		if(ep->idx == epmax-1)
3805d9de2d3SDavid du Colombier 			epmax--;
3815d9de2d3SDavid du Colombier 		if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen)
3825d9de2d3SDavid du Colombier 			usbidgen--;
3835d9de2d3SDavid du Colombier 		qunlock(&epslck);
3845d9de2d3SDavid du Colombier 		if(d != nil){
3855d9de2d3SDavid du Colombier 			qlock(ep->ep0);
3865d9de2d3SDavid du Colombier 			d->eps[ep->nb] = nil;
3875d9de2d3SDavid du Colombier 			qunlock(ep->ep0);
3885d9de2d3SDavid du Colombier 		}
3895d9de2d3SDavid du Colombier 		if(ep->ep0 != ep){
3905d9de2d3SDavid du Colombier 			putep(ep->ep0);
3915d9de2d3SDavid du Colombier 			ep->ep0 = nil;
3925d9de2d3SDavid du Colombier 		}
3935d9de2d3SDavid du Colombier 		free(ep->info);
3945d9de2d3SDavid du Colombier 		free(ep->name);
3955d9de2d3SDavid du Colombier 		free(ep);
3965d9de2d3SDavid du Colombier 	}
3975d9de2d3SDavid du Colombier }
3985d9de2d3SDavid du Colombier 
3995d9de2d3SDavid du Colombier static void
dumpeps(void)4005d9de2d3SDavid du Colombier dumpeps(void)
4015d9de2d3SDavid du Colombier {
4025d9de2d3SDavid du Colombier 	int i;
4035d9de2d3SDavid du Colombier 	static char buf[512];
404b4d1cf41SDavid du Colombier 	char *s;
405b4d1cf41SDavid du Colombier 	char *e;
406b4d1cf41SDavid du Colombier 	Ep *ep;
4075d9de2d3SDavid du Colombier 
4085d9de2d3SDavid du Colombier 	print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps);
4095d9de2d3SDavid du Colombier 	for(i = 0; i < epmax; i++){
4105d9de2d3SDavid du Colombier 		s = buf;
4115d9de2d3SDavid du Colombier 		e = buf+sizeof(buf);
4125d9de2d3SDavid du Colombier 		ep = getep(i);
4135d9de2d3SDavid du Colombier 		if(ep != nil){
4145d9de2d3SDavid du Colombier 			if(waserror()){
4155d9de2d3SDavid du Colombier 				putep(ep);
4165d9de2d3SDavid du Colombier 				nexterror();
4175d9de2d3SDavid du Colombier 			}
4185d9de2d3SDavid du Colombier 			s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb);
4195d9de2d3SDavid du Colombier 			seprintep(s, e, ep, 1);
4205d9de2d3SDavid du Colombier 			print("%s", buf);
4215d9de2d3SDavid du Colombier 			ep->hp->seprintep(buf, e, ep);
4225d9de2d3SDavid du Colombier 			print("%s", buf);
4235d9de2d3SDavid du Colombier 			poperror();
4245d9de2d3SDavid du Colombier 			putep(ep);
4255d9de2d3SDavid du Colombier 		}
4265d9de2d3SDavid du Colombier 	}
4275d9de2d3SDavid du Colombier 	print("usb dump hcis:\n");
4285d9de2d3SDavid du Colombier 	for(i = 0; i < Nhcis; i++)
4295d9de2d3SDavid du Colombier 		if(hcis[i] != nil)
4305d9de2d3SDavid du Colombier 			hcis[i]->dump(hcis[i]);
4315d9de2d3SDavid du Colombier }
4325d9de2d3SDavid du Colombier 
4335d9de2d3SDavid du Colombier static int
newusbid(Hci *)4345d9de2d3SDavid du Colombier newusbid(Hci *)
4355d9de2d3SDavid du Colombier {
4365d9de2d3SDavid du Colombier 	int id;
4375d9de2d3SDavid du Colombier 
4385d9de2d3SDavid du Colombier 	qlock(&epslck);
4395d9de2d3SDavid du Colombier 	id = ++usbidgen;
4405d9de2d3SDavid du Colombier 	if(id >= 0x7F)
4415d9de2d3SDavid du Colombier 		print("#u: too many device addresses; reuse them more\n");
4425d9de2d3SDavid du Colombier 	qunlock(&epslck);
4435d9de2d3SDavid du Colombier 	return id;
4445d9de2d3SDavid du Colombier }
4455d9de2d3SDavid du Colombier 
4465d9de2d3SDavid du Colombier /*
4475d9de2d3SDavid du Colombier  * Create endpoint 0 for a new device
4485d9de2d3SDavid du Colombier  */
4495d9de2d3SDavid du Colombier static Ep*
newdev(Hci * hp,int ishub,int isroot)4505d9de2d3SDavid du Colombier newdev(Hci *hp, int ishub, int isroot)
4515d9de2d3SDavid du Colombier {
4525d9de2d3SDavid du Colombier 	Ep *ep;
4535d9de2d3SDavid du Colombier 	Udev *d;
4545d9de2d3SDavid du Colombier 
4555d9de2d3SDavid du Colombier 	ep = epalloc(hp);
4565d9de2d3SDavid du Colombier 	d = ep->dev = smalloc(sizeof(Udev));
4575d9de2d3SDavid du Colombier 	d->nb = newusbid(hp);
458*5c47fe09SDavid du Colombier 	d->addr = 0;
4595d9de2d3SDavid du Colombier 	d->eps[0] = ep;
4605d9de2d3SDavid du Colombier 	ep->nb = 0;
4615d9de2d3SDavid du Colombier 	ep->toggle[0] = ep->toggle[1] = 0;
4625d9de2d3SDavid du Colombier 	d->ishub = ishub;
4635d9de2d3SDavid du Colombier 	d->isroot = isroot;
464*5c47fe09SDavid du Colombier 	d->rootport = 0;
465*5c47fe09SDavid du Colombier 	d->routestr = 0;
466*5c47fe09SDavid du Colombier 	d->depth = -1;
4675d9de2d3SDavid du Colombier 	d->speed = Fullspeed;
4685d9de2d3SDavid du Colombier 	d->state = Dconfig;		/* address not yet set */
4695d9de2d3SDavid du Colombier 	ep->dev = d;
4705d9de2d3SDavid du Colombier 	ep->ep0 = ep;			/* no ref counted here */
4715d9de2d3SDavid du Colombier 	ep->ttype = Tctl;
4725d9de2d3SDavid du Colombier 	ep->tmout = Xfertmout;
4735d9de2d3SDavid du Colombier 	ep->mode = ORDWR;
4745d9de2d3SDavid du Colombier 	dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep);
4755d9de2d3SDavid du Colombier 	return ep;
4765d9de2d3SDavid du Colombier }
4775d9de2d3SDavid du Colombier 
4785d9de2d3SDavid du Colombier /*
4795d9de2d3SDavid du Colombier  * Create a new endpoint for the device
4805d9de2d3SDavid du Colombier  * accessed via the given endpoint 0.
4815d9de2d3SDavid du Colombier  */
4825d9de2d3SDavid du Colombier static Ep*
newdevep(Ep * ep,int i,int tt,int mode)4835d9de2d3SDavid du Colombier newdevep(Ep *ep, int i, int tt, int mode)
4845d9de2d3SDavid du Colombier {
4855d9de2d3SDavid du Colombier 	Ep *nep;
4865d9de2d3SDavid du Colombier 	Udev *d;
4875d9de2d3SDavid du Colombier 
4885d9de2d3SDavid du Colombier 	d = ep->dev;
4895d9de2d3SDavid du Colombier 	if(d->eps[i] != nil)
4905d9de2d3SDavid du Colombier 		error("endpoint already in use");
4915d9de2d3SDavid du Colombier 	nep = epalloc(ep->hp);
4925d9de2d3SDavid du Colombier 	incref(ep);
4935d9de2d3SDavid du Colombier 	d->eps[i] = nep;
4945d9de2d3SDavid du Colombier 	nep->nb = i;
4955d9de2d3SDavid du Colombier 	nep->toggle[0] = nep->toggle[1] = 0;
4965d9de2d3SDavid du Colombier 	nep->ep0 = ep;
4975d9de2d3SDavid du Colombier 	nep->dev = ep->dev;
4985d9de2d3SDavid du Colombier 	nep->mode = mode;
4995d9de2d3SDavid du Colombier 	nep->ttype = tt;
5005d9de2d3SDavid du Colombier 	nep->debug = ep->debug;
5015d9de2d3SDavid du Colombier 	/* set defaults */
5025d9de2d3SDavid du Colombier 	switch(tt){
5035d9de2d3SDavid du Colombier 	case Tctl:
5045d9de2d3SDavid du Colombier 		nep->tmout = Xfertmout;
5055d9de2d3SDavid du Colombier 		break;
5065d9de2d3SDavid du Colombier 	case Tintr:
5075d9de2d3SDavid du Colombier 		nep->pollival = 10;
5085d9de2d3SDavid du Colombier 		break;
5095d9de2d3SDavid du Colombier 	case Tiso:
5105d9de2d3SDavid du Colombier 		nep->tmout = Xfertmout;
5115d9de2d3SDavid du Colombier 		nep->pollival = 10;
5125d9de2d3SDavid du Colombier 		nep->samplesz = 4;
5135d9de2d3SDavid du Colombier 		nep->hz = 44100;
5145d9de2d3SDavid du Colombier 		break;
5155d9de2d3SDavid du Colombier 	}
5165d9de2d3SDavid du Colombier 	deprint("newdevep ep%d.%d %#p\n", d->nb, nep->nb, nep);
5175d9de2d3SDavid du Colombier 	return ep;
5185d9de2d3SDavid du Colombier }
5195d9de2d3SDavid du Colombier 
5205d9de2d3SDavid du Colombier static int
epdataperm(int mode)5215d9de2d3SDavid du Colombier epdataperm(int mode)
5225d9de2d3SDavid du Colombier {
523b4d1cf41SDavid du Colombier 
5245d9de2d3SDavid du Colombier 	switch(mode){
5255d9de2d3SDavid du Colombier 	case OREAD:
5265d9de2d3SDavid du Colombier 		return 0440|DMEXCL;
5275d9de2d3SDavid du Colombier 		break;
5285d9de2d3SDavid du Colombier 	case OWRITE:
5295d9de2d3SDavid du Colombier 		return 0220|DMEXCL;
5305d9de2d3SDavid du Colombier 		break;
5315d9de2d3SDavid du Colombier 	default:
5325d9de2d3SDavid du Colombier 		return 0660|DMEXCL;
5335d9de2d3SDavid du Colombier 	}
5345d9de2d3SDavid du Colombier }
5355d9de2d3SDavid du Colombier 
5365d9de2d3SDavid du Colombier static int
usbgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)5375d9de2d3SDavid du Colombier usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
5385d9de2d3SDavid du Colombier {
5395d9de2d3SDavid du Colombier 	Qid q;
540b4d1cf41SDavid du Colombier 	Dirtab *dir;
541b4d1cf41SDavid du Colombier 	int perm;
542b4d1cf41SDavid du Colombier 	char *se;
543b4d1cf41SDavid du Colombier 	Ep *ep;
544b4d1cf41SDavid du Colombier 	int nb;
545b4d1cf41SDavid du Colombier 	int mode;
5465d9de2d3SDavid du Colombier 
5475d9de2d3SDavid du Colombier 	if(0)ddprint("usbgen q %#x s %d...", QID(c->qid), s);
5485d9de2d3SDavid du Colombier 	if(s == DEVDOTDOT){
5495d9de2d3SDavid du Colombier 		if(QID(c->qid) <= Qusbdir){
5505d9de2d3SDavid du Colombier 			mkqid(&q, Qdir, 0, QTDIR);
5515d9de2d3SDavid du Colombier 			devdir(c, q, "#u", 0, eve, 0555, dp);
5525d9de2d3SDavid du Colombier 		}else{
5535d9de2d3SDavid du Colombier 			mkqid(&q, Qusbdir, 0, QTDIR);
5545d9de2d3SDavid du Colombier 			devdir(c, q, "usb", 0, eve, 0555, dp);
5555d9de2d3SDavid du Colombier 		}
5565d9de2d3SDavid du Colombier 		if(0)ddprint("ok\n");
5575d9de2d3SDavid du Colombier 		return 1;
5585d9de2d3SDavid du Colombier 	}
5595d9de2d3SDavid du Colombier 
5605d9de2d3SDavid du Colombier 	switch(QID(c->qid)){
5615d9de2d3SDavid du Colombier 	case Qdir:				/* list #u */
5625d9de2d3SDavid du Colombier 		if(s == 0){
5635d9de2d3SDavid du Colombier 			mkqid(&q, Qusbdir, 0, QTDIR);
5645d9de2d3SDavid du Colombier 			devdir(c, q, "usb", 0, eve, 0555, dp);
5655d9de2d3SDavid du Colombier 			if(0)ddprint("ok\n");
5665d9de2d3SDavid du Colombier 			return 1;
5675d9de2d3SDavid du Colombier 		}
5685d9de2d3SDavid du Colombier 		s--;
5695d9de2d3SDavid du Colombier 		if(s < 0 || s >= epmax)
5705d9de2d3SDavid du Colombier 			goto Fail;
5715d9de2d3SDavid du Colombier 		ep = getep(s);
5725d9de2d3SDavid du Colombier 		if(ep == nil || ep->name == nil){
5735d9de2d3SDavid du Colombier 			if(ep != nil)
5745d9de2d3SDavid du Colombier 				putep(ep);
5755d9de2d3SDavid du Colombier 			if(0)ddprint("skip\n");
5765d9de2d3SDavid du Colombier 			return 0;
5775d9de2d3SDavid du Colombier 		}
5785d9de2d3SDavid du Colombier 		if(waserror()){
5795d9de2d3SDavid du Colombier 			putep(ep);
5805d9de2d3SDavid du Colombier 			nexterror();
5815d9de2d3SDavid du Colombier 		}
5825d9de2d3SDavid du Colombier 		mkqid(&q, Qep0io+s*4, 0, QTFILE);
5835d9de2d3SDavid du Colombier 		devdir(c, q, ep->name, 0, eve, epdataperm(ep->mode), dp);
5845d9de2d3SDavid du Colombier 		putep(ep);
5855d9de2d3SDavid du Colombier 		poperror();
5865d9de2d3SDavid du Colombier 		if(0)ddprint("ok\n");
5875d9de2d3SDavid du Colombier 		return 1;
5885d9de2d3SDavid du Colombier 
5895d9de2d3SDavid du Colombier 	case Qusbdir:				/* list #u/usb */
5905d9de2d3SDavid du Colombier 	Usbdir:
5915d9de2d3SDavid du Colombier 		if(s < nelem(usbdir)){
5925d9de2d3SDavid du Colombier 			dir = &usbdir[s];
5935d9de2d3SDavid du Colombier 			mkqid(&q, dir->qid.path, 0, QTFILE);
5945d9de2d3SDavid du Colombier 			devdir(c, q, dir->name, dir->length, eve, dir->perm, dp);
5955d9de2d3SDavid du Colombier 			if(0)ddprint("ok\n");
5965d9de2d3SDavid du Colombier 			return 1;
5975d9de2d3SDavid du Colombier 		}
5985d9de2d3SDavid du Colombier 		s -= nelem(usbdir);
5995d9de2d3SDavid du Colombier 		if(s < 0 || s >= epmax)
6005d9de2d3SDavid du Colombier 			goto Fail;
6015d9de2d3SDavid du Colombier 		ep = getep(s);
6025d9de2d3SDavid du Colombier 		if(ep == nil){
6035d9de2d3SDavid du Colombier 			if(0)ddprint("skip\n");
6045d9de2d3SDavid du Colombier 			return 0;
6055d9de2d3SDavid du Colombier 		}
6065d9de2d3SDavid du Colombier 		if(waserror()){
6075d9de2d3SDavid du Colombier 			putep(ep);
6085d9de2d3SDavid du Colombier 			nexterror();
6095d9de2d3SDavid du Colombier 		}
6105d9de2d3SDavid du Colombier 		se = up->genbuf+sizeof(up->genbuf);
6115d9de2d3SDavid du Colombier 		seprint(up->genbuf, se, "ep%d.%d", ep->dev->nb, ep->nb);
6125d9de2d3SDavid du Colombier 		mkqid(&q, Qep0dir+4*s, 0, QTDIR);
6135d9de2d3SDavid du Colombier 		putep(ep);
6145d9de2d3SDavid du Colombier 		poperror();
6155d9de2d3SDavid du Colombier 		devdir(c, q, up->genbuf, 0, eve, 0755, dp);
6165d9de2d3SDavid du Colombier 		if(0)ddprint("ok\n");
6175d9de2d3SDavid du Colombier 		return 1;
6185d9de2d3SDavid du Colombier 
6195d9de2d3SDavid du Colombier 	case Qctl:
6205d9de2d3SDavid du Colombier 		s = 0;
6215d9de2d3SDavid du Colombier 		goto Usbdir;
6225d9de2d3SDavid du Colombier 
6235d9de2d3SDavid du Colombier 	default:				/* list #u/usb/epN.M */
6245d9de2d3SDavid du Colombier 		nb = qid2epidx(QID(c->qid));
6255d9de2d3SDavid du Colombier 		ep = getep(nb);
6265d9de2d3SDavid du Colombier 		if(ep == nil)
6275d9de2d3SDavid du Colombier 			goto Fail;
6285d9de2d3SDavid du Colombier 		mode = ep->mode;
6295d9de2d3SDavid du Colombier 		putep(ep);
6305d9de2d3SDavid du Colombier 		if(isqtype(QID(c->qid), Qepdir)){
6315d9de2d3SDavid du Colombier 		Epdir:
6325d9de2d3SDavid du Colombier 			switch(s){
6335d9de2d3SDavid du Colombier 			case 0:
6345d9de2d3SDavid du Colombier 				mkqid(&q, Qep0io+nb*4, 0, QTFILE);
6355d9de2d3SDavid du Colombier 				perm = epdataperm(mode);
6365d9de2d3SDavid du Colombier 				devdir(c, q, "data", 0, eve, perm, dp);
6375d9de2d3SDavid du Colombier 				break;
6385d9de2d3SDavid du Colombier 			case 1:
6395d9de2d3SDavid du Colombier 				mkqid(&q, Qep0ctl+nb*4, 0, QTFILE);
6405d9de2d3SDavid du Colombier 				devdir(c, q, "ctl", 0, eve, 0664, dp);
6415d9de2d3SDavid du Colombier 				break;
6425d9de2d3SDavid du Colombier 			default:
6435d9de2d3SDavid du Colombier 				goto Fail;
6445d9de2d3SDavid du Colombier 			}
6455d9de2d3SDavid du Colombier 		}else if(isqtype(QID(c->qid), Qepctl)){
6465d9de2d3SDavid du Colombier 			s = 1;
6475d9de2d3SDavid du Colombier 			goto Epdir;
6485d9de2d3SDavid du Colombier 		}else{
6495d9de2d3SDavid du Colombier 			s = 0;
6505d9de2d3SDavid du Colombier 			goto Epdir;
6515d9de2d3SDavid du Colombier 		}
6525d9de2d3SDavid du Colombier 		if(0)ddprint("ok\n");
6535d9de2d3SDavid du Colombier 		return 1;
6545d9de2d3SDavid du Colombier 	}
6555d9de2d3SDavid du Colombier Fail:
6565d9de2d3SDavid du Colombier 	if(0)ddprint("fail\n");
6575d9de2d3SDavid du Colombier 	return -1;
6585d9de2d3SDavid du Colombier }
6595d9de2d3SDavid du Colombier 
6605d9de2d3SDavid du Colombier static Hci*
hciprobe(int cardno,int ctlrno)6615d9de2d3SDavid du Colombier hciprobe(int cardno, int ctlrno)
6625d9de2d3SDavid du Colombier {
6635d9de2d3SDavid du Colombier 	Hci *hp;
6645d9de2d3SDavid du Colombier 	char *type;
6655d9de2d3SDavid du Colombier 	char name[64];
6665d9de2d3SDavid du Colombier 	static int epnb = 1;	/* guess the endpoint nb. for the controller */
6675d9de2d3SDavid du Colombier 
6685d9de2d3SDavid du Colombier 	ddprint("hciprobe %d %d\n", cardno, ctlrno);
6695d9de2d3SDavid du Colombier 	hp = smalloc(sizeof(Hci));
6705d9de2d3SDavid du Colombier 	hp->ctlrno = ctlrno;
6715d9de2d3SDavid du Colombier 
6725d9de2d3SDavid du Colombier 	if(cardno < 0)
6735d9de2d3SDavid du Colombier 		for(cardno = 0; cardno < Nhcis; cardno++){
6745d9de2d3SDavid du Colombier 			if(hcitypes[cardno].type == nil)
6755d9de2d3SDavid du Colombier 				break;
6765d9de2d3SDavid du Colombier 			type = hp->type;
6775d9de2d3SDavid du Colombier 			if(type==nil || *type==0)
6785d9de2d3SDavid du Colombier 				type = "uhci";
6795d9de2d3SDavid du Colombier 			if(cistrcmp(hcitypes[cardno].type, type) == 0)
6805d9de2d3SDavid du Colombier 				break;
6815d9de2d3SDavid du Colombier 		}
6825d9de2d3SDavid du Colombier 
6835d9de2d3SDavid du Colombier 	if(cardno >= Nhcis || hcitypes[cardno].type == nil){
6845d9de2d3SDavid du Colombier 		free(hp);
6855d9de2d3SDavid du Colombier 		return nil;
6865d9de2d3SDavid du Colombier 	}
6875d9de2d3SDavid du Colombier 	dprint("%s...", hcitypes[cardno].type);
6885d9de2d3SDavid du Colombier 	if(hcitypes[cardno].reset(hp) < 0){
6895d9de2d3SDavid du Colombier 		free(hp);
6905d9de2d3SDavid du Colombier 		return nil;
6915d9de2d3SDavid du Colombier 	}
6925d9de2d3SDavid du Colombier 
6935d9de2d3SDavid du Colombier 	snprint(name, sizeof(name), "usb%s", hcitypes[cardno].type);
6945d9de2d3SDavid du Colombier 	intrenable(hp->irq, hp->interrupt, hp, UNKNOWN, name);
6955d9de2d3SDavid du Colombier 
6965d9de2d3SDavid du Colombier 	print("#u/usb/ep%d.0: %s: port %#luX irq %d\n",
6975d9de2d3SDavid du Colombier 		epnb, hcitypes[cardno].type, hp->port, hp->irq);
6985d9de2d3SDavid du Colombier 	epnb++;
6995d9de2d3SDavid du Colombier 
7005d9de2d3SDavid du Colombier 	return hp;
7015d9de2d3SDavid du Colombier }
7025d9de2d3SDavid du Colombier 
7035d9de2d3SDavid du Colombier static void
usbreset(void)7045d9de2d3SDavid du Colombier usbreset(void)
7055d9de2d3SDavid du Colombier {
7065d9de2d3SDavid du Colombier 	int cardno, ctlrno;
7075d9de2d3SDavid du Colombier 	Hci *hp;
7085d9de2d3SDavid du Colombier 
7095d9de2d3SDavid du Colombier 	dprint("usbreset\n");
7105d9de2d3SDavid du Colombier 
7115d9de2d3SDavid du Colombier 	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++)
7125d9de2d3SDavid du Colombier 		if((hp = hciprobe(-1, ctlrno)) != nil)
7135d9de2d3SDavid du Colombier 			hcis[ctlrno] = hp;
7145d9de2d3SDavid du Colombier 	cardno = ctlrno = 0;
7155d9de2d3SDavid du Colombier 	while(cardno < Nhcis && ctlrno < Nhcis && hcitypes[cardno].type != nil)
7165d9de2d3SDavid du Colombier 		if(hcis[ctlrno] != nil)
7175d9de2d3SDavid du Colombier 			ctlrno++;
7185d9de2d3SDavid du Colombier 		else{
7195d9de2d3SDavid du Colombier 			hp = hciprobe(cardno, ctlrno);
7205d9de2d3SDavid du Colombier 			if(hp == nil)
7215d9de2d3SDavid du Colombier 				cardno++;
7225d9de2d3SDavid du Colombier 			hcis[ctlrno++] = hp;
7235d9de2d3SDavid du Colombier 		}
7245d9de2d3SDavid du Colombier 	if(hcis[Nhcis-1] != nil)
7255d9de2d3SDavid du Colombier 		print("usbreset: bug: Nhcis too small\n");
7265d9de2d3SDavid du Colombier }
7275d9de2d3SDavid du Colombier 
728*5c47fe09SDavid du Colombier static int
numbits(uint n)729*5c47fe09SDavid du Colombier numbits(uint n)
730*5c47fe09SDavid du Colombier {
731*5c47fe09SDavid du Colombier 	int c = 0;
732*5c47fe09SDavid du Colombier 	while(n != 0){
733*5c47fe09SDavid du Colombier 		c++;
734*5c47fe09SDavid du Colombier 		n = (n-1) & n;
735*5c47fe09SDavid du Colombier 	}
736*5c47fe09SDavid du Colombier 	return c;
737*5c47fe09SDavid du Colombier }
738*5c47fe09SDavid du Colombier 
7395d9de2d3SDavid du Colombier static void
usbinit(void)7405d9de2d3SDavid du Colombier usbinit(void)
7415d9de2d3SDavid du Colombier {
7425d9de2d3SDavid du Colombier 	Hci *hp;
7435d9de2d3SDavid du Colombier 	int ctlrno;
7445d9de2d3SDavid du Colombier 	Ep *d;
7455d9de2d3SDavid du Colombier 	char info[40];
7465d9de2d3SDavid du Colombier 
7475d9de2d3SDavid du Colombier 	dprint("usbinit\n");
7485d9de2d3SDavid du Colombier 	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){
7495d9de2d3SDavid du Colombier 		hp = hcis[ctlrno];
7505d9de2d3SDavid du Colombier 		if(hp != nil){
751*5c47fe09SDavid du Colombier 			int n;
752*5c47fe09SDavid du Colombier 
753*5c47fe09SDavid du Colombier 			if(hp->init != nil){
754*5c47fe09SDavid du Colombier 				if(waserror()){
755*5c47fe09SDavid du Colombier 					print("usbinit: %s: %s\n", hp->type, up->errstr);
756*5c47fe09SDavid du Colombier 					continue;
757*5c47fe09SDavid du Colombier 				}
7585d9de2d3SDavid du Colombier 				hp->init(hp);
759*5c47fe09SDavid du Colombier 				poperror();
760*5c47fe09SDavid du Colombier 			}
761*5c47fe09SDavid du Colombier 
762*5c47fe09SDavid du Colombier 			hp->superspeed &= (1<<hp->nports)-1;
763*5c47fe09SDavid du Colombier 			n = hp->nports - numbits(hp->superspeed);
764*5c47fe09SDavid du Colombier 			if(n > 0){
765*5c47fe09SDavid du Colombier 				d = newdev(hp, 1, 1);		/* new LS/FS/HS root hub */
7665d9de2d3SDavid du Colombier 				d->maxpkt = 64;
767*5c47fe09SDavid du Colombier 				if(hp->highspeed != 0)
768*5c47fe09SDavid du Colombier 					d->dev->speed = Highspeed;
769*5c47fe09SDavid du Colombier 				d->dev->state = Denabled;	/* although addr == 0 */
770*5c47fe09SDavid du Colombier 				snprint(info, sizeof(info), "roothub ports %d", n);
7715d9de2d3SDavid du Colombier 				kstrdup(&d->info, info);
7725d9de2d3SDavid du Colombier 			}
773*5c47fe09SDavid du Colombier 			n = numbits(hp->superspeed);
774*5c47fe09SDavid du Colombier 			if(n > 0){
775*5c47fe09SDavid du Colombier 				d = newdev(hp, 1, 1);		/* new SS root hub */
776*5c47fe09SDavid du Colombier 				d->maxpkt = 512;
777*5c47fe09SDavid du Colombier 				d->dev->speed = Superspeed;
778*5c47fe09SDavid du Colombier 				d->dev->state = Denabled;	/* although addr == 0 */
779*5c47fe09SDavid du Colombier 				snprint(info, sizeof(info), "roothub ports %d", n);
780*5c47fe09SDavid du Colombier 				kstrdup(&d->info, info);
781*5c47fe09SDavid du Colombier 			}
782*5c47fe09SDavid du Colombier 		}
7835d9de2d3SDavid du Colombier 	}
7845d9de2d3SDavid du Colombier }
7855d9de2d3SDavid du Colombier 
7865d9de2d3SDavid du Colombier static Chan*
usbattach(char * spec)7875d9de2d3SDavid du Colombier usbattach(char *spec)
7885d9de2d3SDavid du Colombier {
7895d9de2d3SDavid du Colombier 	return devattach(L'u', spec);
7905d9de2d3SDavid du Colombier }
7915d9de2d3SDavid du Colombier 
7925d9de2d3SDavid du Colombier static Walkqid*
usbwalk(Chan * c,Chan * nc,char ** name,int nname)7935d9de2d3SDavid du Colombier usbwalk(Chan *c, Chan *nc, char **name, int nname)
7945d9de2d3SDavid du Colombier {
7955d9de2d3SDavid du Colombier 	return devwalk(c, nc, name, nname, nil, 0, usbgen);
7965d9de2d3SDavid du Colombier }
7975d9de2d3SDavid du Colombier 
7985d9de2d3SDavid du Colombier static int
usbstat(Chan * c,uchar * db,int n)7995d9de2d3SDavid du Colombier usbstat(Chan *c, uchar *db, int n)
8005d9de2d3SDavid du Colombier {
8015d9de2d3SDavid du Colombier 	return devstat(c, db, n, nil, 0, usbgen);
8025d9de2d3SDavid du Colombier }
8035d9de2d3SDavid du Colombier 
8045d9de2d3SDavid du Colombier /*
8055d9de2d3SDavid du Colombier  * µs for the given transfer, for bandwidth allocation.
8065d9de2d3SDavid du Colombier  * This is a very rough worst case for what 5.11.3
8075d9de2d3SDavid du Colombier  * of the usb 2.0 spec says.
8085d9de2d3SDavid du Colombier  * Also, we are using maxpkt and not actual transfer sizes.
8095d9de2d3SDavid du Colombier  * Only when we are sure we
8105d9de2d3SDavid du Colombier  * are not exceeding b/w might we consider adjusting it.
8115d9de2d3SDavid du Colombier  */
8125d9de2d3SDavid du Colombier static ulong
usbload(int speed,int maxpkt)8135d9de2d3SDavid du Colombier usbload(int speed, int maxpkt)
8145d9de2d3SDavid du Colombier {
8155d9de2d3SDavid du Colombier 	enum{ Hostns = 1000, Hubns = 333 };
816b4d1cf41SDavid du Colombier 	ulong l;
817b4d1cf41SDavid du Colombier 	ulong bs;
8185d9de2d3SDavid du Colombier 
8195d9de2d3SDavid du Colombier 	l = 0;
8205d9de2d3SDavid du Colombier 	bs = 10UL * maxpkt;
8215d9de2d3SDavid du Colombier 	switch(speed){
8225d9de2d3SDavid du Colombier 	case Highspeed:
8235d9de2d3SDavid du Colombier 		l = 55*8*2 + 2 * (3 + bs) + Hostns;
8245d9de2d3SDavid du Colombier 		break;
8255d9de2d3SDavid du Colombier 	case Fullspeed:
8265d9de2d3SDavid du Colombier 		l = 9107 + 84 * (4 + bs) + Hostns;
8275d9de2d3SDavid du Colombier 		break;
8285d9de2d3SDavid du Colombier 	case Lowspeed:
8295d9de2d3SDavid du Colombier 		l = 64107 + 2 * Hubns + 667 * (3 + bs) + Hostns;
8305d9de2d3SDavid du Colombier 		break;
8315d9de2d3SDavid du Colombier 	default:
8325d9de2d3SDavid du Colombier 		print("usbload: bad speed %d\n", speed);
8335d9de2d3SDavid du Colombier 		/* let it run */
8345d9de2d3SDavid du Colombier 	}
8355d9de2d3SDavid du Colombier 	return l / 1000UL;	/* in µs */
8365d9de2d3SDavid du Colombier }
8375d9de2d3SDavid du Colombier 
8385d9de2d3SDavid du Colombier static Chan*
usbopen(Chan * c,int omode)8395d9de2d3SDavid du Colombier usbopen(Chan *c, int omode)
8405d9de2d3SDavid du Colombier {
841b4d1cf41SDavid du Colombier 	int q;
8425d9de2d3SDavid du Colombier 	Ep *ep;
843b4d1cf41SDavid du Colombier 	int mode;
8445d9de2d3SDavid du Colombier 
8455d9de2d3SDavid du Colombier 	mode = openmode(omode);
8465d9de2d3SDavid du Colombier 	q = QID(c->qid);
8475d9de2d3SDavid du Colombier 
8485d9de2d3SDavid du Colombier 	if(q >= Qep0dir && qid2epidx(q) < 0)
8495d9de2d3SDavid du Colombier 		error(Eio);
8505d9de2d3SDavid du Colombier 	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
8515d9de2d3SDavid du Colombier 		return devopen(c, omode, nil, 0, usbgen);
8525d9de2d3SDavid du Colombier 
8535d9de2d3SDavid du Colombier 	ep = getep(qid2epidx(q));
8545d9de2d3SDavid du Colombier 	if(ep == nil)
8555d9de2d3SDavid du Colombier 		error(Eio);
8565d9de2d3SDavid du Colombier 	deprint("usbopen q %#x fid %d omode %d\n", q, c->fid, mode);
8575d9de2d3SDavid du Colombier 	if(waserror()){
8585d9de2d3SDavid du Colombier 		putep(ep);
8595d9de2d3SDavid du Colombier 		nexterror();
8605d9de2d3SDavid du Colombier 	}
8615d9de2d3SDavid du Colombier 	qlock(ep);
8625d9de2d3SDavid du Colombier 	if(ep->inuse){
8635d9de2d3SDavid du Colombier 		qunlock(ep);
8645d9de2d3SDavid du Colombier 		error(Einuse);
8655d9de2d3SDavid du Colombier 	}
8665d9de2d3SDavid du Colombier 	ep->inuse = 1;
8675d9de2d3SDavid du Colombier 	qunlock(ep);
8685d9de2d3SDavid du Colombier 	if(waserror()){
8695d9de2d3SDavid du Colombier 		ep->inuse = 0;
8705d9de2d3SDavid du Colombier 		nexterror();
8715d9de2d3SDavid du Colombier 	}
8725d9de2d3SDavid du Colombier 	if(mode != OREAD && ep->mode == OREAD)
8735d9de2d3SDavid du Colombier 		error(Eperm);
8745d9de2d3SDavid du Colombier 	if(mode != OWRITE && ep->mode == OWRITE)
8755d9de2d3SDavid du Colombier 		error(Eperm);
8765d9de2d3SDavid du Colombier 	if(ep->ttype == Tnone)
8775d9de2d3SDavid du Colombier 		error(Enotconf);
8785d9de2d3SDavid du Colombier 	ep->clrhalt = 0;
8795d9de2d3SDavid du Colombier 	ep->rhrepl = -1;
880*5c47fe09SDavid du Colombier 	if(ep->load == 0 && ep->dev->speed != Superspeed)
8815d9de2d3SDavid du Colombier 		ep->load = usbload(ep->dev->speed, ep->maxpkt);
8825d9de2d3SDavid du Colombier 	ep->hp->epopen(ep);
8835d9de2d3SDavid du Colombier 
8845d9de2d3SDavid du Colombier 	poperror();	/* ep->inuse */
8855d9de2d3SDavid du Colombier 	poperror();	/* don't putep(): ref kept for fid using the ep. */
8865d9de2d3SDavid du Colombier 
8875d9de2d3SDavid du Colombier 	c->mode = mode;
8885d9de2d3SDavid du Colombier 	c->flag |= COPEN;
8895d9de2d3SDavid du Colombier 	c->offset = 0;
8905d9de2d3SDavid du Colombier 	c->aux = nil;	/* paranoia */
8915d9de2d3SDavid du Colombier 	return c;
8925d9de2d3SDavid du Colombier }
8935d9de2d3SDavid du Colombier 
8945d9de2d3SDavid du Colombier static void
epclose(Ep * ep)8955d9de2d3SDavid du Colombier epclose(Ep *ep)
8965d9de2d3SDavid du Colombier {
8975d9de2d3SDavid du Colombier 	qlock(ep);
8985d9de2d3SDavid du Colombier 	if(waserror()){
8995d9de2d3SDavid du Colombier 		qunlock(ep);
9005d9de2d3SDavid du Colombier 		nexterror();
9015d9de2d3SDavid du Colombier 	}
9025d9de2d3SDavid du Colombier 	if(ep->inuse){
9035d9de2d3SDavid du Colombier 		ep->hp->epclose(ep);
9045d9de2d3SDavid du Colombier 		ep->inuse = 0;
9055d9de2d3SDavid du Colombier 	}
9065d9de2d3SDavid du Colombier 	qunlock(ep);
9075d9de2d3SDavid du Colombier 	poperror();
9085d9de2d3SDavid du Colombier }
9095d9de2d3SDavid du Colombier 
9105d9de2d3SDavid du Colombier static void
usbclose(Chan * c)9115d9de2d3SDavid du Colombier usbclose(Chan *c)
9125d9de2d3SDavid du Colombier {
9135d9de2d3SDavid du Colombier 	int q;
9145d9de2d3SDavid du Colombier 	Ep *ep;
9155d9de2d3SDavid du Colombier 
9165d9de2d3SDavid du Colombier 	q = QID(c->qid);
9175d9de2d3SDavid du Colombier 	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
9185d9de2d3SDavid du Colombier 		return;
9195d9de2d3SDavid du Colombier 
9205d9de2d3SDavid du Colombier 	ep = getep(qid2epidx(q));
9215d9de2d3SDavid du Colombier 	if(ep == nil)
9225d9de2d3SDavid du Colombier 		return;
9235d9de2d3SDavid du Colombier 	deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref);
9245d9de2d3SDavid du Colombier 	if(waserror()){
9255d9de2d3SDavid du Colombier 		putep(ep);
9265d9de2d3SDavid du Colombier 		nexterror();
9275d9de2d3SDavid du Colombier 	}
9285d9de2d3SDavid du Colombier 	if(c->flag & COPEN){
9295d9de2d3SDavid du Colombier 		free(c->aux);
9305d9de2d3SDavid du Colombier 		c->aux = nil;
9315d9de2d3SDavid du Colombier 		epclose(ep);
9325d9de2d3SDavid du Colombier 		putep(ep);	/* release ref kept since usbopen */
9335d9de2d3SDavid du Colombier 		c->flag &= ~COPEN;
9345d9de2d3SDavid du Colombier 	}
9355d9de2d3SDavid du Colombier 	poperror();
9365d9de2d3SDavid du Colombier 	putep(ep);
9375d9de2d3SDavid du Colombier }
9385d9de2d3SDavid du Colombier 
9395d9de2d3SDavid du Colombier static long
ctlread(Chan * c,void * a,long n,vlong offset)9405d9de2d3SDavid du Colombier ctlread(Chan *c, void *a, long n, vlong offset)
9415d9de2d3SDavid du Colombier {
942b4d1cf41SDavid du Colombier 	int q;
943b4d1cf41SDavid du Colombier 	char *s;
944b4d1cf41SDavid du Colombier 	char *us;
945b4d1cf41SDavid du Colombier 	char *se;
9465d9de2d3SDavid du Colombier 	Ep *ep;
947b4d1cf41SDavid du Colombier 	int i;
9485d9de2d3SDavid du Colombier 
9495d9de2d3SDavid du Colombier 	q = QID(c->qid);
9505d9de2d3SDavid du Colombier 	us = s = smalloc(READSTR);
9515d9de2d3SDavid du Colombier 	se = s + READSTR;
9525d9de2d3SDavid du Colombier 	if(waserror()){
9535d9de2d3SDavid du Colombier 		free(us);
9545d9de2d3SDavid du Colombier 		nexterror();
9555d9de2d3SDavid du Colombier 	}
9565d9de2d3SDavid du Colombier 	if(q == Qctl)
9575d9de2d3SDavid du Colombier 		for(i = 0; i < epmax; i++){
9585d9de2d3SDavid du Colombier 			ep = getep(i);
9595d9de2d3SDavid du Colombier 			if(ep != nil){
9605d9de2d3SDavid du Colombier 				if(waserror()){
9615d9de2d3SDavid du Colombier 					putep(ep);
9625d9de2d3SDavid du Colombier 					nexterror();
9635d9de2d3SDavid du Colombier 				}
964b4d1cf41SDavid du Colombier 				s = seprint(s, se, "ep%d.%d ", ep->dev->nb, ep->nb);
9655d9de2d3SDavid du Colombier 				s = seprintep(s, se, ep, 0);
9665d9de2d3SDavid du Colombier 				poperror();
9675d9de2d3SDavid du Colombier 			}
9685d9de2d3SDavid du Colombier 			putep(ep);
9695d9de2d3SDavid du Colombier 		}
9705d9de2d3SDavid du Colombier 	else{
9715d9de2d3SDavid du Colombier 		ep = getep(qid2epidx(q));
9725d9de2d3SDavid du Colombier 		if(ep == nil)
9735d9de2d3SDavid du Colombier 			error(Eio);
9745d9de2d3SDavid du Colombier 		if(waserror()){
9755d9de2d3SDavid du Colombier 			putep(ep);
9765d9de2d3SDavid du Colombier 			nexterror();
9775d9de2d3SDavid du Colombier 		}
9785d9de2d3SDavid du Colombier 		if(c->aux != nil){
979b4d1cf41SDavid du Colombier 			/* After a new endpoint request we read
9805d9de2d3SDavid du Colombier 			 * the new endpoint name back.
9815d9de2d3SDavid du Colombier 			 */
9825d9de2d3SDavid du Colombier 			strecpy(s, se, c->aux);
9835d9de2d3SDavid du Colombier 			free(c->aux);
9845d9de2d3SDavid du Colombier 			c->aux = nil;
9855d9de2d3SDavid du Colombier 		}else
9865d9de2d3SDavid du Colombier 			seprintep(s, se, ep, 0);
9875d9de2d3SDavid du Colombier 		poperror();
9885d9de2d3SDavid du Colombier 		putep(ep);
9895d9de2d3SDavid du Colombier 	}
9905d9de2d3SDavid du Colombier 	n = readstr(offset, a, n, us);
9915d9de2d3SDavid du Colombier 	poperror();
9925d9de2d3SDavid du Colombier 	free(us);
9935d9de2d3SDavid du Colombier 	return n;
9945d9de2d3SDavid du Colombier }
9955d9de2d3SDavid du Colombier 
9965d9de2d3SDavid du Colombier /*
9975d9de2d3SDavid du Colombier  * Fake root hub emulation.
9985d9de2d3SDavid du Colombier  */
9995d9de2d3SDavid du Colombier static long
rhubread(Ep * ep,void * a,long n)10005d9de2d3SDavid du Colombier rhubread(Ep *ep, void *a, long n)
10015d9de2d3SDavid du Colombier {
1002*5c47fe09SDavid du Colombier 	uchar b[8];
10035d9de2d3SDavid du Colombier 
1004*5c47fe09SDavid du Colombier 	if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2 || ep->rhrepl == -1)
10055d9de2d3SDavid du Colombier 		return -1;
10065d9de2d3SDavid du Colombier 
1007*5c47fe09SDavid du Colombier 	b[0] = ep->rhrepl;
1008*5c47fe09SDavid du Colombier 	b[1] = ep->rhrepl>>8;
1009*5c47fe09SDavid du Colombier 	b[2] = ep->rhrepl>>16;
1010*5c47fe09SDavid du Colombier 	b[3] = ep->rhrepl>>24;
1011*5c47fe09SDavid du Colombier 	b[4] = ep->rhrepl>>32;
1012*5c47fe09SDavid du Colombier 	b[5] = ep->rhrepl>>40;
1013*5c47fe09SDavid du Colombier 	b[6] = ep->rhrepl>>48;
1014*5c47fe09SDavid du Colombier 	b[7] = ep->rhrepl>>56;
1015*5c47fe09SDavid du Colombier 
10165d9de2d3SDavid du Colombier 	ep->rhrepl = -1;
1017*5c47fe09SDavid du Colombier 
1018*5c47fe09SDavid du Colombier 	if(n > sizeof(b))
1019*5c47fe09SDavid du Colombier 		n = sizeof(b);
1020*5c47fe09SDavid du Colombier 	memmove(a, b, n);
1021*5c47fe09SDavid du Colombier 
10225d9de2d3SDavid du Colombier 	return n;
10235d9de2d3SDavid du Colombier }
10245d9de2d3SDavid du Colombier 
1025*5c47fe09SDavid du Colombier static int
rootport(Ep * ep,int port)1026*5c47fe09SDavid du Colombier rootport(Ep *ep, int port)
1027*5c47fe09SDavid du Colombier {
1028*5c47fe09SDavid du Colombier 	Hci *hp;
1029*5c47fe09SDavid du Colombier 	Udev *hub;
1030*5c47fe09SDavid du Colombier 	uint mask;
1031*5c47fe09SDavid du Colombier 	int rootport;
1032*5c47fe09SDavid du Colombier 
1033*5c47fe09SDavid du Colombier 	hp = ep->hp;
1034*5c47fe09SDavid du Colombier 	hub = ep->dev;
1035*5c47fe09SDavid du Colombier 	if(!hub->isroot)
1036*5c47fe09SDavid du Colombier 		return hub->rootport;
1037*5c47fe09SDavid du Colombier 
1038*5c47fe09SDavid du Colombier 	mask = hp->superspeed;
1039*5c47fe09SDavid du Colombier 	if(hub->speed != Superspeed)
1040*5c47fe09SDavid du Colombier 		mask = (1<<hp->nports)-1 & ~mask;
1041*5c47fe09SDavid du Colombier 
1042*5c47fe09SDavid du Colombier 	for(rootport = 1; mask != 0; rootport++){
1043*5c47fe09SDavid du Colombier 		if(mask & 1){
1044*5c47fe09SDavid du Colombier 			if(--port == 0)
1045*5c47fe09SDavid du Colombier 				return rootport;
1046*5c47fe09SDavid du Colombier 		}
1047*5c47fe09SDavid du Colombier 		mask >>= 1;
1048*5c47fe09SDavid du Colombier 	}
1049*5c47fe09SDavid du Colombier 
1050*5c47fe09SDavid du Colombier 	return 0;
1051*5c47fe09SDavid du Colombier }
1052*5c47fe09SDavid du Colombier 
10535d9de2d3SDavid du Colombier static long
rhubwrite(Ep * ep,void * a,long n)10545d9de2d3SDavid du Colombier rhubwrite(Ep *ep, void *a, long n)
10555d9de2d3SDavid du Colombier {
10565d9de2d3SDavid du Colombier 	uchar *s;
1057b4d1cf41SDavid du Colombier 	int cmd;
1058b4d1cf41SDavid du Colombier 	int feature;
1059b4d1cf41SDavid du Colombier 	int port;
10605d9de2d3SDavid du Colombier 	Hci *hp;
10615d9de2d3SDavid du Colombier 
10625d9de2d3SDavid du Colombier 	if(ep->dev == nil || ep->dev->isroot == 0 || ep->nb != 0)
10635d9de2d3SDavid du Colombier 		return -1;
10645d9de2d3SDavid du Colombier 	if(n != Rsetuplen)
10655d9de2d3SDavid du Colombier 		error("root hub is a toy hub");
10665d9de2d3SDavid du Colombier 	ep->rhrepl = -1;
10675d9de2d3SDavid du Colombier 	s = a;
10685d9de2d3SDavid du Colombier 	if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother))
10695d9de2d3SDavid du Colombier 		error("root hub is a toy hub");
10705d9de2d3SDavid du Colombier 	hp = ep->hp;
10715d9de2d3SDavid du Colombier 	cmd = s[Rreq];
10725d9de2d3SDavid du Colombier 	feature = GET2(s+Rvalue);
1073*5c47fe09SDavid du Colombier 	port = rootport(ep, GET2(s+Rindex));
1074*5c47fe09SDavid du Colombier 	if(port == 0)
10755d9de2d3SDavid du Colombier 		error("bad hub port number");
10765d9de2d3SDavid du Colombier 	switch(feature){
10775d9de2d3SDavid du Colombier 	case Rportenable:
10785d9de2d3SDavid du Colombier 		ep->rhrepl = hp->portenable(hp, port, cmd == Rsetfeature);
10795d9de2d3SDavid du Colombier 		break;
10805d9de2d3SDavid du Colombier 	case Rportreset:
10815d9de2d3SDavid du Colombier 		ep->rhrepl = hp->portreset(hp, port, cmd == Rsetfeature);
10825d9de2d3SDavid du Colombier 		break;
10835d9de2d3SDavid du Colombier 	case Rgetstatus:
10845d9de2d3SDavid du Colombier 		ep->rhrepl = hp->portstatus(hp, port);
10855d9de2d3SDavid du Colombier 		break;
10865d9de2d3SDavid du Colombier 	default:
10875d9de2d3SDavid du Colombier 		ep->rhrepl = 0;
10885d9de2d3SDavid du Colombier 	}
10895d9de2d3SDavid du Colombier 	return n;
10905d9de2d3SDavid du Colombier }
10915d9de2d3SDavid du Colombier 
10925d9de2d3SDavid du Colombier static long
usbread(Chan * c,void * a,long n,vlong offset)10935d9de2d3SDavid du Colombier usbread(Chan *c, void *a, long n, vlong offset)
10945d9de2d3SDavid du Colombier {
1095b4d1cf41SDavid du Colombier 	int q;
10965d9de2d3SDavid du Colombier 	Ep *ep;
1097b4d1cf41SDavid du Colombier 	int nr;
10985d9de2d3SDavid du Colombier 
10995d9de2d3SDavid du Colombier 	q = QID(c->qid);
11005d9de2d3SDavid du Colombier 
11015d9de2d3SDavid du Colombier 	if(c->qid.type == QTDIR)
11025d9de2d3SDavid du Colombier 		return devdirread(c, a, n, nil, 0, usbgen);
11035d9de2d3SDavid du Colombier 
11045d9de2d3SDavid du Colombier 	if(q == Qctl || isqtype(q, Qepctl))
11055d9de2d3SDavid du Colombier 		return ctlread(c, a, n, offset);
11065d9de2d3SDavid du Colombier 
11075d9de2d3SDavid du Colombier 	ep = getep(qid2epidx(q));
11085d9de2d3SDavid du Colombier 	if(ep == nil)
11095d9de2d3SDavid du Colombier 		error(Eio);
11105d9de2d3SDavid du Colombier 	if(waserror()){
11115d9de2d3SDavid du Colombier 		putep(ep);
11125d9de2d3SDavid du Colombier 		nexterror();
11135d9de2d3SDavid du Colombier 	}
11145d9de2d3SDavid du Colombier 	if(ep->dev->state == Ddetach)
11155d9de2d3SDavid du Colombier 		error(Edetach);
11165d9de2d3SDavid du Colombier 	if(ep->mode == OWRITE || ep->inuse == 0)
11175d9de2d3SDavid du Colombier 		error(Ebadusefd);
11185d9de2d3SDavid du Colombier 	switch(ep->ttype){
11195d9de2d3SDavid du Colombier 	case Tnone:
11205d9de2d3SDavid du Colombier 		error("endpoint not configured");
11215d9de2d3SDavid du Colombier 	case Tctl:
11225d9de2d3SDavid du Colombier 		nr = rhubread(ep, a, n);
11235d9de2d3SDavid du Colombier 		if(nr >= 0){
11245d9de2d3SDavid du Colombier 			n = nr;
11255d9de2d3SDavid du Colombier 			break;
11265d9de2d3SDavid du Colombier 		}
1127b4d1cf41SDavid du Colombier 		/* else fall */
11285d9de2d3SDavid du Colombier 	default:
1129b4d1cf41SDavid du Colombier 		ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",q,c->fid,n,offset);
11305d9de2d3SDavid du Colombier 		n = ep->hp->epread(ep, a, n);
11315d9de2d3SDavid du Colombier 		break;
11325d9de2d3SDavid du Colombier 	}
11335d9de2d3SDavid du Colombier 	poperror();
11345d9de2d3SDavid du Colombier 	putep(ep);
11355d9de2d3SDavid du Colombier 	return n;
11365d9de2d3SDavid du Colombier }
11375d9de2d3SDavid du Colombier 
11385d9de2d3SDavid du Colombier static long
pow2(int n)11395d9de2d3SDavid du Colombier pow2(int n)
11405d9de2d3SDavid du Colombier {
11415d9de2d3SDavid du Colombier 	return 1 << n;
11425d9de2d3SDavid du Colombier }
11435d9de2d3SDavid du Colombier 
11445d9de2d3SDavid du Colombier static void
setmaxpkt(Ep * ep,char * s)11455d9de2d3SDavid du Colombier setmaxpkt(Ep *ep, char* s)
11465d9de2d3SDavid du Colombier {
1147*5c47fe09SDavid du Colombier 	long spp, max;	/* samples per packet */
11485d9de2d3SDavid du Colombier 
1149*5c47fe09SDavid du Colombier 	if(ep->dev->speed == Fullspeed)
11505d9de2d3SDavid du Colombier 		spp = (ep->hz * ep->pollival + 999) / 1000;
1151*5c47fe09SDavid du Colombier 	else
1152*5c47fe09SDavid du Colombier 		spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000;
11535d9de2d3SDavid du Colombier 	ep->maxpkt = spp * ep->samplesz;
11545d9de2d3SDavid du Colombier 	deprint("usb: %s: setmaxpkt: hz %ld poll %ld"
11555d9de2d3SDavid du Colombier 		" ntds %d %s speed -> spp %ld maxpkt %ld\n", s,
11565d9de2d3SDavid du Colombier 		ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed],
11575d9de2d3SDavid du Colombier 		spp, ep->maxpkt);
1158*5c47fe09SDavid du Colombier 	switch(ep->dev->speed){
1159*5c47fe09SDavid du Colombier 	case Fullspeed:
1160*5c47fe09SDavid du Colombier 		max = 1024;
1161*5c47fe09SDavid du Colombier 		break;
1162*5c47fe09SDavid du Colombier 	case Highspeed:
1163*5c47fe09SDavid du Colombier 		max = 3*1024;
1164*5c47fe09SDavid du Colombier 		break;
1165*5c47fe09SDavid du Colombier 	case Superspeed:
1166*5c47fe09SDavid du Colombier 		max = 48*1024;
1167*5c47fe09SDavid du Colombier 		break;
1168*5c47fe09SDavid du Colombier 	default:
1169*5c47fe09SDavid du Colombier 		return;
1170*5c47fe09SDavid du Colombier 	}
1171*5c47fe09SDavid du Colombier 	if(ep->maxpkt*ep->ntds > max){
1172*5c47fe09SDavid du Colombier 		print("usb: %s: maxpkt %ld > %ld for %s, truncating\n",
1173*5c47fe09SDavid du Colombier 			s, ep->maxpkt*ep->ntds, max, spname[ep->dev->speed]);
1174*5c47fe09SDavid du Colombier 		ep->maxpkt = max/ep->ntds;
11755d9de2d3SDavid du Colombier 	}
11765d9de2d3SDavid du Colombier }
11775d9de2d3SDavid du Colombier 
11785d9de2d3SDavid du Colombier /*
1179b4d1cf41SDavid du Colombier  * Many endpoint ctls. simply update the portable representation
11805d9de2d3SDavid du Colombier  * of the endpoint. The actual controller driver will look
11815d9de2d3SDavid du Colombier  * at them to setup the endpoints as dictated.
11825d9de2d3SDavid du Colombier  */
11835d9de2d3SDavid du Colombier static long
epctl(Ep * ep,Chan * c,void * a,long n)11845d9de2d3SDavid du Colombier epctl(Ep *ep, Chan *c, void *a, long n)
11855d9de2d3SDavid du Colombier {
11865d9de2d3SDavid du Colombier 	int i, l, mode, nb, tt;
11875d9de2d3SDavid du Colombier 	char *b, *s;
11885d9de2d3SDavid du Colombier 	Cmdbuf *cb;
11895d9de2d3SDavid du Colombier 	Cmdtab *ct;
11905d9de2d3SDavid du Colombier 	Ep *nep;
11915d9de2d3SDavid du Colombier 	Udev *d;
11925d9de2d3SDavid du Colombier 	static char *Info = "info ";
11935d9de2d3SDavid du Colombier 
11945d9de2d3SDavid du Colombier 	d = ep->dev;
11955d9de2d3SDavid du Colombier 
11965d9de2d3SDavid du Colombier 	cb = parsecmd(a, n);
11975d9de2d3SDavid du Colombier 	if(waserror()){
11985d9de2d3SDavid du Colombier 		free(cb);
11995d9de2d3SDavid du Colombier 		nexterror();
12005d9de2d3SDavid du Colombier 	}
12015d9de2d3SDavid du Colombier 	ct = lookupcmd(cb, epctls, nelem(epctls));
12025d9de2d3SDavid du Colombier 	if(ct == nil)
12035d9de2d3SDavid du Colombier 		error(Ebadctl);
12045d9de2d3SDavid du Colombier 	i = ct->index;
12055d9de2d3SDavid du Colombier 	if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset)
12065d9de2d3SDavid du Colombier 		if(ep != ep->ep0)
12075d9de2d3SDavid du Colombier 			error("allowed only on a setup endpoint");
12085d9de2d3SDavid du Colombier 	if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname)
12095d9de2d3SDavid du Colombier 		if(ep != ep->ep0 && ep->inuse != 0)
12105d9de2d3SDavid du Colombier 			error("must configure before using");
12115d9de2d3SDavid du Colombier 	switch(i){
12125d9de2d3SDavid du Colombier 	case CMnew:
12135d9de2d3SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
12145d9de2d3SDavid du Colombier 		nb = strtol(cb->f[1], nil, 0);
12155d9de2d3SDavid du Colombier 		if(nb < 0 || nb >= Ndeveps)
12165d9de2d3SDavid du Colombier 			error("bad endpoint number");
12175d9de2d3SDavid du Colombier 		tt = name2ttype(cb->f[2]);
12185d9de2d3SDavid du Colombier 		if(tt == Tnone)
12195d9de2d3SDavid du Colombier 			error("unknown endpoint type");
12205d9de2d3SDavid du Colombier 		mode = name2mode(cb->f[3]);
12215d9de2d3SDavid du Colombier 		if(mode < 0)
12225d9de2d3SDavid du Colombier 			error("unknown i/o mode");
12235d9de2d3SDavid du Colombier 		newdevep(ep, nb, tt, mode);
12245d9de2d3SDavid du Colombier 		break;
12255d9de2d3SDavid du Colombier 	case CMnewdev:
12265d9de2d3SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
12275d9de2d3SDavid du Colombier 		if(ep != ep->ep0 || d->ishub == 0)
12285d9de2d3SDavid du Colombier 			error("not a hub setup endpoint");
12295d9de2d3SDavid du Colombier 		l = name2speed(cb->f[1]);
12305d9de2d3SDavid du Colombier 		if(l == Nospeed)
1231*5c47fe09SDavid du Colombier 			error("speed must be full|low|high|super");
1232*5c47fe09SDavid du Colombier 		if(l != d->speed && (l == Superspeed || d->speed == Superspeed))
1233*5c47fe09SDavid du Colombier 			error("wrong speed for superspeed hub/device");
12345d9de2d3SDavid du Colombier 		nep = newdev(ep->hp, 0, 0);
12355d9de2d3SDavid du Colombier 		nep->dev->speed = l;
1236*5c47fe09SDavid du Colombier 		if(l == Superspeed)
1237*5c47fe09SDavid du Colombier 			nep->maxpkt = 512;
1238*5c47fe09SDavid du Colombier 		else if(l != Lowspeed)
12395d9de2d3SDavid du Colombier 			nep->maxpkt = 64;	/* assume full speed */
1240*5c47fe09SDavid du Colombier 		nep->dev->hub = d->addr;
12415d9de2d3SDavid du Colombier 		nep->dev->port = atoi(cb->f[2]);
1242*5c47fe09SDavid du Colombier 		nep->dev->depth = d->depth+1;
1243*5c47fe09SDavid du Colombier 		nep->dev->rootport = rootport(ep, nep->dev->port);
1244*5c47fe09SDavid du Colombier 		nep->dev->routestr = d->routestr | (((nep->dev->port&15) << 4*nep->dev->depth) >> 4);
1245b4d1cf41SDavid du Colombier 		/* next read request will read
12465d9de2d3SDavid du Colombier 		 * the name for the new endpoint
12475d9de2d3SDavid du Colombier 		 */
12485d9de2d3SDavid du Colombier 		l = sizeof(up->genbuf);
12495d9de2d3SDavid du Colombier 		snprint(up->genbuf, l, "ep%d.%d", nep->dev->nb, nep->nb);
12505d9de2d3SDavid du Colombier 		kstrdup(&c->aux, up->genbuf);
12515d9de2d3SDavid du Colombier 		break;
12525d9de2d3SDavid du Colombier 	case CMhub:
12535d9de2d3SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
12545d9de2d3SDavid du Colombier 		d->ishub = 1;
12555d9de2d3SDavid du Colombier 		break;
12565d9de2d3SDavid du Colombier 	case CMspeed:
12575d9de2d3SDavid du Colombier 		l = name2speed(cb->f[1]);
12585d9de2d3SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
12595d9de2d3SDavid du Colombier 		if(l == Nospeed)
1260*5c47fe09SDavid du Colombier 			error("speed must be full|low|high|super");
1261*5c47fe09SDavid du Colombier 		if(l != d->speed && (l == Superspeed || d->speed == Superspeed))
1262*5c47fe09SDavid du Colombier 			error("cannot change speed on superspeed device");
12635d9de2d3SDavid du Colombier 		qlock(ep->ep0);
12645d9de2d3SDavid du Colombier 		d->speed = l;
12655d9de2d3SDavid du Colombier 		qunlock(ep->ep0);
12665d9de2d3SDavid du Colombier 		break;
12675d9de2d3SDavid du Colombier 	case CMmaxpkt:
12685d9de2d3SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
12695d9de2d3SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
12705d9de2d3SDavid du Colombier 		if(l < 1 || l > 1024)
12715d9de2d3SDavid du Colombier 			error("maxpkt not in [1:1024]");
12725d9de2d3SDavid du Colombier 		qlock(ep);
12735d9de2d3SDavid du Colombier 		ep->maxpkt = l;
12745d9de2d3SDavid du Colombier 		qunlock(ep);
12755d9de2d3SDavid du Colombier 		break;
12765d9de2d3SDavid du Colombier 	case CMntds:
12775d9de2d3SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
12785d9de2d3SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
12795d9de2d3SDavid du Colombier 		if(l < 1 || l > 3)
12805d9de2d3SDavid du Colombier 			error("ntds not in [1:3]");
12815d9de2d3SDavid du Colombier 		qlock(ep);
12825d9de2d3SDavid du Colombier 		ep->ntds = l;
12835d9de2d3SDavid du Colombier 		qunlock(ep);
12845d9de2d3SDavid du Colombier 		break;
12855d9de2d3SDavid du Colombier 	case CMpollival:
12865d9de2d3SDavid du Colombier 		if(ep->ttype != Tintr && ep->ttype != Tiso)
12875d9de2d3SDavid du Colombier 			error("not an intr or iso endpoint");
12885d9de2d3SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
12895d9de2d3SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
12905d9de2d3SDavid du Colombier 		if(ep->ttype == Tiso ||
12915d9de2d3SDavid du Colombier 		   (ep->ttype == Tintr && ep->dev->speed == Highspeed)){
12925d9de2d3SDavid du Colombier 			if(l < 1 || l > 16)
12935d9de2d3SDavid du Colombier 				error("pollival power not in [1:16]");
12945d9de2d3SDavid du Colombier 			l = pow2(l-1);
12955d9de2d3SDavid du Colombier 		}else
12965d9de2d3SDavid du Colombier 			if(l < 1 || l > 255)
12975d9de2d3SDavid du Colombier 				error("pollival not in [1:255]");
12985d9de2d3SDavid du Colombier 		qlock(ep);
12995d9de2d3SDavid du Colombier 		ep->pollival = l;
13005d9de2d3SDavid du Colombier 		if(ep->ttype == Tiso)
13015d9de2d3SDavid du Colombier 			setmaxpkt(ep, "pollival");
13025d9de2d3SDavid du Colombier 		qunlock(ep);
13035d9de2d3SDavid du Colombier 		break;
13045d9de2d3SDavid du Colombier 	case CMsamplesz:
13055d9de2d3SDavid du Colombier 		if(ep->ttype != Tiso)
13065d9de2d3SDavid du Colombier 			error("not an iso endpoint");
13075d9de2d3SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
13085d9de2d3SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
13095d9de2d3SDavid du Colombier 		if(l <= 0 || l > 8)
13105d9de2d3SDavid du Colombier 			error("samplesz not in [1:8]");
13115d9de2d3SDavid du Colombier 		qlock(ep);
13125d9de2d3SDavid du Colombier 		ep->samplesz = l;
13135d9de2d3SDavid du Colombier 		setmaxpkt(ep, "samplesz");
13145d9de2d3SDavid du Colombier 		qunlock(ep);
13155d9de2d3SDavid du Colombier 		break;
13165d9de2d3SDavid du Colombier 	case CMhz:
13175d9de2d3SDavid du Colombier 		if(ep->ttype != Tiso)
13185d9de2d3SDavid du Colombier 			error("not an iso endpoint");
13195d9de2d3SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
13205d9de2d3SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
13215d9de2d3SDavid du Colombier 		if(l <= 0 || l > 100000)
13225d9de2d3SDavid du Colombier 			error("hz not in [1:100000]");
13235d9de2d3SDavid du Colombier 		qlock(ep);
13245d9de2d3SDavid du Colombier 		ep->hz = l;
13255d9de2d3SDavid du Colombier 		setmaxpkt(ep, "hz");
13265d9de2d3SDavid du Colombier 		qunlock(ep);
13275d9de2d3SDavid du Colombier 		break;
13285d9de2d3SDavid du Colombier 	case CMclrhalt:
13295d9de2d3SDavid du Colombier 		qlock(ep);
13305d9de2d3SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
13315d9de2d3SDavid du Colombier 		ep->clrhalt = 1;
13325d9de2d3SDavid du Colombier 		qunlock(ep);
13335d9de2d3SDavid du Colombier 		break;
13345d9de2d3SDavid du Colombier 	case CMinfo:
13355d9de2d3SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
13365d9de2d3SDavid du Colombier 		l = strlen(Info);
13375d9de2d3SDavid du Colombier 		s = a;
13385d9de2d3SDavid du Colombier 		if(n < l+2 || strncmp(Info, s, l) != 0)
13395d9de2d3SDavid du Colombier 			error(Ebadctl);
13405d9de2d3SDavid du Colombier 		if(n > 1024)
13415d9de2d3SDavid du Colombier 			n = 1024;
13425d9de2d3SDavid du Colombier 		b = smalloc(n);
13435d9de2d3SDavid du Colombier 		memmove(b, s+l, n-l);
13445d9de2d3SDavid du Colombier 		b[n-l] = 0;
13455d9de2d3SDavid du Colombier 		if(b[n-l-1] == '\n')
13465d9de2d3SDavid du Colombier 			b[n-l-1] = 0;
13475d9de2d3SDavid du Colombier 		qlock(ep);
13485d9de2d3SDavid du Colombier 		free(ep->info);
13495d9de2d3SDavid du Colombier 		ep->info = b;
13505d9de2d3SDavid du Colombier 		qunlock(ep);
13515d9de2d3SDavid du Colombier 		break;
13525d9de2d3SDavid du Colombier 	case CMaddress:
13535d9de2d3SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1354*5c47fe09SDavid du Colombier 		if(ep->dev->addr == 0)
1355*5c47fe09SDavid du Colombier 			ep->dev->addr = ep->dev->nb;
13565d9de2d3SDavid du Colombier 		ep->dev->state = Denabled;
13575d9de2d3SDavid du Colombier 		break;
13585d9de2d3SDavid du Colombier 	case CMdetach:
13595d9de2d3SDavid du Colombier 		if(ep->dev->isroot != 0)
13605d9de2d3SDavid du Colombier 			error("can't detach a root hub");
13615d9de2d3SDavid du Colombier 		deprint("usb epctl %s ep%d.%d\n",
13625d9de2d3SDavid du Colombier 			cb->f[0], ep->dev->nb, ep->nb);
13635d9de2d3SDavid du Colombier 		ep->dev->state = Ddetach;
13645d9de2d3SDavid du Colombier 		/* Release file system ref. for its endpoints */
13655d9de2d3SDavid du Colombier 		for(i = 0; i < nelem(ep->dev->eps); i++)
13665d9de2d3SDavid du Colombier 			putep(ep->dev->eps[i]);
13675d9de2d3SDavid du Colombier 		break;
13685d9de2d3SDavid du Colombier 	case CMdebugep:
13695d9de2d3SDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0)
13705d9de2d3SDavid du Colombier 			ep->debug = 1;
13715d9de2d3SDavid du Colombier 		else if(strcmp(cb->f[1], "off") == 0)
13725d9de2d3SDavid du Colombier 			ep->debug = 0;
13735d9de2d3SDavid du Colombier 		else
13745d9de2d3SDavid du Colombier 			ep->debug = strtoul(cb->f[1], nil, 0);
13755d9de2d3SDavid du Colombier 		print("usb: ep%d.%d debug %d\n",
13765d9de2d3SDavid du Colombier 			ep->dev->nb, ep->nb, ep->debug);
13775d9de2d3SDavid du Colombier 		break;
13785d9de2d3SDavid du Colombier 	case CMname:
13795d9de2d3SDavid du Colombier 		deprint("usb epctl %s %s\n", cb->f[0], cb->f[1]);
13805d9de2d3SDavid du Colombier 		validname(cb->f[1], 0);
13815d9de2d3SDavid du Colombier 		kstrdup(&ep->name, cb->f[1]);
13825d9de2d3SDavid du Colombier 		break;
13835d9de2d3SDavid du Colombier 	case CMtmout:
13845d9de2d3SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
13855d9de2d3SDavid du Colombier 		if(ep->ttype == Tiso || ep->ttype == Tctl)
13865d9de2d3SDavid du Colombier 			error("ctl ignored for this endpoint type");
13875d9de2d3SDavid du Colombier 		ep->tmout = strtoul(cb->f[1], nil, 0);
13885d9de2d3SDavid du Colombier 		if(ep->tmout != 0 && ep->tmout < Xfertmout)
13895d9de2d3SDavid du Colombier 			ep->tmout = Xfertmout;
13905d9de2d3SDavid du Colombier 		break;
13915d9de2d3SDavid du Colombier 	case CMpreset:
13925d9de2d3SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
13935d9de2d3SDavid du Colombier 		if(ep->ttype != Tctl)
13945d9de2d3SDavid du Colombier 			error("not a control endpoint");
13955d9de2d3SDavid du Colombier 		if(ep->dev->state != Denabled)
13965d9de2d3SDavid du Colombier 			error("forbidden on devices not enabled");
13975d9de2d3SDavid du Colombier 		ep->dev->state = Dreset;
13985d9de2d3SDavid du Colombier 		break;
13995d9de2d3SDavid du Colombier 	default:
14005d9de2d3SDavid du Colombier 		panic("usb: unknown epctl %d", ct->index);
14015d9de2d3SDavid du Colombier 	}
14025d9de2d3SDavid du Colombier 	free(cb);
14035d9de2d3SDavid du Colombier 	poperror();
14045d9de2d3SDavid du Colombier 	return n;
14055d9de2d3SDavid du Colombier }
14065d9de2d3SDavid du Colombier 
14075d9de2d3SDavid du Colombier static long
usbctl(void * a,long n)14085d9de2d3SDavid du Colombier usbctl(void *a, long n)
14095d9de2d3SDavid du Colombier {
14105d9de2d3SDavid du Colombier 	Cmdtab *ct;
14115d9de2d3SDavid du Colombier 	Cmdbuf *cb;
14125d9de2d3SDavid du Colombier 	Ep *ep;
14135d9de2d3SDavid du Colombier 	int i;
14145d9de2d3SDavid du Colombier 
14155d9de2d3SDavid du Colombier 	cb = parsecmd(a, n);
14165d9de2d3SDavid du Colombier 	if(waserror()){
14175d9de2d3SDavid du Colombier 		free(cb);
14185d9de2d3SDavid du Colombier 		nexterror();
14195d9de2d3SDavid du Colombier 	}
14205d9de2d3SDavid du Colombier 	ct = lookupcmd(cb, usbctls, nelem(usbctls));
14215d9de2d3SDavid du Colombier 	dprint("usb ctl %s\n", cb->f[0]);
14225d9de2d3SDavid du Colombier 	switch(ct->index){
14235d9de2d3SDavid du Colombier 	case CMdebug:
14245d9de2d3SDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0)
14255d9de2d3SDavid du Colombier 			debug = 1;
14265d9de2d3SDavid du Colombier 		else if(strcmp(cb->f[1], "off") == 0)
14275d9de2d3SDavid du Colombier 			debug = 0;
14285d9de2d3SDavid du Colombier 		else
14295d9de2d3SDavid du Colombier 			debug = strtol(cb->f[1], nil, 0);
14305d9de2d3SDavid du Colombier 		print("usb: debug %d\n", debug);
14315d9de2d3SDavid du Colombier 		for(i = 0; i < epmax; i++)
14325d9de2d3SDavid du Colombier 			if((ep = getep(i)) != nil){
14335d9de2d3SDavid du Colombier 				ep->hp->debug(ep->hp, debug);
14345d9de2d3SDavid du Colombier 				putep(ep);
14355d9de2d3SDavid du Colombier 			}
14365d9de2d3SDavid du Colombier 		break;
14375d9de2d3SDavid du Colombier 	case CMdump:
14385d9de2d3SDavid du Colombier 		dumpeps();
14395d9de2d3SDavid du Colombier 		break;
14405d9de2d3SDavid du Colombier 	}
14415d9de2d3SDavid du Colombier 	free(cb);
14425d9de2d3SDavid du Colombier 	poperror();
14435d9de2d3SDavid du Colombier 	return n;
14445d9de2d3SDavid du Colombier }
14455d9de2d3SDavid du Colombier 
14465d9de2d3SDavid du Colombier static long
ctlwrite(Chan * c,void * a,long n)14475d9de2d3SDavid du Colombier ctlwrite(Chan *c, void *a, long n)
14485d9de2d3SDavid du Colombier {
14495d9de2d3SDavid du Colombier 	int q;
14505d9de2d3SDavid du Colombier 	Ep *ep;
14515d9de2d3SDavid du Colombier 
14525d9de2d3SDavid du Colombier 	q = QID(c->qid);
14535d9de2d3SDavid du Colombier 	if(q == Qctl)
14545d9de2d3SDavid du Colombier 		return usbctl(a, n);
14555d9de2d3SDavid du Colombier 
14565d9de2d3SDavid du Colombier 	ep = getep(qid2epidx(q));
14575d9de2d3SDavid du Colombier 	if(ep == nil)
14585d9de2d3SDavid du Colombier 		error(Eio);
14595d9de2d3SDavid du Colombier 	if(waserror()){
14605d9de2d3SDavid du Colombier 		putep(ep);
14615d9de2d3SDavid du Colombier 		nexterror();
14625d9de2d3SDavid du Colombier 	}
14635d9de2d3SDavid du Colombier 	if(ep->dev->state == Ddetach)
14645d9de2d3SDavid du Colombier 		error(Edetach);
14655d9de2d3SDavid du Colombier 	if(isqtype(q, Qepctl) && c->aux != nil){
14665d9de2d3SDavid du Colombier 		/* Be sure we don't keep a cloned ep name */
14675d9de2d3SDavid du Colombier 		free(c->aux);
14685d9de2d3SDavid du Colombier 		c->aux = nil;
14695d9de2d3SDavid du Colombier 		error("read, not write, expected");
14705d9de2d3SDavid du Colombier 	}
14715d9de2d3SDavid du Colombier 	n = epctl(ep, c, a, n);
14725d9de2d3SDavid du Colombier 	putep(ep);
14735d9de2d3SDavid du Colombier 	poperror();
14745d9de2d3SDavid du Colombier 	return n;
14755d9de2d3SDavid du Colombier }
14765d9de2d3SDavid du Colombier 
14775d9de2d3SDavid du Colombier static long
usbwrite(Chan * c,void * a,long n,vlong off)14785d9de2d3SDavid du Colombier usbwrite(Chan *c, void *a, long n, vlong off)
14795d9de2d3SDavid du Colombier {
14805d9de2d3SDavid du Colombier 	int nr, q;
14815d9de2d3SDavid du Colombier 	Ep *ep;
14825d9de2d3SDavid du Colombier 
14835d9de2d3SDavid du Colombier 	if(c->qid.type == QTDIR)
14845d9de2d3SDavid du Colombier 		error(Eisdir);
14855d9de2d3SDavid du Colombier 
14865d9de2d3SDavid du Colombier 	q = QID(c->qid);
14875d9de2d3SDavid du Colombier 
14885d9de2d3SDavid du Colombier 	if(q == Qctl || isqtype(q, Qepctl))
14895d9de2d3SDavid du Colombier 		return ctlwrite(c, a, n);
14905d9de2d3SDavid du Colombier 
14915d9de2d3SDavid du Colombier 	ep = getep(qid2epidx(q));
14925d9de2d3SDavid du Colombier 	if(ep == nil)
14935d9de2d3SDavid du Colombier 		error(Eio);
14945d9de2d3SDavid du Colombier 	if(waserror()){
14955d9de2d3SDavid du Colombier 		putep(ep);
14965d9de2d3SDavid du Colombier 		nexterror();
14975d9de2d3SDavid du Colombier 	}
14985d9de2d3SDavid du Colombier 	if(ep->dev->state == Ddetach)
14995d9de2d3SDavid du Colombier 		error(Edetach);
15005d9de2d3SDavid du Colombier 	if(ep->mode == OREAD || ep->inuse == 0)
15015d9de2d3SDavid du Colombier 		error(Ebadusefd);
15025d9de2d3SDavid du Colombier 
15035d9de2d3SDavid du Colombier 	switch(ep->ttype){
15045d9de2d3SDavid du Colombier 	case Tnone:
15055d9de2d3SDavid du Colombier 		error("endpoint not configured");
15065d9de2d3SDavid du Colombier 	case Tctl:
15075d9de2d3SDavid du Colombier 		nr = rhubwrite(ep, a, n);
15085d9de2d3SDavid du Colombier 		if(nr >= 0){
15095d9de2d3SDavid du Colombier 			n = nr;
15105d9de2d3SDavid du Colombier 			break;
15115d9de2d3SDavid du Colombier 		}
1512b4d1cf41SDavid du Colombier 		/* else fall */
15135d9de2d3SDavid du Colombier 	default:
1514b4d1cf41SDavid du Colombier 		ddeprint("\nusbwrite q %#x fid %d cnt %ld off %lld\n",q, c->fid, n, off);
15155d9de2d3SDavid du Colombier 		ep->hp->epwrite(ep, a, n);
15165d9de2d3SDavid du Colombier 	}
15175d9de2d3SDavid du Colombier 	putep(ep);
15185d9de2d3SDavid du Colombier 	poperror();
15195d9de2d3SDavid du Colombier 	return n;
15205d9de2d3SDavid du Colombier }
15215d9de2d3SDavid du Colombier 
15225d9de2d3SDavid du Colombier Block*
usbbread(Chan * c,long n,ulong offset)15235d9de2d3SDavid du Colombier usbbread(Chan *c, long n, ulong offset)
15245d9de2d3SDavid du Colombier {
15255d9de2d3SDavid du Colombier 	Block *bp;
15265d9de2d3SDavid du Colombier 
15275d9de2d3SDavid du Colombier 	bp = allocb(n);
15285d9de2d3SDavid du Colombier 	if(bp == 0)
15295d9de2d3SDavid du Colombier 		error(Enomem);
15305d9de2d3SDavid du Colombier 	if(waserror()) {
15315d9de2d3SDavid du Colombier 		freeb(bp);
15325d9de2d3SDavid du Colombier 		nexterror();
15335d9de2d3SDavid du Colombier 	}
15345d9de2d3SDavid du Colombier 	bp->wp += usbread(c, bp->wp, n, offset);
15355d9de2d3SDavid du Colombier 	poperror();
15365d9de2d3SDavid du Colombier 	return bp;
15375d9de2d3SDavid du Colombier }
15385d9de2d3SDavid du Colombier 
15395d9de2d3SDavid du Colombier long
usbbwrite(Chan * c,Block * bp,ulong offset)15405d9de2d3SDavid du Colombier usbbwrite(Chan *c, Block *bp, ulong offset)
15415d9de2d3SDavid du Colombier {
15425d9de2d3SDavid du Colombier 	long n;
15435d9de2d3SDavid du Colombier 
15445d9de2d3SDavid du Colombier 	if(waserror()) {
15455d9de2d3SDavid du Colombier 		freeb(bp);
15465d9de2d3SDavid du Colombier 		nexterror();
15475d9de2d3SDavid du Colombier 	}
15485d9de2d3SDavid du Colombier 	n = usbwrite(c, bp->rp, BLEN(bp), offset);
15495d9de2d3SDavid du Colombier 	poperror();
15505d9de2d3SDavid du Colombier 	freeb(bp);
15515d9de2d3SDavid du Colombier 
15525d9de2d3SDavid du Colombier 	return n;
15535d9de2d3SDavid du Colombier }
15545d9de2d3SDavid du Colombier 
15555d9de2d3SDavid du Colombier void
usbshutdown(void)15565d9de2d3SDavid du Colombier usbshutdown(void)
15575d9de2d3SDavid du Colombier {
15585d9de2d3SDavid du Colombier 	Hci *hp;
15595d9de2d3SDavid du Colombier 	int i;
15605d9de2d3SDavid du Colombier 
15615d9de2d3SDavid du Colombier 	for(i = 0; i < Nhcis; i++){
15625d9de2d3SDavid du Colombier 		hp = hcis[i];
15635d9de2d3SDavid du Colombier 		if(hp == nil)
15645d9de2d3SDavid du Colombier 			continue;
15655d9de2d3SDavid du Colombier 		if(hp->shutdown == nil)
15665d9de2d3SDavid du Colombier 			print("#u: no shutdown function for %s\n", hp->type);
15675d9de2d3SDavid du Colombier 		else
15685d9de2d3SDavid du Colombier 			hp->shutdown(hp);
15695d9de2d3SDavid du Colombier 	}
15705d9de2d3SDavid du Colombier }
15715d9de2d3SDavid du Colombier 
15725d9de2d3SDavid du Colombier Dev usbdevtab = {
15735d9de2d3SDavid du Colombier 	L'u',
15745d9de2d3SDavid du Colombier 	"usb",
15755d9de2d3SDavid du Colombier 
15765d9de2d3SDavid du Colombier 	usbreset,
15775d9de2d3SDavid du Colombier 	usbinit,
15785d9de2d3SDavid du Colombier 	usbshutdown,
15795d9de2d3SDavid du Colombier 	usbattach,
15805d9de2d3SDavid du Colombier 	usbwalk,
15815d9de2d3SDavid du Colombier 	usbstat,
15825d9de2d3SDavid du Colombier 	usbopen,
15835d9de2d3SDavid du Colombier 	devcreate,
15845d9de2d3SDavid du Colombier 	usbclose,
15855d9de2d3SDavid du Colombier 	usbread,
15865d9de2d3SDavid du Colombier 	usbbread,
15875d9de2d3SDavid du Colombier 	usbwrite,
15885d9de2d3SDavid du Colombier 	usbbwrite,
15895d9de2d3SDavid du Colombier 	devremove,
15905d9de2d3SDavid du Colombier 	devwstat,
15915d9de2d3SDavid du Colombier };
1592