xref: /plan9/sys/src/9/bcm/devusb.c (revision a587111c8770e522e3667ff2b63cba8a77811dd9)
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"
485d9de2d3SDavid du Colombier #include	"../port/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) */
765d9de2d3SDavid du Colombier 	CMnewdev,		/* newdev full|low|high 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 {
1575d9de2d3SDavid du Colombier 	[Fullspeed]	"full",
1585d9de2d3SDavid du Colombier 	[Lowspeed]	"low",
1595d9de2d3SDavid du Colombier 	[Highspeed]	"high",
1605d9de2d3SDavid du Colombier 	[Nospeed]	"no",
1615d9de2d3SDavid du Colombier };
1625d9de2d3SDavid du Colombier 
1635d9de2d3SDavid du Colombier static int	debug;
1645d9de2d3SDavid du Colombier static Hcitype	hcitypes[Nhcis];
1655d9de2d3SDavid du Colombier static Hci*	hcis[Nhcis];
1665d9de2d3SDavid du Colombier static QLock	epslck;		/* add, del, lookup endpoints */
1675d9de2d3SDavid du Colombier static Ep*	eps[Neps];	/* all endpoints known */
1685d9de2d3SDavid du Colombier static int	epmax;		/* 1 + last endpoint index used  */
1695d9de2d3SDavid du Colombier static int	usbidgen;	/* device address generator */
1705d9de2d3SDavid du Colombier 
1715d9de2d3SDavid du Colombier /*
1725d9de2d3SDavid du Colombier  * Is there something like this in a library? should it be?
1735d9de2d3SDavid du Colombier  */
1745d9de2d3SDavid du Colombier char*
seprintdata(char * s,char * se,uchar * d,int n)1755d9de2d3SDavid du Colombier seprintdata(char *s, char *se, uchar *d, int n)
1765d9de2d3SDavid du Colombier {
1775d9de2d3SDavid du Colombier 	int i, l;
1785d9de2d3SDavid du Colombier 
1795d9de2d3SDavid du Colombier 	s = seprint(s, se, " %#p[%d]: ", d, n);
1805d9de2d3SDavid du Colombier 	l = n;
1815d9de2d3SDavid du Colombier 	if(l > 10)
1825d9de2d3SDavid du Colombier 		l = 10;
1835d9de2d3SDavid du Colombier 	for(i=0; i<l; i++)
1845d9de2d3SDavid du Colombier 		s = seprint(s, se, " %2.2ux", d[i]);
1855d9de2d3SDavid du Colombier 	if(l < n)
1865d9de2d3SDavid du Colombier 		s = seprint(s, se, "...");
1875d9de2d3SDavid du Colombier 	return s;
1885d9de2d3SDavid du Colombier }
1895d9de2d3SDavid du Colombier 
1905d9de2d3SDavid du Colombier static int
name2speed(char * name)1915d9de2d3SDavid du Colombier name2speed(char *name)
1925d9de2d3SDavid du Colombier {
1935d9de2d3SDavid du Colombier 	int i;
1945d9de2d3SDavid du Colombier 
1955d9de2d3SDavid du Colombier 	for(i = 0; i < nelem(spname); i++)
1965d9de2d3SDavid du Colombier 		if(strcmp(name, spname[i]) == 0)
1975d9de2d3SDavid du Colombier 			return i;
1985d9de2d3SDavid du Colombier 	return Nospeed;
1995d9de2d3SDavid du Colombier }
2005d9de2d3SDavid du Colombier 
2015d9de2d3SDavid du Colombier static int
name2ttype(char * name)2025d9de2d3SDavid du Colombier name2ttype(char *name)
2035d9de2d3SDavid du Colombier {
2045d9de2d3SDavid du Colombier 	int i;
2055d9de2d3SDavid du Colombier 
2065d9de2d3SDavid du Colombier 	for(i = 0; i < nelem(ttname); i++)
2075d9de2d3SDavid du Colombier 		if(strcmp(name, ttname[i]) == 0)
2085d9de2d3SDavid du Colombier 			return i;
2095d9de2d3SDavid du Colombier 	/* may be a std. USB ep. type */
2105d9de2d3SDavid du Colombier 	i = strtol(name, nil, 0);
2115d9de2d3SDavid du Colombier 	switch(i+1){
2125d9de2d3SDavid du Colombier 	case Tctl:
2135d9de2d3SDavid du Colombier 	case Tiso:
2145d9de2d3SDavid du Colombier 	case Tbulk:
2155d9de2d3SDavid du Colombier 	case Tintr:
2165d9de2d3SDavid du Colombier 		return i+1;
2175d9de2d3SDavid du Colombier 	default:
2185d9de2d3SDavid du Colombier 		return Tnone;
2195d9de2d3SDavid du Colombier 	}
2205d9de2d3SDavid du Colombier }
2215d9de2d3SDavid du Colombier 
2225d9de2d3SDavid du Colombier static int
name2mode(char * mode)2235d9de2d3SDavid du Colombier name2mode(char *mode)
2245d9de2d3SDavid du Colombier {
2255d9de2d3SDavid du Colombier 	int i;
2265d9de2d3SDavid du Colombier 
2275d9de2d3SDavid du Colombier 	for(i = 0; i < nelem(usbmodename); i++)
2285d9de2d3SDavid du Colombier 		if(strcmp(mode, usbmodename[i]) == 0)
2295d9de2d3SDavid du Colombier 			return i;
2305d9de2d3SDavid du Colombier 	return -1;
2315d9de2d3SDavid du Colombier }
2325d9de2d3SDavid du Colombier 
2335d9de2d3SDavid du Colombier static int
qid2epidx(int q)2345d9de2d3SDavid du Colombier qid2epidx(int q)
2355d9de2d3SDavid du Colombier {
2365d9de2d3SDavid du Colombier 	q = (q-Qep0dir)/4;
2375d9de2d3SDavid du Colombier 	if(q < 0 || q >= epmax || eps[q] == nil)
2385d9de2d3SDavid du Colombier 		return -1;
2395d9de2d3SDavid du Colombier 	return q;
2405d9de2d3SDavid du Colombier }
2415d9de2d3SDavid du Colombier 
2425d9de2d3SDavid du Colombier static int
isqtype(int q,int type)2435d9de2d3SDavid du Colombier isqtype(int q, int type)
2445d9de2d3SDavid du Colombier {
2455d9de2d3SDavid du Colombier 	if(q < Qep0dir)
2465d9de2d3SDavid du Colombier 		return 0;
2475d9de2d3SDavid du Colombier 	q -= Qep0dir;
2485d9de2d3SDavid du Colombier 	return (q & 3) == type;
2495d9de2d3SDavid du Colombier }
2505d9de2d3SDavid du Colombier 
2515d9de2d3SDavid du Colombier void
addhcitype(char * t,int (* r)(Hci *))2525d9de2d3SDavid du Colombier addhcitype(char* t, int (*r)(Hci*))
2535d9de2d3SDavid du Colombier {
2545d9de2d3SDavid du Colombier 	static int ntype;
2555d9de2d3SDavid du Colombier 
2565d9de2d3SDavid du Colombier 	if(ntype == Nhcis)
2575d9de2d3SDavid du Colombier 		panic("too many USB host interface types");
2585d9de2d3SDavid du Colombier 	hcitypes[ntype].type = t;
2595d9de2d3SDavid du Colombier 	hcitypes[ntype].reset = r;
2605d9de2d3SDavid du Colombier 	ntype++;
2615d9de2d3SDavid du Colombier }
2625d9de2d3SDavid du Colombier 
2635d9de2d3SDavid du Colombier static char*
seprintep(char * s,char * se,Ep * ep,int all)2645d9de2d3SDavid du Colombier seprintep(char *s, char *se, Ep *ep, int all)
2655d9de2d3SDavid du Colombier {
2665d9de2d3SDavid du Colombier 	static char* dsnames[] = { "config", "enabled", "detached", "reset" };
267*b4d1cf41SDavid du Colombier 	Udev *d;
268*b4d1cf41SDavid du Colombier 	int i;
269*b4d1cf41SDavid du Colombier 	int di;
2705d9de2d3SDavid du Colombier 
2715d9de2d3SDavid du Colombier 	d = ep->dev;
2725d9de2d3SDavid du Colombier 
2735d9de2d3SDavid du Colombier 	qlock(ep);
2745d9de2d3SDavid du Colombier 	if(waserror()){
2755d9de2d3SDavid du Colombier 		qunlock(ep);
2765d9de2d3SDavid du Colombier 		nexterror();
2775d9de2d3SDavid du Colombier 	}
2785d9de2d3SDavid du Colombier 	di = ep->dev->nb;
2795d9de2d3SDavid du Colombier 	if(all)
2805d9de2d3SDavid du Colombier 		s = seprint(s, se, "dev %d ep %d ", di, ep->nb);
2815d9de2d3SDavid du Colombier 	s = seprint(s, se, "%s", dsnames[ep->dev->state]);
2825d9de2d3SDavid du Colombier 	s = seprint(s, se, " %s", ttname[ep->ttype]);
2835d9de2d3SDavid du Colombier 	assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR);
2845d9de2d3SDavid du Colombier 	s = seprint(s, se, " %s", usbmodename[ep->mode]);
2855d9de2d3SDavid du Colombier 	s = seprint(s, se, " speed %s", spname[d->speed]);
2865d9de2d3SDavid du Colombier 	s = seprint(s, se, " maxpkt %ld", ep->maxpkt);
2875d9de2d3SDavid du Colombier 	s = seprint(s, se, " pollival %ld", ep->pollival);
2885d9de2d3SDavid du Colombier 	s = seprint(s, se, " samplesz %ld", ep->samplesz);
2895d9de2d3SDavid du Colombier 	s = seprint(s, se, " hz %ld", ep->hz);
2905d9de2d3SDavid du Colombier 	s = seprint(s, se, " hub %d", ep->dev->hub);
2915d9de2d3SDavid du Colombier 	s = seprint(s, se, " port %d", ep->dev->port);
2925d9de2d3SDavid du Colombier 	if(ep->inuse)
2935d9de2d3SDavid du Colombier 		s = seprint(s, se, " busy");
2945d9de2d3SDavid du Colombier 	else
2955d9de2d3SDavid du Colombier 		s = seprint(s, se, " idle");
2965d9de2d3SDavid du Colombier 	if(all){
2975d9de2d3SDavid du Colombier 		s = seprint(s, se, " load %uld", ep->load);
2985d9de2d3SDavid du Colombier 		s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep);
2995d9de2d3SDavid du Colombier 		s = seprint(s, se, " idx %d", ep->idx);
3005d9de2d3SDavid du Colombier 		if(ep->name != nil)
3015d9de2d3SDavid du Colombier 			s = seprint(s, se, " name '%s'", ep->name);
3025d9de2d3SDavid du Colombier 		if(ep->tmout != 0)
3035d9de2d3SDavid du Colombier 			s = seprint(s, se, " tmout");
3045d9de2d3SDavid du Colombier 		if(ep == ep->ep0){
3055d9de2d3SDavid du Colombier 			s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno);
3065d9de2d3SDavid du Colombier 			s = seprint(s, se, " eps:");
3075d9de2d3SDavid du Colombier 			for(i = 0; i < nelem(d->eps); i++)
3085d9de2d3SDavid du Colombier 				if(d->eps[i] != nil)
3095d9de2d3SDavid du Colombier 					s = seprint(s, se, " ep%d.%d", di, i);
3105d9de2d3SDavid du Colombier 		}
3115d9de2d3SDavid du Colombier 	}
3125d9de2d3SDavid du Colombier 	if(ep->info != nil)
3135d9de2d3SDavid du Colombier 		s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type);
3145d9de2d3SDavid du Colombier 	else
3155d9de2d3SDavid du Colombier 		s = seprint(s, se, "\n");
3165d9de2d3SDavid du Colombier 	qunlock(ep);
3175d9de2d3SDavid du Colombier 	poperror();
3185d9de2d3SDavid du Colombier 	return s;
3195d9de2d3SDavid du Colombier }
3205d9de2d3SDavid du Colombier 
3215d9de2d3SDavid du Colombier static Ep*
epalloc(Hci * hp)3225d9de2d3SDavid du Colombier epalloc(Hci *hp)
3235d9de2d3SDavid du Colombier {
3245d9de2d3SDavid du Colombier 	Ep *ep;
3255d9de2d3SDavid du Colombier 	int i;
3265d9de2d3SDavid du Colombier 
3275d9de2d3SDavid du Colombier 	ep = smalloc(sizeof(Ep));
3285d9de2d3SDavid du Colombier 	ep->ref = 1;
3295d9de2d3SDavid du Colombier 	qlock(&epslck);
3305d9de2d3SDavid du Colombier 	for(i = 0; i < Neps; i++)
3315d9de2d3SDavid du Colombier 		if(eps[i] == nil)
3325d9de2d3SDavid du Colombier 			break;
3335d9de2d3SDavid du Colombier 	if(i == Neps){
3345d9de2d3SDavid du Colombier 		qunlock(&epslck);
3355d9de2d3SDavid du Colombier 		free(ep);
3365d9de2d3SDavid du Colombier 		print("usb: bug: too few endpoints.\n");
3375d9de2d3SDavid du Colombier 		return nil;
3385d9de2d3SDavid du Colombier 	}
3395d9de2d3SDavid du Colombier 	ep->idx = i;
3405d9de2d3SDavid du Colombier 	if(epmax <= i)
3415d9de2d3SDavid du Colombier 		epmax = i+1;
3425d9de2d3SDavid du Colombier 	eps[i] = ep;
3435d9de2d3SDavid du Colombier 	ep->hp = hp;
3445d9de2d3SDavid du Colombier 	ep->maxpkt = 8;
3455d9de2d3SDavid du Colombier 	ep->ntds = 1;
3465d9de2d3SDavid du Colombier 	ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */
3475d9de2d3SDavid du Colombier 	qunlock(&epslck);
3485d9de2d3SDavid du Colombier 	return ep;
3495d9de2d3SDavid du Colombier }
3505d9de2d3SDavid du Colombier 
3515d9de2d3SDavid du Colombier static Ep*
getep(int i)3525d9de2d3SDavid du Colombier getep(int i)
3535d9de2d3SDavid du Colombier {
3545d9de2d3SDavid du Colombier 	Ep *ep;
3555d9de2d3SDavid du Colombier 
3565d9de2d3SDavid du Colombier 	if(i < 0 || i >= epmax || eps[i] == nil)
3575d9de2d3SDavid du Colombier 		return nil;
3585d9de2d3SDavid du Colombier 	qlock(&epslck);
3595d9de2d3SDavid du Colombier 	ep = eps[i];
3605d9de2d3SDavid du Colombier 	if(ep != nil)
3615d9de2d3SDavid du Colombier 		incref(ep);
3625d9de2d3SDavid du Colombier 	qunlock(&epslck);
3635d9de2d3SDavid du Colombier 	return ep;
3645d9de2d3SDavid du Colombier }
3655d9de2d3SDavid du Colombier 
3665d9de2d3SDavid du Colombier static void
putep(Ep * ep)3675d9de2d3SDavid du Colombier putep(Ep *ep)
3685d9de2d3SDavid du Colombier {
3695d9de2d3SDavid du Colombier 	Udev *d;
3705d9de2d3SDavid du Colombier 
3715d9de2d3SDavid du Colombier 	if(ep != nil && decref(ep) == 0){
3725d9de2d3SDavid du Colombier 		d = ep->dev;
3735d9de2d3SDavid du Colombier 		deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep);
3745d9de2d3SDavid du Colombier 		qlock(&epslck);
3755d9de2d3SDavid du Colombier 		eps[ep->idx] = nil;
3765d9de2d3SDavid du Colombier 		if(ep->idx == epmax-1)
3775d9de2d3SDavid du Colombier 			epmax--;
3785d9de2d3SDavid du Colombier 		if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen)
3795d9de2d3SDavid du Colombier 			usbidgen--;
3805d9de2d3SDavid du Colombier 		qunlock(&epslck);
3815d9de2d3SDavid du Colombier 		if(d != nil){
3825d9de2d3SDavid du Colombier 			qlock(ep->ep0);
3835d9de2d3SDavid du Colombier 			d->eps[ep->nb] = nil;
3845d9de2d3SDavid du Colombier 			qunlock(ep->ep0);
3855d9de2d3SDavid du Colombier 		}
3865d9de2d3SDavid du Colombier 		if(ep->ep0 != ep){
3875d9de2d3SDavid du Colombier 			putep(ep->ep0);
3885d9de2d3SDavid du Colombier 			ep->ep0 = nil;
3895d9de2d3SDavid du Colombier 		}
3905d9de2d3SDavid du Colombier 		free(ep->info);
3915d9de2d3SDavid du Colombier 		free(ep->name);
3925d9de2d3SDavid du Colombier 		free(ep);
3935d9de2d3SDavid du Colombier 	}
3945d9de2d3SDavid du Colombier }
3955d9de2d3SDavid du Colombier 
3965d9de2d3SDavid du Colombier static void
dumpeps(void)3975d9de2d3SDavid du Colombier dumpeps(void)
3985d9de2d3SDavid du Colombier {
3995d9de2d3SDavid du Colombier 	int i;
4005d9de2d3SDavid du Colombier 	static char buf[512];
401*b4d1cf41SDavid du Colombier 	char *s;
402*b4d1cf41SDavid du Colombier 	char *e;
403*b4d1cf41SDavid du Colombier 	Ep *ep;
4045d9de2d3SDavid du Colombier 
4055d9de2d3SDavid du Colombier 	print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps);
4065d9de2d3SDavid du Colombier 	for(i = 0; i < epmax; i++){
4075d9de2d3SDavid du Colombier 		s = buf;
4085d9de2d3SDavid du Colombier 		e = buf+sizeof(buf);
4095d9de2d3SDavid du Colombier 		ep = getep(i);
4105d9de2d3SDavid du Colombier 		if(ep != nil){
4115d9de2d3SDavid du Colombier 			if(waserror()){
4125d9de2d3SDavid du Colombier 				putep(ep);
4135d9de2d3SDavid du Colombier 				nexterror();
4145d9de2d3SDavid du Colombier 			}
4155d9de2d3SDavid du Colombier 			s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb);
4165d9de2d3SDavid du Colombier 			seprintep(s, e, ep, 1);
4175d9de2d3SDavid du Colombier 			print("%s", buf);
4185d9de2d3SDavid du Colombier 			ep->hp->seprintep(buf, e, ep);
4195d9de2d3SDavid du Colombier 			print("%s", buf);
4205d9de2d3SDavid du Colombier 			poperror();
4215d9de2d3SDavid du Colombier 			putep(ep);
4225d9de2d3SDavid du Colombier 		}
4235d9de2d3SDavid du Colombier 	}
4245d9de2d3SDavid du Colombier 	print("usb dump hcis:\n");
4255d9de2d3SDavid du Colombier 	for(i = 0; i < Nhcis; i++)
4265d9de2d3SDavid du Colombier 		if(hcis[i] != nil)
4275d9de2d3SDavid du Colombier 			hcis[i]->dump(hcis[i]);
4285d9de2d3SDavid du Colombier }
4295d9de2d3SDavid du Colombier 
4305d9de2d3SDavid du Colombier static int
newusbid(Hci *)4315d9de2d3SDavid du Colombier newusbid(Hci *)
4325d9de2d3SDavid du Colombier {
4335d9de2d3SDavid du Colombier 	int id;
4345d9de2d3SDavid du Colombier 
4355d9de2d3SDavid du Colombier 	qlock(&epslck);
4365d9de2d3SDavid du Colombier 	id = ++usbidgen;
4375d9de2d3SDavid du Colombier 	if(id >= 0x7F)
4385d9de2d3SDavid du Colombier 		print("#u: too many device addresses; reuse them more\n");
4395d9de2d3SDavid du Colombier 	qunlock(&epslck);
4405d9de2d3SDavid du Colombier 	return id;
4415d9de2d3SDavid du Colombier }
4425d9de2d3SDavid du Colombier 
4435d9de2d3SDavid du Colombier /*
4445d9de2d3SDavid du Colombier  * Create endpoint 0 for a new device
4455d9de2d3SDavid du Colombier  */
4465d9de2d3SDavid du Colombier static Ep*
newdev(Hci * hp,int ishub,int isroot)4475d9de2d3SDavid du Colombier newdev(Hci *hp, int ishub, int isroot)
4485d9de2d3SDavid du Colombier {
4495d9de2d3SDavid du Colombier 	Ep *ep;
4505d9de2d3SDavid du Colombier 	Udev *d;
4515d9de2d3SDavid du Colombier 
4525d9de2d3SDavid du Colombier 	ep = epalloc(hp);
4535d9de2d3SDavid du Colombier 	d = ep->dev = smalloc(sizeof(Udev));
4545d9de2d3SDavid du Colombier 	d->nb = newusbid(hp);
4555d9de2d3SDavid du Colombier 	d->eps[0] = ep;
4565d9de2d3SDavid du Colombier 	ep->nb = 0;
4575d9de2d3SDavid du Colombier 	ep->toggle[0] = ep->toggle[1] = 0;
4585d9de2d3SDavid du Colombier 	d->ishub = ishub;
4595d9de2d3SDavid du Colombier 	d->isroot = isroot;
4605d9de2d3SDavid du Colombier 	if(hp->highspeed != 0)
4615d9de2d3SDavid du Colombier 		d->speed = Highspeed;
4625d9de2d3SDavid du Colombier 	else
4635d9de2d3SDavid du Colombier 		d->speed = Fullspeed;
4645d9de2d3SDavid du Colombier 	d->state = Dconfig;		/* address not yet set */
4655d9de2d3SDavid du Colombier 	ep->dev = d;
4665d9de2d3SDavid du Colombier 	ep->ep0 = ep;			/* no ref counted here */
4675d9de2d3SDavid du Colombier 	ep->ttype = Tctl;
4685d9de2d3SDavid du Colombier 	ep->tmout = Xfertmout;
4695d9de2d3SDavid du Colombier 	ep->mode = ORDWR;
4705d9de2d3SDavid du Colombier 	dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep);
4715d9de2d3SDavid du Colombier 	return ep;
4725d9de2d3SDavid du Colombier }
4735d9de2d3SDavid du Colombier 
4745d9de2d3SDavid du Colombier /*
4755d9de2d3SDavid du Colombier  * Create a new endpoint for the device
4765d9de2d3SDavid du Colombier  * accessed via the given endpoint 0.
4775d9de2d3SDavid du Colombier  */
4785d9de2d3SDavid du Colombier static Ep*
newdevep(Ep * ep,int i,int tt,int mode)4795d9de2d3SDavid du Colombier newdevep(Ep *ep, int i, int tt, int mode)
4805d9de2d3SDavid du Colombier {
4815d9de2d3SDavid du Colombier 	Ep *nep;
4825d9de2d3SDavid du Colombier 	Udev *d;
4835d9de2d3SDavid du Colombier 
4845d9de2d3SDavid du Colombier 	d = ep->dev;
4855d9de2d3SDavid du Colombier 	if(d->eps[i] != nil)
4865d9de2d3SDavid du Colombier 		error("endpoint already in use");
4875d9de2d3SDavid du Colombier 	nep = epalloc(ep->hp);
4885d9de2d3SDavid du Colombier 	incref(ep);
4895d9de2d3SDavid du Colombier 	d->eps[i] = nep;
4905d9de2d3SDavid du Colombier 	nep->nb = i;
4915d9de2d3SDavid du Colombier 	nep->toggle[0] = nep->toggle[1] = 0;
4925d9de2d3SDavid du Colombier 	nep->ep0 = ep;
4935d9de2d3SDavid du Colombier 	nep->dev = ep->dev;
4945d9de2d3SDavid du Colombier 	nep->mode = mode;
4955d9de2d3SDavid du Colombier 	nep->ttype = tt;
4965d9de2d3SDavid du Colombier 	nep->debug = ep->debug;
4975d9de2d3SDavid du Colombier 	/* set defaults */
4985d9de2d3SDavid du Colombier 	switch(tt){
4995d9de2d3SDavid du Colombier 	case Tctl:
5005d9de2d3SDavid du Colombier 		nep->tmout = Xfertmout;
5015d9de2d3SDavid du Colombier 		break;
5025d9de2d3SDavid du Colombier 	case Tintr:
5035d9de2d3SDavid du Colombier 		nep->pollival = 10;
5045d9de2d3SDavid du Colombier 		break;
5055d9de2d3SDavid du Colombier 	case Tiso:
5065d9de2d3SDavid du Colombier 		nep->tmout = Xfertmout;
5075d9de2d3SDavid du Colombier 		nep->pollival = 10;
5085d9de2d3SDavid du Colombier 		nep->samplesz = 4;
5095d9de2d3SDavid du Colombier 		nep->hz = 44100;
5105d9de2d3SDavid du Colombier 		break;
5115d9de2d3SDavid du Colombier 	}
5125d9de2d3SDavid du Colombier 	deprint("newdevep ep%d.%d %#p\n", d->nb, nep->nb, nep);
5135d9de2d3SDavid du Colombier 	return ep;
5145d9de2d3SDavid du Colombier }
5155d9de2d3SDavid du Colombier 
5165d9de2d3SDavid du Colombier static int
epdataperm(int mode)5175d9de2d3SDavid du Colombier epdataperm(int mode)
5185d9de2d3SDavid du Colombier {
519*b4d1cf41SDavid du Colombier 
5205d9de2d3SDavid du Colombier 	switch(mode){
5215d9de2d3SDavid du Colombier 	case OREAD:
5225d9de2d3SDavid du Colombier 		return 0440|DMEXCL;
5235d9de2d3SDavid du Colombier 		break;
5245d9de2d3SDavid du Colombier 	case OWRITE:
5255d9de2d3SDavid du Colombier 		return 0220|DMEXCL;
5265d9de2d3SDavid du Colombier 		break;
5275d9de2d3SDavid du Colombier 	default:
5285d9de2d3SDavid du Colombier 		return 0660|DMEXCL;
5295d9de2d3SDavid du Colombier 	}
5305d9de2d3SDavid du Colombier }
5315d9de2d3SDavid du Colombier 
5325d9de2d3SDavid du Colombier static int
usbgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)5335d9de2d3SDavid du Colombier usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
5345d9de2d3SDavid du Colombier {
5355d9de2d3SDavid du Colombier 	Qid q;
536*b4d1cf41SDavid du Colombier 	Dirtab *dir;
537*b4d1cf41SDavid du Colombier 	int perm;
538*b4d1cf41SDavid du Colombier 	char *se;
539*b4d1cf41SDavid du Colombier 	Ep *ep;
540*b4d1cf41SDavid du Colombier 	int nb;
541*b4d1cf41SDavid du Colombier 	int mode;
5425d9de2d3SDavid du Colombier 
5435d9de2d3SDavid du Colombier 	if(0)ddprint("usbgen q %#x s %d...", QID(c->qid), s);
5445d9de2d3SDavid du Colombier 	if(s == DEVDOTDOT){
5455d9de2d3SDavid du Colombier 		if(QID(c->qid) <= Qusbdir){
5465d9de2d3SDavid du Colombier 			mkqid(&q, Qdir, 0, QTDIR);
5475d9de2d3SDavid du Colombier 			devdir(c, q, "#u", 0, eve, 0555, dp);
5485d9de2d3SDavid du Colombier 		}else{
5495d9de2d3SDavid du Colombier 			mkqid(&q, Qusbdir, 0, QTDIR);
5505d9de2d3SDavid du Colombier 			devdir(c, q, "usb", 0, eve, 0555, dp);
5515d9de2d3SDavid du Colombier 		}
5525d9de2d3SDavid du Colombier 		if(0)ddprint("ok\n");
5535d9de2d3SDavid du Colombier 		return 1;
5545d9de2d3SDavid du Colombier 	}
5555d9de2d3SDavid du Colombier 
5565d9de2d3SDavid du Colombier 	switch(QID(c->qid)){
5575d9de2d3SDavid du Colombier 	case Qdir:				/* list #u */
5585d9de2d3SDavid du Colombier 		if(s == 0){
5595d9de2d3SDavid du Colombier 			mkqid(&q, Qusbdir, 0, QTDIR);
5605d9de2d3SDavid du Colombier 			devdir(c, q, "usb", 0, eve, 0555, dp);
5615d9de2d3SDavid du Colombier 			if(0)ddprint("ok\n");
5625d9de2d3SDavid du Colombier 			return 1;
5635d9de2d3SDavid du Colombier 		}
5645d9de2d3SDavid du Colombier 		s--;
5655d9de2d3SDavid du Colombier 		if(s < 0 || s >= epmax)
5665d9de2d3SDavid du Colombier 			goto Fail;
5675d9de2d3SDavid du Colombier 		ep = getep(s);
5685d9de2d3SDavid du Colombier 		if(ep == nil || ep->name == nil){
5695d9de2d3SDavid du Colombier 			if(ep != nil)
5705d9de2d3SDavid du Colombier 				putep(ep);
5715d9de2d3SDavid du Colombier 			if(0)ddprint("skip\n");
5725d9de2d3SDavid du Colombier 			return 0;
5735d9de2d3SDavid du Colombier 		}
5745d9de2d3SDavid du Colombier 		if(waserror()){
5755d9de2d3SDavid du Colombier 			putep(ep);
5765d9de2d3SDavid du Colombier 			nexterror();
5775d9de2d3SDavid du Colombier 		}
5785d9de2d3SDavid du Colombier 		mkqid(&q, Qep0io+s*4, 0, QTFILE);
5795d9de2d3SDavid du Colombier 		devdir(c, q, ep->name, 0, eve, epdataperm(ep->mode), dp);
5805d9de2d3SDavid du Colombier 		putep(ep);
5815d9de2d3SDavid du Colombier 		poperror();
5825d9de2d3SDavid du Colombier 		if(0)ddprint("ok\n");
5835d9de2d3SDavid du Colombier 		return 1;
5845d9de2d3SDavid du Colombier 
5855d9de2d3SDavid du Colombier 	case Qusbdir:				/* list #u/usb */
5865d9de2d3SDavid du Colombier 	Usbdir:
5875d9de2d3SDavid du Colombier 		if(s < nelem(usbdir)){
5885d9de2d3SDavid du Colombier 			dir = &usbdir[s];
5895d9de2d3SDavid du Colombier 			mkqid(&q, dir->qid.path, 0, QTFILE);
5905d9de2d3SDavid du Colombier 			devdir(c, q, dir->name, dir->length, eve, dir->perm, dp);
5915d9de2d3SDavid du Colombier 			if(0)ddprint("ok\n");
5925d9de2d3SDavid du Colombier 			return 1;
5935d9de2d3SDavid du Colombier 		}
5945d9de2d3SDavid du Colombier 		s -= nelem(usbdir);
5955d9de2d3SDavid du Colombier 		if(s < 0 || s >= epmax)
5965d9de2d3SDavid du Colombier 			goto Fail;
5975d9de2d3SDavid du Colombier 		ep = getep(s);
5985d9de2d3SDavid du Colombier 		if(ep == nil){
5995d9de2d3SDavid du Colombier 			if(0)ddprint("skip\n");
6005d9de2d3SDavid du Colombier 			return 0;
6015d9de2d3SDavid du Colombier 		}
6025d9de2d3SDavid du Colombier 		if(waserror()){
6035d9de2d3SDavid du Colombier 			putep(ep);
6045d9de2d3SDavid du Colombier 			nexterror();
6055d9de2d3SDavid du Colombier 		}
6065d9de2d3SDavid du Colombier 		se = up->genbuf+sizeof(up->genbuf);
6075d9de2d3SDavid du Colombier 		seprint(up->genbuf, se, "ep%d.%d", ep->dev->nb, ep->nb);
6085d9de2d3SDavid du Colombier 		mkqid(&q, Qep0dir+4*s, 0, QTDIR);
6095d9de2d3SDavid du Colombier 		putep(ep);
6105d9de2d3SDavid du Colombier 		poperror();
6115d9de2d3SDavid du Colombier 		devdir(c, q, up->genbuf, 0, eve, 0755, dp);
6125d9de2d3SDavid du Colombier 		if(0)ddprint("ok\n");
6135d9de2d3SDavid du Colombier 		return 1;
6145d9de2d3SDavid du Colombier 
6155d9de2d3SDavid du Colombier 	case Qctl:
6165d9de2d3SDavid du Colombier 		s = 0;
6175d9de2d3SDavid du Colombier 		goto Usbdir;
6185d9de2d3SDavid du Colombier 
6195d9de2d3SDavid du Colombier 	default:				/* list #u/usb/epN.M */
6205d9de2d3SDavid du Colombier 		nb = qid2epidx(QID(c->qid));
6215d9de2d3SDavid du Colombier 		ep = getep(nb);
6225d9de2d3SDavid du Colombier 		if(ep == nil)
6235d9de2d3SDavid du Colombier 			goto Fail;
6245d9de2d3SDavid du Colombier 		mode = ep->mode;
6255d9de2d3SDavid du Colombier 		putep(ep);
6265d9de2d3SDavid du Colombier 		if(isqtype(QID(c->qid), Qepdir)){
6275d9de2d3SDavid du Colombier 		Epdir:
6285d9de2d3SDavid du Colombier 			switch(s){
6295d9de2d3SDavid du Colombier 			case 0:
6305d9de2d3SDavid du Colombier 				mkqid(&q, Qep0io+nb*4, 0, QTFILE);
6315d9de2d3SDavid du Colombier 				perm = epdataperm(mode);
6325d9de2d3SDavid du Colombier 				devdir(c, q, "data", 0, eve, perm, dp);
6335d9de2d3SDavid du Colombier 				break;
6345d9de2d3SDavid du Colombier 			case 1:
6355d9de2d3SDavid du Colombier 				mkqid(&q, Qep0ctl+nb*4, 0, QTFILE);
6365d9de2d3SDavid du Colombier 				devdir(c, q, "ctl", 0, eve, 0664, dp);
6375d9de2d3SDavid du Colombier 				break;
6385d9de2d3SDavid du Colombier 			default:
6395d9de2d3SDavid du Colombier 				goto Fail;
6405d9de2d3SDavid du Colombier 			}
6415d9de2d3SDavid du Colombier 		}else if(isqtype(QID(c->qid), Qepctl)){
6425d9de2d3SDavid du Colombier 			s = 1;
6435d9de2d3SDavid du Colombier 			goto Epdir;
6445d9de2d3SDavid du Colombier 		}else{
6455d9de2d3SDavid du Colombier 			s = 0;
6465d9de2d3SDavid du Colombier 			goto Epdir;
6475d9de2d3SDavid du Colombier 		}
6485d9de2d3SDavid du Colombier 		if(0)ddprint("ok\n");
6495d9de2d3SDavid du Colombier 		return 1;
6505d9de2d3SDavid du Colombier 	}
6515d9de2d3SDavid du Colombier Fail:
6525d9de2d3SDavid du Colombier 	if(0)ddprint("fail\n");
6535d9de2d3SDavid du Colombier 	return -1;
6545d9de2d3SDavid du Colombier }
6555d9de2d3SDavid du Colombier 
6565d9de2d3SDavid du Colombier static Hci*
hciprobe(int cardno,int ctlrno)6575d9de2d3SDavid du Colombier hciprobe(int cardno, int ctlrno)
6585d9de2d3SDavid du Colombier {
6595d9de2d3SDavid du Colombier 	Hci *hp;
6605d9de2d3SDavid du Colombier 	char *type;
6615d9de2d3SDavid du Colombier 	char name[64];
6625d9de2d3SDavid du Colombier 	static int epnb = 1;	/* guess the endpoint nb. for the controller */
6635d9de2d3SDavid du Colombier 
6645d9de2d3SDavid du Colombier 	ddprint("hciprobe %d %d\n", cardno, ctlrno);
6655d9de2d3SDavid du Colombier 	hp = smalloc(sizeof(Hci));
6665d9de2d3SDavid du Colombier 	hp->ctlrno = ctlrno;
6675d9de2d3SDavid du Colombier 
6685d9de2d3SDavid du Colombier 	if(cardno < 0)
6695d9de2d3SDavid du Colombier 		for(cardno = 0; cardno < Nhcis; cardno++){
6705d9de2d3SDavid du Colombier 			if(hcitypes[cardno].type == nil)
6715d9de2d3SDavid du Colombier 				break;
6725d9de2d3SDavid du Colombier 			type = hp->type;
6735d9de2d3SDavid du Colombier 			if(type==nil || *type==0)
6745d9de2d3SDavid du Colombier 				type = "uhci";
6755d9de2d3SDavid du Colombier 			if(cistrcmp(hcitypes[cardno].type, type) == 0)
6765d9de2d3SDavid du Colombier 				break;
6775d9de2d3SDavid du Colombier 		}
6785d9de2d3SDavid du Colombier 
6795d9de2d3SDavid du Colombier 	if(cardno >= Nhcis || hcitypes[cardno].type == nil){
6805d9de2d3SDavid du Colombier 		free(hp);
6815d9de2d3SDavid du Colombier 		return nil;
6825d9de2d3SDavid du Colombier 	}
6835d9de2d3SDavid du Colombier 	dprint("%s...", hcitypes[cardno].type);
6845d9de2d3SDavid du Colombier 	if(hcitypes[cardno].reset(hp) < 0){
6855d9de2d3SDavid du Colombier 		free(hp);
6865d9de2d3SDavid du Colombier 		return nil;
6875d9de2d3SDavid du Colombier 	}
6885d9de2d3SDavid du Colombier 
6895d9de2d3SDavid du Colombier 	snprint(name, sizeof(name), "usb%s", hcitypes[cardno].type);
6905d9de2d3SDavid du Colombier 	intrenable(hp->irq, hp->interrupt, hp, UNKNOWN, name);
6915d9de2d3SDavid du Colombier 
6925d9de2d3SDavid du Colombier 	print("#u/usb/ep%d.0: %s: port %#luX irq %d\n",
6935d9de2d3SDavid du Colombier 		epnb, hcitypes[cardno].type, hp->port, hp->irq);
6945d9de2d3SDavid du Colombier 	epnb++;
6955d9de2d3SDavid du Colombier 
6965d9de2d3SDavid du Colombier 	return hp;
6975d9de2d3SDavid du Colombier }
6985d9de2d3SDavid du Colombier 
6995d9de2d3SDavid du Colombier static void
usbreset(void)7005d9de2d3SDavid du Colombier usbreset(void)
7015d9de2d3SDavid du Colombier {
7025d9de2d3SDavid du Colombier 	int cardno, ctlrno;
7035d9de2d3SDavid du Colombier 	Hci *hp;
7045d9de2d3SDavid du Colombier 
7055d9de2d3SDavid du Colombier 	dprint("usbreset\n");
7065d9de2d3SDavid du Colombier 
7075d9de2d3SDavid du Colombier 	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++)
7085d9de2d3SDavid du Colombier 		if((hp = hciprobe(-1, ctlrno)) != nil)
7095d9de2d3SDavid du Colombier 			hcis[ctlrno] = hp;
7105d9de2d3SDavid du Colombier 	cardno = ctlrno = 0;
7115d9de2d3SDavid du Colombier 	while(cardno < Nhcis && ctlrno < Nhcis && hcitypes[cardno].type != nil)
7125d9de2d3SDavid du Colombier 		if(hcis[ctlrno] != nil)
7135d9de2d3SDavid du Colombier 			ctlrno++;
7145d9de2d3SDavid du Colombier 		else{
7155d9de2d3SDavid du Colombier 			hp = hciprobe(cardno, ctlrno);
7165d9de2d3SDavid du Colombier 			if(hp == nil)
7175d9de2d3SDavid du Colombier 				cardno++;
7185d9de2d3SDavid du Colombier 			hcis[ctlrno++] = hp;
7195d9de2d3SDavid du Colombier 		}
7205d9de2d3SDavid du Colombier 	if(hcis[Nhcis-1] != nil)
7215d9de2d3SDavid du Colombier 		print("usbreset: bug: Nhcis too small\n");
7225d9de2d3SDavid du Colombier }
7235d9de2d3SDavid du Colombier 
7245d9de2d3SDavid du Colombier static void
usbinit(void)7255d9de2d3SDavid du Colombier usbinit(void)
7265d9de2d3SDavid du Colombier {
7275d9de2d3SDavid du Colombier 	Hci *hp;
7285d9de2d3SDavid du Colombier 	int ctlrno;
7295d9de2d3SDavid du Colombier 	Ep *d;
7305d9de2d3SDavid du Colombier 	char info[40];
7315d9de2d3SDavid du Colombier 
7325d9de2d3SDavid du Colombier 	dprint("usbinit\n");
7335d9de2d3SDavid du Colombier 	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){
7345d9de2d3SDavid du Colombier 		hp = hcis[ctlrno];
7355d9de2d3SDavid du Colombier 		if(hp != nil){
7365d9de2d3SDavid du Colombier 			if(hp->init != nil)
7375d9de2d3SDavid du Colombier 				hp->init(hp);
7385d9de2d3SDavid du Colombier 			d = newdev(hp, 1, 1);		/* new root hub */
7395d9de2d3SDavid du Colombier 			d->dev->state = Denabled;	/* although addr == 0 */
7405d9de2d3SDavid du Colombier 			d->maxpkt = 64;
7415d9de2d3SDavid du Colombier 			snprint(info, sizeof(info), "ports %d", hp->nports);
7425d9de2d3SDavid du Colombier 			kstrdup(&d->info, info);
7435d9de2d3SDavid du Colombier 		}
7445d9de2d3SDavid du Colombier 	}
7455d9de2d3SDavid du Colombier }
7465d9de2d3SDavid du Colombier 
7475d9de2d3SDavid du Colombier static Chan*
usbattach(char * spec)7485d9de2d3SDavid du Colombier usbattach(char *spec)
7495d9de2d3SDavid du Colombier {
7505d9de2d3SDavid du Colombier 	return devattach(L'u', spec);
7515d9de2d3SDavid du Colombier }
7525d9de2d3SDavid du Colombier 
7535d9de2d3SDavid du Colombier static Walkqid*
usbwalk(Chan * c,Chan * nc,char ** name,int nname)7545d9de2d3SDavid du Colombier usbwalk(Chan *c, Chan *nc, char **name, int nname)
7555d9de2d3SDavid du Colombier {
7565d9de2d3SDavid du Colombier 	return devwalk(c, nc, name, nname, nil, 0, usbgen);
7575d9de2d3SDavid du Colombier }
7585d9de2d3SDavid du Colombier 
7595d9de2d3SDavid du Colombier static int
usbstat(Chan * c,uchar * db,int n)7605d9de2d3SDavid du Colombier usbstat(Chan *c, uchar *db, int n)
7615d9de2d3SDavid du Colombier {
7625d9de2d3SDavid du Colombier 	return devstat(c, db, n, nil, 0, usbgen);
7635d9de2d3SDavid du Colombier }
7645d9de2d3SDavid du Colombier 
7655d9de2d3SDavid du Colombier /*
7665d9de2d3SDavid du Colombier  * µs for the given transfer, for bandwidth allocation.
7675d9de2d3SDavid du Colombier  * This is a very rough worst case for what 5.11.3
7685d9de2d3SDavid du Colombier  * of the usb 2.0 spec says.
7695d9de2d3SDavid du Colombier  * Also, we are using maxpkt and not actual transfer sizes.
7705d9de2d3SDavid du Colombier  * Only when we are sure we
7715d9de2d3SDavid du Colombier  * are not exceeding b/w might we consider adjusting it.
7725d9de2d3SDavid du Colombier  */
7735d9de2d3SDavid du Colombier static ulong
usbload(int speed,int maxpkt)7745d9de2d3SDavid du Colombier usbload(int speed, int maxpkt)
7755d9de2d3SDavid du Colombier {
7765d9de2d3SDavid du Colombier 	enum{ Hostns = 1000, Hubns = 333 };
777*b4d1cf41SDavid du Colombier 	ulong l;
778*b4d1cf41SDavid du Colombier 	ulong bs;
7795d9de2d3SDavid du Colombier 
7805d9de2d3SDavid du Colombier 	l = 0;
7815d9de2d3SDavid du Colombier 	bs = 10UL * maxpkt;
7825d9de2d3SDavid du Colombier 	switch(speed){
7835d9de2d3SDavid du Colombier 	case Highspeed:
7845d9de2d3SDavid du Colombier 		l = 55*8*2 + 2 * (3 + bs) + Hostns;
7855d9de2d3SDavid du Colombier 		break;
7865d9de2d3SDavid du Colombier 	case Fullspeed:
7875d9de2d3SDavid du Colombier 		l = 9107 + 84 * (4 + bs) + Hostns;
7885d9de2d3SDavid du Colombier 		break;
7895d9de2d3SDavid du Colombier 	case Lowspeed:
7905d9de2d3SDavid du Colombier 		l = 64107 + 2 * Hubns + 667 * (3 + bs) + Hostns;
7915d9de2d3SDavid du Colombier 		break;
7925d9de2d3SDavid du Colombier 	default:
7935d9de2d3SDavid du Colombier 		print("usbload: bad speed %d\n", speed);
7945d9de2d3SDavid du Colombier 		/* let it run */
7955d9de2d3SDavid du Colombier 	}
7965d9de2d3SDavid du Colombier 	return l / 1000UL;	/* in µs */
7975d9de2d3SDavid du Colombier }
7985d9de2d3SDavid du Colombier 
7995d9de2d3SDavid du Colombier static Chan*
usbopen(Chan * c,int omode)8005d9de2d3SDavid du Colombier usbopen(Chan *c, int omode)
8015d9de2d3SDavid du Colombier {
802*b4d1cf41SDavid du Colombier 	int q;
8035d9de2d3SDavid du Colombier 	Ep *ep;
804*b4d1cf41SDavid du Colombier 	int mode;
8055d9de2d3SDavid du Colombier 
8065d9de2d3SDavid du Colombier 	mode = openmode(omode);
8075d9de2d3SDavid du Colombier 	q = QID(c->qid);
8085d9de2d3SDavid du Colombier 
8095d9de2d3SDavid du Colombier 	if(q >= Qep0dir && qid2epidx(q) < 0)
8105d9de2d3SDavid du Colombier 		error(Eio);
8115d9de2d3SDavid du Colombier 	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
8125d9de2d3SDavid du Colombier 		return devopen(c, omode, nil, 0, usbgen);
8135d9de2d3SDavid du Colombier 
8145d9de2d3SDavid du Colombier 	ep = getep(qid2epidx(q));
8155d9de2d3SDavid du Colombier 	if(ep == nil)
8165d9de2d3SDavid du Colombier 		error(Eio);
8175d9de2d3SDavid du Colombier 	deprint("usbopen q %#x fid %d omode %d\n", q, c->fid, mode);
8185d9de2d3SDavid du Colombier 	if(waserror()){
8195d9de2d3SDavid du Colombier 		putep(ep);
8205d9de2d3SDavid du Colombier 		nexterror();
8215d9de2d3SDavid du Colombier 	}
8225d9de2d3SDavid du Colombier 	qlock(ep);
8235d9de2d3SDavid du Colombier 	if(ep->inuse){
8245d9de2d3SDavid du Colombier 		qunlock(ep);
8255d9de2d3SDavid du Colombier 		error(Einuse);
8265d9de2d3SDavid du Colombier 	}
8275d9de2d3SDavid du Colombier 	ep->inuse = 1;
8285d9de2d3SDavid du Colombier 	qunlock(ep);
8295d9de2d3SDavid du Colombier 	if(waserror()){
8305d9de2d3SDavid du Colombier 		ep->inuse = 0;
8315d9de2d3SDavid du Colombier 		nexterror();
8325d9de2d3SDavid du Colombier 	}
8335d9de2d3SDavid du Colombier 	if(mode != OREAD && ep->mode == OREAD)
8345d9de2d3SDavid du Colombier 		error(Eperm);
8355d9de2d3SDavid du Colombier 	if(mode != OWRITE && ep->mode == OWRITE)
8365d9de2d3SDavid du Colombier 		error(Eperm);
8375d9de2d3SDavid du Colombier 	if(ep->ttype == Tnone)
8385d9de2d3SDavid du Colombier 		error(Enotconf);
8395d9de2d3SDavid du Colombier 	ep->clrhalt = 0;
8405d9de2d3SDavid du Colombier 	ep->rhrepl = -1;
8415d9de2d3SDavid du Colombier 	if(ep->load == 0)
8425d9de2d3SDavid du Colombier 		ep->load = usbload(ep->dev->speed, ep->maxpkt);
8435d9de2d3SDavid du Colombier 	ep->hp->epopen(ep);
8445d9de2d3SDavid du Colombier 
8455d9de2d3SDavid du Colombier 	poperror();	/* ep->inuse */
8465d9de2d3SDavid du Colombier 	poperror();	/* don't putep(): ref kept for fid using the ep. */
8475d9de2d3SDavid du Colombier 
8485d9de2d3SDavid du Colombier 	c->mode = mode;
8495d9de2d3SDavid du Colombier 	c->flag |= COPEN;
8505d9de2d3SDavid du Colombier 	c->offset = 0;
8515d9de2d3SDavid du Colombier 	c->aux = nil;	/* paranoia */
8525d9de2d3SDavid du Colombier 	return c;
8535d9de2d3SDavid du Colombier }
8545d9de2d3SDavid du Colombier 
8555d9de2d3SDavid du Colombier static void
epclose(Ep * ep)8565d9de2d3SDavid du Colombier epclose(Ep *ep)
8575d9de2d3SDavid du Colombier {
8585d9de2d3SDavid du Colombier 	qlock(ep);
8595d9de2d3SDavid du Colombier 	if(waserror()){
8605d9de2d3SDavid du Colombier 		qunlock(ep);
8615d9de2d3SDavid du Colombier 		nexterror();
8625d9de2d3SDavid du Colombier 	}
8635d9de2d3SDavid du Colombier 	if(ep->inuse){
8645d9de2d3SDavid du Colombier 		ep->hp->epclose(ep);
8655d9de2d3SDavid du Colombier 		ep->inuse = 0;
8665d9de2d3SDavid du Colombier 	}
8675d9de2d3SDavid du Colombier 	qunlock(ep);
8685d9de2d3SDavid du Colombier 	poperror();
8695d9de2d3SDavid du Colombier }
8705d9de2d3SDavid du Colombier 
8715d9de2d3SDavid du Colombier static void
usbclose(Chan * c)8725d9de2d3SDavid du Colombier usbclose(Chan *c)
8735d9de2d3SDavid du Colombier {
8745d9de2d3SDavid du Colombier 	int q;
8755d9de2d3SDavid du Colombier 	Ep *ep;
8765d9de2d3SDavid du Colombier 
8775d9de2d3SDavid du Colombier 	q = QID(c->qid);
8785d9de2d3SDavid du Colombier 	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
8795d9de2d3SDavid du Colombier 		return;
8805d9de2d3SDavid du Colombier 
8815d9de2d3SDavid du Colombier 	ep = getep(qid2epidx(q));
8825d9de2d3SDavid du Colombier 	if(ep == nil)
8835d9de2d3SDavid du Colombier 		return;
8845d9de2d3SDavid du Colombier 	deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref);
8855d9de2d3SDavid du Colombier 	if(waserror()){
8865d9de2d3SDavid du Colombier 		putep(ep);
8875d9de2d3SDavid du Colombier 		nexterror();
8885d9de2d3SDavid du Colombier 	}
8895d9de2d3SDavid du Colombier 	if(c->flag & COPEN){
8905d9de2d3SDavid du Colombier 		free(c->aux);
8915d9de2d3SDavid du Colombier 		c->aux = nil;
8925d9de2d3SDavid du Colombier 		epclose(ep);
8935d9de2d3SDavid du Colombier 		putep(ep);	/* release ref kept since usbopen */
8945d9de2d3SDavid du Colombier 		c->flag &= ~COPEN;
8955d9de2d3SDavid du Colombier 	}
8965d9de2d3SDavid du Colombier 	poperror();
8975d9de2d3SDavid du Colombier 	putep(ep);
8985d9de2d3SDavid du Colombier }
8995d9de2d3SDavid du Colombier 
9005d9de2d3SDavid du Colombier static long
ctlread(Chan * c,void * a,long n,vlong offset)9015d9de2d3SDavid du Colombier ctlread(Chan *c, void *a, long n, vlong offset)
9025d9de2d3SDavid du Colombier {
903*b4d1cf41SDavid du Colombier 	int q;
904*b4d1cf41SDavid du Colombier 	char *s;
905*b4d1cf41SDavid du Colombier 	char *us;
906*b4d1cf41SDavid du Colombier 	char *se;
9075d9de2d3SDavid du Colombier 	Ep *ep;
908*b4d1cf41SDavid du Colombier 	int i;
9095d9de2d3SDavid du Colombier 
9105d9de2d3SDavid du Colombier 	q = QID(c->qid);
9115d9de2d3SDavid du Colombier 	us = s = smalloc(READSTR);
9125d9de2d3SDavid du Colombier 	se = s + READSTR;
9135d9de2d3SDavid du Colombier 	if(waserror()){
9145d9de2d3SDavid du Colombier 		free(us);
9155d9de2d3SDavid du Colombier 		nexterror();
9165d9de2d3SDavid du Colombier 	}
9175d9de2d3SDavid du Colombier 	if(q == Qctl)
9185d9de2d3SDavid du Colombier 		for(i = 0; i < epmax; i++){
9195d9de2d3SDavid du Colombier 			ep = getep(i);
9205d9de2d3SDavid du Colombier 			if(ep != nil){
9215d9de2d3SDavid du Colombier 				if(waserror()){
9225d9de2d3SDavid du Colombier 					putep(ep);
9235d9de2d3SDavid du Colombier 					nexterror();
9245d9de2d3SDavid du Colombier 				}
925*b4d1cf41SDavid du Colombier 				s = seprint(s, se, "ep%d.%d ", ep->dev->nb, ep->nb);
9265d9de2d3SDavid du Colombier 				s = seprintep(s, se, ep, 0);
9275d9de2d3SDavid du Colombier 				poperror();
9285d9de2d3SDavid du Colombier 			}
9295d9de2d3SDavid du Colombier 			putep(ep);
9305d9de2d3SDavid du Colombier 		}
9315d9de2d3SDavid du Colombier 	else{
9325d9de2d3SDavid du Colombier 		ep = getep(qid2epidx(q));
9335d9de2d3SDavid du Colombier 		if(ep == nil)
9345d9de2d3SDavid du Colombier 			error(Eio);
9355d9de2d3SDavid du Colombier 		if(waserror()){
9365d9de2d3SDavid du Colombier 			putep(ep);
9375d9de2d3SDavid du Colombier 			nexterror();
9385d9de2d3SDavid du Colombier 		}
9395d9de2d3SDavid du Colombier 		if(c->aux != nil){
940*b4d1cf41SDavid du Colombier 			/* After a new endpoint request we read
9415d9de2d3SDavid du Colombier 			 * the new endpoint name back.
9425d9de2d3SDavid du Colombier 			 */
9435d9de2d3SDavid du Colombier 			strecpy(s, se, c->aux);
9445d9de2d3SDavid du Colombier 			free(c->aux);
9455d9de2d3SDavid du Colombier 			c->aux = nil;
9465d9de2d3SDavid du Colombier 		}else
9475d9de2d3SDavid du Colombier 			seprintep(s, se, ep, 0);
9485d9de2d3SDavid du Colombier 		poperror();
9495d9de2d3SDavid du Colombier 		putep(ep);
9505d9de2d3SDavid du Colombier 	}
9515d9de2d3SDavid du Colombier 	n = readstr(offset, a, n, us);
9525d9de2d3SDavid du Colombier 	poperror();
9535d9de2d3SDavid du Colombier 	free(us);
9545d9de2d3SDavid du Colombier 	return n;
9555d9de2d3SDavid du Colombier }
9565d9de2d3SDavid du Colombier 
9575d9de2d3SDavid du Colombier /*
9585d9de2d3SDavid du Colombier  * Fake root hub emulation.
9595d9de2d3SDavid du Colombier  */
9605d9de2d3SDavid du Colombier static long
rhubread(Ep * ep,void * a,long n)9615d9de2d3SDavid du Colombier rhubread(Ep *ep, void *a, long n)
9625d9de2d3SDavid du Colombier {
9635d9de2d3SDavid du Colombier 	char *b;
9645d9de2d3SDavid du Colombier 
9655d9de2d3SDavid du Colombier 	if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2)
9665d9de2d3SDavid du Colombier 		return -1;
9675d9de2d3SDavid du Colombier 	if(ep->rhrepl < 0)
9685d9de2d3SDavid du Colombier 		return -1;
9695d9de2d3SDavid du Colombier 
9705d9de2d3SDavid du Colombier 	b = a;
9715d9de2d3SDavid du Colombier 	memset(b, 0, n);
9725d9de2d3SDavid du Colombier 	PUT2(b, ep->rhrepl);
9735d9de2d3SDavid du Colombier 	ep->rhrepl = -1;
9745d9de2d3SDavid du Colombier 	return n;
9755d9de2d3SDavid du Colombier }
9765d9de2d3SDavid du Colombier 
9775d9de2d3SDavid du Colombier static long
rhubwrite(Ep * ep,void * a,long n)9785d9de2d3SDavid du Colombier rhubwrite(Ep *ep, void *a, long n)
9795d9de2d3SDavid du Colombier {
9805d9de2d3SDavid du Colombier 	uchar *s;
981*b4d1cf41SDavid du Colombier 	int cmd;
982*b4d1cf41SDavid du Colombier 	int feature;
983*b4d1cf41SDavid du Colombier 	int port;
9845d9de2d3SDavid du Colombier 	Hci *hp;
9855d9de2d3SDavid du Colombier 
9865d9de2d3SDavid du Colombier 	if(ep->dev == nil || ep->dev->isroot == 0 || ep->nb != 0)
9875d9de2d3SDavid du Colombier 		return -1;
9885d9de2d3SDavid du Colombier 	if(n != Rsetuplen)
9895d9de2d3SDavid du Colombier 		error("root hub is a toy hub");
9905d9de2d3SDavid du Colombier 	ep->rhrepl = -1;
9915d9de2d3SDavid du Colombier 	s = a;
9925d9de2d3SDavid du Colombier 	if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother))
9935d9de2d3SDavid du Colombier 		error("root hub is a toy hub");
9945d9de2d3SDavid du Colombier 	hp = ep->hp;
9955d9de2d3SDavid du Colombier 	cmd = s[Rreq];
9965d9de2d3SDavid du Colombier 	feature = GET2(s+Rvalue);
9975d9de2d3SDavid du Colombier 	port = GET2(s+Rindex);
9985d9de2d3SDavid du Colombier 	if(port < 1 || port > hp->nports)
9995d9de2d3SDavid du Colombier 		error("bad hub port number");
10005d9de2d3SDavid du Colombier 	switch(feature){
10015d9de2d3SDavid du Colombier 	case Rportenable:
10025d9de2d3SDavid du Colombier 		ep->rhrepl = hp->portenable(hp, port, cmd == Rsetfeature);
10035d9de2d3SDavid du Colombier 		break;
10045d9de2d3SDavid du Colombier 	case Rportreset:
10055d9de2d3SDavid du Colombier 		ep->rhrepl = hp->portreset(hp, port, cmd == Rsetfeature);
10065d9de2d3SDavid du Colombier 		break;
10075d9de2d3SDavid du Colombier 	case Rgetstatus:
10085d9de2d3SDavid du Colombier 		ep->rhrepl = hp->portstatus(hp, port);
10095d9de2d3SDavid du Colombier 		break;
10105d9de2d3SDavid du Colombier 	default:
10115d9de2d3SDavid du Colombier 		ep->rhrepl = 0;
10125d9de2d3SDavid du Colombier 	}
10135d9de2d3SDavid du Colombier 	return n;
10145d9de2d3SDavid du Colombier }
10155d9de2d3SDavid du Colombier 
10165d9de2d3SDavid du Colombier static long
usbread(Chan * c,void * a,long n,vlong offset)10175d9de2d3SDavid du Colombier usbread(Chan *c, void *a, long n, vlong offset)
10185d9de2d3SDavid du Colombier {
1019*b4d1cf41SDavid du Colombier 	int q;
10205d9de2d3SDavid du Colombier 	Ep *ep;
1021*b4d1cf41SDavid du Colombier 	int nr;
10225d9de2d3SDavid du Colombier 
10235d9de2d3SDavid du Colombier 	q = QID(c->qid);
10245d9de2d3SDavid du Colombier 
10255d9de2d3SDavid du Colombier 	if(c->qid.type == QTDIR)
10265d9de2d3SDavid du Colombier 		return devdirread(c, a, n, nil, 0, usbgen);
10275d9de2d3SDavid du Colombier 
10285d9de2d3SDavid du Colombier 	if(q == Qctl || isqtype(q, Qepctl))
10295d9de2d3SDavid du Colombier 		return ctlread(c, a, n, offset);
10305d9de2d3SDavid du Colombier 
10315d9de2d3SDavid du Colombier 	ep = getep(qid2epidx(q));
10325d9de2d3SDavid du Colombier 	if(ep == nil)
10335d9de2d3SDavid du Colombier 		error(Eio);
10345d9de2d3SDavid du Colombier 	if(waserror()){
10355d9de2d3SDavid du Colombier 		putep(ep);
10365d9de2d3SDavid du Colombier 		nexterror();
10375d9de2d3SDavid du Colombier 	}
10385d9de2d3SDavid du Colombier 	if(ep->dev->state == Ddetach)
10395d9de2d3SDavid du Colombier 		error(Edetach);
10405d9de2d3SDavid du Colombier 	if(ep->mode == OWRITE || ep->inuse == 0)
10415d9de2d3SDavid du Colombier 		error(Ebadusefd);
10425d9de2d3SDavid du Colombier 	switch(ep->ttype){
10435d9de2d3SDavid du Colombier 	case Tnone:
10445d9de2d3SDavid du Colombier 		error("endpoint not configured");
10455d9de2d3SDavid du Colombier 	case Tctl:
10465d9de2d3SDavid du Colombier 		nr = rhubread(ep, a, n);
10475d9de2d3SDavid du Colombier 		if(nr >= 0){
10485d9de2d3SDavid du Colombier 			n = nr;
10495d9de2d3SDavid du Colombier 			break;
10505d9de2d3SDavid du Colombier 		}
1051*b4d1cf41SDavid du Colombier 		/* else fall */
10525d9de2d3SDavid du Colombier 	default:
1053*b4d1cf41SDavid du Colombier 		ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",q,c->fid,n,offset);
10545d9de2d3SDavid du Colombier 		n = ep->hp->epread(ep, a, n);
10555d9de2d3SDavid du Colombier 		break;
10565d9de2d3SDavid du Colombier 	}
10575d9de2d3SDavid du Colombier 	poperror();
10585d9de2d3SDavid du Colombier 	putep(ep);
10595d9de2d3SDavid du Colombier 	return n;
10605d9de2d3SDavid du Colombier }
10615d9de2d3SDavid du Colombier 
10625d9de2d3SDavid du Colombier static long
pow2(int n)10635d9de2d3SDavid du Colombier pow2(int n)
10645d9de2d3SDavid du Colombier {
10655d9de2d3SDavid du Colombier 	return 1 << n;
10665d9de2d3SDavid du Colombier }
10675d9de2d3SDavid du Colombier 
10685d9de2d3SDavid du Colombier static void
setmaxpkt(Ep * ep,char * s)10695d9de2d3SDavid du Colombier setmaxpkt(Ep *ep, char* s)
10705d9de2d3SDavid du Colombier {
10715d9de2d3SDavid du Colombier 	long spp;	/* samples per packet */
10725d9de2d3SDavid du Colombier 
10735d9de2d3SDavid du Colombier 	if(ep->dev->speed == Highspeed)
10745d9de2d3SDavid du Colombier 		spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000;
10755d9de2d3SDavid du Colombier 	else
10765d9de2d3SDavid du Colombier 		spp = (ep->hz * ep->pollival + 999) / 1000;
10775d9de2d3SDavid du Colombier 	ep->maxpkt = spp * ep->samplesz;
10785d9de2d3SDavid du Colombier 	deprint("usb: %s: setmaxpkt: hz %ld poll %ld"
10795d9de2d3SDavid du Colombier 		" ntds %d %s speed -> spp %ld maxpkt %ld\n", s,
10805d9de2d3SDavid du Colombier 		ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed],
10815d9de2d3SDavid du Colombier 		spp, ep->maxpkt);
10825d9de2d3SDavid du Colombier 	if(ep->maxpkt > 1024){
10835d9de2d3SDavid du Colombier 		print("usb: %s: maxpkt %ld > 1024. truncating\n", s, ep->maxpkt);
10845d9de2d3SDavid du Colombier 		ep->maxpkt = 1024;
10855d9de2d3SDavid du Colombier 	}
10865d9de2d3SDavid du Colombier }
10875d9de2d3SDavid du Colombier 
10885d9de2d3SDavid du Colombier /*
1089*b4d1cf41SDavid du Colombier  * Many endpoint ctls. simply update the portable representation
10905d9de2d3SDavid du Colombier  * of the endpoint. The actual controller driver will look
10915d9de2d3SDavid du Colombier  * at them to setup the endpoints as dictated.
10925d9de2d3SDavid du Colombier  */
10935d9de2d3SDavid du Colombier static long
epctl(Ep * ep,Chan * c,void * a,long n)10945d9de2d3SDavid du Colombier epctl(Ep *ep, Chan *c, void *a, long n)
10955d9de2d3SDavid du Colombier {
10965d9de2d3SDavid du Colombier 	int i, l, mode, nb, tt;
10975d9de2d3SDavid du Colombier 	char *b, *s;
10985d9de2d3SDavid du Colombier 	Cmdbuf *cb;
10995d9de2d3SDavid du Colombier 	Cmdtab *ct;
11005d9de2d3SDavid du Colombier 	Ep *nep;
11015d9de2d3SDavid du Colombier 	Udev *d;
11025d9de2d3SDavid du Colombier 	static char *Info = "info ";
11035d9de2d3SDavid du Colombier 
11045d9de2d3SDavid du Colombier 	d = ep->dev;
11055d9de2d3SDavid du Colombier 
11065d9de2d3SDavid du Colombier 	cb = parsecmd(a, n);
11075d9de2d3SDavid du Colombier 	if(waserror()){
11085d9de2d3SDavid du Colombier 		free(cb);
11095d9de2d3SDavid du Colombier 		nexterror();
11105d9de2d3SDavid du Colombier 	}
11115d9de2d3SDavid du Colombier 	ct = lookupcmd(cb, epctls, nelem(epctls));
11125d9de2d3SDavid du Colombier 	if(ct == nil)
11135d9de2d3SDavid du Colombier 		error(Ebadctl);
11145d9de2d3SDavid du Colombier 	i = ct->index;
11155d9de2d3SDavid du Colombier 	if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset)
11165d9de2d3SDavid du Colombier 		if(ep != ep->ep0)
11175d9de2d3SDavid du Colombier 			error("allowed only on a setup endpoint");
11185d9de2d3SDavid du Colombier 	if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname)
11195d9de2d3SDavid du Colombier 		if(ep != ep->ep0 && ep->inuse != 0)
11205d9de2d3SDavid du Colombier 			error("must configure before using");
11215d9de2d3SDavid du Colombier 	switch(i){
11225d9de2d3SDavid du Colombier 	case CMnew:
11235d9de2d3SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
11245d9de2d3SDavid du Colombier 		nb = strtol(cb->f[1], nil, 0);
11255d9de2d3SDavid du Colombier 		if(nb < 0 || nb >= Ndeveps)
11265d9de2d3SDavid du Colombier 			error("bad endpoint number");
11275d9de2d3SDavid du Colombier 		tt = name2ttype(cb->f[2]);
11285d9de2d3SDavid du Colombier 		if(tt == Tnone)
11295d9de2d3SDavid du Colombier 			error("unknown endpoint type");
11305d9de2d3SDavid du Colombier 		mode = name2mode(cb->f[3]);
11315d9de2d3SDavid du Colombier 		if(mode < 0)
11325d9de2d3SDavid du Colombier 			error("unknown i/o mode");
11335d9de2d3SDavid du Colombier 		newdevep(ep, nb, tt, mode);
11345d9de2d3SDavid du Colombier 		break;
11355d9de2d3SDavid du Colombier 	case CMnewdev:
11365d9de2d3SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
11375d9de2d3SDavid du Colombier 		if(ep != ep->ep0 || d->ishub == 0)
11385d9de2d3SDavid du Colombier 			error("not a hub setup endpoint");
11395d9de2d3SDavid du Colombier 		l = name2speed(cb->f[1]);
11405d9de2d3SDavid du Colombier 		if(l == Nospeed)
11415d9de2d3SDavid du Colombier 			error("speed must be full|low|high");
11425d9de2d3SDavid du Colombier 		nep = newdev(ep->hp, 0, 0);
11435d9de2d3SDavid du Colombier 		nep->dev->speed = l;
11445d9de2d3SDavid du Colombier 		if(nep->dev->speed  != Lowspeed)
11455d9de2d3SDavid du Colombier 			nep->maxpkt = 64;	/* assume full speed */
11465d9de2d3SDavid du Colombier 		nep->dev->hub = d->nb;
11475d9de2d3SDavid du Colombier 		nep->dev->port = atoi(cb->f[2]);
1148*b4d1cf41SDavid du Colombier 		/* next read request will read
11495d9de2d3SDavid du Colombier 		 * the name for the new endpoint
11505d9de2d3SDavid du Colombier 		 */
11515d9de2d3SDavid du Colombier 		l = sizeof(up->genbuf);
11525d9de2d3SDavid du Colombier 		snprint(up->genbuf, l, "ep%d.%d", nep->dev->nb, nep->nb);
11535d9de2d3SDavid du Colombier 		kstrdup(&c->aux, up->genbuf);
11545d9de2d3SDavid du Colombier 		break;
11555d9de2d3SDavid du Colombier 	case CMhub:
11565d9de2d3SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
11575d9de2d3SDavid du Colombier 		d->ishub = 1;
11585d9de2d3SDavid du Colombier 		break;
11595d9de2d3SDavid du Colombier 	case CMspeed:
11605d9de2d3SDavid du Colombier 		l = name2speed(cb->f[1]);
11615d9de2d3SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
11625d9de2d3SDavid du Colombier 		if(l == Nospeed)
11635d9de2d3SDavid du Colombier 			error("speed must be full|low|high");
11645d9de2d3SDavid du Colombier 		qlock(ep->ep0);
11655d9de2d3SDavid du Colombier 		d->speed = l;
11665d9de2d3SDavid du Colombier 		qunlock(ep->ep0);
11675d9de2d3SDavid du Colombier 		break;
11685d9de2d3SDavid du Colombier 	case CMmaxpkt:
11695d9de2d3SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
11705d9de2d3SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
11715d9de2d3SDavid du Colombier 		if(l < 1 || l > 1024)
11725d9de2d3SDavid du Colombier 			error("maxpkt not in [1:1024]");
11735d9de2d3SDavid du Colombier 		qlock(ep);
11745d9de2d3SDavid du Colombier 		ep->maxpkt = l;
11755d9de2d3SDavid du Colombier 		qunlock(ep);
11765d9de2d3SDavid du Colombier 		break;
11775d9de2d3SDavid du Colombier 	case CMntds:
11785d9de2d3SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
11795d9de2d3SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
11805d9de2d3SDavid du Colombier 		if(l < 1 || l > 3)
11815d9de2d3SDavid du Colombier 			error("ntds not in [1:3]");
11825d9de2d3SDavid du Colombier 		qlock(ep);
11835d9de2d3SDavid du Colombier 		ep->ntds = l;
11845d9de2d3SDavid du Colombier 		qunlock(ep);
11855d9de2d3SDavid du Colombier 		break;
11865d9de2d3SDavid du Colombier 	case CMpollival:
11875d9de2d3SDavid du Colombier 		if(ep->ttype != Tintr && ep->ttype != Tiso)
11885d9de2d3SDavid du Colombier 			error("not an intr or iso endpoint");
11895d9de2d3SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
11905d9de2d3SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
11915d9de2d3SDavid du Colombier 		if(ep->ttype == Tiso ||
11925d9de2d3SDavid du Colombier 		   (ep->ttype == Tintr && ep->dev->speed == Highspeed)){
11935d9de2d3SDavid du Colombier 			if(l < 1 || l > 16)
11945d9de2d3SDavid du Colombier 				error("pollival power not in [1:16]");
11955d9de2d3SDavid du Colombier 			l = pow2(l-1);
11965d9de2d3SDavid du Colombier 		}else
11975d9de2d3SDavid du Colombier 			if(l < 1 || l > 255)
11985d9de2d3SDavid du Colombier 				error("pollival not in [1:255]");
11995d9de2d3SDavid du Colombier 		qlock(ep);
12005d9de2d3SDavid du Colombier 		ep->pollival = l;
12015d9de2d3SDavid du Colombier 		if(ep->ttype == Tiso)
12025d9de2d3SDavid du Colombier 			setmaxpkt(ep, "pollival");
12035d9de2d3SDavid du Colombier 		qunlock(ep);
12045d9de2d3SDavid du Colombier 		break;
12055d9de2d3SDavid du Colombier 	case CMsamplesz:
12065d9de2d3SDavid du Colombier 		if(ep->ttype != Tiso)
12075d9de2d3SDavid du Colombier 			error("not an iso endpoint");
12085d9de2d3SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
12095d9de2d3SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
12105d9de2d3SDavid du Colombier 		if(l <= 0 || l > 8)
12115d9de2d3SDavid du Colombier 			error("samplesz not in [1:8]");
12125d9de2d3SDavid du Colombier 		qlock(ep);
12135d9de2d3SDavid du Colombier 		ep->samplesz = l;
12145d9de2d3SDavid du Colombier 		setmaxpkt(ep, "samplesz");
12155d9de2d3SDavid du Colombier 		qunlock(ep);
12165d9de2d3SDavid du Colombier 		break;
12175d9de2d3SDavid du Colombier 	case CMhz:
12185d9de2d3SDavid du Colombier 		if(ep->ttype != Tiso)
12195d9de2d3SDavid du Colombier 			error("not an iso endpoint");
12205d9de2d3SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
12215d9de2d3SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
12225d9de2d3SDavid du Colombier 		if(l <= 0 || l > 100000)
12235d9de2d3SDavid du Colombier 			error("hz not in [1:100000]");
12245d9de2d3SDavid du Colombier 		qlock(ep);
12255d9de2d3SDavid du Colombier 		ep->hz = l;
12265d9de2d3SDavid du Colombier 		setmaxpkt(ep, "hz");
12275d9de2d3SDavid du Colombier 		qunlock(ep);
12285d9de2d3SDavid du Colombier 		break;
12295d9de2d3SDavid du Colombier 	case CMclrhalt:
12305d9de2d3SDavid du Colombier 		qlock(ep);
12315d9de2d3SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
12325d9de2d3SDavid du Colombier 		ep->clrhalt = 1;
12335d9de2d3SDavid du Colombier 		qunlock(ep);
12345d9de2d3SDavid du Colombier 		break;
12355d9de2d3SDavid du Colombier 	case CMinfo:
12365d9de2d3SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
12375d9de2d3SDavid du Colombier 		l = strlen(Info);
12385d9de2d3SDavid du Colombier 		s = a;
12395d9de2d3SDavid du Colombier 		if(n < l+2 || strncmp(Info, s, l) != 0)
12405d9de2d3SDavid du Colombier 			error(Ebadctl);
12415d9de2d3SDavid du Colombier 		if(n > 1024)
12425d9de2d3SDavid du Colombier 			n = 1024;
12435d9de2d3SDavid du Colombier 		b = smalloc(n);
12445d9de2d3SDavid du Colombier 		memmove(b, s+l, n-l);
12455d9de2d3SDavid du Colombier 		b[n-l] = 0;
12465d9de2d3SDavid du Colombier 		if(b[n-l-1] == '\n')
12475d9de2d3SDavid du Colombier 			b[n-l-1] = 0;
12485d9de2d3SDavid du Colombier 		qlock(ep);
12495d9de2d3SDavid du Colombier 		free(ep->info);
12505d9de2d3SDavid du Colombier 		ep->info = b;
12515d9de2d3SDavid du Colombier 		qunlock(ep);
12525d9de2d3SDavid du Colombier 		break;
12535d9de2d3SDavid du Colombier 	case CMaddress:
12545d9de2d3SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
12555d9de2d3SDavid du Colombier 		ep->dev->state = Denabled;
12565d9de2d3SDavid du Colombier 		break;
12575d9de2d3SDavid du Colombier 	case CMdetach:
12585d9de2d3SDavid du Colombier 		if(ep->dev->isroot != 0)
12595d9de2d3SDavid du Colombier 			error("can't detach a root hub");
12605d9de2d3SDavid du Colombier 		deprint("usb epctl %s ep%d.%d\n",
12615d9de2d3SDavid du Colombier 			cb->f[0], ep->dev->nb, ep->nb);
12625d9de2d3SDavid du Colombier 		ep->dev->state = Ddetach;
12635d9de2d3SDavid du Colombier 		/* Release file system ref. for its endpoints */
12645d9de2d3SDavid du Colombier 		for(i = 0; i < nelem(ep->dev->eps); i++)
12655d9de2d3SDavid du Colombier 			putep(ep->dev->eps[i]);
12665d9de2d3SDavid du Colombier 		break;
12675d9de2d3SDavid du Colombier 	case CMdebugep:
12685d9de2d3SDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0)
12695d9de2d3SDavid du Colombier 			ep->debug = 1;
12705d9de2d3SDavid du Colombier 		else if(strcmp(cb->f[1], "off") == 0)
12715d9de2d3SDavid du Colombier 			ep->debug = 0;
12725d9de2d3SDavid du Colombier 		else
12735d9de2d3SDavid du Colombier 			ep->debug = strtoul(cb->f[1], nil, 0);
12745d9de2d3SDavid du Colombier 		print("usb: ep%d.%d debug %d\n",
12755d9de2d3SDavid du Colombier 			ep->dev->nb, ep->nb, ep->debug);
12765d9de2d3SDavid du Colombier 		break;
12775d9de2d3SDavid du Colombier 	case CMname:
12785d9de2d3SDavid du Colombier 		deprint("usb epctl %s %s\n", cb->f[0], cb->f[1]);
12795d9de2d3SDavid du Colombier 		validname(cb->f[1], 0);
12805d9de2d3SDavid du Colombier 		kstrdup(&ep->name, cb->f[1]);
12815d9de2d3SDavid du Colombier 		break;
12825d9de2d3SDavid du Colombier 	case CMtmout:
12835d9de2d3SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
12845d9de2d3SDavid du Colombier 		if(ep->ttype == Tiso || ep->ttype == Tctl)
12855d9de2d3SDavid du Colombier 			error("ctl ignored for this endpoint type");
12865d9de2d3SDavid du Colombier 		ep->tmout = strtoul(cb->f[1], nil, 0);
12875d9de2d3SDavid du Colombier 		if(ep->tmout != 0 && ep->tmout < Xfertmout)
12885d9de2d3SDavid du Colombier 			ep->tmout = Xfertmout;
12895d9de2d3SDavid du Colombier 		break;
12905d9de2d3SDavid du Colombier 	case CMpreset:
12915d9de2d3SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
12925d9de2d3SDavid du Colombier 		if(ep->ttype != Tctl)
12935d9de2d3SDavid du Colombier 			error("not a control endpoint");
12945d9de2d3SDavid du Colombier 		if(ep->dev->state != Denabled)
12955d9de2d3SDavid du Colombier 			error("forbidden on devices not enabled");
12965d9de2d3SDavid du Colombier 		ep->dev->state = Dreset;
12975d9de2d3SDavid du Colombier 		break;
12985d9de2d3SDavid du Colombier 	default:
12995d9de2d3SDavid du Colombier 		panic("usb: unknown epctl %d", ct->index);
13005d9de2d3SDavid du Colombier 	}
13015d9de2d3SDavid du Colombier 	free(cb);
13025d9de2d3SDavid du Colombier 	poperror();
13035d9de2d3SDavid du Colombier 	return n;
13045d9de2d3SDavid du Colombier }
13055d9de2d3SDavid du Colombier 
13065d9de2d3SDavid du Colombier static long
usbctl(void * a,long n)13075d9de2d3SDavid du Colombier usbctl(void *a, long n)
13085d9de2d3SDavid du Colombier {
13095d9de2d3SDavid du Colombier 	Cmdtab *ct;
13105d9de2d3SDavid du Colombier 	Cmdbuf *cb;
13115d9de2d3SDavid du Colombier 	Ep *ep;
13125d9de2d3SDavid du Colombier 	int i;
13135d9de2d3SDavid du Colombier 
13145d9de2d3SDavid du Colombier 	cb = parsecmd(a, n);
13155d9de2d3SDavid du Colombier 	if(waserror()){
13165d9de2d3SDavid du Colombier 		free(cb);
13175d9de2d3SDavid du Colombier 		nexterror();
13185d9de2d3SDavid du Colombier 	}
13195d9de2d3SDavid du Colombier 	ct = lookupcmd(cb, usbctls, nelem(usbctls));
13205d9de2d3SDavid du Colombier 	dprint("usb ctl %s\n", cb->f[0]);
13215d9de2d3SDavid du Colombier 	switch(ct->index){
13225d9de2d3SDavid du Colombier 	case CMdebug:
13235d9de2d3SDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0)
13245d9de2d3SDavid du Colombier 			debug = 1;
13255d9de2d3SDavid du Colombier 		else if(strcmp(cb->f[1], "off") == 0)
13265d9de2d3SDavid du Colombier 			debug = 0;
13275d9de2d3SDavid du Colombier 		else
13285d9de2d3SDavid du Colombier 			debug = strtol(cb->f[1], nil, 0);
13295d9de2d3SDavid du Colombier 		print("usb: debug %d\n", debug);
13305d9de2d3SDavid du Colombier 		for(i = 0; i < epmax; i++)
13315d9de2d3SDavid du Colombier 			if((ep = getep(i)) != nil){
13325d9de2d3SDavid du Colombier 				ep->hp->debug(ep->hp, debug);
13335d9de2d3SDavid du Colombier 				putep(ep);
13345d9de2d3SDavid du Colombier 			}
13355d9de2d3SDavid du Colombier 		break;
13365d9de2d3SDavid du Colombier 	case CMdump:
13375d9de2d3SDavid du Colombier 		dumpeps();
13385d9de2d3SDavid du Colombier 		break;
13395d9de2d3SDavid du Colombier 	}
13405d9de2d3SDavid du Colombier 	free(cb);
13415d9de2d3SDavid du Colombier 	poperror();
13425d9de2d3SDavid du Colombier 	return n;
13435d9de2d3SDavid du Colombier }
13445d9de2d3SDavid du Colombier 
13455d9de2d3SDavid du Colombier static long
ctlwrite(Chan * c,void * a,long n)13465d9de2d3SDavid du Colombier ctlwrite(Chan *c, void *a, long n)
13475d9de2d3SDavid du Colombier {
13485d9de2d3SDavid du Colombier 	int q;
13495d9de2d3SDavid du Colombier 	Ep *ep;
13505d9de2d3SDavid du Colombier 
13515d9de2d3SDavid du Colombier 	q = QID(c->qid);
13525d9de2d3SDavid du Colombier 	if(q == Qctl)
13535d9de2d3SDavid du Colombier 		return usbctl(a, n);
13545d9de2d3SDavid du Colombier 
13555d9de2d3SDavid du Colombier 	ep = getep(qid2epidx(q));
13565d9de2d3SDavid du Colombier 	if(ep == nil)
13575d9de2d3SDavid du Colombier 		error(Eio);
13585d9de2d3SDavid du Colombier 	if(waserror()){
13595d9de2d3SDavid du Colombier 		putep(ep);
13605d9de2d3SDavid du Colombier 		nexterror();
13615d9de2d3SDavid du Colombier 	}
13625d9de2d3SDavid du Colombier 	if(ep->dev->state == Ddetach)
13635d9de2d3SDavid du Colombier 		error(Edetach);
13645d9de2d3SDavid du Colombier 	if(isqtype(q, Qepctl) && c->aux != nil){
13655d9de2d3SDavid du Colombier 		/* Be sure we don't keep a cloned ep name */
13665d9de2d3SDavid du Colombier 		free(c->aux);
13675d9de2d3SDavid du Colombier 		c->aux = nil;
13685d9de2d3SDavid du Colombier 		error("read, not write, expected");
13695d9de2d3SDavid du Colombier 	}
13705d9de2d3SDavid du Colombier 	n = epctl(ep, c, a, n);
13715d9de2d3SDavid du Colombier 	putep(ep);
13725d9de2d3SDavid du Colombier 	poperror();
13735d9de2d3SDavid du Colombier 	return n;
13745d9de2d3SDavid du Colombier }
13755d9de2d3SDavid du Colombier 
13765d9de2d3SDavid du Colombier static long
usbwrite(Chan * c,void * a,long n,vlong off)13775d9de2d3SDavid du Colombier usbwrite(Chan *c, void *a, long n, vlong off)
13785d9de2d3SDavid du Colombier {
13795d9de2d3SDavid du Colombier 	int nr, q;
13805d9de2d3SDavid du Colombier 	Ep *ep;
13815d9de2d3SDavid du Colombier 
13825d9de2d3SDavid du Colombier 	if(c->qid.type == QTDIR)
13835d9de2d3SDavid du Colombier 		error(Eisdir);
13845d9de2d3SDavid du Colombier 
13855d9de2d3SDavid du Colombier 	q = QID(c->qid);
13865d9de2d3SDavid du Colombier 
13875d9de2d3SDavid du Colombier 	if(q == Qctl || isqtype(q, Qepctl))
13885d9de2d3SDavid du Colombier 		return ctlwrite(c, a, n);
13895d9de2d3SDavid du Colombier 
13905d9de2d3SDavid du Colombier 	ep = getep(qid2epidx(q));
13915d9de2d3SDavid du Colombier 	if(ep == nil)
13925d9de2d3SDavid du Colombier 		error(Eio);
13935d9de2d3SDavid du Colombier 	if(waserror()){
13945d9de2d3SDavid du Colombier 		putep(ep);
13955d9de2d3SDavid du Colombier 		nexterror();
13965d9de2d3SDavid du Colombier 	}
13975d9de2d3SDavid du Colombier 	if(ep->dev->state == Ddetach)
13985d9de2d3SDavid du Colombier 		error(Edetach);
13995d9de2d3SDavid du Colombier 	if(ep->mode == OREAD || ep->inuse == 0)
14005d9de2d3SDavid du Colombier 		error(Ebadusefd);
14015d9de2d3SDavid du Colombier 
14025d9de2d3SDavid du Colombier 	switch(ep->ttype){
14035d9de2d3SDavid du Colombier 	case Tnone:
14045d9de2d3SDavid du Colombier 		error("endpoint not configured");
14055d9de2d3SDavid du Colombier 	case Tctl:
14065d9de2d3SDavid du Colombier 		nr = rhubwrite(ep, a, n);
14075d9de2d3SDavid du Colombier 		if(nr >= 0){
14085d9de2d3SDavid du Colombier 			n = nr;
14095d9de2d3SDavid du Colombier 			break;
14105d9de2d3SDavid du Colombier 		}
1411*b4d1cf41SDavid du Colombier 		/* else fall */
14125d9de2d3SDavid du Colombier 	default:
1413*b4d1cf41SDavid du Colombier 		ddeprint("\nusbwrite q %#x fid %d cnt %ld off %lld\n",q, c->fid, n, off);
14145d9de2d3SDavid du Colombier 		ep->hp->epwrite(ep, a, n);
14155d9de2d3SDavid du Colombier 	}
14165d9de2d3SDavid du Colombier 	putep(ep);
14175d9de2d3SDavid du Colombier 	poperror();
14185d9de2d3SDavid du Colombier 	return n;
14195d9de2d3SDavid du Colombier }
14205d9de2d3SDavid du Colombier 
14215d9de2d3SDavid du Colombier Block*
usbbread(Chan * c,long n,ulong offset)14225d9de2d3SDavid du Colombier usbbread(Chan *c, long n, ulong offset)
14235d9de2d3SDavid du Colombier {
14245d9de2d3SDavid du Colombier 	Block *bp;
14255d9de2d3SDavid du Colombier 
14265d9de2d3SDavid du Colombier 	bp = allocb(n);
14275d9de2d3SDavid du Colombier 	if(bp == 0)
14285d9de2d3SDavid du Colombier 		error(Enomem);
14295d9de2d3SDavid du Colombier 	if(waserror()) {
14305d9de2d3SDavid du Colombier 		freeb(bp);
14315d9de2d3SDavid du Colombier 		nexterror();
14325d9de2d3SDavid du Colombier 	}
14335d9de2d3SDavid du Colombier 	bp->wp += usbread(c, bp->wp, n, offset);
14345d9de2d3SDavid du Colombier 	poperror();
14355d9de2d3SDavid du Colombier 	return bp;
14365d9de2d3SDavid du Colombier }
14375d9de2d3SDavid du Colombier 
14385d9de2d3SDavid du Colombier long
usbbwrite(Chan * c,Block * bp,ulong offset)14395d9de2d3SDavid du Colombier usbbwrite(Chan *c, Block *bp, ulong offset)
14405d9de2d3SDavid du Colombier {
14415d9de2d3SDavid du Colombier 	long n;
14425d9de2d3SDavid du Colombier 
14435d9de2d3SDavid du Colombier 	if(waserror()) {
14445d9de2d3SDavid du Colombier 		freeb(bp);
14455d9de2d3SDavid du Colombier 		nexterror();
14465d9de2d3SDavid du Colombier 	}
14475d9de2d3SDavid du Colombier 	n = usbwrite(c, bp->rp, BLEN(bp), offset);
14485d9de2d3SDavid du Colombier 	poperror();
14495d9de2d3SDavid du Colombier 	freeb(bp);
14505d9de2d3SDavid du Colombier 
14515d9de2d3SDavid du Colombier 	return n;
14525d9de2d3SDavid du Colombier }
14535d9de2d3SDavid du Colombier 
14545d9de2d3SDavid du Colombier void
usbshutdown(void)14555d9de2d3SDavid du Colombier usbshutdown(void)
14565d9de2d3SDavid du Colombier {
14575d9de2d3SDavid du Colombier 	Hci *hp;
14585d9de2d3SDavid du Colombier 	int i;
14595d9de2d3SDavid du Colombier 
14605d9de2d3SDavid du Colombier 	for(i = 0; i < Nhcis; i++){
14615d9de2d3SDavid du Colombier 		hp = hcis[i];
14625d9de2d3SDavid du Colombier 		if(hp == nil)
14635d9de2d3SDavid du Colombier 			continue;
14645d9de2d3SDavid du Colombier 		if(hp->shutdown == nil)
14655d9de2d3SDavid du Colombier 			print("#u: no shutdown function for %s\n", hp->type);
14665d9de2d3SDavid du Colombier 		else
14675d9de2d3SDavid du Colombier 			hp->shutdown(hp);
14685d9de2d3SDavid du Colombier 	}
14695d9de2d3SDavid du Colombier }
14705d9de2d3SDavid du Colombier 
14715d9de2d3SDavid du Colombier Dev usbdevtab = {
14725d9de2d3SDavid du Colombier 	L'u',
14735d9de2d3SDavid du Colombier 	"usb",
14745d9de2d3SDavid du Colombier 
14755d9de2d3SDavid du Colombier 	usbreset,
14765d9de2d3SDavid du Colombier 	usbinit,
14775d9de2d3SDavid du Colombier 	usbshutdown,
14785d9de2d3SDavid du Colombier 	usbattach,
14795d9de2d3SDavid du Colombier 	usbwalk,
14805d9de2d3SDavid du Colombier 	usbstat,
14815d9de2d3SDavid du Colombier 	usbopen,
14825d9de2d3SDavid du Colombier 	devcreate,
14835d9de2d3SDavid du Colombier 	usbclose,
14845d9de2d3SDavid du Colombier 	usbread,
14855d9de2d3SDavid du Colombier 	usbbread,
14865d9de2d3SDavid du Colombier 	usbwrite,
14875d9de2d3SDavid du Colombier 	usbbwrite,
14885d9de2d3SDavid du Colombier 	devremove,
14895d9de2d3SDavid du Colombier 	devwstat,
14905d9de2d3SDavid du Colombier };
1491