xref: /plan9/sys/src/9/pc/devusb.c (revision a587111c8770e522e3667ff2b63cba8a77811dd9)
1ade43d10SDavid du Colombier /*
284860c5dSDavid du Colombier  * USB device driver framework.
3906943f9SDavid du Colombier  *
4906943f9SDavid du Colombier  * This is in charge of providing access to actual HCIs
5906943f9SDavid du Colombier  * and providing I/O to the various endpoints of devices.
6906943f9SDavid du Colombier  * A separate user program (usbd) is in charge of
7906943f9SDavid du Colombier  * enumerating the bus, setting up endpoints and
8906943f9SDavid du Colombier  * starting devices (also user programs).
9906943f9SDavid du Colombier  *
10906943f9SDavid du Colombier  * The interface provided is a violation of the standard:
11906943f9SDavid du Colombier  * you're welcome.
12906943f9SDavid du Colombier  *
13906943f9SDavid du Colombier  * The interface consists of a root directory with several files
14906943f9SDavid du Colombier  * plus a directory (epN.M) with two files per endpoint.
15906943f9SDavid du Colombier  * A device is represented by its first endpoint, which
16906943f9SDavid du Colombier  * is a control endpoint automatically allocated for each device.
17906943f9SDavid du Colombier  * Device control endpoints may be used to create new endpoints.
18906943f9SDavid du Colombier  * Devices corresponding to hubs may also allocate new devices,
19906943f9SDavid du Colombier  * perhaps also hubs. Initially, a hub device is allocated for
20906943f9SDavid du Colombier  * each controller present, to represent its root hub. Those can
21906943f9SDavid du Colombier  * never be removed.
22906943f9SDavid du Colombier  *
23906943f9SDavid du Colombier  * All endpoints refer to the first endpoint (epN.0) of the device,
24906943f9SDavid du Colombier  * which keeps per-device information, and also to the HCI used
25906943f9SDavid du Colombier  * to reach them. Although all endpoints cache that information.
26906943f9SDavid du Colombier  *
27906943f9SDavid du Colombier  * epN.M/data files permit I/O and are considered DMEXCL.
28906943f9SDavid du Colombier  * epN.M/ctl files provide status info and accept control requests.
29906943f9SDavid du Colombier  *
30906943f9SDavid du Colombier  * Endpoints may be given file names to be listed also at #u,
31906943f9SDavid du Colombier  * for those drivers that have nothing to do after configuring the
32906943f9SDavid du Colombier  * device and its endpoints.
33906943f9SDavid du Colombier  *
34906943f9SDavid du Colombier  * Drivers for different controllers are kept at usb[oue]hci.c
35906943f9SDavid du Colombier  * It's likely we could factor out much from controllers into
36906943f9SDavid du Colombier  * a generic controller driver, the problem is that details
37906943f9SDavid du Colombier  * regarding how to handle toggles, tokens, Tds, etc. will
38906943f9SDavid du Colombier  * get in the way. Thus, code is probably easier the way it is.
39ade43d10SDavid du Colombier  */
40906943f9SDavid du Colombier 
419a747e4fSDavid du Colombier #include	"u.h"
429a747e4fSDavid du Colombier #include	"../port/lib.h"
439a747e4fSDavid du Colombier #include	"mem.h"
449a747e4fSDavid du Colombier #include	"dat.h"
459a747e4fSDavid du Colombier #include	"fns.h"
469a747e4fSDavid du Colombier #include	"io.h"
479a747e4fSDavid du Colombier #include	"../port/error.h"
4884860c5dSDavid du Colombier #include	"../port/usb.h"
499a747e4fSDavid du Colombier 
50906943f9SDavid du Colombier typedef struct Hcitype Hcitype;
519a747e4fSDavid du Colombier 
529a747e4fSDavid du Colombier enum
539a747e4fSDavid du Colombier {
54906943f9SDavid du Colombier 	/* Qid numbers */
55906943f9SDavid du Colombier 	Qdir = 0,		/* #u */
56906943f9SDavid du Colombier 	Qusbdir,			/* #u/usb */
57906943f9SDavid du Colombier 	Qctl,			/* #u/usb/ctl - control requests */
58906943f9SDavid du Colombier 
59906943f9SDavid du Colombier 	Qep0dir,			/* #u/usb/ep0.0 - endpoint 0 dir */
60906943f9SDavid du Colombier 	Qep0io,			/* #u/usb/ep0.0/data - endpoint 0 I/O */
61906943f9SDavid du Colombier 	Qep0ctl,		/* #u/usb/ep0.0/ctl - endpoint 0 ctl. */
62906943f9SDavid du Colombier 	Qep0dummy,		/* give 4 qids to each endpoint */
63906943f9SDavid du Colombier 
64906943f9SDavid du Colombier 	Qepdir = 0,		/* (qid-qep0dir)&3 is one of these */
65906943f9SDavid du Colombier 	Qepio,			/* to identify which file for the endpoint */
66906943f9SDavid du Colombier 	Qepctl,
67906943f9SDavid du Colombier 
68906943f9SDavid du Colombier 	/* ... */
69906943f9SDavid du Colombier 
70906943f9SDavid du Colombier 	/* Usb ctls. */
71906943f9SDavid du Colombier 	CMdebug = 0,		/* debug on|off */
72906943f9SDavid du Colombier 	CMdump,			/* dump (data structures for debug) */
73906943f9SDavid du Colombier 
74906943f9SDavid du Colombier 	/* Ep. ctls */
75906943f9SDavid du Colombier 	CMnew = 0,		/* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */
76906943f9SDavid du Colombier 	CMnewdev,		/* newdev full|low|high portnb (allocate new devices) */
77906943f9SDavid du Colombier 	CMhub,			/* hub (set the device as a hub) */
78906943f9SDavid du Colombier 	CMspeed,		/* speed full|low|high|no */
79906943f9SDavid du Colombier 	CMmaxpkt,		/* maxpkt size */
80906943f9SDavid du Colombier 	CMntds,			/* ntds nb (max nb. of tds per µframe) */
81906943f9SDavid du Colombier 	CMclrhalt,		/* clrhalt (halt was cleared on endpoint) */
82906943f9SDavid du Colombier 	CMpollival,		/* pollival interval (interrupt/iso) */
83906943f9SDavid du Colombier 	CMhz,			/* hz n (samples/sec; iso) */
84906943f9SDavid du Colombier 	CMsamplesz,		/* samplesz n (sample size; iso) */
85906943f9SDavid du Colombier 	CMinfo,			/* info infostr (ke.ep info for humans) */
86906943f9SDavid du Colombier 	CMdetach,		/* detach (abort I/O forever on this ep). */
87906943f9SDavid du Colombier 	CMaddress,		/* address (address is assigned) */
88906943f9SDavid du Colombier 	CMdebugep,		/* debug n (set/clear debug for this ep) */
89906943f9SDavid du Colombier 	CMname,			/* name str (show up as #u/name as well) */
90d37e33ffSDavid du Colombier 	CMtmout,		/* timeout n (activate timeouts for ep) */
91a23bc242SDavid du Colombier 	CMpreset,		/* reset the port */
92906943f9SDavid du Colombier 
93906943f9SDavid du Colombier 	/* Hub feature selectors */
94906943f9SDavid du Colombier 	Rportenable	= 1,
95906943f9SDavid du Colombier 	Rportreset	= 4,
96906943f9SDavid du Colombier 
979a747e4fSDavid du Colombier };
989a747e4fSDavid du Colombier 
99906943f9SDavid du Colombier struct Hcitype
1009a747e4fSDavid du Colombier {
1019a747e4fSDavid du Colombier 	char*	type;
102906943f9SDavid du Colombier 	int	(*reset)(Hci*);
103906943f9SDavid du Colombier };
104906943f9SDavid du Colombier 
105906943f9SDavid du Colombier #define QID(q)	((int)(q).path)
106906943f9SDavid du Colombier 
107906943f9SDavid du Colombier static Cmdtab usbctls[] =
108906943f9SDavid du Colombier {
109906943f9SDavid du Colombier 	{CMdebug,	"debug",	2},
110906943f9SDavid du Colombier 	{CMdump,	"dump",		1},
111906943f9SDavid du Colombier };
112906943f9SDavid du Colombier 
113906943f9SDavid du Colombier static Cmdtab epctls[] =
114906943f9SDavid du Colombier {
115906943f9SDavid du Colombier 	{CMnew,		"new",		4},
116906943f9SDavid du Colombier 	{CMnewdev,	"newdev",	3},
117906943f9SDavid du Colombier 	{CMhub,		"hub",		1},
118906943f9SDavid du Colombier 	{CMspeed,	"speed",	2},
119906943f9SDavid du Colombier 	{CMmaxpkt,	"maxpkt",	2},
120906943f9SDavid du Colombier 	{CMntds,	"ntds",		2},
121906943f9SDavid du Colombier 	{CMpollival,	"pollival",	2},
122906943f9SDavid du Colombier 	{CMsamplesz,	"samplesz",	2},
123906943f9SDavid du Colombier 	{CMhz,		"hz",		2},
124906943f9SDavid du Colombier 	{CMinfo,	"info",		0},
125906943f9SDavid du Colombier 	{CMdetach,	"detach",	1},
126906943f9SDavid du Colombier 	{CMaddress,	"address",	1},
127906943f9SDavid du Colombier 	{CMdebugep,	"debug",	2},
128906943f9SDavid du Colombier 	{CMclrhalt,	"clrhalt",	1},
129906943f9SDavid du Colombier 	{CMname,	"name",		2},
130d37e33ffSDavid du Colombier 	{CMtmout,	"timeout",	2},
131a23bc242SDavid du Colombier 	{CMpreset,	"reset",	1},
132906943f9SDavid du Colombier };
133906943f9SDavid du Colombier 
134906943f9SDavid du Colombier static Dirtab usbdir[] =
135906943f9SDavid du Colombier {
136906943f9SDavid du Colombier 	"ctl",		{Qctl},		0,	0666,
137906943f9SDavid du Colombier };
138906943f9SDavid du Colombier 
139906943f9SDavid du Colombier char *usbmodename[] =
140906943f9SDavid du Colombier {
141906943f9SDavid du Colombier 	[OREAD]	"r",
142906943f9SDavid du Colombier 	[OWRITE]	"w",
143906943f9SDavid du Colombier 	[ORDWR]	"rw",
144906943f9SDavid du Colombier };
145906943f9SDavid du Colombier 
146906943f9SDavid du Colombier static char *ttname[] =
147906943f9SDavid du Colombier {
148906943f9SDavid du Colombier 	[Tnone]	"none",
149906943f9SDavid du Colombier 	[Tctl]	"control",
150906943f9SDavid du Colombier 	[Tiso]	"iso",
151906943f9SDavid du Colombier 	[Tintr]	"interrupt",
152906943f9SDavid du Colombier 	[Tbulk]	"bulk",
153906943f9SDavid du Colombier };
154906943f9SDavid du Colombier 
155906943f9SDavid du Colombier static char *spname[] =
156906943f9SDavid du Colombier {
157906943f9SDavid du Colombier 	[Fullspeed]	"full",
158906943f9SDavid du Colombier 	[Lowspeed]	"low",
159906943f9SDavid du Colombier 	[Highspeed]	"high",
160906943f9SDavid du Colombier 	[Nospeed]	"no",
161906943f9SDavid du Colombier };
162906943f9SDavid du Colombier 
163906943f9SDavid du Colombier static int	debug;
164906943f9SDavid du Colombier static Hcitype	hcitypes[Nhcis];
165906943f9SDavid du Colombier static Hci*	hcis[Nhcis];
166906943f9SDavid du Colombier static QLock	epslck;		/* add, del, lookup endpoints */
167906943f9SDavid du Colombier static Ep*	eps[Neps];	/* all endpoints known */
168906943f9SDavid du Colombier static int	epmax;		/* 1 + last endpoint index used  */
169906943f9SDavid du Colombier static int	usbidgen;	/* device address generator */
170906943f9SDavid du Colombier 
171906943f9SDavid du Colombier /*
172906943f9SDavid du Colombier  * Is there something like this in a library? should it be?
173906943f9SDavid du Colombier  */
174906943f9SDavid du Colombier char*
seprintdata(char * s,char * se,uchar * d,int n)175906943f9SDavid du Colombier seprintdata(char *s, char *se, uchar *d, int n)
176906943f9SDavid du Colombier {
17784860c5dSDavid du Colombier 	int i, l;
178906943f9SDavid du Colombier 
179906943f9SDavid du Colombier 	s = seprint(s, se, " %#p[%d]: ", d, n);
180906943f9SDavid du Colombier 	l = n;
181906943f9SDavid du Colombier 	if(l > 10)
182906943f9SDavid du Colombier 		l = 10;
183906943f9SDavid du Colombier 	for(i=0; i<l; i++)
184906943f9SDavid du Colombier 		s = seprint(s, se, " %2.2ux", d[i]);
185906943f9SDavid du Colombier 	if(l < n)
186906943f9SDavid du Colombier 		s = seprint(s, se, "...");
187906943f9SDavid du Colombier 	return s;
188906943f9SDavid du Colombier }
189906943f9SDavid du Colombier 
190906943f9SDavid du Colombier static int
name2speed(char * name)191906943f9SDavid du Colombier name2speed(char *name)
192906943f9SDavid du Colombier {
193906943f9SDavid du Colombier 	int i;
194906943f9SDavid du Colombier 
195906943f9SDavid du Colombier 	for(i = 0; i < nelem(spname); i++)
196906943f9SDavid du Colombier 		if(strcmp(name, spname[i]) == 0)
197906943f9SDavid du Colombier 			return i;
198906943f9SDavid du Colombier 	return Nospeed;
199906943f9SDavid du Colombier }
200906943f9SDavid du Colombier 
201906943f9SDavid du Colombier static int
name2ttype(char * name)202906943f9SDavid du Colombier name2ttype(char *name)
203906943f9SDavid du Colombier {
204906943f9SDavid du Colombier 	int i;
205906943f9SDavid du Colombier 
206906943f9SDavid du Colombier 	for(i = 0; i < nelem(ttname); i++)
207906943f9SDavid du Colombier 		if(strcmp(name, ttname[i]) == 0)
208906943f9SDavid du Colombier 			return i;
209906943f9SDavid du Colombier 	/* may be a std. USB ep. type */
210906943f9SDavid du Colombier 	i = strtol(name, nil, 0);
211906943f9SDavid du Colombier 	switch(i+1){
212906943f9SDavid du Colombier 	case Tctl:
213906943f9SDavid du Colombier 	case Tiso:
214906943f9SDavid du Colombier 	case Tbulk:
215906943f9SDavid du Colombier 	case Tintr:
216906943f9SDavid du Colombier 		return i+1;
217906943f9SDavid du Colombier 	default:
218906943f9SDavid du Colombier 		return Tnone;
219906943f9SDavid du Colombier 	}
220906943f9SDavid du Colombier }
221906943f9SDavid du Colombier 
222906943f9SDavid du Colombier static int
name2mode(char * mode)223906943f9SDavid du Colombier name2mode(char *mode)
224906943f9SDavid du Colombier {
225906943f9SDavid du Colombier 	int i;
226906943f9SDavid du Colombier 
227906943f9SDavid du Colombier 	for(i = 0; i < nelem(usbmodename); i++)
228906943f9SDavid du Colombier 		if(strcmp(mode, usbmodename[i]) == 0)
229906943f9SDavid du Colombier 			return i;
230906943f9SDavid du Colombier 	return -1;
231906943f9SDavid du Colombier }
232906943f9SDavid du Colombier 
233906943f9SDavid du Colombier static int
qid2epidx(int q)234906943f9SDavid du Colombier qid2epidx(int q)
235906943f9SDavid du Colombier {
236906943f9SDavid du Colombier 	q = (q-Qep0dir)/4;
237906943f9SDavid du Colombier 	if(q < 0 || q >= epmax || eps[q] == nil)
238906943f9SDavid du Colombier 		return -1;
239906943f9SDavid du Colombier 	return q;
240906943f9SDavid du Colombier }
241906943f9SDavid du Colombier 
242906943f9SDavid du Colombier static int
isqtype(int q,int type)243906943f9SDavid du Colombier isqtype(int q, int type)
244906943f9SDavid du Colombier {
245906943f9SDavid du Colombier 	if(q < Qep0dir)
246906943f9SDavid du Colombier 		return 0;
247906943f9SDavid du Colombier 	q -= Qep0dir;
248906943f9SDavid du Colombier 	return (q & 3) == type;
249906943f9SDavid du Colombier }
2509a747e4fSDavid du Colombier 
2519a747e4fSDavid du Colombier void
addhcitype(char * t,int (* r)(Hci *))252906943f9SDavid du Colombier addhcitype(char* t, int (*r)(Hci*))
2539a747e4fSDavid du Colombier {
2549a747e4fSDavid du Colombier 	static int ntype;
2559a747e4fSDavid du Colombier 
256906943f9SDavid du Colombier 	if(ntype == Nhcis)
2579a747e4fSDavid du Colombier 		panic("too many USB host interface types");
258906943f9SDavid du Colombier 	hcitypes[ntype].type = t;
259906943f9SDavid du Colombier 	hcitypes[ntype].reset = r;
2609a747e4fSDavid du Colombier 	ntype++;
2619a747e4fSDavid du Colombier }
2629a747e4fSDavid du Colombier 
263906943f9SDavid du Colombier static char*
seprintep(char * s,char * se,Ep * ep,int all)264906943f9SDavid du Colombier seprintep(char *s, char *se, Ep *ep, int all)
2659a747e4fSDavid du Colombier {
266a23bc242SDavid du Colombier 	static char* dsnames[] = { "config", "enabled", "detached", "reset" };
2679a747e4fSDavid du Colombier 	Udev *d;
2689a747e4fSDavid du Colombier 	int i;
269906943f9SDavid du Colombier 	int di;
2709a747e4fSDavid du Colombier 
271906943f9SDavid du Colombier 	d = ep->dev;
272906943f9SDavid du Colombier 
273906943f9SDavid du Colombier 	qlock(ep);
2749a747e4fSDavid du Colombier 	if(waserror()){
275906943f9SDavid du Colombier 		qunlock(ep);
2769a747e4fSDavid du Colombier 		nexterror();
2779a747e4fSDavid du Colombier 	}
278906943f9SDavid du Colombier 	di = ep->dev->nb;
279906943f9SDavid du Colombier 	if(all)
280906943f9SDavid du Colombier 		s = seprint(s, se, "dev %d ep %d ", di, ep->nb);
281906943f9SDavid du Colombier 	s = seprint(s, se, "%s", dsnames[ep->dev->state]);
282906943f9SDavid du Colombier 	s = seprint(s, se, " %s", ttname[ep->ttype]);
283906943f9SDavid du Colombier 	assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR);
284906943f9SDavid du Colombier 	s = seprint(s, se, " %s", usbmodename[ep->mode]);
285906943f9SDavid du Colombier 	s = seprint(s, se, " speed %s", spname[d->speed]);
286906943f9SDavid du Colombier 	s = seprint(s, se, " maxpkt %ld", ep->maxpkt);
287906943f9SDavid du Colombier 	s = seprint(s, se, " pollival %ld", ep->pollival);
288906943f9SDavid du Colombier 	s = seprint(s, se, " samplesz %ld", ep->samplesz);
289906943f9SDavid du Colombier 	s = seprint(s, se, " hz %ld", ep->hz);
290906943f9SDavid du Colombier 	s = seprint(s, se, " hub %d", ep->dev->hub);
291906943f9SDavid du Colombier 	s = seprint(s, se, " port %d", ep->dev->port);
292906943f9SDavid du Colombier 	if(ep->inuse)
293906943f9SDavid du Colombier 		s = seprint(s, se, " busy");
294906943f9SDavid du Colombier 	else
295906943f9SDavid du Colombier 		s = seprint(s, se, " idle");
296906943f9SDavid du Colombier 	if(all){
297906943f9SDavid du Colombier 		s = seprint(s, se, " load %uld", ep->load);
298906943f9SDavid du Colombier 		s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep);
299906943f9SDavid du Colombier 		s = seprint(s, se, " idx %d", ep->idx);
300906943f9SDavid du Colombier 		if(ep->name != nil)
301906943f9SDavid du Colombier 			s = seprint(s, se, " name '%s'", ep->name);
302d37e33ffSDavid du Colombier 		if(ep->tmout != 0)
303d37e33ffSDavid du Colombier 			s = seprint(s, se, " tmout");
304906943f9SDavid du Colombier 		if(ep == ep->ep0){
305906943f9SDavid du Colombier 			s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno);
306906943f9SDavid du Colombier 			s = seprint(s, se, " eps:");
307906943f9SDavid du Colombier 			for(i = 0; i < nelem(d->eps); i++)
308906943f9SDavid du Colombier 				if(d->eps[i] != nil)
309906943f9SDavid du Colombier 					s = seprint(s, se, " ep%d.%d", di, i);
3109a747e4fSDavid du Colombier 		}
311906943f9SDavid du Colombier 	}
312906943f9SDavid du Colombier 	if(ep->info != nil)
313c8cbc0e9SDavid du Colombier 		s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type);
314906943f9SDavid du Colombier 	else
315906943f9SDavid du Colombier 		s = seprint(s, se, "\n");
316906943f9SDavid du Colombier 	qunlock(ep);
3179a747e4fSDavid du Colombier 	poperror();
318906943f9SDavid du Colombier 	return s;
319906943f9SDavid du Colombier }
320906943f9SDavid du Colombier 
321906943f9SDavid du Colombier static Ep*
epalloc(Hci * hp)322906943f9SDavid du Colombier epalloc(Hci *hp)
323906943f9SDavid du Colombier {
324906943f9SDavid du Colombier 	Ep *ep;
325906943f9SDavid du Colombier 	int i;
326906943f9SDavid du Colombier 
327305b51b8SDavid du Colombier 	ep = smalloc(sizeof(Ep));
328906943f9SDavid du Colombier 	ep->ref = 1;
329906943f9SDavid du Colombier 	qlock(&epslck);
330906943f9SDavid du Colombier 	for(i = 0; i < Neps; i++)
331906943f9SDavid du Colombier 		if(eps[i] == nil)
332906943f9SDavid du Colombier 			break;
333906943f9SDavid du Colombier 	if(i == Neps){
334906943f9SDavid du Colombier 		qunlock(&epslck);
335906943f9SDavid du Colombier 		free(ep);
336*81fda2e3SDavid du Colombier 		panic("usb: epalloc: too few endpoints (%d)", Neps);
337906943f9SDavid du Colombier 	}
338906943f9SDavid du Colombier 	ep->idx = i;
339906943f9SDavid du Colombier 	if(epmax <= i)
340906943f9SDavid du Colombier 		epmax = i+1;
341906943f9SDavid du Colombier 	eps[i] = ep;
342906943f9SDavid du Colombier 	ep->hp = hp;
343906943f9SDavid du Colombier 	ep->maxpkt = 8;
344906943f9SDavid du Colombier 	ep->ntds = 1;
345906943f9SDavid du Colombier 	ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */
346906943f9SDavid du Colombier 	qunlock(&epslck);
347906943f9SDavid du Colombier 	return ep;
348906943f9SDavid du Colombier }
349906943f9SDavid du Colombier 
350906943f9SDavid du Colombier static Ep*
getep(int i)351906943f9SDavid du Colombier getep(int i)
352906943f9SDavid du Colombier {
353906943f9SDavid du Colombier 	Ep *ep;
354906943f9SDavid du Colombier 
355906943f9SDavid du Colombier 	if(i < 0 || i >= epmax || eps[i] == nil)
356906943f9SDavid du Colombier 		return nil;
357906943f9SDavid du Colombier 	qlock(&epslck);
358906943f9SDavid du Colombier 	ep = eps[i];
359906943f9SDavid du Colombier 	if(ep != nil)
360906943f9SDavid du Colombier 		incref(ep);
361906943f9SDavid du Colombier 	qunlock(&epslck);
362906943f9SDavid du Colombier 	return ep;
3639a747e4fSDavid du Colombier }
3649a747e4fSDavid du Colombier 
3659a747e4fSDavid du Colombier static void
putep(Ep * ep)366906943f9SDavid du Colombier putep(Ep *ep)
367906943f9SDavid du Colombier {
368906943f9SDavid du Colombier 	Udev *d;
369906943f9SDavid du Colombier 
370906943f9SDavid du Colombier 	if(ep != nil && decref(ep) == 0){
371906943f9SDavid du Colombier 		d = ep->dev;
372906943f9SDavid du Colombier 		deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep);
373906943f9SDavid du Colombier 		qlock(&epslck);
374906943f9SDavid du Colombier 		eps[ep->idx] = nil;
375906943f9SDavid du Colombier 		if(ep->idx == epmax-1)
376906943f9SDavid du Colombier 			epmax--;
377906943f9SDavid du Colombier 		if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen)
378906943f9SDavid du Colombier 			usbidgen--;
379906943f9SDavid du Colombier 		qunlock(&epslck);
380906943f9SDavid du Colombier 		if(d != nil){
381906943f9SDavid du Colombier 			qlock(ep->ep0);
382906943f9SDavid du Colombier 			d->eps[ep->nb] = nil;
383906943f9SDavid du Colombier 			qunlock(ep->ep0);
384906943f9SDavid du Colombier 		}
385906943f9SDavid du Colombier 		if(ep->ep0 != ep){
386906943f9SDavid du Colombier 			putep(ep->ep0);
387906943f9SDavid du Colombier 			ep->ep0 = nil;
388906943f9SDavid du Colombier 		}
389906943f9SDavid du Colombier 		free(ep->info);
390906943f9SDavid du Colombier 		free(ep->name);
391906943f9SDavid du Colombier 		free(ep);
392906943f9SDavid du Colombier 	}
393906943f9SDavid du Colombier }
394906943f9SDavid du Colombier 
395906943f9SDavid du Colombier static void
dumpeps(void)396906943f9SDavid du Colombier dumpeps(void)
3979a747e4fSDavid du Colombier {
3989a747e4fSDavid du Colombier 	int i;
399906943f9SDavid du Colombier 	static char buf[512];
400906943f9SDavid du Colombier 	char *s;
401906943f9SDavid du Colombier 	char *e;
402906943f9SDavid du Colombier 	Ep *ep;
4039a747e4fSDavid du Colombier 
404906943f9SDavid du Colombier 	print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps);
405906943f9SDavid du Colombier 	for(i = 0; i < epmax; i++){
406906943f9SDavid du Colombier 		s = buf;
407906943f9SDavid du Colombier 		e = buf+sizeof(buf);
408906943f9SDavid du Colombier 		ep = getep(i);
409906943f9SDavid du Colombier 		if(ep != nil){
410906943f9SDavid du Colombier 			if(waserror()){
411906943f9SDavid du Colombier 				putep(ep);
412906943f9SDavid du Colombier 				nexterror();
4139a747e4fSDavid du Colombier 			}
414906943f9SDavid du Colombier 			s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb);
415906943f9SDavid du Colombier 			seprintep(s, e, ep, 1);
416906943f9SDavid du Colombier 			print("%s", buf);
417906943f9SDavid du Colombier 			ep->hp->seprintep(buf, e, ep);
418906943f9SDavid du Colombier 			print("%s", buf);
419906943f9SDavid du Colombier 			poperror();
420906943f9SDavid du Colombier 			putep(ep);
421906943f9SDavid du Colombier 		}
422906943f9SDavid du Colombier 	}
423906943f9SDavid du Colombier 	print("usb dump hcis:\n");
424906943f9SDavid du Colombier 	for(i = 0; i < Nhcis; i++)
425906943f9SDavid du Colombier 		if(hcis[i] != nil)
426906943f9SDavid du Colombier 			hcis[i]->dump(hcis[i]);
427906943f9SDavid du Colombier }
428906943f9SDavid du Colombier 
429906943f9SDavid du Colombier static int
newusbid(Hci *)430906943f9SDavid du Colombier newusbid(Hci *)
431906943f9SDavid du Colombier {
432906943f9SDavid du Colombier 	int id;
433906943f9SDavid du Colombier 
434906943f9SDavid du Colombier 	qlock(&epslck);
435906943f9SDavid du Colombier 	id = ++usbidgen;
436906943f9SDavid du Colombier 	if(id >= 0x7F)
437906943f9SDavid du Colombier 		print("#u: too many device addresses; reuse them more\n");
438906943f9SDavid du Colombier 	qunlock(&epslck);
439906943f9SDavid du Colombier 	return id;
440906943f9SDavid du Colombier }
441906943f9SDavid du Colombier 
442906943f9SDavid du Colombier /*
443906943f9SDavid du Colombier  * Create endpoint 0 for a new device
444906943f9SDavid du Colombier  */
445906943f9SDavid du Colombier static Ep*
newdev(Hci * hp,int ishub,int isroot)446906943f9SDavid du Colombier newdev(Hci *hp, int ishub, int isroot)
447906943f9SDavid du Colombier {
448906943f9SDavid du Colombier 	Ep *ep;
449906943f9SDavid du Colombier 	Udev *d;
450906943f9SDavid du Colombier 
451906943f9SDavid du Colombier 	ep = epalloc(hp);
452305b51b8SDavid du Colombier 	d = ep->dev = smalloc(sizeof(Udev));
453906943f9SDavid du Colombier 	d->nb = newusbid(hp);
454906943f9SDavid du Colombier 	d->eps[0] = ep;
455906943f9SDavid du Colombier 	ep->nb = 0;
456906943f9SDavid du Colombier 	ep->toggle[0] = ep->toggle[1] = 0;
457906943f9SDavid du Colombier 	d->ishub = ishub;
458906943f9SDavid du Colombier 	d->isroot = isroot;
459906943f9SDavid du Colombier 	if(hp->highspeed != 0)
460906943f9SDavid du Colombier 		d->speed = Highspeed;
461906943f9SDavid du Colombier 	else
462906943f9SDavid du Colombier 		d->speed = Fullspeed;
463906943f9SDavid du Colombier 	d->state = Dconfig;		/* address not yet set */
464906943f9SDavid du Colombier 	ep->dev = d;
465906943f9SDavid du Colombier 	ep->ep0 = ep;			/* no ref counted here */
466906943f9SDavid du Colombier 	ep->ttype = Tctl;
467d37e33ffSDavid du Colombier 	ep->tmout = Xfertmout;
468906943f9SDavid du Colombier 	ep->mode = ORDWR;
469906943f9SDavid du Colombier 	dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep);
470906943f9SDavid du Colombier 	return ep;
471906943f9SDavid du Colombier }
472906943f9SDavid du Colombier 
473906943f9SDavid du Colombier /*
474906943f9SDavid du Colombier  * Create a new endpoint for the device
475906943f9SDavid du Colombier  * accessed via the given endpoint 0.
476906943f9SDavid du Colombier  */
477906943f9SDavid du Colombier static Ep*
newdevep(Ep * ep,int i,int tt,int mode)478906943f9SDavid du Colombier newdevep(Ep *ep, int i, int tt, int mode)
479906943f9SDavid du Colombier {
480906943f9SDavid du Colombier 	Ep *nep;
481906943f9SDavid du Colombier 	Udev *d;
482906943f9SDavid du Colombier 
483906943f9SDavid du Colombier 	d = ep->dev;
484906943f9SDavid du Colombier 	if(d->eps[i] != nil)
485906943f9SDavid du Colombier 		error("endpoint already in use");
486906943f9SDavid du Colombier 	nep = epalloc(ep->hp);
487906943f9SDavid du Colombier 	incref(ep);
488906943f9SDavid du Colombier 	d->eps[i] = nep;
489906943f9SDavid du Colombier 	nep->nb = i;
490906943f9SDavid du Colombier 	nep->toggle[0] = nep->toggle[1] = 0;
491906943f9SDavid du Colombier 	nep->ep0 = ep;
492906943f9SDavid du Colombier 	nep->dev = ep->dev;
493906943f9SDavid du Colombier 	nep->mode = mode;
494906943f9SDavid du Colombier 	nep->ttype = tt;
495906943f9SDavid du Colombier 	nep->debug = ep->debug;
496d37e33ffSDavid du Colombier 	/* set defaults */
497d37e33ffSDavid du Colombier 	switch(tt){
498d37e33ffSDavid du Colombier 	case Tctl:
499d37e33ffSDavid du Colombier 		nep->tmout = Xfertmout;
500d37e33ffSDavid du Colombier 		break;
501d37e33ffSDavid du Colombier 	case Tintr:
502906943f9SDavid du Colombier 		nep->pollival = 10;
503d37e33ffSDavid du Colombier 		break;
504d37e33ffSDavid du Colombier 	case Tiso:
505d37e33ffSDavid du Colombier 		nep->tmout = Xfertmout;
506d37e33ffSDavid du Colombier 		nep->pollival = 10;
507906943f9SDavid du Colombier 		nep->samplesz = 4;
508906943f9SDavid du Colombier 		nep->hz = 44100;
509d37e33ffSDavid du Colombier 		break;
510906943f9SDavid du Colombier 	}
511906943f9SDavid du Colombier 	deprint("newdevep ep%d.%d %#p\n", d->nb, nep->nb, nep);
512906943f9SDavid du Colombier 	return ep;
513906943f9SDavid du Colombier }
514906943f9SDavid du Colombier 
515906943f9SDavid du Colombier static int
epdataperm(int mode)516906943f9SDavid du Colombier epdataperm(int mode)
517906943f9SDavid du Colombier {
518906943f9SDavid du Colombier 
519906943f9SDavid du Colombier 	switch(mode){
520906943f9SDavid du Colombier 	case OREAD:
521906943f9SDavid du Colombier 		return 0440|DMEXCL;
522906943f9SDavid du Colombier 		break;
523906943f9SDavid du Colombier 	case OWRITE:
524906943f9SDavid du Colombier 		return 0220|DMEXCL;
525906943f9SDavid du Colombier 		break;
526906943f9SDavid du Colombier 	default:
527906943f9SDavid du Colombier 		return 0660|DMEXCL;
5289a747e4fSDavid du Colombier 	}
5299a747e4fSDavid du Colombier }
5309a747e4fSDavid du Colombier 
5319a747e4fSDavid du Colombier static int
usbgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)5329a747e4fSDavid du Colombier usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
5339a747e4fSDavid du Colombier {
5349a747e4fSDavid du Colombier 	Qid q;
535906943f9SDavid du Colombier 	Dirtab *dir;
536906943f9SDavid du Colombier 	int perm;
537906943f9SDavid du Colombier 	char *se;
538906943f9SDavid du Colombier 	Ep *ep;
539906943f9SDavid du Colombier 	int nb;
540906943f9SDavid du Colombier 	int mode;
5419a747e4fSDavid du Colombier 
542906943f9SDavid du Colombier 	if(0)ddprint("usbgen q %#x s %d...", QID(c->qid), s);
5439a747e4fSDavid du Colombier 	if(s == DEVDOTDOT){
544906943f9SDavid du Colombier 		if(QID(c->qid) <= Qusbdir){
545906943f9SDavid du Colombier 			mkqid(&q, Qdir, 0, QTDIR);
546906943f9SDavid du Colombier 			devdir(c, q, "#u", 0, eve, 0555, dp);
547906943f9SDavid du Colombier 		}else{
548906943f9SDavid du Colombier 			mkqid(&q, Qusbdir, 0, QTDIR);
549906943f9SDavid du Colombier 			devdir(c, q, "usb", 0, eve, 0555, dp);
550906943f9SDavid du Colombier 		}
551906943f9SDavid du Colombier 		if(0)ddprint("ok\n");
5529a747e4fSDavid du Colombier 		return 1;
5539a747e4fSDavid du Colombier 	}
5549a747e4fSDavid du Colombier 
555906943f9SDavid du Colombier 	switch(QID(c->qid)){
556906943f9SDavid du Colombier 	case Qdir:				/* list #u */
557906943f9SDavid du Colombier 		if(s == 0){
558906943f9SDavid du Colombier 			mkqid(&q, Qusbdir, 0, QTDIR);
559906943f9SDavid du Colombier 			devdir(c, q, "usb", 0, eve, 0555, dp);
560906943f9SDavid du Colombier 			if(0)ddprint("ok\n");
5619a747e4fSDavid du Colombier 			return 1;
5629a747e4fSDavid du Colombier 		}
563906943f9SDavid du Colombier 		s--;
564906943f9SDavid du Colombier 		if(s < 0 || s >= epmax)
565906943f9SDavid du Colombier 			goto Fail;
566906943f9SDavid du Colombier 		ep = getep(s);
567906943f9SDavid du Colombier 		if(ep == nil || ep->name == nil){
568906943f9SDavid du Colombier 			if(ep != nil)
569906943f9SDavid du Colombier 				putep(ep);
570906943f9SDavid du Colombier 			if(0)ddprint("skip\n");
5719a747e4fSDavid du Colombier 			return 0;
572906943f9SDavid du Colombier 		}
573906943f9SDavid du Colombier 		if(waserror()){
574906943f9SDavid du Colombier 			putep(ep);
575906943f9SDavid du Colombier 			nexterror();
576906943f9SDavid du Colombier 		}
577906943f9SDavid du Colombier 		mkqid(&q, Qep0io+s*4, 0, QTFILE);
578906943f9SDavid du Colombier 		devdir(c, q, ep->name, 0, eve, epdataperm(ep->mode), dp);
579906943f9SDavid du Colombier 		putep(ep);
580906943f9SDavid du Colombier 		poperror();
581906943f9SDavid du Colombier 		if(0)ddprint("ok\n");
5829a747e4fSDavid du Colombier 		return 1;
5839a747e4fSDavid du Colombier 
584906943f9SDavid du Colombier 	case Qusbdir:				/* list #u/usb */
585906943f9SDavid du Colombier 	Usbdir:
586906943f9SDavid du Colombier 		if(s < nelem(usbdir)){
587906943f9SDavid du Colombier 			dir = &usbdir[s];
588906943f9SDavid du Colombier 			mkqid(&q, dir->qid.path, 0, QTFILE);
589906943f9SDavid du Colombier 			devdir(c, q, dir->name, dir->length, eve, dir->perm, dp);
590906943f9SDavid du Colombier 			if(0)ddprint("ok\n");
5919a747e4fSDavid du Colombier 			return 1;
5929a747e4fSDavid du Colombier 		}
593906943f9SDavid du Colombier 		s -= nelem(usbdir);
594906943f9SDavid du Colombier 		if(s < 0 || s >= epmax)
595906943f9SDavid du Colombier 			goto Fail;
596906943f9SDavid du Colombier 		ep = getep(s);
597906943f9SDavid du Colombier 		if(ep == nil){
598906943f9SDavid du Colombier 			if(0)ddprint("skip\n");
5999a747e4fSDavid du Colombier 			return 0;
600906943f9SDavid du Colombier 		}
601906943f9SDavid du Colombier 		if(waserror()){
602906943f9SDavid du Colombier 			putep(ep);
603906943f9SDavid du Colombier 			nexterror();
604906943f9SDavid du Colombier 		}
605906943f9SDavid du Colombier 		se = up->genbuf+sizeof(up->genbuf);
606906943f9SDavid du Colombier 		seprint(up->genbuf, se, "ep%d.%d", ep->dev->nb, ep->nb);
607906943f9SDavid du Colombier 		mkqid(&q, Qep0dir+4*s, 0, QTDIR);
608906943f9SDavid du Colombier 		putep(ep);
609906943f9SDavid du Colombier 		poperror();
610906943f9SDavid du Colombier 		devdir(c, q, up->genbuf, 0, eve, 0755, dp);
611906943f9SDavid du Colombier 		if(0)ddprint("ok\n");
612906943f9SDavid du Colombier 		return 1;
613906943f9SDavid du Colombier 
614906943f9SDavid du Colombier 	case Qctl:
615906943f9SDavid du Colombier 		s = 0;
616906943f9SDavid du Colombier 		goto Usbdir;
617906943f9SDavid du Colombier 
618906943f9SDavid du Colombier 	default:				/* list #u/usb/epN.M */
619906943f9SDavid du Colombier 		nb = qid2epidx(QID(c->qid));
620906943f9SDavid du Colombier 		ep = getep(nb);
621906943f9SDavid du Colombier 		if(ep == nil)
622906943f9SDavid du Colombier 			goto Fail;
623906943f9SDavid du Colombier 		mode = ep->mode;
624906943f9SDavid du Colombier 		putep(ep);
625906943f9SDavid du Colombier 		if(isqtype(QID(c->qid), Qepdir)){
626906943f9SDavid du Colombier 		Epdir:
627906943f9SDavid du Colombier 			switch(s){
628906943f9SDavid du Colombier 			case 0:
629906943f9SDavid du Colombier 				mkqid(&q, Qep0io+nb*4, 0, QTFILE);
630906943f9SDavid du Colombier 				perm = epdataperm(mode);
631906943f9SDavid du Colombier 				devdir(c, q, "data", 0, eve, perm, dp);
6329a747e4fSDavid du Colombier 				break;
633906943f9SDavid du Colombier 			case 1:
634906943f9SDavid du Colombier 				mkqid(&q, Qep0ctl+nb*4, 0, QTFILE);
635906943f9SDavid du Colombier 				devdir(c, q, "ctl", 0, eve, 0664, dp);
6369a747e4fSDavid du Colombier 				break;
6379a747e4fSDavid du Colombier 			default:
638906943f9SDavid du Colombier 				goto Fail;
6399a747e4fSDavid du Colombier 			}
640906943f9SDavid du Colombier 		}else if(isqtype(QID(c->qid), Qepctl)){
641906943f9SDavid du Colombier 			s = 1;
642906943f9SDavid du Colombier 			goto Epdir;
643906943f9SDavid du Colombier 		}else{
644906943f9SDavid du Colombier 			s = 0;
645906943f9SDavid du Colombier 			goto Epdir;
646906943f9SDavid du Colombier 		}
647906943f9SDavid du Colombier 		if(0)ddprint("ok\n");
6489a747e4fSDavid du Colombier 		return 1;
6499a747e4fSDavid du Colombier 	}
650906943f9SDavid du Colombier Fail:
651906943f9SDavid du Colombier 	if(0)ddprint("fail\n");
652906943f9SDavid du Colombier 	return -1;
653906943f9SDavid du Colombier }
654906943f9SDavid du Colombier 
655906943f9SDavid du Colombier static Hci*
hciprobe(int cardno,int ctlrno)656906943f9SDavid du Colombier hciprobe(int cardno, int ctlrno)
6579a747e4fSDavid du Colombier {
658906943f9SDavid du Colombier 	Hci *hp;
659906943f9SDavid du Colombier 	char *type;
660e4ac449cSDavid du Colombier 	char name[64];
661906943f9SDavid du Colombier 	static int epnb = 1;	/* guess the endpoint nb. for the controller */
6629a747e4fSDavid du Colombier 
663906943f9SDavid du Colombier 	ddprint("hciprobe %d %d\n", cardno, ctlrno);
664305b51b8SDavid du Colombier 	hp = smalloc(sizeof(Hci));
665906943f9SDavid du Colombier 	hp->ctlrno = ctlrno;
666906943f9SDavid du Colombier 	hp->tbdf = BUSUNKNOWN;
6679a747e4fSDavid du Colombier 
6689a747e4fSDavid du Colombier 	if(cardno < 0){
669906943f9SDavid du Colombier 		if(isaconfig("usb", ctlrno, hp) == 0){
670906943f9SDavid du Colombier 			free(hp);
6719a747e4fSDavid du Colombier 			return nil;
6729a747e4fSDavid du Colombier 		}
673906943f9SDavid du Colombier 		for(cardno = 0; cardno < Nhcis; cardno++){
674906943f9SDavid du Colombier 			if(hcitypes[cardno].type == nil)
675906943f9SDavid du Colombier 				break;
676906943f9SDavid du Colombier 			type = hp->type;
6779a747e4fSDavid du Colombier 			if(type==nil || *type==0)
6789a747e4fSDavid du Colombier 				type = "uhci";
679906943f9SDavid du Colombier 			if(cistrcmp(hcitypes[cardno].type, type) == 0)
6809a747e4fSDavid du Colombier 				break;
6819a747e4fSDavid du Colombier 		}
6829a747e4fSDavid du Colombier 	}
6839a747e4fSDavid du Colombier 
684c8cbc0e9SDavid du Colombier 	if(cardno >= Nhcis || hcitypes[cardno].type == nil){
685c8cbc0e9SDavid du Colombier 		free(hp);
686c8cbc0e9SDavid du Colombier 		return nil;
687c8cbc0e9SDavid du Colombier 	}
688c8cbc0e9SDavid du Colombier 	dprint("%s...", hcitypes[cardno].type);
689c8cbc0e9SDavid du Colombier 	if(hcitypes[cardno].reset(hp) < 0){
690906943f9SDavid du Colombier 		free(hp);
6919a747e4fSDavid du Colombier 		return nil;
6929a747e4fSDavid du Colombier 	}
6939a747e4fSDavid du Colombier 
6949a747e4fSDavid du Colombier 	/*
6959a747e4fSDavid du Colombier 	 * IRQ2 doesn't really exist, it's used to gang the interrupt
6969a747e4fSDavid du Colombier 	 * controllers together. A device set to IRQ2 will appear on
6979a747e4fSDavid du Colombier 	 * the second interrupt controller as IRQ9.
6989a747e4fSDavid du Colombier 	 */
699906943f9SDavid du Colombier 	if(hp->irq == 2)
700906943f9SDavid du Colombier 		hp->irq = 9;
701906943f9SDavid du Colombier 	snprint(name, sizeof(name), "usb%s", hcitypes[cardno].type);
702906943f9SDavid du Colombier 	intrenable(hp->irq, hp->interrupt, hp, hp->tbdf, name);
7039a747e4fSDavid du Colombier 
704e4ac449cSDavid du Colombier 	/*
705e4ac449cSDavid du Colombier 	 * modern machines have too many usb controllers to list on
706e4ac449cSDavid du Colombier 	 * the console.
707e4ac449cSDavid du Colombier 	 */
708a23bc242SDavid du Colombier 	dprint("#u/usb/ep%d.0: %s: port 0x%luX irq %d\n",
709a23bc242SDavid du Colombier 		epnb, hcitypes[cardno].type, hp->port, hp->irq);
710e4ac449cSDavid du Colombier 	epnb++;
711906943f9SDavid du Colombier 	return hp;
7129a747e4fSDavid du Colombier }
7139a747e4fSDavid du Colombier 
7149a747e4fSDavid du Colombier static void
usbreset(void)7159a747e4fSDavid du Colombier usbreset(void)
7169a747e4fSDavid du Colombier {
7179a747e4fSDavid du Colombier 	int cardno, ctlrno;
718906943f9SDavid du Colombier 	Hci *hp;
7199a747e4fSDavid du Colombier 
720ea58ad6fSDavid du Colombier 	if(getconf("*nousbprobe"))
721ea58ad6fSDavid du Colombier 		return;
722906943f9SDavid du Colombier 	dprint("usbreset\n");
723ea58ad6fSDavid du Colombier 
724906943f9SDavid du Colombier 	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++)
725906943f9SDavid du Colombier 		if((hp = hciprobe(-1, ctlrno)) != nil)
726906943f9SDavid du Colombier 			hcis[ctlrno] = hp;
7279a747e4fSDavid du Colombier 	cardno = ctlrno = 0;
728906943f9SDavid du Colombier 	while(cardno < Nhcis && ctlrno < Nhcis && hcitypes[cardno].type != nil)
729906943f9SDavid du Colombier 		if(hcis[ctlrno] != nil)
7309a747e4fSDavid du Colombier 			ctlrno++;
731906943f9SDavid du Colombier 		else{
732906943f9SDavid du Colombier 			hp = hciprobe(cardno, ctlrno);
733906943f9SDavid du Colombier 			if(hp == nil)
7349a747e4fSDavid du Colombier 				cardno++;
735906943f9SDavid du Colombier 			hcis[ctlrno++] = hp;
7369a747e4fSDavid du Colombier 		}
737906943f9SDavid du Colombier 	if(hcis[Nhcis-1] != nil)
73884860c5dSDavid du Colombier 		print("usbreset: bug: Nhcis (%d) too small\n", Nhcis);
7399a747e4fSDavid du Colombier }
7409a747e4fSDavid du Colombier 
741906943f9SDavid du Colombier static void
usbinit(void)7429a747e4fSDavid du Colombier usbinit(void)
7439a747e4fSDavid du Colombier {
744906943f9SDavid du Colombier 	Hci *hp;
7459a747e4fSDavid du Colombier 	int ctlrno;
746906943f9SDavid du Colombier 	Ep *d;
747906943f9SDavid du Colombier 	char info[40];
7489a747e4fSDavid du Colombier 
749906943f9SDavid du Colombier 	dprint("usbinit\n");
750906943f9SDavid du Colombier 	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){
751906943f9SDavid du Colombier 		hp = hcis[ctlrno];
752906943f9SDavid du Colombier 		if(hp != nil){
753906943f9SDavid du Colombier 			if(hp->init != nil)
754906943f9SDavid du Colombier 				hp->init(hp);
755906943f9SDavid du Colombier 			d = newdev(hp, 1, 1);		/* new root hub */
756906943f9SDavid du Colombier 			d->dev->state = Denabled;	/* although addr == 0 */
757906943f9SDavid du Colombier 			d->maxpkt = 64;
758906943f9SDavid du Colombier 			snprint(info, sizeof(info), "ports %d", hp->nports);
759906943f9SDavid du Colombier 			kstrdup(&d->info, info);
760906943f9SDavid du Colombier 		}
7619a747e4fSDavid du Colombier 	}
7629a747e4fSDavid du Colombier }
7639a747e4fSDavid du Colombier 
764906943f9SDavid du Colombier static Chan*
usbattach(char * spec)7659a747e4fSDavid du Colombier usbattach(char *spec)
7669a747e4fSDavid du Colombier {
767906943f9SDavid du Colombier 	return devattach(L'u', spec);
7689a747e4fSDavid du Colombier }
7699a747e4fSDavid du Colombier 
7709a747e4fSDavid du Colombier static Walkqid*
usbwalk(Chan * c,Chan * nc,char ** name,int nname)7719a747e4fSDavid du Colombier usbwalk(Chan *c, Chan *nc, char **name, int nname)
7729a747e4fSDavid du Colombier {
7739a747e4fSDavid du Colombier 	return devwalk(c, nc, name, nname, nil, 0, usbgen);
7749a747e4fSDavid du Colombier }
7759a747e4fSDavid du Colombier 
7769a747e4fSDavid du Colombier static int
usbstat(Chan * c,uchar * db,int n)7779a747e4fSDavid du Colombier usbstat(Chan *c, uchar *db, int n)
7789a747e4fSDavid du Colombier {
7799a747e4fSDavid du Colombier 	return devstat(c, db, n, nil, 0, usbgen);
7809a747e4fSDavid du Colombier }
7819a747e4fSDavid du Colombier 
782906943f9SDavid du Colombier /*
783906943f9SDavid du Colombier  * µs for the given transfer, for bandwidth allocation.
784906943f9SDavid du Colombier  * This is a very rough worst case for what 5.11.3
785906943f9SDavid du Colombier  * of the usb 2.0 spec says.
786906943f9SDavid du Colombier  * Also, we are using maxpkt and not actual transfer sizes.
787906943f9SDavid du Colombier  * Only when we are sure we
788906943f9SDavid du Colombier  * are not exceeding b/w might we consider adjusting it.
789906943f9SDavid du Colombier  */
790906943f9SDavid du Colombier static ulong
usbload(int speed,int maxpkt)791906943f9SDavid du Colombier usbload(int speed, int maxpkt)
792906943f9SDavid du Colombier {
793906943f9SDavid du Colombier 	enum{ Hostns = 1000, Hubns = 333 };
794906943f9SDavid du Colombier 	ulong l;
795906943f9SDavid du Colombier 	ulong bs;
796906943f9SDavid du Colombier 
797906943f9SDavid du Colombier 	l = 0;
798906943f9SDavid du Colombier 	bs = 10UL * maxpkt;
799906943f9SDavid du Colombier 	switch(speed){
800906943f9SDavid du Colombier 	case Highspeed:
801906943f9SDavid du Colombier 		l = 55*8*2 + 2 * (3 + bs) + Hostns;
802906943f9SDavid du Colombier 		break;
803906943f9SDavid du Colombier 	case Fullspeed:
804906943f9SDavid du Colombier 		l = 9107 + 84 * (4 + bs) + Hostns;
805906943f9SDavid du Colombier 		break;
806906943f9SDavid du Colombier 	case Lowspeed:
807906943f9SDavid du Colombier 		l = 64107 + 2 * Hubns + 667 * (3 + bs) + Hostns;
808906943f9SDavid du Colombier 		break;
809906943f9SDavid du Colombier 	default:
810906943f9SDavid du Colombier 		print("usbload: bad speed %d\n", speed);
811906943f9SDavid du Colombier 		/* let it run */
812906943f9SDavid du Colombier 	}
813906943f9SDavid du Colombier 	return l / 1000UL;	/* in µs */
814906943f9SDavid du Colombier }
815906943f9SDavid du Colombier 
816906943f9SDavid du Colombier static Chan*
usbopen(Chan * c,int omode)8179a747e4fSDavid du Colombier usbopen(Chan *c, int omode)
8189a747e4fSDavid du Colombier {
819906943f9SDavid du Colombier 	int q;
820906943f9SDavid du Colombier 	Ep *ep;
821906943f9SDavid du Colombier 	int mode;
8229a747e4fSDavid du Colombier 
823906943f9SDavid du Colombier 	mode = openmode(omode);
824906943f9SDavid du Colombier 	q = QID(c->qid);
825906943f9SDavid du Colombier 
826906943f9SDavid du Colombier 	if(q >= Qep0dir && qid2epidx(q) < 0)
827906943f9SDavid du Colombier 		error(Eio);
828906943f9SDavid du Colombier 	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
8299a747e4fSDavid du Colombier 		return devopen(c, omode, nil, 0, usbgen);
8309a747e4fSDavid du Colombier 
831906943f9SDavid du Colombier 	ep = getep(qid2epidx(q));
832906943f9SDavid du Colombier 	if(ep == nil)
833906943f9SDavid du Colombier 		error(Eio);
834906943f9SDavid du Colombier 	deprint("usbopen q %#x fid %d omode %d\n", q, c->fid, mode);
8359a747e4fSDavid du Colombier 	if(waserror()){
836906943f9SDavid du Colombier 		putep(ep);
8379a747e4fSDavid du Colombier 		nexterror();
8389a747e4fSDavid du Colombier 	}
839906943f9SDavid du Colombier 	qlock(ep);
840906943f9SDavid du Colombier 	if(ep->inuse){
841906943f9SDavid du Colombier 		qunlock(ep);
8429a747e4fSDavid du Colombier 		error(Einuse);
843906943f9SDavid du Colombier 	}
844906943f9SDavid du Colombier 	ep->inuse = 1;
845906943f9SDavid du Colombier 	qunlock(ep);
846906943f9SDavid du Colombier 	if(waserror()){
847906943f9SDavid du Colombier 		ep->inuse = 0;
848906943f9SDavid du Colombier 		nexterror();
849906943f9SDavid du Colombier 	}
850906943f9SDavid du Colombier 	if(mode != OREAD && ep->mode == OREAD)
851906943f9SDavid du Colombier 		error(Eperm);
852906943f9SDavid du Colombier 	if(mode != OWRITE && ep->mode == OWRITE)
853906943f9SDavid du Colombier 		error(Eperm);
854906943f9SDavid du Colombier 	if(ep->ttype == Tnone)
855906943f9SDavid du Colombier 		error(Enotconf);
856906943f9SDavid du Colombier 	ep->clrhalt = 0;
857906943f9SDavid du Colombier 	ep->rhrepl = -1;
858906943f9SDavid du Colombier 	if(ep->load == 0)
859906943f9SDavid du Colombier 		ep->load = usbload(ep->dev->speed, ep->maxpkt);
860906943f9SDavid du Colombier 	ep->hp->epopen(ep);
8619a747e4fSDavid du Colombier 
862906943f9SDavid du Colombier 	poperror();	/* ep->inuse */
863906943f9SDavid du Colombier 	poperror();	/* don't putep(): ref kept for fid using the ep. */
864ade43d10SDavid du Colombier 
865906943f9SDavid du Colombier 	c->mode = mode;
8669a747e4fSDavid du Colombier 	c->flag |= COPEN;
8679a747e4fSDavid du Colombier 	c->offset = 0;
868906943f9SDavid du Colombier 	c->aux = nil;	/* paranoia */
8699a747e4fSDavid du Colombier 	return c;
8709a747e4fSDavid du Colombier }
8719a747e4fSDavid du Colombier 
872906943f9SDavid du Colombier static void
epclose(Ep * ep)873906943f9SDavid du Colombier epclose(Ep *ep)
8749a747e4fSDavid du Colombier {
875906943f9SDavid du Colombier 	qlock(ep);
8769a747e4fSDavid du Colombier 	if(waserror()){
877906943f9SDavid du Colombier 		qunlock(ep);
8789a747e4fSDavid du Colombier 		nexterror();
8799a747e4fSDavid du Colombier 	}
880906943f9SDavid du Colombier 	if(ep->inuse){
881906943f9SDavid du Colombier 		ep->hp->epclose(ep);
882906943f9SDavid du Colombier 		ep->inuse = 0;
883906943f9SDavid du Colombier 	}
884906943f9SDavid du Colombier 	qunlock(ep);
885906943f9SDavid du Colombier 	poperror();
886906943f9SDavid du Colombier }
887906943f9SDavid du Colombier 
888906943f9SDavid du Colombier static void
usbclose(Chan * c)889906943f9SDavid du Colombier usbclose(Chan *c)
890906943f9SDavid du Colombier {
891906943f9SDavid du Colombier 	int q;
892906943f9SDavid du Colombier 	Ep *ep;
893906943f9SDavid du Colombier 
894906943f9SDavid du Colombier 	q = QID(c->qid);
895906943f9SDavid du Colombier 	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
896906943f9SDavid du Colombier 		return;
897906943f9SDavid du Colombier 
898906943f9SDavid du Colombier 	ep = getep(qid2epidx(q));
899906943f9SDavid du Colombier 	if(ep == nil)
900906943f9SDavid du Colombier 		return;
901906943f9SDavid du Colombier 	deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref);
902906943f9SDavid du Colombier 	if(waserror()){
903906943f9SDavid du Colombier 		putep(ep);
904906943f9SDavid du Colombier 		nexterror();
905906943f9SDavid du Colombier 	}
9069a747e4fSDavid du Colombier 	if(c->flag & COPEN){
907906943f9SDavid du Colombier 		free(c->aux);
908906943f9SDavid du Colombier 		c->aux = nil;
909906943f9SDavid du Colombier 		epclose(ep);
910906943f9SDavid du Colombier 		putep(ep);	/* release ref kept since usbopen */
911906943f9SDavid du Colombier 		c->flag &= ~COPEN;
9129a747e4fSDavid du Colombier 	}
9139a747e4fSDavid du Colombier 	poperror();
914906943f9SDavid du Colombier 	putep(ep);
9159a747e4fSDavid du Colombier }
9169a747e4fSDavid du Colombier 
917906943f9SDavid du Colombier static long
ctlread(Chan * c,void * a,long n,vlong offset)918906943f9SDavid du Colombier ctlread(Chan *c, void *a, long n, vlong offset)
9199a747e4fSDavid du Colombier {
920906943f9SDavid du Colombier 	int q;
921906943f9SDavid du Colombier 	char *s;
922906943f9SDavid du Colombier 	char *us;
923906943f9SDavid du Colombier 	char *se;
924906943f9SDavid du Colombier 	Ep *ep;
925906943f9SDavid du Colombier 	int i;
9269a747e4fSDavid du Colombier 
927906943f9SDavid du Colombier 	q = QID(c->qid);
928906943f9SDavid du Colombier 	us = s = smalloc(READSTR);
929906943f9SDavid du Colombier 	se = s + READSTR;
930906943f9SDavid du Colombier 	if(waserror()){
931906943f9SDavid du Colombier 		free(us);
932906943f9SDavid du Colombier 		nexterror();
9339a747e4fSDavid du Colombier 	}
934906943f9SDavid du Colombier 	if(q == Qctl)
935906943f9SDavid du Colombier 		for(i = 0; i < epmax; i++){
936906943f9SDavid du Colombier 			ep = getep(i);
937906943f9SDavid du Colombier 			if(ep != nil){
938906943f9SDavid du Colombier 				if(waserror()){
939906943f9SDavid du Colombier 					putep(ep);
940906943f9SDavid du Colombier 					nexterror();
941906943f9SDavid du Colombier 				}
942906943f9SDavid du Colombier 				s = seprint(s, se, "ep%d.%d ", ep->dev->nb, ep->nb);
943906943f9SDavid du Colombier 				s = seprintep(s, se, ep, 0);
944906943f9SDavid du Colombier 				poperror();
945906943f9SDavid du Colombier 			}
946906943f9SDavid du Colombier 			putep(ep);
947906943f9SDavid du Colombier 		}
948906943f9SDavid du Colombier 	else{
949906943f9SDavid du Colombier 		ep = getep(qid2epidx(q));
950906943f9SDavid du Colombier 		if(ep == nil)
951906943f9SDavid du Colombier 			error(Eio);
952906943f9SDavid du Colombier 		if(waserror()){
953906943f9SDavid du Colombier 			putep(ep);
954906943f9SDavid du Colombier 			nexterror();
955906943f9SDavid du Colombier 		}
956906943f9SDavid du Colombier 		if(c->aux != nil){
957906943f9SDavid du Colombier 			/* After a new endpoint request we read
958906943f9SDavid du Colombier 			 * the new endpoint name back.
959906943f9SDavid du Colombier 			 */
960906943f9SDavid du Colombier 			strecpy(s, se, c->aux);
961906943f9SDavid du Colombier 			free(c->aux);
962906943f9SDavid du Colombier 			c->aux = nil;
963906943f9SDavid du Colombier 		}else
964906943f9SDavid du Colombier 			seprintep(s, se, ep, 0);
965906943f9SDavid du Colombier 		poperror();
966906943f9SDavid du Colombier 		putep(ep);
967906943f9SDavid du Colombier 	}
968906943f9SDavid du Colombier 	n = readstr(offset, a, n, us);
969906943f9SDavid du Colombier 	poperror();
970906943f9SDavid du Colombier 	free(us);
971906943f9SDavid du Colombier 	return n;
9729a747e4fSDavid du Colombier }
9739a747e4fSDavid du Colombier 
974906943f9SDavid du Colombier /*
975906943f9SDavid du Colombier  * Fake root hub emulation.
976906943f9SDavid du Colombier  */
977906943f9SDavid du Colombier static long
rhubread(Ep * ep,void * a,long n)978906943f9SDavid du Colombier rhubread(Ep *ep, void *a, long n)
979906943f9SDavid du Colombier {
980906943f9SDavid du Colombier 	char *b;
981906943f9SDavid du Colombier 
982906943f9SDavid du Colombier 	if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2)
983906943f9SDavid du Colombier 		return -1;
984906943f9SDavid du Colombier 	if(ep->rhrepl < 0)
985906943f9SDavid du Colombier 		return -1;
986906943f9SDavid du Colombier 
987906943f9SDavid du Colombier 	b = a;
988906943f9SDavid du Colombier 	memset(b, 0, n);
989906943f9SDavid du Colombier 	PUT2(b, ep->rhrepl);
990906943f9SDavid du Colombier 	ep->rhrepl = -1;
991906943f9SDavid du Colombier 	return n;
992906943f9SDavid du Colombier }
993906943f9SDavid du Colombier 
994906943f9SDavid du Colombier static long
rhubwrite(Ep * ep,void * a,long n)995906943f9SDavid du Colombier rhubwrite(Ep *ep, void *a, long n)
996906943f9SDavid du Colombier {
997906943f9SDavid du Colombier 	uchar *s;
998906943f9SDavid du Colombier 	int cmd;
999906943f9SDavid du Colombier 	int feature;
1000906943f9SDavid du Colombier 	int port;
1001906943f9SDavid du Colombier 	Hci *hp;
1002906943f9SDavid du Colombier 
1003906943f9SDavid du Colombier 	if(ep->dev == nil || ep->dev->isroot == 0 || ep->nb != 0)
1004906943f9SDavid du Colombier 		return -1;
1005906943f9SDavid du Colombier 	if(n != Rsetuplen)
1006906943f9SDavid du Colombier 		error("root hub is a toy hub");
1007906943f9SDavid du Colombier 	ep->rhrepl = -1;
1008906943f9SDavid du Colombier 	s = a;
1009906943f9SDavid du Colombier 	if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother))
1010906943f9SDavid du Colombier 		error("root hub is a toy hub");
1011906943f9SDavid du Colombier 	hp = ep->hp;
1012906943f9SDavid du Colombier 	cmd = s[Rreq];
1013906943f9SDavid du Colombier 	feature = GET2(s+Rvalue);
1014906943f9SDavid du Colombier 	port = GET2(s+Rindex);
1015906943f9SDavid du Colombier 	if(port < 1 || port > hp->nports)
1016906943f9SDavid du Colombier 		error("bad hub port number");
1017906943f9SDavid du Colombier 	switch(feature){
1018906943f9SDavid du Colombier 	case Rportenable:
1019906943f9SDavid du Colombier 		ep->rhrepl = hp->portenable(hp, port, cmd == Rsetfeature);
1020906943f9SDavid du Colombier 		break;
1021906943f9SDavid du Colombier 	case Rportreset:
1022906943f9SDavid du Colombier 		ep->rhrepl = hp->portreset(hp, port, cmd == Rsetfeature);
1023906943f9SDavid du Colombier 		break;
1024906943f9SDavid du Colombier 	case Rgetstatus:
1025906943f9SDavid du Colombier 		ep->rhrepl = hp->portstatus(hp, port);
1026906943f9SDavid du Colombier 		break;
1027906943f9SDavid du Colombier 	default:
1028906943f9SDavid du Colombier 		ep->rhrepl = 0;
1029906943f9SDavid du Colombier 	}
1030906943f9SDavid du Colombier 	return n;
1031906943f9SDavid du Colombier }
1032906943f9SDavid du Colombier 
1033906943f9SDavid du Colombier static long
usbread(Chan * c,void * a,long n,vlong offset)10349a747e4fSDavid du Colombier usbread(Chan *c, void *a, long n, vlong offset)
10359a747e4fSDavid du Colombier {
1036906943f9SDavid du Colombier 	int q;
1037906943f9SDavid du Colombier 	Ep *ep;
1038906943f9SDavid du Colombier 	int nr;
1039906943f9SDavid du Colombier 
1040906943f9SDavid du Colombier 	q = QID(c->qid);
10419a747e4fSDavid du Colombier 
10429a747e4fSDavid du Colombier 	if(c->qid.type == QTDIR)
10439a747e4fSDavid du Colombier 		return devdirread(c, a, n, nil, 0, usbgen);
10449a747e4fSDavid du Colombier 
1045906943f9SDavid du Colombier 	if(q == Qctl || isqtype(q, Qepctl))
1046906943f9SDavid du Colombier 		return ctlread(c, a, n, offset);
10479a747e4fSDavid du Colombier 
1048906943f9SDavid du Colombier 	ep = getep(qid2epidx(q));
1049906943f9SDavid du Colombier 	if(ep == nil)
10509a747e4fSDavid du Colombier 		error(Eio);
10519a747e4fSDavid du Colombier 	if(waserror()){
1052906943f9SDavid du Colombier 		putep(ep);
10539a747e4fSDavid du Colombier 		nexterror();
10549a747e4fSDavid du Colombier 	}
1055906943f9SDavid du Colombier 	if(ep->dev->state == Ddetach)
1056906943f9SDavid du Colombier 		error(Edetach);
1057906943f9SDavid du Colombier 	if(ep->mode == OWRITE || ep->inuse == 0)
1058906943f9SDavid du Colombier 		error(Ebadusefd);
1059906943f9SDavid du Colombier 	switch(ep->ttype){
1060906943f9SDavid du Colombier 	case Tnone:
1061906943f9SDavid du Colombier 		error("endpoint not configured");
1062906943f9SDavid du Colombier 	case Tctl:
1063906943f9SDavid du Colombier 		nr = rhubread(ep, a, n);
1064906943f9SDavid du Colombier 		if(nr >= 0){
1065906943f9SDavid du Colombier 			n = nr;
106602c76561SDavid du Colombier 			break;
10679a747e4fSDavid du Colombier 		}
1068906943f9SDavid du Colombier 		/* else fall */
1069906943f9SDavid du Colombier 	default:
1070906943f9SDavid du Colombier 		ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",q,c->fid,n,offset);
1071906943f9SDavid du Colombier 		n = ep->hp->epread(ep, a, n);
1072906943f9SDavid du Colombier 		break;
10739a747e4fSDavid du Colombier 	}
10749a747e4fSDavid du Colombier 	poperror();
1075906943f9SDavid du Colombier 	putep(ep);
10769a747e4fSDavid du Colombier 	return n;
10779a747e4fSDavid du Colombier }
10789a747e4fSDavid du Colombier 
1079906943f9SDavid du Colombier static long
pow2(int n)1080906943f9SDavid du Colombier pow2(int n)
10819a747e4fSDavid du Colombier {
108284860c5dSDavid du Colombier 	return 1 << n;
1083906943f9SDavid du Colombier }
1084906943f9SDavid du Colombier 
1085906943f9SDavid du Colombier static void
setmaxpkt(Ep * ep,char * s)1086906943f9SDavid du Colombier setmaxpkt(Ep *ep, char* s)
1087906943f9SDavid du Colombier {
1088906943f9SDavid du Colombier 	long spp;	/* samples per packet */
1089906943f9SDavid du Colombier 
1090906943f9SDavid du Colombier 	if(ep->dev->speed == Highspeed)
1091906943f9SDavid du Colombier 		spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000;
1092906943f9SDavid du Colombier 	else
1093906943f9SDavid du Colombier 		spp = (ep->hz * ep->pollival + 999) / 1000;
1094906943f9SDavid du Colombier 	ep->maxpkt = spp * ep->samplesz;
1095906943f9SDavid du Colombier 	deprint("usb: %s: setmaxpkt: hz %ld poll %ld"
1096906943f9SDavid du Colombier 		" ntds %d %s speed -> spp %ld maxpkt %ld\n", s,
1097906943f9SDavid du Colombier 		ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed],
1098906943f9SDavid du Colombier 		spp, ep->maxpkt);
1099906943f9SDavid du Colombier 	if(ep->maxpkt > 1024){
1100906943f9SDavid du Colombier 		print("usb: %s: maxpkt %ld > 1024. truncating\n", s, ep->maxpkt);
1101906943f9SDavid du Colombier 		ep->maxpkt = 1024;
1102906943f9SDavid du Colombier 	}
1103906943f9SDavid du Colombier }
1104906943f9SDavid du Colombier 
1105906943f9SDavid du Colombier /*
1106906943f9SDavid du Colombier  * Many endpoint ctls. simply update the portable representation
1107906943f9SDavid du Colombier  * of the endpoint. The actual controller driver will look
1108906943f9SDavid du Colombier  * at them to setup the endpoints as dictated.
1109906943f9SDavid du Colombier  */
1110906943f9SDavid du Colombier static long
epctl(Ep * ep,Chan * c,void * a,long n)1111906943f9SDavid du Colombier epctl(Ep *ep, Chan *c, void *a, long n)
1112906943f9SDavid du Colombier {
111384860c5dSDavid du Colombier 	int i, l, mode, nb, tt;
111484860c5dSDavid du Colombier 	char *b, *s;
111584860c5dSDavid du Colombier 	Cmdbuf *cb;
111684860c5dSDavid du Colombier 	Cmdtab *ct;
1117906943f9SDavid du Colombier 	Ep *nep;
11189a747e4fSDavid du Colombier 	Udev *d;
111984860c5dSDavid du Colombier 	static char *Info = "info ";
1120906943f9SDavid du Colombier 
1121906943f9SDavid du Colombier 	d = ep->dev;
1122906943f9SDavid du Colombier 
1123906943f9SDavid du Colombier 	cb = parsecmd(a, n);
1124906943f9SDavid du Colombier 	if(waserror()){
1125906943f9SDavid du Colombier 		free(cb);
1126906943f9SDavid du Colombier 		nexterror();
1127906943f9SDavid du Colombier 	}
1128906943f9SDavid du Colombier 	ct = lookupcmd(cb, epctls, nelem(epctls));
1129906943f9SDavid du Colombier 	if(ct == nil)
1130906943f9SDavid du Colombier 		error(Ebadctl);
1131906943f9SDavid du Colombier 	i = ct->index;
1132a23bc242SDavid du Colombier 	if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset)
1133906943f9SDavid du Colombier 		if(ep != ep->ep0)
1134906943f9SDavid du Colombier 			error("allowed only on a setup endpoint");
1135906943f9SDavid du Colombier 	if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname)
1136906943f9SDavid du Colombier 		if(ep != ep->ep0 && ep->inuse != 0)
1137906943f9SDavid du Colombier 			error("must configure before using");
1138906943f9SDavid du Colombier 	switch(i){
1139906943f9SDavid du Colombier 	case CMnew:
1140906943f9SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1141906943f9SDavid du Colombier 		nb = strtol(cb->f[1], nil, 0);
1142906943f9SDavid du Colombier 		if(nb < 0 || nb >= Ndeveps)
1143906943f9SDavid du Colombier 			error("bad endpoint number");
1144906943f9SDavid du Colombier 		tt = name2ttype(cb->f[2]);
1145906943f9SDavid du Colombier 		if(tt == Tnone)
1146906943f9SDavid du Colombier 			error("unknown endpoint type");
1147906943f9SDavid du Colombier 		mode = name2mode(cb->f[3]);
1148906943f9SDavid du Colombier 		if(mode < 0)
1149906943f9SDavid du Colombier 			error("unknown i/o mode");
1150906943f9SDavid du Colombier 		newdevep(ep, nb, tt, mode);
1151906943f9SDavid du Colombier 		break;
1152906943f9SDavid du Colombier 	case CMnewdev:
1153906943f9SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1154906943f9SDavid du Colombier 		if(ep != ep->ep0 || d->ishub == 0)
1155906943f9SDavid du Colombier 			error("not a hub setup endpoint");
1156906943f9SDavid du Colombier 		l = name2speed(cb->f[1]);
1157906943f9SDavid du Colombier 		if(l == Nospeed)
1158906943f9SDavid du Colombier 			error("speed must be full|low|high");
1159906943f9SDavid du Colombier 		nep = newdev(ep->hp, 0, 0);
1160906943f9SDavid du Colombier 		nep->dev->speed = l;
1161906943f9SDavid du Colombier 		if(nep->dev->speed  != Lowspeed)
1162906943f9SDavid du Colombier 			nep->maxpkt = 64;	/* assume full speed */
1163906943f9SDavid du Colombier 		nep->dev->hub = d->nb;
1164906943f9SDavid du Colombier 		nep->dev->port = atoi(cb->f[2]);
1165906943f9SDavid du Colombier 		/* next read request will read
1166906943f9SDavid du Colombier 		 * the name for the new endpoint
1167906943f9SDavid du Colombier 		 */
1168906943f9SDavid du Colombier 		l = sizeof(up->genbuf);
1169906943f9SDavid du Colombier 		snprint(up->genbuf, l, "ep%d.%d", nep->dev->nb, nep->nb);
1170906943f9SDavid du Colombier 		kstrdup(&c->aux, up->genbuf);
1171906943f9SDavid du Colombier 		break;
1172906943f9SDavid du Colombier 	case CMhub:
1173906943f9SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1174906943f9SDavid du Colombier 		d->ishub = 1;
1175906943f9SDavid du Colombier 		break;
1176906943f9SDavid du Colombier 	case CMspeed:
1177906943f9SDavid du Colombier 		l = name2speed(cb->f[1]);
1178906943f9SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
1179906943f9SDavid du Colombier 		if(l == Nospeed)
1180906943f9SDavid du Colombier 			error("speed must be full|low|high");
1181906943f9SDavid du Colombier 		qlock(ep->ep0);
1182906943f9SDavid du Colombier 		d->speed = l;
1183906943f9SDavid du Colombier 		qunlock(ep->ep0);
1184906943f9SDavid du Colombier 		break;
1185906943f9SDavid du Colombier 	case CMmaxpkt:
1186906943f9SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
1187906943f9SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
1188906943f9SDavid du Colombier 		if(l < 1 || l > 1024)
1189906943f9SDavid du Colombier 			error("maxpkt not in [1:1024]");
1190906943f9SDavid du Colombier 		qlock(ep);
1191906943f9SDavid du Colombier 		ep->maxpkt = l;
1192906943f9SDavid du Colombier 		qunlock(ep);
1193906943f9SDavid du Colombier 		break;
1194906943f9SDavid du Colombier 	case CMntds:
1195906943f9SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
1196906943f9SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
1197906943f9SDavid du Colombier 		if(l < 1 || l > 3)
1198906943f9SDavid du Colombier 			error("ntds not in [1:3]");
1199906943f9SDavid du Colombier 		qlock(ep);
1200906943f9SDavid du Colombier 		ep->ntds = l;
1201906943f9SDavid du Colombier 		qunlock(ep);
1202906943f9SDavid du Colombier 		break;
1203906943f9SDavid du Colombier 	case CMpollival:
1204906943f9SDavid du Colombier 		if(ep->ttype != Tintr && ep->ttype != Tiso)
1205906943f9SDavid du Colombier 			error("not an intr or iso endpoint");
1206906943f9SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
1207906943f9SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
1208906943f9SDavid du Colombier 		if(ep->ttype == Tiso ||
1209906943f9SDavid du Colombier 		   (ep->ttype == Tintr && ep->dev->speed == Highspeed)){
1210906943f9SDavid du Colombier 			if(l < 1 || l > 16)
1211906943f9SDavid du Colombier 				error("pollival power not in [1:16]");
1212906943f9SDavid du Colombier 			l = pow2(l-1);
1213906943f9SDavid du Colombier 		}else
1214906943f9SDavid du Colombier 			if(l < 1 || l > 255)
1215906943f9SDavid du Colombier 				error("pollival not in [1:255]");
1216906943f9SDavid du Colombier 		qlock(ep);
1217906943f9SDavid du Colombier 		ep->pollival = l;
1218906943f9SDavid du Colombier 		if(ep->ttype == Tiso)
1219906943f9SDavid du Colombier 			setmaxpkt(ep, "pollival");
1220906943f9SDavid du Colombier 		qunlock(ep);
1221906943f9SDavid du Colombier 		break;
1222906943f9SDavid du Colombier 	case CMsamplesz:
1223906943f9SDavid du Colombier 		if(ep->ttype != Tiso)
1224906943f9SDavid du Colombier 			error("not an iso endpoint");
1225906943f9SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
1226906943f9SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
1227906943f9SDavid du Colombier 		if(l <= 0 || l > 8)
1228906943f9SDavid du Colombier 			error("samplesz not in [1:8]");
1229906943f9SDavid du Colombier 		qlock(ep);
1230906943f9SDavid du Colombier 		ep->samplesz = l;
1231906943f9SDavid du Colombier 		setmaxpkt(ep, "samplesz");
1232906943f9SDavid du Colombier 		qunlock(ep);
1233906943f9SDavid du Colombier 		break;
1234906943f9SDavid du Colombier 	case CMhz:
1235906943f9SDavid du Colombier 		if(ep->ttype != Tiso)
1236906943f9SDavid du Colombier 			error("not an iso endpoint");
1237906943f9SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
1238906943f9SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
1239906943f9SDavid du Colombier 		if(l <= 0 || l > 100000)
1240906943f9SDavid du Colombier 			error("hz not in [1:100000]");
1241906943f9SDavid du Colombier 		qlock(ep);
1242906943f9SDavid du Colombier 		ep->hz = l;
1243906943f9SDavid du Colombier 		setmaxpkt(ep, "hz");
1244906943f9SDavid du Colombier 		qunlock(ep);
1245906943f9SDavid du Colombier 		break;
1246906943f9SDavid du Colombier 	case CMclrhalt:
1247906943f9SDavid du Colombier 		qlock(ep);
1248906943f9SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1249906943f9SDavid du Colombier 		ep->clrhalt = 1;
1250906943f9SDavid du Colombier 		qunlock(ep);
1251906943f9SDavid du Colombier 		break;
1252906943f9SDavid du Colombier 	case CMinfo:
1253906943f9SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1254906943f9SDavid du Colombier 		l = strlen(Info);
1255906943f9SDavid du Colombier 		s = a;
1256906943f9SDavid du Colombier 		if(n < l+2 || strncmp(Info, s, l) != 0)
1257906943f9SDavid du Colombier 			error(Ebadctl);
1258906943f9SDavid du Colombier 		if(n > 1024)
1259906943f9SDavid du Colombier 			n = 1024;
1260906943f9SDavid du Colombier 		b = smalloc(n);
1261906943f9SDavid du Colombier 		memmove(b, s+l, n-l);
1262906943f9SDavid du Colombier 		b[n-l] = 0;
1263906943f9SDavid du Colombier 		if(b[n-l-1] == '\n')
1264906943f9SDavid du Colombier 			b[n-l-1] = 0;
1265906943f9SDavid du Colombier 		qlock(ep);
1266906943f9SDavid du Colombier 		free(ep->info);
1267906943f9SDavid du Colombier 		ep->info = b;
1268906943f9SDavid du Colombier 		qunlock(ep);
1269906943f9SDavid du Colombier 		break;
1270906943f9SDavid du Colombier 	case CMaddress:
1271906943f9SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1272906943f9SDavid du Colombier 		ep->dev->state = Denabled;
1273906943f9SDavid du Colombier 		break;
1274906943f9SDavid du Colombier 	case CMdetach:
1275906943f9SDavid du Colombier 		if(ep->dev->isroot != 0)
1276906943f9SDavid du Colombier 			error("can't detach a root hub");
1277906943f9SDavid du Colombier 		deprint("usb epctl %s ep%d.%d\n",
1278906943f9SDavid du Colombier 			cb->f[0], ep->dev->nb, ep->nb);
1279906943f9SDavid du Colombier 		ep->dev->state = Ddetach;
1280906943f9SDavid du Colombier 		/* Release file system ref. for its endpoints */
1281906943f9SDavid du Colombier 		for(i = 0; i < nelem(ep->dev->eps); i++)
1282906943f9SDavid du Colombier 			putep(ep->dev->eps[i]);
1283906943f9SDavid du Colombier 		break;
1284906943f9SDavid du Colombier 	case CMdebugep:
1285906943f9SDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0)
1286906943f9SDavid du Colombier 			ep->debug = 1;
1287906943f9SDavid du Colombier 		else if(strcmp(cb->f[1], "off") == 0)
1288906943f9SDavid du Colombier 			ep->debug = 0;
1289906943f9SDavid du Colombier 		else
1290906943f9SDavid du Colombier 			ep->debug = strtoul(cb->f[1], nil, 0);
1291906943f9SDavid du Colombier 		print("usb: ep%d.%d debug %d\n",
1292906943f9SDavid du Colombier 			ep->dev->nb, ep->nb, ep->debug);
1293906943f9SDavid du Colombier 		break;
1294906943f9SDavid du Colombier 	case CMname:
1295906943f9SDavid du Colombier 		deprint("usb epctl %s %s\n", cb->f[0], cb->f[1]);
1296906943f9SDavid du Colombier 		validname(cb->f[1], 0);
1297906943f9SDavid du Colombier 		kstrdup(&ep->name, cb->f[1]);
1298906943f9SDavid du Colombier 		break;
1299d37e33ffSDavid du Colombier 	case CMtmout:
1300d37e33ffSDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1301d37e33ffSDavid du Colombier 		if(ep->ttype == Tiso || ep->ttype == Tctl)
1302d37e33ffSDavid du Colombier 			error("ctl ignored for this endpoint type");
1303d37e33ffSDavid du Colombier 		ep->tmout = strtoul(cb->f[1], nil, 0);
1304d37e33ffSDavid du Colombier 		if(ep->tmout != 0 && ep->tmout < Xfertmout)
1305d37e33ffSDavid du Colombier 			ep->tmout = Xfertmout;
1306d37e33ffSDavid du Colombier 		break;
1307a23bc242SDavid du Colombier 	case CMpreset:
1308a23bc242SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1309a23bc242SDavid du Colombier 		if(ep->ttype != Tctl)
1310a23bc242SDavid du Colombier 			error("not a control endpoint");
1311a23bc242SDavid du Colombier 		if(ep->dev->state != Denabled)
1312a23bc242SDavid du Colombier 			error("forbidden on devices not enabled");
1313a23bc242SDavid du Colombier 		ep->dev->state = Dreset;
1314a23bc242SDavid du Colombier 		break;
1315906943f9SDavid du Colombier 	default:
1316c9b6d007SDavid du Colombier 		panic("usb: unknown epctl %d", ct->index);
1317906943f9SDavid du Colombier 	}
1318906943f9SDavid du Colombier 	free(cb);
1319906943f9SDavid du Colombier 	poperror();
1320906943f9SDavid du Colombier 	return n;
1321906943f9SDavid du Colombier }
1322906943f9SDavid du Colombier 
1323906943f9SDavid du Colombier static long
usbctl(void * a,long n)1324906943f9SDavid du Colombier usbctl(void *a, long n)
1325906943f9SDavid du Colombier {
1326906943f9SDavid du Colombier 	Cmdtab *ct;
1327906943f9SDavid du Colombier 	Cmdbuf *cb;
1328906943f9SDavid du Colombier 	Ep *ep;
1329906943f9SDavid du Colombier 	int i;
1330906943f9SDavid du Colombier 
1331906943f9SDavid du Colombier 	cb = parsecmd(a, n);
1332906943f9SDavid du Colombier 	if(waserror()){
1333906943f9SDavid du Colombier 		free(cb);
1334906943f9SDavid du Colombier 		nexterror();
1335906943f9SDavid du Colombier 	}
1336906943f9SDavid du Colombier 	ct = lookupcmd(cb, usbctls, nelem(usbctls));
1337906943f9SDavid du Colombier 	dprint("usb ctl %s\n", cb->f[0]);
1338906943f9SDavid du Colombier 	switch(ct->index){
1339906943f9SDavid du Colombier 	case CMdebug:
1340906943f9SDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0)
1341906943f9SDavid du Colombier 			debug = 1;
1342906943f9SDavid du Colombier 		else if(strcmp(cb->f[1], "off") == 0)
1343906943f9SDavid du Colombier 			debug = 0;
1344906943f9SDavid du Colombier 		else
1345906943f9SDavid du Colombier 			debug = strtol(cb->f[1], nil, 0);
1346906943f9SDavid du Colombier 		print("usb: debug %d\n", debug);
1347906943f9SDavid du Colombier 		for(i = 0; i < epmax; i++)
1348906943f9SDavid du Colombier 			if((ep = getep(i)) != nil){
1349906943f9SDavid du Colombier 				ep->hp->debug(ep->hp, debug);
1350906943f9SDavid du Colombier 				putep(ep);
1351906943f9SDavid du Colombier 			}
1352906943f9SDavid du Colombier 		break;
1353906943f9SDavid du Colombier 	case CMdump:
1354906943f9SDavid du Colombier 		dumpeps();
1355906943f9SDavid du Colombier 		break;
1356906943f9SDavid du Colombier 	}
1357906943f9SDavid du Colombier 	free(cb);
1358906943f9SDavid du Colombier 	poperror();
1359906943f9SDavid du Colombier 	return n;
1360906943f9SDavid du Colombier }
1361906943f9SDavid du Colombier 
1362906943f9SDavid du Colombier static long
ctlwrite(Chan * c,void * a,long n)1363906943f9SDavid du Colombier ctlwrite(Chan *c, void *a, long n)
1364906943f9SDavid du Colombier {
1365906943f9SDavid du Colombier 	int q;
1366906943f9SDavid du Colombier 	Ep *ep;
1367906943f9SDavid du Colombier 
1368906943f9SDavid du Colombier 	q = QID(c->qid);
1369906943f9SDavid du Colombier 	if(q == Qctl)
1370906943f9SDavid du Colombier 		return usbctl(a, n);
1371906943f9SDavid du Colombier 
1372906943f9SDavid du Colombier 	ep = getep(qid2epidx(q));
1373906943f9SDavid du Colombier 	if(ep == nil)
1374906943f9SDavid du Colombier 		error(Eio);
1375906943f9SDavid du Colombier 	if(waserror()){
1376906943f9SDavid du Colombier 		putep(ep);
1377906943f9SDavid du Colombier 		nexterror();
1378906943f9SDavid du Colombier 	}
1379906943f9SDavid du Colombier 	if(ep->dev->state == Ddetach)
1380906943f9SDavid du Colombier 		error(Edetach);
1381906943f9SDavid du Colombier 	if(isqtype(q, Qepctl) && c->aux != nil){
1382906943f9SDavid du Colombier 		/* Be sure we don't keep a cloned ep name */
1383906943f9SDavid du Colombier 		free(c->aux);
1384906943f9SDavid du Colombier 		c->aux = nil;
1385906943f9SDavid du Colombier 		error("read, not write, expected");
1386906943f9SDavid du Colombier 	}
1387906943f9SDavid du Colombier 	n = epctl(ep, c, a, n);
1388906943f9SDavid du Colombier 	putep(ep);
1389906943f9SDavid du Colombier 	poperror();
1390906943f9SDavid du Colombier 	return n;
1391906943f9SDavid du Colombier }
1392906943f9SDavid du Colombier 
1393906943f9SDavid du Colombier static long
usbwrite(Chan * c,void * a,long n,vlong off)1394906943f9SDavid du Colombier usbwrite(Chan *c, void *a, long n, vlong off)
1395906943f9SDavid du Colombier {
139684860c5dSDavid du Colombier 	int nr, q;
1397906943f9SDavid du Colombier 	Ep *ep;
13989a747e4fSDavid du Colombier 
13999a747e4fSDavid du Colombier 	if(c->qid.type == QTDIR)
140045d5944cSDavid du Colombier 		error(Eisdir);
14019a747e4fSDavid du Colombier 
1402906943f9SDavid du Colombier 	q = QID(c->qid);
14039a747e4fSDavid du Colombier 
1404906943f9SDavid du Colombier 	if(q == Qctl || isqtype(q, Qepctl))
1405906943f9SDavid du Colombier 		return ctlwrite(c, a, n);
14069a747e4fSDavid du Colombier 
1407906943f9SDavid du Colombier 	ep = getep(qid2epidx(q));
1408906943f9SDavid du Colombier 	if(ep == nil)
14099a747e4fSDavid du Colombier 		error(Eio);
1410906943f9SDavid du Colombier 	if(waserror()){
1411906943f9SDavid du Colombier 		putep(ep);
1412906943f9SDavid du Colombier 		nexterror();
1413906943f9SDavid du Colombier 	}
1414906943f9SDavid du Colombier 	if(ep->dev->state == Ddetach)
1415906943f9SDavid du Colombier 		error(Edetach);
1416906943f9SDavid du Colombier 	if(ep->mode == OREAD || ep->inuse == 0)
1417906943f9SDavid du Colombier 		error(Ebadusefd);
14189a747e4fSDavid du Colombier 
1419906943f9SDavid du Colombier 	switch(ep->ttype){
1420906943f9SDavid du Colombier 	case Tnone:
1421906943f9SDavid du Colombier 		error("endpoint not configured");
1422906943f9SDavid du Colombier 	case Tctl:
1423906943f9SDavid du Colombier 		nr = rhubwrite(ep, a, n);
1424906943f9SDavid du Colombier 		if(nr >= 0){
1425906943f9SDavid du Colombier 			n = nr;
14269a747e4fSDavid du Colombier 			break;
14279a747e4fSDavid du Colombier 		}
1428906943f9SDavid du Colombier 		/* else fall */
1429906943f9SDavid du Colombier 	default:
1430906943f9SDavid du Colombier 		ddeprint("\nusbwrite q %#x fid %d cnt %ld off %lld\n",q, c->fid, n, off);
1431906943f9SDavid du Colombier 		ep->hp->epwrite(ep, a, n);
1432906943f9SDavid du Colombier 	}
1433906943f9SDavid du Colombier 	putep(ep);
1434906943f9SDavid du Colombier 	poperror();
14359a747e4fSDavid du Colombier 	return n;
14369a747e4fSDavid du Colombier }
14379a747e4fSDavid du Colombier 
1438c8cbc0e9SDavid du Colombier void
usbshutdown(void)1439c8cbc0e9SDavid du Colombier usbshutdown(void)
1440c8cbc0e9SDavid du Colombier {
1441c8cbc0e9SDavid du Colombier 	Hci *hp;
1442c8cbc0e9SDavid du Colombier 	int i;
1443c8cbc0e9SDavid du Colombier 
1444c8cbc0e9SDavid du Colombier 	for(i = 0; i < Nhcis; i++){
1445c8cbc0e9SDavid du Colombier 		hp = hcis[i];
1446c8cbc0e9SDavid du Colombier 		if(hp == nil)
1447c8cbc0e9SDavid du Colombier 			continue;
1448c8cbc0e9SDavid du Colombier 		if(hp->shutdown == nil)
1449c8cbc0e9SDavid du Colombier 			print("#u: no shutdown function for %s\n", hp->type);
1450c8cbc0e9SDavid du Colombier 		else
1451c8cbc0e9SDavid du Colombier 			hp->shutdown(hp);
1452c8cbc0e9SDavid du Colombier 	}
1453c8cbc0e9SDavid du Colombier }
1454c8cbc0e9SDavid du Colombier 
14559a747e4fSDavid du Colombier Dev usbdevtab = {
1456906943f9SDavid du Colombier 	L'u',
14579a747e4fSDavid du Colombier 	"usb",
14589a747e4fSDavid du Colombier 
14599a747e4fSDavid du Colombier 	usbreset,
14609a747e4fSDavid du Colombier 	usbinit,
1461c8cbc0e9SDavid du Colombier 	usbshutdown,
14629a747e4fSDavid du Colombier 	usbattach,
14639a747e4fSDavid du Colombier 	usbwalk,
14649a747e4fSDavid du Colombier 	usbstat,
14659a747e4fSDavid du Colombier 	usbopen,
14669a747e4fSDavid du Colombier 	devcreate,
14679a747e4fSDavid du Colombier 	usbclose,
14689a747e4fSDavid du Colombier 	usbread,
14699a747e4fSDavid du Colombier 	devbread,
14709a747e4fSDavid du Colombier 	usbwrite,
14719a747e4fSDavid du Colombier 	devbwrite,
14729a747e4fSDavid du Colombier 	devremove,
14739a747e4fSDavid du Colombier 	devwstat,
14749a747e4fSDavid du Colombier };
1475