xref: /plan9/sys/src/9/kw/devusb.c (revision 305b51b870592dafc6b4483fa85b31f528e7ab21)
1154abd99SDavid du Colombier /*
2c8a340cdSDavid du Colombier  * USB device driver framework.
3154abd99SDavid du Colombier  *
4154abd99SDavid du Colombier  * This is in charge of providing access to actual HCIs
5154abd99SDavid du Colombier  * and providing I/O to the various endpoints of devices.
6154abd99SDavid du Colombier  * A separate user program (usbd) is in charge of
7154abd99SDavid du Colombier  * enumerating the bus, setting up endpoints and
8154abd99SDavid du Colombier  * starting devices (also user programs).
9154abd99SDavid du Colombier  *
10154abd99SDavid du Colombier  * The interface provided is a violation of the standard:
11154abd99SDavid du Colombier  * you're welcome.
12154abd99SDavid du Colombier  *
13154abd99SDavid du Colombier  * The interface consists of a root directory with several files
14154abd99SDavid du Colombier  * plus a directory (epN.M) with two files per endpoint.
15154abd99SDavid du Colombier  * A device is represented by its first endpoint, which
16154abd99SDavid du Colombier  * is a control endpoint automatically allocated for each device.
17154abd99SDavid du Colombier  * Device control endpoints may be used to create new endpoints.
18154abd99SDavid du Colombier  * Devices corresponding to hubs may also allocate new devices,
19154abd99SDavid du Colombier  * perhaps also hubs. Initially, a hub device is allocated for
20154abd99SDavid du Colombier  * each controller present, to represent its root hub. Those can
21154abd99SDavid du Colombier  * never be removed.
22154abd99SDavid du Colombier  *
23154abd99SDavid du Colombier  * All endpoints refer to the first endpoint (epN.0) of the device,
24154abd99SDavid du Colombier  * which keeps per-device information, and also to the HCI used
25154abd99SDavid du Colombier  * to reach them. Although all endpoints cache that information.
26154abd99SDavid du Colombier  *
27154abd99SDavid du Colombier  * epN.M/data files permit I/O and are considered DMEXCL.
28154abd99SDavid du Colombier  * epN.M/ctl files provide status info and accept control requests.
29154abd99SDavid du Colombier  *
30154abd99SDavid du Colombier  * Endpoints may be given file names to be listed also at #u,
31154abd99SDavid du Colombier  * for those drivers that have nothing to do after configuring the
32154abd99SDavid du Colombier  * device and its endpoints.
33154abd99SDavid du Colombier  *
34154abd99SDavid du Colombier  * Drivers for different controllers are kept at usb[oue]hci.c
35154abd99SDavid du Colombier  * It's likely we could factor out much from controllers into
36154abd99SDavid du Colombier  * a generic controller driver, the problem is that details
37154abd99SDavid du Colombier  * regarding how to handle toggles, tokens, Tds, etc. will
38154abd99SDavid du Colombier  * get in the way. Thus, code is probably easier the way it is.
39154abd99SDavid du Colombier  */
40154abd99SDavid du Colombier 
41154abd99SDavid du Colombier #include	"u.h"
42154abd99SDavid du Colombier #include	"../port/lib.h"
43154abd99SDavid du Colombier #include	"mem.h"
44154abd99SDavid du Colombier #include	"dat.h"
45154abd99SDavid du Colombier #include	"fns.h"
46154abd99SDavid du Colombier #include	"io.h"
47154abd99SDavid du Colombier #include	"../port/error.h"
4884860c5dSDavid du Colombier #include	"../port/usb.h"
49154abd99SDavid du Colombier 
50154abd99SDavid du Colombier typedef struct Hcitype Hcitype;
51154abd99SDavid du Colombier 
52154abd99SDavid du Colombier enum
53154abd99SDavid du Colombier {
54154abd99SDavid du Colombier 	/* Qid numbers */
55154abd99SDavid du Colombier 	Qdir = 0,		/* #u */
56154abd99SDavid du Colombier 	Qusbdir,			/* #u/usb */
57154abd99SDavid du Colombier 	Qctl,			/* #u/usb/ctl - control requests */
58154abd99SDavid du Colombier 
59154abd99SDavid du Colombier 	Qep0dir,			/* #u/usb/ep0.0 - endpoint 0 dir */
60154abd99SDavid du Colombier 	Qep0io,			/* #u/usb/ep0.0/data - endpoint 0 I/O */
61154abd99SDavid du Colombier 	Qep0ctl,		/* #u/usb/ep0.0/ctl - endpoint 0 ctl. */
62154abd99SDavid du Colombier 	Qep0dummy,		/* give 4 qids to each endpoint */
63154abd99SDavid du Colombier 
64154abd99SDavid du Colombier 	Qepdir = 0,		/* (qid-qep0dir)&3 is one of these */
65154abd99SDavid du Colombier 	Qepio,			/* to identify which file for the endpoint */
66154abd99SDavid du Colombier 	Qepctl,
67154abd99SDavid du Colombier 
68154abd99SDavid du Colombier 	/* ... */
69154abd99SDavid du Colombier 
70154abd99SDavid du Colombier 	/* Usb ctls. */
71154abd99SDavid du Colombier 	CMdebug = 0,		/* debug on|off */
72154abd99SDavid du Colombier 	CMdump,			/* dump (data structures for debug) */
73154abd99SDavid du Colombier 
74154abd99SDavid du Colombier 	/* Ep. ctls */
75154abd99SDavid du Colombier 	CMnew = 0,		/* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */
76154abd99SDavid du Colombier 	CMnewdev,		/* newdev full|low|high portnb (allocate new devices) */
77154abd99SDavid du Colombier 	CMhub,			/* hub (set the device as a hub) */
78154abd99SDavid du Colombier 	CMspeed,		/* speed full|low|high|no */
79154abd99SDavid du Colombier 	CMmaxpkt,		/* maxpkt size */
80154abd99SDavid du Colombier 	CMntds,			/* ntds nb (max nb. of tds per µframe) */
81154abd99SDavid du Colombier 	CMclrhalt,		/* clrhalt (halt was cleared on endpoint) */
82154abd99SDavid du Colombier 	CMpollival,		/* pollival interval (interrupt/iso) */
83154abd99SDavid du Colombier 	CMhz,			/* hz n (samples/sec; iso) */
84154abd99SDavid du Colombier 	CMsamplesz,		/* samplesz n (sample size; iso) */
85154abd99SDavid du Colombier 	CMinfo,			/* info infostr (ke.ep info for humans) */
86154abd99SDavid du Colombier 	CMdetach,		/* detach (abort I/O forever on this ep). */
87154abd99SDavid du Colombier 	CMaddress,		/* address (address is assigned) */
88154abd99SDavid du Colombier 	CMdebugep,		/* debug n (set/clear debug for this ep) */
89154abd99SDavid du Colombier 	CMname,			/* name str (show up as #u/name as well) */
90cd52453fSDavid du Colombier 	CMtmout,		/* timeout n (activate timeouts for ep) */
91cd52453fSDavid du Colombier 	CMpreset,		/* reset the port */
92154abd99SDavid du Colombier 
93154abd99SDavid du Colombier 	/* Hub feature selectors */
94154abd99SDavid du Colombier 	Rportenable	= 1,
95154abd99SDavid du Colombier 	Rportreset	= 4,
96154abd99SDavid du Colombier 
97154abd99SDavid du Colombier };
98154abd99SDavid du Colombier 
99154abd99SDavid du Colombier struct Hcitype
100154abd99SDavid du Colombier {
101154abd99SDavid du Colombier 	char*	type;
102154abd99SDavid du Colombier 	int	(*reset)(Hci*);
103154abd99SDavid du Colombier };
104154abd99SDavid du Colombier 
105154abd99SDavid du Colombier #define QID(q)	((int)(q).path)
106154abd99SDavid du Colombier 
107154abd99SDavid du Colombier static char Edetach[] = "device is detached";
108154abd99SDavid du Colombier static char Enotconf[] = "endpoint not configured";
109154abd99SDavid du Colombier char Estalled[] = "endpoint stalled";
110154abd99SDavid du Colombier 
111154abd99SDavid du Colombier static Cmdtab usbctls[] =
112154abd99SDavid du Colombier {
113154abd99SDavid du Colombier 	{CMdebug,	"debug",	2},
114154abd99SDavid du Colombier 	{CMdump,	"dump",		1},
115154abd99SDavid du Colombier };
116154abd99SDavid du Colombier 
117154abd99SDavid du Colombier static Cmdtab epctls[] =
118154abd99SDavid du Colombier {
119154abd99SDavid du Colombier 	{CMnew,		"new",		4},
120154abd99SDavid du Colombier 	{CMnewdev,	"newdev",	3},
121154abd99SDavid du Colombier 	{CMhub,		"hub",		1},
122154abd99SDavid du Colombier 	{CMspeed,	"speed",	2},
123154abd99SDavid du Colombier 	{CMmaxpkt,	"maxpkt",	2},
124154abd99SDavid du Colombier 	{CMntds,	"ntds",		2},
125154abd99SDavid du Colombier 	{CMpollival,	"pollival",	2},
126154abd99SDavid du Colombier 	{CMsamplesz,	"samplesz",	2},
127154abd99SDavid du Colombier 	{CMhz,		"hz",		2},
128154abd99SDavid du Colombier 	{CMinfo,	"info",		0},
129154abd99SDavid du Colombier 	{CMdetach,	"detach",	1},
130154abd99SDavid du Colombier 	{CMaddress,	"address",	1},
131154abd99SDavid du Colombier 	{CMdebugep,	"debug",	2},
132154abd99SDavid du Colombier 	{CMclrhalt,	"clrhalt",	1},
133154abd99SDavid du Colombier 	{CMname,	"name",		2},
134cd52453fSDavid du Colombier 	{CMtmout,	"timeout",	2},
135cd52453fSDavid du Colombier 	{CMpreset,	"reset",	1},
136154abd99SDavid du Colombier };
137154abd99SDavid du Colombier 
138154abd99SDavid du Colombier static Dirtab usbdir[] =
139154abd99SDavid du Colombier {
140154abd99SDavid du Colombier 	"ctl",		{Qctl},		0,	0666,
141154abd99SDavid du Colombier };
142154abd99SDavid du Colombier 
143154abd99SDavid du Colombier char *usbmodename[] =
144154abd99SDavid du Colombier {
145154abd99SDavid du Colombier 	[OREAD]	"r",
146154abd99SDavid du Colombier 	[OWRITE]	"w",
147154abd99SDavid du Colombier 	[ORDWR]	"rw",
148154abd99SDavid du Colombier };
149154abd99SDavid du Colombier 
150154abd99SDavid du Colombier static char *ttname[] =
151154abd99SDavid du Colombier {
152154abd99SDavid du Colombier 	[Tnone]	"none",
153154abd99SDavid du Colombier 	[Tctl]	"control",
154154abd99SDavid du Colombier 	[Tiso]	"iso",
155154abd99SDavid du Colombier 	[Tintr]	"interrupt",
156154abd99SDavid du Colombier 	[Tbulk]	"bulk",
157154abd99SDavid du Colombier };
158154abd99SDavid du Colombier 
159154abd99SDavid du Colombier static char *spname[] =
160154abd99SDavid du Colombier {
161154abd99SDavid du Colombier 	[Fullspeed]	"full",
162154abd99SDavid du Colombier 	[Lowspeed]	"low",
163154abd99SDavid du Colombier 	[Highspeed]	"high",
164154abd99SDavid du Colombier 	[Nospeed]	"no",
165154abd99SDavid du Colombier };
166154abd99SDavid du Colombier 
167154abd99SDavid du Colombier static int	debug;
168154abd99SDavid du Colombier static Hcitype	hcitypes[Nhcis];
169154abd99SDavid du Colombier static Hci*	hcis[Nhcis];
170154abd99SDavid du Colombier static QLock	epslck;		/* add, del, lookup endpoints */
171154abd99SDavid du Colombier static Ep*	eps[Neps];	/* all endpoints known */
172154abd99SDavid du Colombier static int	epmax;		/* 1 + last endpoint index used  */
173154abd99SDavid du Colombier static int	usbidgen;	/* device address generator */
174154abd99SDavid du Colombier 
175154abd99SDavid du Colombier /*
176154abd99SDavid du Colombier  * Is there something like this in a library? should it be?
177154abd99SDavid du Colombier  */
178154abd99SDavid du Colombier char*
179154abd99SDavid du Colombier seprintdata(char *s, char *se, uchar *d, int n)
180154abd99SDavid du Colombier {
181c8a340cdSDavid du Colombier 	int i, l;
182154abd99SDavid du Colombier 
183154abd99SDavid du Colombier 	s = seprint(s, se, " %#p[%d]: ", d, n);
184154abd99SDavid du Colombier 	l = n;
185154abd99SDavid du Colombier 	if(l > 10)
186154abd99SDavid du Colombier 		l = 10;
187154abd99SDavid du Colombier 	for(i=0; i<l; i++)
188154abd99SDavid du Colombier 		s = seprint(s, se, " %2.2ux", d[i]);
189154abd99SDavid du Colombier 	if(l < n)
190154abd99SDavid du Colombier 		s = seprint(s, se, "...");
191154abd99SDavid du Colombier 	return s;
192154abd99SDavid du Colombier }
193154abd99SDavid du Colombier 
194154abd99SDavid du Colombier static int
195154abd99SDavid du Colombier name2speed(char *name)
196154abd99SDavid du Colombier {
197154abd99SDavid du Colombier 	int i;
198154abd99SDavid du Colombier 
199154abd99SDavid du Colombier 	for(i = 0; i < nelem(spname); i++)
200154abd99SDavid du Colombier 		if(strcmp(name, spname[i]) == 0)
201154abd99SDavid du Colombier 			return i;
202154abd99SDavid du Colombier 	return Nospeed;
203154abd99SDavid du Colombier }
204154abd99SDavid du Colombier 
205154abd99SDavid du Colombier static int
206154abd99SDavid du Colombier name2ttype(char *name)
207154abd99SDavid du Colombier {
208154abd99SDavid du Colombier 	int i;
209154abd99SDavid du Colombier 
210154abd99SDavid du Colombier 	for(i = 0; i < nelem(ttname); i++)
211154abd99SDavid du Colombier 		if(strcmp(name, ttname[i]) == 0)
212154abd99SDavid du Colombier 			return i;
213154abd99SDavid du Colombier 	/* may be a std. USB ep. type */
214154abd99SDavid du Colombier 	i = strtol(name, nil, 0);
215154abd99SDavid du Colombier 	switch(i+1){
216154abd99SDavid du Colombier 	case Tctl:
217154abd99SDavid du Colombier 	case Tiso:
218154abd99SDavid du Colombier 	case Tbulk:
219154abd99SDavid du Colombier 	case Tintr:
220154abd99SDavid du Colombier 		return i+1;
221154abd99SDavid du Colombier 	default:
222154abd99SDavid du Colombier 		return Tnone;
223154abd99SDavid du Colombier 	}
224154abd99SDavid du Colombier }
225154abd99SDavid du Colombier 
226154abd99SDavid du Colombier static int
227154abd99SDavid du Colombier name2mode(char *mode)
228154abd99SDavid du Colombier {
229154abd99SDavid du Colombier 	int i;
230154abd99SDavid du Colombier 
231154abd99SDavid du Colombier 	for(i = 0; i < nelem(usbmodename); i++)
232154abd99SDavid du Colombier 		if(strcmp(mode, usbmodename[i]) == 0)
233154abd99SDavid du Colombier 			return i;
234154abd99SDavid du Colombier 	return -1;
235154abd99SDavid du Colombier }
236154abd99SDavid du Colombier 
237154abd99SDavid du Colombier static int
238154abd99SDavid du Colombier qid2epidx(int q)
239154abd99SDavid du Colombier {
240154abd99SDavid du Colombier 	q = (q-Qep0dir)/4;
241154abd99SDavid du Colombier 	if(q < 0 || q >= epmax || eps[q] == nil)
242154abd99SDavid du Colombier 		return -1;
243154abd99SDavid du Colombier 	return q;
244154abd99SDavid du Colombier }
245154abd99SDavid du Colombier 
246154abd99SDavid du Colombier static int
247154abd99SDavid du Colombier isqtype(int q, int type)
248154abd99SDavid du Colombier {
249154abd99SDavid du Colombier 	if(q < Qep0dir)
250154abd99SDavid du Colombier 		return 0;
251154abd99SDavid du Colombier 	q -= Qep0dir;
252154abd99SDavid du Colombier 	return (q & 3) == type;
253154abd99SDavid du Colombier }
254154abd99SDavid du Colombier 
255154abd99SDavid du Colombier void
256154abd99SDavid du Colombier addhcitype(char* t, int (*r)(Hci*))
257154abd99SDavid du Colombier {
258154abd99SDavid du Colombier 	static int ntype;
259154abd99SDavid du Colombier 
260154abd99SDavid du Colombier 	if(ntype == Nhcis)
261154abd99SDavid du Colombier 		panic("too many USB host interface types");
262154abd99SDavid du Colombier 	hcitypes[ntype].type = t;
263154abd99SDavid du Colombier 	hcitypes[ntype].reset = r;
264154abd99SDavid du Colombier 	ntype++;
265154abd99SDavid du Colombier }
266154abd99SDavid du Colombier 
267154abd99SDavid du Colombier static char*
268154abd99SDavid du Colombier seprintep(char *s, char *se, Ep *ep, int all)
269154abd99SDavid du Colombier {
270cd52453fSDavid du Colombier 	static char* dsnames[] = { "config", "enabled", "detached", "reset" };
271154abd99SDavid du Colombier 	Udev *d;
272154abd99SDavid du Colombier 	int i;
273154abd99SDavid du Colombier 	int di;
274154abd99SDavid du Colombier 
275154abd99SDavid du Colombier 	d = ep->dev;
276154abd99SDavid du Colombier 
277154abd99SDavid du Colombier 	qlock(ep);
278154abd99SDavid du Colombier 	if(waserror()){
279154abd99SDavid du Colombier 		qunlock(ep);
280154abd99SDavid du Colombier 		nexterror();
281154abd99SDavid du Colombier 	}
282154abd99SDavid du Colombier 	di = ep->dev->nb;
283154abd99SDavid du Colombier 	if(all)
284154abd99SDavid du Colombier 		s = seprint(s, se, "dev %d ep %d ", di, ep->nb);
285154abd99SDavid du Colombier 	s = seprint(s, se, "%s", dsnames[ep->dev->state]);
286154abd99SDavid du Colombier 	s = seprint(s, se, " %s", ttname[ep->ttype]);
287154abd99SDavid du Colombier 	assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR);
288154abd99SDavid du Colombier 	s = seprint(s, se, " %s", usbmodename[ep->mode]);
289154abd99SDavid du Colombier 	s = seprint(s, se, " speed %s", spname[d->speed]);
290154abd99SDavid du Colombier 	s = seprint(s, se, " maxpkt %ld", ep->maxpkt);
291154abd99SDavid du Colombier 	s = seprint(s, se, " pollival %ld", ep->pollival);
292154abd99SDavid du Colombier 	s = seprint(s, se, " samplesz %ld", ep->samplesz);
293154abd99SDavid du Colombier 	s = seprint(s, se, " hz %ld", ep->hz);
294154abd99SDavid du Colombier 	s = seprint(s, se, " hub %d", ep->dev->hub);
295154abd99SDavid du Colombier 	s = seprint(s, se, " port %d", ep->dev->port);
296154abd99SDavid du Colombier 	if(ep->inuse)
297154abd99SDavid du Colombier 		s = seprint(s, se, " busy");
298154abd99SDavid du Colombier 	else
299154abd99SDavid du Colombier 		s = seprint(s, se, " idle");
300154abd99SDavid du Colombier 	if(all){
301154abd99SDavid du Colombier 		s = seprint(s, se, " load %uld", ep->load);
302154abd99SDavid du Colombier 		s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep);
303154abd99SDavid du Colombier 		s = seprint(s, se, " idx %d", ep->idx);
304154abd99SDavid du Colombier 		if(ep->name != nil)
305154abd99SDavid du Colombier 			s = seprint(s, se, " name '%s'", ep->name);
306cd52453fSDavid du Colombier 		if(ep->tmout != 0)
307cd52453fSDavid du Colombier 			s = seprint(s, se, " tmout");
308154abd99SDavid du Colombier 		if(ep == ep->ep0){
309154abd99SDavid du Colombier 			s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno);
310154abd99SDavid du Colombier 			s = seprint(s, se, " eps:");
311154abd99SDavid du Colombier 			for(i = 0; i < nelem(d->eps); i++)
312154abd99SDavid du Colombier 				if(d->eps[i] != nil)
313154abd99SDavid du Colombier 					s = seprint(s, se, " ep%d.%d", di, i);
314154abd99SDavid du Colombier 		}
315154abd99SDavid du Colombier 	}
316154abd99SDavid du Colombier 	if(ep->info != nil)
317cd52453fSDavid du Colombier 		s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type);
318154abd99SDavid du Colombier 	else
319154abd99SDavid du Colombier 		s = seprint(s, se, "\n");
320154abd99SDavid du Colombier 	qunlock(ep);
321154abd99SDavid du Colombier 	poperror();
322154abd99SDavid du Colombier 	return s;
323154abd99SDavid du Colombier }
324154abd99SDavid du Colombier 
325154abd99SDavid du Colombier static Ep*
326154abd99SDavid du Colombier epalloc(Hci *hp)
327154abd99SDavid du Colombier {
328154abd99SDavid du Colombier 	Ep *ep;
329154abd99SDavid du Colombier 	int i;
330154abd99SDavid du Colombier 
331*305b51b8SDavid du Colombier 	ep = smalloc(sizeof(Ep));
332154abd99SDavid du Colombier 	ep->ref = 1;
333154abd99SDavid du Colombier 	qlock(&epslck);
334154abd99SDavid du Colombier 	for(i = 0; i < Neps; i++)
335154abd99SDavid du Colombier 		if(eps[i] == nil)
336154abd99SDavid du Colombier 			break;
337154abd99SDavid du Colombier 	if(i == Neps){
338154abd99SDavid du Colombier 		qunlock(&epslck);
339154abd99SDavid du Colombier 		free(ep);
340154abd99SDavid du Colombier 		print("usb: bug: too few endpoints.\n");
341154abd99SDavid du Colombier 		return nil;
342154abd99SDavid du Colombier 	}
343154abd99SDavid du Colombier 	ep->idx = i;
344154abd99SDavid du Colombier 	if(epmax <= i)
345154abd99SDavid du Colombier 		epmax = i+1;
346154abd99SDavid du Colombier 	eps[i] = ep;
347154abd99SDavid du Colombier 	ep->hp = hp;
348154abd99SDavid du Colombier 	ep->maxpkt = 8;
349154abd99SDavid du Colombier 	ep->ntds = 1;
350154abd99SDavid du Colombier 	ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */
351154abd99SDavid du Colombier 	qunlock(&epslck);
352154abd99SDavid du Colombier 	return ep;
353154abd99SDavid du Colombier }
354154abd99SDavid du Colombier 
355154abd99SDavid du Colombier static Ep*
356154abd99SDavid du Colombier getep(int i)
357154abd99SDavid du Colombier {
358154abd99SDavid du Colombier 	Ep *ep;
359154abd99SDavid du Colombier 
360154abd99SDavid du Colombier 	if(i < 0 || i >= epmax || eps[i] == nil)
361154abd99SDavid du Colombier 		return nil;
362154abd99SDavid du Colombier 	qlock(&epslck);
363154abd99SDavid du Colombier 	ep = eps[i];
364154abd99SDavid du Colombier 	if(ep != nil)
365154abd99SDavid du Colombier 		incref(ep);
366154abd99SDavid du Colombier 	qunlock(&epslck);
367154abd99SDavid du Colombier 	return ep;
368154abd99SDavid du Colombier }
369154abd99SDavid du Colombier 
370154abd99SDavid du Colombier static void
371154abd99SDavid du Colombier putep(Ep *ep)
372154abd99SDavid du Colombier {
373154abd99SDavid du Colombier 	Udev *d;
374154abd99SDavid du Colombier 
375154abd99SDavid du Colombier 	if(ep != nil && decref(ep) == 0){
376154abd99SDavid du Colombier 		d = ep->dev;
377154abd99SDavid du Colombier 		deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep);
378154abd99SDavid du Colombier 		qlock(&epslck);
379154abd99SDavid du Colombier 		eps[ep->idx] = nil;
380154abd99SDavid du Colombier 		if(ep->idx == epmax-1)
381154abd99SDavid du Colombier 			epmax--;
382154abd99SDavid du Colombier 		if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen)
383154abd99SDavid du Colombier 			usbidgen--;
384154abd99SDavid du Colombier 		qunlock(&epslck);
385154abd99SDavid du Colombier 		if(d != nil){
386154abd99SDavid du Colombier 			qlock(ep->ep0);
387154abd99SDavid du Colombier 			d->eps[ep->nb] = nil;
388154abd99SDavid du Colombier 			qunlock(ep->ep0);
389154abd99SDavid du Colombier 		}
390154abd99SDavid du Colombier 		if(ep->ep0 != ep){
391154abd99SDavid du Colombier 			putep(ep->ep0);
392154abd99SDavid du Colombier 			ep->ep0 = nil;
393154abd99SDavid du Colombier 		}
394154abd99SDavid du Colombier 		free(ep->info);
395154abd99SDavid du Colombier 		free(ep->name);
396154abd99SDavid du Colombier 		free(ep);
397154abd99SDavid du Colombier 	}
398154abd99SDavid du Colombier }
399154abd99SDavid du Colombier 
400154abd99SDavid du Colombier static void
401154abd99SDavid du Colombier dumpeps(void)
402154abd99SDavid du Colombier {
403154abd99SDavid du Colombier 	int i;
404154abd99SDavid du Colombier 	static char buf[512];
405154abd99SDavid du Colombier 	char *s;
406154abd99SDavid du Colombier 	char *e;
407154abd99SDavid du Colombier 	Ep *ep;
408154abd99SDavid du Colombier 
409154abd99SDavid du Colombier 	print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps);
410154abd99SDavid du Colombier 	for(i = 0; i < epmax; i++){
411154abd99SDavid du Colombier 		s = buf;
412154abd99SDavid du Colombier 		e = buf+sizeof(buf);
413154abd99SDavid du Colombier 		ep = getep(i);
414154abd99SDavid du Colombier 		if(ep != nil){
415154abd99SDavid du Colombier 			if(waserror()){
416154abd99SDavid du Colombier 				putep(ep);
417154abd99SDavid du Colombier 				nexterror();
418154abd99SDavid du Colombier 			}
419154abd99SDavid du Colombier 			s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb);
420154abd99SDavid du Colombier 			seprintep(s, e, ep, 1);
421154abd99SDavid du Colombier 			print("%s", buf);
422154abd99SDavid du Colombier 			ep->hp->seprintep(buf, e, ep);
423154abd99SDavid du Colombier 			print("%s", buf);
424154abd99SDavid du Colombier 			poperror();
425154abd99SDavid du Colombier 			putep(ep);
426154abd99SDavid du Colombier 		}
427154abd99SDavid du Colombier 	}
428154abd99SDavid du Colombier 	print("usb dump hcis:\n");
429154abd99SDavid du Colombier 	for(i = 0; i < Nhcis; i++)
430154abd99SDavid du Colombier 		if(hcis[i] != nil)
431154abd99SDavid du Colombier 			hcis[i]->dump(hcis[i]);
432154abd99SDavid du Colombier }
433154abd99SDavid du Colombier 
434154abd99SDavid du Colombier static int
435154abd99SDavid du Colombier newusbid(Hci *)
436154abd99SDavid du Colombier {
437154abd99SDavid du Colombier 	int id;
438154abd99SDavid du Colombier 
439154abd99SDavid du Colombier 	qlock(&epslck);
440154abd99SDavid du Colombier 	id = ++usbidgen;
441154abd99SDavid du Colombier 	if(id >= 0x7F)
442154abd99SDavid du Colombier 		print("#u: too many device addresses; reuse them more\n");
443154abd99SDavid du Colombier 	qunlock(&epslck);
444154abd99SDavid du Colombier 	return id;
445154abd99SDavid du Colombier }
446154abd99SDavid du Colombier 
447154abd99SDavid du Colombier /*
448154abd99SDavid du Colombier  * Create endpoint 0 for a new device
449154abd99SDavid du Colombier  */
450154abd99SDavid du Colombier static Ep*
451154abd99SDavid du Colombier newdev(Hci *hp, int ishub, int isroot)
452154abd99SDavid du Colombier {
453154abd99SDavid du Colombier 	Ep *ep;
454154abd99SDavid du Colombier 	Udev *d;
455154abd99SDavid du Colombier 
456154abd99SDavid du Colombier 	ep = epalloc(hp);
457*305b51b8SDavid du Colombier 	d = ep->dev = smalloc(sizeof(Udev));
458154abd99SDavid du Colombier 	d->nb = newusbid(hp);
459154abd99SDavid du Colombier 	d->eps[0] = ep;
460154abd99SDavid du Colombier 	ep->nb = 0;
461154abd99SDavid du Colombier 	ep->toggle[0] = ep->toggle[1] = 0;
462154abd99SDavid du Colombier 	d->ishub = ishub;
463154abd99SDavid du Colombier 	d->isroot = isroot;
464154abd99SDavid du Colombier 	if(hp->highspeed != 0)
465154abd99SDavid du Colombier 		d->speed = Highspeed;
466154abd99SDavid du Colombier 	else
467154abd99SDavid du Colombier 		d->speed = Fullspeed;
468154abd99SDavid du Colombier 	d->state = Dconfig;		/* address not yet set */
469154abd99SDavid du Colombier 	ep->dev = d;
470154abd99SDavid du Colombier 	ep->ep0 = ep;			/* no ref counted here */
471154abd99SDavid du Colombier 	ep->ttype = Tctl;
472cd52453fSDavid du Colombier 	ep->tmout = Xfertmout;
473154abd99SDavid du Colombier 	ep->mode = ORDWR;
474154abd99SDavid du Colombier 	dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep);
475154abd99SDavid du Colombier 	return ep;
476154abd99SDavid du Colombier }
477154abd99SDavid du Colombier 
478154abd99SDavid du Colombier /*
479154abd99SDavid du Colombier  * Create a new endpoint for the device
480154abd99SDavid du Colombier  * accessed via the given endpoint 0.
481154abd99SDavid du Colombier  */
482154abd99SDavid du Colombier static Ep*
483154abd99SDavid du Colombier newdevep(Ep *ep, int i, int tt, int mode)
484154abd99SDavid du Colombier {
485154abd99SDavid du Colombier 	Ep *nep;
486154abd99SDavid du Colombier 	Udev *d;
487154abd99SDavid du Colombier 
488154abd99SDavid du Colombier 	d = ep->dev;
489154abd99SDavid du Colombier 	if(d->eps[i] != nil)
490154abd99SDavid du Colombier 		error("endpoint already in use");
491154abd99SDavid du Colombier 	nep = epalloc(ep->hp);
492154abd99SDavid du Colombier 	incref(ep);
493154abd99SDavid du Colombier 	d->eps[i] = nep;
494154abd99SDavid du Colombier 	nep->nb = i;
495154abd99SDavid du Colombier 	nep->toggle[0] = nep->toggle[1] = 0;
496154abd99SDavid du Colombier 	nep->ep0 = ep;
497154abd99SDavid du Colombier 	nep->dev = ep->dev;
498154abd99SDavid du Colombier 	nep->mode = mode;
499154abd99SDavid du Colombier 	nep->ttype = tt;
500154abd99SDavid du Colombier 	nep->debug = ep->debug;
501cd52453fSDavid du Colombier 	/* set defaults */
502cd52453fSDavid du Colombier 	switch(tt){
503cd52453fSDavid du Colombier 	case Tctl:
504cd52453fSDavid du Colombier 		nep->tmout = Xfertmout;
505cd52453fSDavid du Colombier 		break;
506cd52453fSDavid du Colombier 	case Tintr:
507154abd99SDavid du Colombier 		nep->pollival = 10;
508cd52453fSDavid du Colombier 		break;
509cd52453fSDavid du Colombier 	case Tiso:
510cd52453fSDavid du Colombier 		nep->tmout = Xfertmout;
511cd52453fSDavid du Colombier 		nep->pollival = 10;
512154abd99SDavid du Colombier 		nep->samplesz = 4;
513154abd99SDavid du Colombier 		nep->hz = 44100;
514cd52453fSDavid du Colombier 		break;
515154abd99SDavid du Colombier 	}
516154abd99SDavid du Colombier 	deprint("newdevep ep%d.%d %#p\n", d->nb, nep->nb, nep);
517154abd99SDavid du Colombier 	return ep;
518154abd99SDavid du Colombier }
519154abd99SDavid du Colombier 
520154abd99SDavid du Colombier static int
521154abd99SDavid du Colombier epdataperm(int mode)
522154abd99SDavid du Colombier {
523154abd99SDavid du Colombier 
524154abd99SDavid du Colombier 	switch(mode){
525154abd99SDavid du Colombier 	case OREAD:
526154abd99SDavid du Colombier 		return 0440|DMEXCL;
527154abd99SDavid du Colombier 		break;
528154abd99SDavid du Colombier 	case OWRITE:
529154abd99SDavid du Colombier 		return 0220|DMEXCL;
530154abd99SDavid du Colombier 		break;
531154abd99SDavid du Colombier 	default:
532154abd99SDavid du Colombier 		return 0660|DMEXCL;
533154abd99SDavid du Colombier 	}
534154abd99SDavid du Colombier }
535154abd99SDavid du Colombier 
536154abd99SDavid du Colombier static int
537154abd99SDavid du Colombier usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
538154abd99SDavid du Colombier {
539154abd99SDavid du Colombier 	Qid q;
540154abd99SDavid du Colombier 	Dirtab *dir;
541154abd99SDavid du Colombier 	int perm;
542154abd99SDavid du Colombier 	char *se;
543154abd99SDavid du Colombier 	Ep *ep;
544154abd99SDavid du Colombier 	int nb;
545154abd99SDavid du Colombier 	int mode;
546154abd99SDavid du Colombier 
547154abd99SDavid du Colombier 	if(0)ddprint("usbgen q %#x s %d...", QID(c->qid), s);
548154abd99SDavid du Colombier 	if(s == DEVDOTDOT){
549154abd99SDavid du Colombier 		if(QID(c->qid) <= Qusbdir){
550154abd99SDavid du Colombier 			mkqid(&q, Qdir, 0, QTDIR);
551154abd99SDavid du Colombier 			devdir(c, q, "#u", 0, eve, 0555, dp);
552154abd99SDavid du Colombier 		}else{
553154abd99SDavid du Colombier 			mkqid(&q, Qusbdir, 0, QTDIR);
554154abd99SDavid du Colombier 			devdir(c, q, "usb", 0, eve, 0555, dp);
555154abd99SDavid du Colombier 		}
556154abd99SDavid du Colombier 		if(0)ddprint("ok\n");
557154abd99SDavid du Colombier 		return 1;
558154abd99SDavid du Colombier 	}
559154abd99SDavid du Colombier 
560154abd99SDavid du Colombier 	switch(QID(c->qid)){
561154abd99SDavid du Colombier 	case Qdir:				/* list #u */
562154abd99SDavid du Colombier 		if(s == 0){
563154abd99SDavid du Colombier 			mkqid(&q, Qusbdir, 0, QTDIR);
564154abd99SDavid du Colombier 			devdir(c, q, "usb", 0, eve, 0555, dp);
565154abd99SDavid du Colombier 			if(0)ddprint("ok\n");
566154abd99SDavid du Colombier 			return 1;
567154abd99SDavid du Colombier 		}
568154abd99SDavid du Colombier 		s--;
569154abd99SDavid du Colombier 		if(s < 0 || s >= epmax)
570154abd99SDavid du Colombier 			goto Fail;
571154abd99SDavid du Colombier 		ep = getep(s);
572154abd99SDavid du Colombier 		if(ep == nil || ep->name == nil){
573154abd99SDavid du Colombier 			if(ep != nil)
574154abd99SDavid du Colombier 				putep(ep);
575154abd99SDavid du Colombier 			if(0)ddprint("skip\n");
576154abd99SDavid du Colombier 			return 0;
577154abd99SDavid du Colombier 		}
578154abd99SDavid du Colombier 		if(waserror()){
579154abd99SDavid du Colombier 			putep(ep);
580154abd99SDavid du Colombier 			nexterror();
581154abd99SDavid du Colombier 		}
582154abd99SDavid du Colombier 		mkqid(&q, Qep0io+s*4, 0, QTFILE);
583154abd99SDavid du Colombier 		devdir(c, q, ep->name, 0, eve, epdataperm(ep->mode), dp);
584154abd99SDavid du Colombier 		putep(ep);
585154abd99SDavid du Colombier 		poperror();
586154abd99SDavid du Colombier 		if(0)ddprint("ok\n");
587154abd99SDavid du Colombier 		return 1;
588154abd99SDavid du Colombier 
589154abd99SDavid du Colombier 	case Qusbdir:				/* list #u/usb */
590154abd99SDavid du Colombier 	Usbdir:
591154abd99SDavid du Colombier 		if(s < nelem(usbdir)){
592154abd99SDavid du Colombier 			dir = &usbdir[s];
593154abd99SDavid du Colombier 			mkqid(&q, dir->qid.path, 0, QTFILE);
594154abd99SDavid du Colombier 			devdir(c, q, dir->name, dir->length, eve, dir->perm, dp);
595154abd99SDavid du Colombier 			if(0)ddprint("ok\n");
596154abd99SDavid du Colombier 			return 1;
597154abd99SDavid du Colombier 		}
598154abd99SDavid du Colombier 		s -= nelem(usbdir);
599154abd99SDavid du Colombier 		if(s < 0 || s >= epmax)
600154abd99SDavid du Colombier 			goto Fail;
601154abd99SDavid du Colombier 		ep = getep(s);
602154abd99SDavid du Colombier 		if(ep == nil){
603154abd99SDavid du Colombier 			if(0)ddprint("skip\n");
604154abd99SDavid du Colombier 			return 0;
605154abd99SDavid du Colombier 		}
606154abd99SDavid du Colombier 		if(waserror()){
607154abd99SDavid du Colombier 			putep(ep);
608154abd99SDavid du Colombier 			nexterror();
609154abd99SDavid du Colombier 		}
610154abd99SDavid du Colombier 		se = up->genbuf+sizeof(up->genbuf);
611154abd99SDavid du Colombier 		seprint(up->genbuf, se, "ep%d.%d", ep->dev->nb, ep->nb);
612154abd99SDavid du Colombier 		mkqid(&q, Qep0dir+4*s, 0, QTDIR);
613154abd99SDavid du Colombier 		putep(ep);
614154abd99SDavid du Colombier 		poperror();
615154abd99SDavid du Colombier 		devdir(c, q, up->genbuf, 0, eve, 0755, dp);
616154abd99SDavid du Colombier 		if(0)ddprint("ok\n");
617154abd99SDavid du Colombier 		return 1;
618154abd99SDavid du Colombier 
619154abd99SDavid du Colombier 	case Qctl:
620154abd99SDavid du Colombier 		s = 0;
621154abd99SDavid du Colombier 		goto Usbdir;
622154abd99SDavid du Colombier 
623154abd99SDavid du Colombier 	default:				/* list #u/usb/epN.M */
624154abd99SDavid du Colombier 		nb = qid2epidx(QID(c->qid));
625154abd99SDavid du Colombier 		ep = getep(nb);
626154abd99SDavid du Colombier 		if(ep == nil)
627154abd99SDavid du Colombier 			goto Fail;
628154abd99SDavid du Colombier 		mode = ep->mode;
629154abd99SDavid du Colombier 		putep(ep);
630154abd99SDavid du Colombier 		if(isqtype(QID(c->qid), Qepdir)){
631154abd99SDavid du Colombier 		Epdir:
632154abd99SDavid du Colombier 			switch(s){
633154abd99SDavid du Colombier 			case 0:
634154abd99SDavid du Colombier 				mkqid(&q, Qep0io+nb*4, 0, QTFILE);
635154abd99SDavid du Colombier 				perm = epdataperm(mode);
636154abd99SDavid du Colombier 				devdir(c, q, "data", 0, eve, perm, dp);
637154abd99SDavid du Colombier 				break;
638154abd99SDavid du Colombier 			case 1:
639154abd99SDavid du Colombier 				mkqid(&q, Qep0ctl+nb*4, 0, QTFILE);
640154abd99SDavid du Colombier 				devdir(c, q, "ctl", 0, eve, 0664, dp);
641154abd99SDavid du Colombier 				break;
642154abd99SDavid du Colombier 			default:
643154abd99SDavid du Colombier 				goto Fail;
644154abd99SDavid du Colombier 			}
645154abd99SDavid du Colombier 		}else if(isqtype(QID(c->qid), Qepctl)){
646154abd99SDavid du Colombier 			s = 1;
647154abd99SDavid du Colombier 			goto Epdir;
648154abd99SDavid du Colombier 		}else{
649154abd99SDavid du Colombier 			s = 0;
650154abd99SDavid du Colombier 			goto Epdir;
651154abd99SDavid du Colombier 		}
652154abd99SDavid du Colombier 		if(0)ddprint("ok\n");
653154abd99SDavid du Colombier 		return 1;
654154abd99SDavid du Colombier 	}
655154abd99SDavid du Colombier Fail:
656154abd99SDavid du Colombier 	if(0)ddprint("fail\n");
657154abd99SDavid du Colombier 	return -1;
658154abd99SDavid du Colombier }
659154abd99SDavid du Colombier 
660154abd99SDavid du Colombier static Hci*
661154abd99SDavid du Colombier hciprobe(int cardno, int ctlrno)
662154abd99SDavid du Colombier {
663154abd99SDavid du Colombier 	Hci *hp;
664154abd99SDavid du Colombier 	char *type;
665154abd99SDavid du Colombier 	char name[64];
666154abd99SDavid du Colombier 	static int epnb = 1;	/* guess the endpoint nb. for the controller */
667154abd99SDavid du Colombier 
668154abd99SDavid du Colombier 	ddprint("hciprobe %d %d\n", cardno, ctlrno);
669*305b51b8SDavid du Colombier 	hp = smalloc(sizeof(Hci));
670154abd99SDavid du Colombier 	hp->ctlrno = ctlrno;
671154abd99SDavid du Colombier 	hp->tbdf = BUSUNKNOWN;
672154abd99SDavid du Colombier 
673154abd99SDavid du Colombier 	if(cardno < 0)
674154abd99SDavid du Colombier 		for(cardno = 0; cardno < Nhcis; cardno++){
675154abd99SDavid du Colombier 			if(hcitypes[cardno].type == nil)
676154abd99SDavid du Colombier 				break;
677154abd99SDavid du Colombier 			type = hp->type;
678154abd99SDavid du Colombier 			if(type==nil || *type==0)
679154abd99SDavid du Colombier 				type = "uhci";
680154abd99SDavid du Colombier 			if(cistrcmp(hcitypes[cardno].type, type) == 0)
681154abd99SDavid du Colombier 				break;
682154abd99SDavid du Colombier 		}
683154abd99SDavid du Colombier 
684154abd99SDavid du Colombier 	if(cardno >= Nhcis || hcitypes[cardno].type == nil){
685154abd99SDavid du Colombier 		free(hp);
686154abd99SDavid du Colombier 		return nil;
687154abd99SDavid du Colombier 	}
688154abd99SDavid du Colombier 	dprint("%s...", hcitypes[cardno].type);
689154abd99SDavid du Colombier 	if(hcitypes[cardno].reset(hp) < 0){
690154abd99SDavid du Colombier 		free(hp);
691154abd99SDavid du Colombier 		return nil;
692154abd99SDavid du Colombier 	}
693154abd99SDavid du Colombier 
694154abd99SDavid du Colombier 	snprint(name, sizeof(name), "usb%s", hcitypes[cardno].type);
695154abd99SDavid du Colombier 	intrenable(Irqlo, hp->irq, hp->interrupt, hp, name);
6969f2e9820SDavid du Colombier 	print("#u/usb/ep%d.0: %s: port %#luX irq %d\n",
697cd52453fSDavid du Colombier 		epnb, hcitypes[cardno].type, hp->port, hp->irq);
698cd52453fSDavid du Colombier 	epnb++;
699154abd99SDavid du Colombier 	return hp;
700154abd99SDavid du Colombier }
701154abd99SDavid du Colombier 
702154abd99SDavid du Colombier static void
703154abd99SDavid du Colombier usbreset(void)
704154abd99SDavid du Colombier {
705154abd99SDavid du Colombier 	int cardno, ctlrno;
706154abd99SDavid du Colombier 	Hci *hp;
707154abd99SDavid du Colombier 
708154abd99SDavid du Colombier 	dprint("usbreset\n");
709154abd99SDavid du Colombier 
710154abd99SDavid du Colombier 	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++)
711154abd99SDavid du Colombier 		if((hp = hciprobe(-1, ctlrno)) != nil)
712154abd99SDavid du Colombier 			hcis[ctlrno] = hp;
713154abd99SDavid du Colombier 	cardno = ctlrno = 0;
714154abd99SDavid du Colombier 	while(cardno < Nhcis && ctlrno < Nhcis && hcitypes[cardno].type != nil)
715154abd99SDavid du Colombier 		if(hcis[ctlrno] != nil)
716154abd99SDavid du Colombier 			ctlrno++;
717154abd99SDavid du Colombier 		else{
718154abd99SDavid du Colombier 			hp = hciprobe(cardno, ctlrno);
719154abd99SDavid du Colombier 			if(hp == nil)
720154abd99SDavid du Colombier 				cardno++;
721154abd99SDavid du Colombier 			hcis[ctlrno++] = hp;
722154abd99SDavid du Colombier 		}
723154abd99SDavid du Colombier 	if(hcis[Nhcis-1] != nil)
724154abd99SDavid du Colombier 		print("usbreset: bug: Nhcis too small\n");
725154abd99SDavid du Colombier }
726154abd99SDavid du Colombier 
727154abd99SDavid du Colombier static void
728154abd99SDavid du Colombier usbinit(void)
729154abd99SDavid du Colombier {
730154abd99SDavid du Colombier 	Hci *hp;
731154abd99SDavid du Colombier 	int ctlrno;
732154abd99SDavid du Colombier 	Ep *d;
733154abd99SDavid du Colombier 	char info[40];
734154abd99SDavid du Colombier 
735154abd99SDavid du Colombier 	dprint("usbinit\n");
736154abd99SDavid du Colombier 	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){
737154abd99SDavid du Colombier 		hp = hcis[ctlrno];
738154abd99SDavid du Colombier 		if(hp != nil){
739154abd99SDavid du Colombier 			if(hp->init != nil)
740154abd99SDavid du Colombier 				hp->init(hp);
741154abd99SDavid du Colombier 			d = newdev(hp, 1, 1);		/* new root hub */
742154abd99SDavid du Colombier 			d->dev->state = Denabled;	/* although addr == 0 */
743154abd99SDavid du Colombier 			d->maxpkt = 64;
744154abd99SDavid du Colombier 			snprint(info, sizeof(info), "ports %d", hp->nports);
745154abd99SDavid du Colombier 			kstrdup(&d->info, info);
746154abd99SDavid du Colombier 		}
747154abd99SDavid du Colombier 	}
748154abd99SDavid du Colombier }
749154abd99SDavid du Colombier 
750154abd99SDavid du Colombier static Chan*
751154abd99SDavid du Colombier usbattach(char *spec)
752154abd99SDavid du Colombier {
753154abd99SDavid du Colombier 	return devattach(L'u', spec);
754154abd99SDavid du Colombier }
755154abd99SDavid du Colombier 
756154abd99SDavid du Colombier static Walkqid*
757154abd99SDavid du Colombier usbwalk(Chan *c, Chan *nc, char **name, int nname)
758154abd99SDavid du Colombier {
759154abd99SDavid du Colombier 	return devwalk(c, nc, name, nname, nil, 0, usbgen);
760154abd99SDavid du Colombier }
761154abd99SDavid du Colombier 
762154abd99SDavid du Colombier static int
763154abd99SDavid du Colombier usbstat(Chan *c, uchar *db, int n)
764154abd99SDavid du Colombier {
765154abd99SDavid du Colombier 	return devstat(c, db, n, nil, 0, usbgen);
766154abd99SDavid du Colombier }
767154abd99SDavid du Colombier 
768154abd99SDavid du Colombier /*
769154abd99SDavid du Colombier  * µs for the given transfer, for bandwidth allocation.
770154abd99SDavid du Colombier  * This is a very rough worst case for what 5.11.3
771154abd99SDavid du Colombier  * of the usb 2.0 spec says.
772154abd99SDavid du Colombier  * Also, we are using maxpkt and not actual transfer sizes.
773154abd99SDavid du Colombier  * Only when we are sure we
774154abd99SDavid du Colombier  * are not exceeding b/w might we consider adjusting it.
775154abd99SDavid du Colombier  */
776154abd99SDavid du Colombier static ulong
777154abd99SDavid du Colombier usbload(int speed, int maxpkt)
778154abd99SDavid du Colombier {
779154abd99SDavid du Colombier 	enum{ Hostns = 1000, Hubns = 333 };
780154abd99SDavid du Colombier 	ulong l;
781154abd99SDavid du Colombier 	ulong bs;
782154abd99SDavid du Colombier 
783154abd99SDavid du Colombier 	l = 0;
784154abd99SDavid du Colombier 	bs = 10UL * maxpkt;
785154abd99SDavid du Colombier 	switch(speed){
786154abd99SDavid du Colombier 	case Highspeed:
787154abd99SDavid du Colombier 		l = 55*8*2 + 2 * (3 + bs) + Hostns;
788154abd99SDavid du Colombier 		break;
789154abd99SDavid du Colombier 	case Fullspeed:
790154abd99SDavid du Colombier 		l = 9107 + 84 * (4 + bs) + Hostns;
791154abd99SDavid du Colombier 		break;
792154abd99SDavid du Colombier 	case Lowspeed:
793154abd99SDavid du Colombier 		l = 64107 + 2 * Hubns + 667 * (3 + bs) + Hostns;
794154abd99SDavid du Colombier 		break;
795154abd99SDavid du Colombier 	default:
796154abd99SDavid du Colombier 		print("usbload: bad speed %d\n", speed);
797154abd99SDavid du Colombier 		/* let it run */
798154abd99SDavid du Colombier 	}
799154abd99SDavid du Colombier 	return l / 1000UL;	/* in µs */
800154abd99SDavid du Colombier }
801154abd99SDavid du Colombier 
802154abd99SDavid du Colombier static Chan*
803154abd99SDavid du Colombier usbopen(Chan *c, int omode)
804154abd99SDavid du Colombier {
805154abd99SDavid du Colombier 	int q;
806154abd99SDavid du Colombier 	Ep *ep;
807154abd99SDavid du Colombier 	int mode;
808154abd99SDavid du Colombier 
809154abd99SDavid du Colombier 	mode = openmode(omode);
810154abd99SDavid du Colombier 	q = QID(c->qid);
811154abd99SDavid du Colombier 
812154abd99SDavid du Colombier 	if(q >= Qep0dir && qid2epidx(q) < 0)
813154abd99SDavid du Colombier 		error(Eio);
814154abd99SDavid du Colombier 	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
815154abd99SDavid du Colombier 		return devopen(c, omode, nil, 0, usbgen);
816154abd99SDavid du Colombier 
817154abd99SDavid du Colombier 	ep = getep(qid2epidx(q));
818154abd99SDavid du Colombier 	if(ep == nil)
819154abd99SDavid du Colombier 		error(Eio);
820154abd99SDavid du Colombier 	deprint("usbopen q %#x fid %d omode %d\n", q, c->fid, mode);
821154abd99SDavid du Colombier 	if(waserror()){
822154abd99SDavid du Colombier 		putep(ep);
823154abd99SDavid du Colombier 		nexterror();
824154abd99SDavid du Colombier 	}
825154abd99SDavid du Colombier 	qlock(ep);
826154abd99SDavid du Colombier 	if(ep->inuse){
827154abd99SDavid du Colombier 		qunlock(ep);
828154abd99SDavid du Colombier 		error(Einuse);
829154abd99SDavid du Colombier 	}
830154abd99SDavid du Colombier 	ep->inuse = 1;
831154abd99SDavid du Colombier 	qunlock(ep);
832154abd99SDavid du Colombier 	if(waserror()){
833154abd99SDavid du Colombier 		ep->inuse = 0;
834154abd99SDavid du Colombier 		nexterror();
835154abd99SDavid du Colombier 	}
836154abd99SDavid du Colombier 	if(mode != OREAD && ep->mode == OREAD)
837154abd99SDavid du Colombier 		error(Eperm);
838154abd99SDavid du Colombier 	if(mode != OWRITE && ep->mode == OWRITE)
839154abd99SDavid du Colombier 		error(Eperm);
840154abd99SDavid du Colombier 	if(ep->ttype == Tnone)
841154abd99SDavid du Colombier 		error(Enotconf);
842154abd99SDavid du Colombier 	ep->clrhalt = 0;
843154abd99SDavid du Colombier 	ep->rhrepl = -1;
844154abd99SDavid du Colombier 	if(ep->load == 0)
845154abd99SDavid du Colombier 		ep->load = usbload(ep->dev->speed, ep->maxpkt);
846154abd99SDavid du Colombier 	ep->hp->epopen(ep);
847154abd99SDavid du Colombier 
848154abd99SDavid du Colombier 	poperror();	/* ep->inuse */
849154abd99SDavid du Colombier 	poperror();	/* don't putep(): ref kept for fid using the ep. */
850154abd99SDavid du Colombier 
851154abd99SDavid du Colombier 	c->mode = mode;
852154abd99SDavid du Colombier 	c->flag |= COPEN;
853154abd99SDavid du Colombier 	c->offset = 0;
854154abd99SDavid du Colombier 	c->aux = nil;	/* paranoia */
855154abd99SDavid du Colombier 	return c;
856154abd99SDavid du Colombier }
857154abd99SDavid du Colombier 
858154abd99SDavid du Colombier static void
859154abd99SDavid du Colombier epclose(Ep *ep)
860154abd99SDavid du Colombier {
861154abd99SDavid du Colombier 	qlock(ep);
862154abd99SDavid du Colombier 	if(waserror()){
863154abd99SDavid du Colombier 		qunlock(ep);
864154abd99SDavid du Colombier 		nexterror();
865154abd99SDavid du Colombier 	}
866154abd99SDavid du Colombier 	if(ep->inuse){
867154abd99SDavid du Colombier 		ep->hp->epclose(ep);
868154abd99SDavid du Colombier 		ep->inuse = 0;
869154abd99SDavid du Colombier 	}
870154abd99SDavid du Colombier 	qunlock(ep);
871154abd99SDavid du Colombier 	poperror();
872154abd99SDavid du Colombier }
873154abd99SDavid du Colombier 
874154abd99SDavid du Colombier static void
875154abd99SDavid du Colombier usbclose(Chan *c)
876154abd99SDavid du Colombier {
877154abd99SDavid du Colombier 	int q;
878154abd99SDavid du Colombier 	Ep *ep;
879154abd99SDavid du Colombier 
880154abd99SDavid du Colombier 	q = QID(c->qid);
881154abd99SDavid du Colombier 	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
882154abd99SDavid du Colombier 		return;
883154abd99SDavid du Colombier 
884154abd99SDavid du Colombier 	ep = getep(qid2epidx(q));
885154abd99SDavid du Colombier 	if(ep == nil)
886154abd99SDavid du Colombier 		return;
887154abd99SDavid du Colombier 	deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref);
888154abd99SDavid du Colombier 	if(waserror()){
889154abd99SDavid du Colombier 		putep(ep);
890154abd99SDavid du Colombier 		nexterror();
891154abd99SDavid du Colombier 	}
892154abd99SDavid du Colombier 	if(c->flag & COPEN){
893154abd99SDavid du Colombier 		free(c->aux);
894154abd99SDavid du Colombier 		c->aux = nil;
895154abd99SDavid du Colombier 		epclose(ep);
896154abd99SDavid du Colombier 		putep(ep);	/* release ref kept since usbopen */
897154abd99SDavid du Colombier 		c->flag &= ~COPEN;
898154abd99SDavid du Colombier 	}
899154abd99SDavid du Colombier 	poperror();
900154abd99SDavid du Colombier 	putep(ep);
901154abd99SDavid du Colombier }
902154abd99SDavid du Colombier 
903154abd99SDavid du Colombier static long
904154abd99SDavid du Colombier ctlread(Chan *c, void *a, long n, vlong offset)
905154abd99SDavid du Colombier {
906154abd99SDavid du Colombier 	int q;
907154abd99SDavid du Colombier 	char *s;
908154abd99SDavid du Colombier 	char *us;
909154abd99SDavid du Colombier 	char *se;
910154abd99SDavid du Colombier 	Ep *ep;
911154abd99SDavid du Colombier 	int i;
912154abd99SDavid du Colombier 
913154abd99SDavid du Colombier 	q = QID(c->qid);
914154abd99SDavid du Colombier 	us = s = smalloc(READSTR);
915154abd99SDavid du Colombier 	se = s + READSTR;
916154abd99SDavid du Colombier 	if(waserror()){
917154abd99SDavid du Colombier 		free(us);
918154abd99SDavid du Colombier 		nexterror();
919154abd99SDavid du Colombier 	}
920154abd99SDavid du Colombier 	if(q == Qctl)
921154abd99SDavid du Colombier 		for(i = 0; i < epmax; i++){
922154abd99SDavid du Colombier 			ep = getep(i);
923154abd99SDavid du Colombier 			if(ep != nil){
924154abd99SDavid du Colombier 				if(waserror()){
925154abd99SDavid du Colombier 					putep(ep);
926154abd99SDavid du Colombier 					nexterror();
927154abd99SDavid du Colombier 				}
928154abd99SDavid du Colombier 				s = seprint(s, se, "ep%d.%d ", ep->dev->nb, ep->nb);
929154abd99SDavid du Colombier 				s = seprintep(s, se, ep, 0);
930154abd99SDavid du Colombier 				poperror();
931154abd99SDavid du Colombier 			}
932154abd99SDavid du Colombier 			putep(ep);
933154abd99SDavid du Colombier 		}
934154abd99SDavid du Colombier 	else{
935154abd99SDavid du Colombier 		ep = getep(qid2epidx(q));
936154abd99SDavid du Colombier 		if(ep == nil)
937154abd99SDavid du Colombier 			error(Eio);
938154abd99SDavid du Colombier 		if(waserror()){
939154abd99SDavid du Colombier 			putep(ep);
940154abd99SDavid du Colombier 			nexterror();
941154abd99SDavid du Colombier 		}
942154abd99SDavid du Colombier 		if(c->aux != nil){
943154abd99SDavid du Colombier 			/* After a new endpoint request we read
944154abd99SDavid du Colombier 			 * the new endpoint name back.
945154abd99SDavid du Colombier 			 */
946154abd99SDavid du Colombier 			strecpy(s, se, c->aux);
947154abd99SDavid du Colombier 			free(c->aux);
948154abd99SDavid du Colombier 			c->aux = nil;
949154abd99SDavid du Colombier 		}else
950154abd99SDavid du Colombier 			seprintep(s, se, ep, 0);
951154abd99SDavid du Colombier 		poperror();
952154abd99SDavid du Colombier 		putep(ep);
953154abd99SDavid du Colombier 	}
954154abd99SDavid du Colombier 	n = readstr(offset, a, n, us);
955154abd99SDavid du Colombier 	poperror();
956154abd99SDavid du Colombier 	free(us);
957154abd99SDavid du Colombier 	return n;
958154abd99SDavid du Colombier }
959154abd99SDavid du Colombier 
960154abd99SDavid du Colombier /*
961154abd99SDavid du Colombier  * Fake root hub emulation.
962154abd99SDavid du Colombier  */
963154abd99SDavid du Colombier static long
964154abd99SDavid du Colombier rhubread(Ep *ep, void *a, long n)
965154abd99SDavid du Colombier {
966154abd99SDavid du Colombier 	char *b;
967154abd99SDavid du Colombier 
968154abd99SDavid du Colombier 	if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2)
969154abd99SDavid du Colombier 		return -1;
970154abd99SDavid du Colombier 	if(ep->rhrepl < 0)
971154abd99SDavid du Colombier 		return -1;
972154abd99SDavid du Colombier 
973154abd99SDavid du Colombier 	b = a;
974154abd99SDavid du Colombier 	memset(b, 0, n);
975154abd99SDavid du Colombier 	PUT2(b, ep->rhrepl);
976154abd99SDavid du Colombier 	ep->rhrepl = -1;
977154abd99SDavid du Colombier 	return n;
978154abd99SDavid du Colombier }
979154abd99SDavid du Colombier 
980154abd99SDavid du Colombier static long
981154abd99SDavid du Colombier rhubwrite(Ep *ep, void *a, long n)
982154abd99SDavid du Colombier {
983154abd99SDavid du Colombier 	uchar *s;
984154abd99SDavid du Colombier 	int cmd;
985154abd99SDavid du Colombier 	int feature;
986154abd99SDavid du Colombier 	int port;
987154abd99SDavid du Colombier 	Hci *hp;
988154abd99SDavid du Colombier 
989154abd99SDavid du Colombier 	if(ep->dev == nil || ep->dev->isroot == 0 || ep->nb != 0)
990154abd99SDavid du Colombier 		return -1;
991154abd99SDavid du Colombier 	if(n != Rsetuplen)
992154abd99SDavid du Colombier 		error("root hub is a toy hub");
993154abd99SDavid du Colombier 	ep->rhrepl = -1;
994154abd99SDavid du Colombier 	s = a;
995154abd99SDavid du Colombier 	if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother))
996154abd99SDavid du Colombier 		error("root hub is a toy hub");
997154abd99SDavid du Colombier 	hp = ep->hp;
998154abd99SDavid du Colombier 	cmd = s[Rreq];
999154abd99SDavid du Colombier 	feature = GET2(s+Rvalue);
1000154abd99SDavid du Colombier 	port = GET2(s+Rindex);
1001154abd99SDavid du Colombier 	if(port < 1 || port > hp->nports)
1002154abd99SDavid du Colombier 		error("bad hub port number");
1003154abd99SDavid du Colombier 	switch(feature){
1004154abd99SDavid du Colombier 	case Rportenable:
1005154abd99SDavid du Colombier 		ep->rhrepl = hp->portenable(hp, port, cmd == Rsetfeature);
1006154abd99SDavid du Colombier 		break;
1007154abd99SDavid du Colombier 	case Rportreset:
1008154abd99SDavid du Colombier 		ep->rhrepl = hp->portreset(hp, port, cmd == Rsetfeature);
1009154abd99SDavid du Colombier 		break;
1010154abd99SDavid du Colombier 	case Rgetstatus:
1011154abd99SDavid du Colombier 		ep->rhrepl = hp->portstatus(hp, port);
1012154abd99SDavid du Colombier 		break;
1013154abd99SDavid du Colombier 	default:
1014154abd99SDavid du Colombier 		ep->rhrepl = 0;
1015154abd99SDavid du Colombier 	}
1016154abd99SDavid du Colombier 	return n;
1017154abd99SDavid du Colombier }
1018154abd99SDavid du Colombier 
1019154abd99SDavid du Colombier static long
1020154abd99SDavid du Colombier usbread(Chan *c, void *a, long n, vlong offset)
1021154abd99SDavid du Colombier {
1022154abd99SDavid du Colombier 	int q;
1023154abd99SDavid du Colombier 	Ep *ep;
1024154abd99SDavid du Colombier 	int nr;
1025154abd99SDavid du Colombier 
1026154abd99SDavid du Colombier 	q = QID(c->qid);
1027154abd99SDavid du Colombier 
1028154abd99SDavid du Colombier 	if(c->qid.type == QTDIR)
1029154abd99SDavid du Colombier 		return devdirread(c, a, n, nil, 0, usbgen);
1030154abd99SDavid du Colombier 
1031154abd99SDavid du Colombier 	if(q == Qctl || isqtype(q, Qepctl))
1032154abd99SDavid du Colombier 		return ctlread(c, a, n, offset);
1033154abd99SDavid du Colombier 
1034154abd99SDavid du Colombier 	ep = getep(qid2epidx(q));
1035154abd99SDavid du Colombier 	if(ep == nil)
1036154abd99SDavid du Colombier 		error(Eio);
1037154abd99SDavid du Colombier 	if(waserror()){
1038154abd99SDavid du Colombier 		putep(ep);
1039154abd99SDavid du Colombier 		nexterror();
1040154abd99SDavid du Colombier 	}
1041154abd99SDavid du Colombier 	if(ep->dev->state == Ddetach)
1042154abd99SDavid du Colombier 		error(Edetach);
1043154abd99SDavid du Colombier 	if(ep->mode == OWRITE || ep->inuse == 0)
1044154abd99SDavid du Colombier 		error(Ebadusefd);
1045154abd99SDavid du Colombier 	switch(ep->ttype){
1046154abd99SDavid du Colombier 	case Tnone:
1047154abd99SDavid du Colombier 		error("endpoint not configured");
1048154abd99SDavid du Colombier 	case Tctl:
1049154abd99SDavid du Colombier 		nr = rhubread(ep, a, n);
1050154abd99SDavid du Colombier 		if(nr >= 0){
1051154abd99SDavid du Colombier 			n = nr;
1052154abd99SDavid du Colombier 			break;
1053154abd99SDavid du Colombier 		}
1054154abd99SDavid du Colombier 		/* else fall */
1055154abd99SDavid du Colombier 	default:
1056154abd99SDavid du Colombier 		ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",q,c->fid,n,offset);
1057154abd99SDavid du Colombier 		n = ep->hp->epread(ep, a, n);
1058154abd99SDavid du Colombier 		break;
1059154abd99SDavid du Colombier 	}
1060154abd99SDavid du Colombier 	poperror();
1061154abd99SDavid du Colombier 	putep(ep);
1062154abd99SDavid du Colombier 	return n;
1063154abd99SDavid du Colombier }
1064154abd99SDavid du Colombier 
1065154abd99SDavid du Colombier static long
1066154abd99SDavid du Colombier pow2(int n)
1067154abd99SDavid du Colombier {
1068c8a340cdSDavid du Colombier 	return 1 << n;
1069154abd99SDavid du Colombier }
1070154abd99SDavid du Colombier 
1071154abd99SDavid du Colombier static void
1072154abd99SDavid du Colombier setmaxpkt(Ep *ep, char* s)
1073154abd99SDavid du Colombier {
1074154abd99SDavid du Colombier 	long spp;	/* samples per packet */
1075154abd99SDavid du Colombier 
1076154abd99SDavid du Colombier 	if(ep->dev->speed == Highspeed)
1077154abd99SDavid du Colombier 		spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000;
1078154abd99SDavid du Colombier 	else
1079154abd99SDavid du Colombier 		spp = (ep->hz * ep->pollival + 999) / 1000;
1080154abd99SDavid du Colombier 	ep->maxpkt = spp * ep->samplesz;
1081154abd99SDavid du Colombier 	deprint("usb: %s: setmaxpkt: hz %ld poll %ld"
1082154abd99SDavid du Colombier 		" ntds %d %s speed -> spp %ld maxpkt %ld\n", s,
1083154abd99SDavid du Colombier 		ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed],
1084154abd99SDavid du Colombier 		spp, ep->maxpkt);
1085154abd99SDavid du Colombier 	if(ep->maxpkt > 1024){
1086154abd99SDavid du Colombier 		print("usb: %s: maxpkt %ld > 1024. truncating\n", s, ep->maxpkt);
1087154abd99SDavid du Colombier 		ep->maxpkt = 1024;
1088154abd99SDavid du Colombier 	}
1089154abd99SDavid du Colombier }
1090154abd99SDavid du Colombier 
1091154abd99SDavid du Colombier /*
1092154abd99SDavid du Colombier  * Many endpoint ctls. simply update the portable representation
1093154abd99SDavid du Colombier  * of the endpoint. The actual controller driver will look
1094154abd99SDavid du Colombier  * at them to setup the endpoints as dictated.
1095154abd99SDavid du Colombier  */
1096154abd99SDavid du Colombier static long
1097154abd99SDavid du Colombier epctl(Ep *ep, Chan *c, void *a, long n)
1098154abd99SDavid du Colombier {
1099c8a340cdSDavid du Colombier 	int i, l, mode, nb, tt;
1100c8a340cdSDavid du Colombier 	char *b, *s;
1101c8a340cdSDavid du Colombier 	Cmdbuf *cb;
1102c8a340cdSDavid du Colombier 	Cmdtab *ct;
1103154abd99SDavid du Colombier 	Ep *nep;
1104154abd99SDavid du Colombier 	Udev *d;
1105c8a340cdSDavid du Colombier 	static char *Info = "info ";
1106154abd99SDavid du Colombier 
1107154abd99SDavid du Colombier 	d = ep->dev;
1108154abd99SDavid du Colombier 
1109154abd99SDavid du Colombier 	cb = parsecmd(a, n);
1110154abd99SDavid du Colombier 	if(waserror()){
1111154abd99SDavid du Colombier 		free(cb);
1112154abd99SDavid du Colombier 		nexterror();
1113154abd99SDavid du Colombier 	}
1114154abd99SDavid du Colombier 	ct = lookupcmd(cb, epctls, nelem(epctls));
1115154abd99SDavid du Colombier 	if(ct == nil)
1116154abd99SDavid du Colombier 		error(Ebadctl);
1117154abd99SDavid du Colombier 	i = ct->index;
1118cd52453fSDavid du Colombier 	if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset)
1119154abd99SDavid du Colombier 		if(ep != ep->ep0)
1120154abd99SDavid du Colombier 			error("allowed only on a setup endpoint");
1121154abd99SDavid du Colombier 	if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname)
1122154abd99SDavid du Colombier 		if(ep != ep->ep0 && ep->inuse != 0)
1123154abd99SDavid du Colombier 			error("must configure before using");
1124154abd99SDavid du Colombier 	switch(i){
1125154abd99SDavid du Colombier 	case CMnew:
1126154abd99SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1127154abd99SDavid du Colombier 		nb = strtol(cb->f[1], nil, 0);
1128154abd99SDavid du Colombier 		if(nb < 0 || nb >= Ndeveps)
1129154abd99SDavid du Colombier 			error("bad endpoint number");
1130154abd99SDavid du Colombier 		tt = name2ttype(cb->f[2]);
1131154abd99SDavid du Colombier 		if(tt == Tnone)
1132154abd99SDavid du Colombier 			error("unknown endpoint type");
1133154abd99SDavid du Colombier 		mode = name2mode(cb->f[3]);
1134154abd99SDavid du Colombier 		if(mode < 0)
1135154abd99SDavid du Colombier 			error("unknown i/o mode");
1136154abd99SDavid du Colombier 		newdevep(ep, nb, tt, mode);
1137154abd99SDavid du Colombier 		break;
1138154abd99SDavid du Colombier 	case CMnewdev:
1139154abd99SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1140154abd99SDavid du Colombier 		if(ep != ep->ep0 || d->ishub == 0)
1141154abd99SDavid du Colombier 			error("not a hub setup endpoint");
1142154abd99SDavid du Colombier 		l = name2speed(cb->f[1]);
1143154abd99SDavid du Colombier 		if(l == Nospeed)
1144154abd99SDavid du Colombier 			error("speed must be full|low|high");
1145154abd99SDavid du Colombier 		nep = newdev(ep->hp, 0, 0);
1146154abd99SDavid du Colombier 		nep->dev->speed = l;
1147154abd99SDavid du Colombier 		if(nep->dev->speed  != Lowspeed)
1148154abd99SDavid du Colombier 			nep->maxpkt = 64;	/* assume full speed */
1149154abd99SDavid du Colombier 		nep->dev->hub = d->nb;
1150154abd99SDavid du Colombier 		nep->dev->port = atoi(cb->f[2]);
1151154abd99SDavid du Colombier 		/* next read request will read
1152154abd99SDavid du Colombier 		 * the name for the new endpoint
1153154abd99SDavid du Colombier 		 */
1154154abd99SDavid du Colombier 		l = sizeof(up->genbuf);
1155154abd99SDavid du Colombier 		snprint(up->genbuf, l, "ep%d.%d", nep->dev->nb, nep->nb);
1156154abd99SDavid du Colombier 		kstrdup(&c->aux, up->genbuf);
1157154abd99SDavid du Colombier 		break;
1158154abd99SDavid du Colombier 	case CMhub:
1159154abd99SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1160154abd99SDavid du Colombier 		d->ishub = 1;
1161154abd99SDavid du Colombier 		break;
1162154abd99SDavid du Colombier 	case CMspeed:
1163154abd99SDavid du Colombier 		l = name2speed(cb->f[1]);
1164154abd99SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
1165154abd99SDavid du Colombier 		if(l == Nospeed)
1166154abd99SDavid du Colombier 			error("speed must be full|low|high");
1167154abd99SDavid du Colombier 		qlock(ep->ep0);
1168154abd99SDavid du Colombier 		d->speed = l;
1169154abd99SDavid du Colombier 		qunlock(ep->ep0);
1170154abd99SDavid du Colombier 		break;
1171154abd99SDavid du Colombier 	case CMmaxpkt:
1172154abd99SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
1173154abd99SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
1174154abd99SDavid du Colombier 		if(l < 1 || l > 1024)
1175154abd99SDavid du Colombier 			error("maxpkt not in [1:1024]");
1176154abd99SDavid du Colombier 		qlock(ep);
1177154abd99SDavid du Colombier 		ep->maxpkt = l;
1178154abd99SDavid du Colombier 		qunlock(ep);
1179154abd99SDavid du Colombier 		break;
1180154abd99SDavid du Colombier 	case CMntds:
1181154abd99SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
1182154abd99SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
1183154abd99SDavid du Colombier 		if(l < 1 || l > 3)
1184154abd99SDavid du Colombier 			error("ntds not in [1:3]");
1185154abd99SDavid du Colombier 		qlock(ep);
1186154abd99SDavid du Colombier 		ep->ntds = l;
1187154abd99SDavid du Colombier 		qunlock(ep);
1188154abd99SDavid du Colombier 		break;
1189154abd99SDavid du Colombier 	case CMpollival:
1190154abd99SDavid du Colombier 		if(ep->ttype != Tintr && ep->ttype != Tiso)
1191154abd99SDavid du Colombier 			error("not an intr or iso endpoint");
1192154abd99SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
1193154abd99SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
1194154abd99SDavid du Colombier 		if(ep->ttype == Tiso ||
1195154abd99SDavid du Colombier 		   (ep->ttype == Tintr && ep->dev->speed == Highspeed)){
1196154abd99SDavid du Colombier 			if(l < 1 || l > 16)
1197154abd99SDavid du Colombier 				error("pollival power not in [1:16]");
1198154abd99SDavid du Colombier 			l = pow2(l-1);
1199154abd99SDavid du Colombier 		}else
1200154abd99SDavid du Colombier 			if(l < 1 || l > 255)
1201154abd99SDavid du Colombier 				error("pollival not in [1:255]");
1202154abd99SDavid du Colombier 		qlock(ep);
1203154abd99SDavid du Colombier 		ep->pollival = l;
1204154abd99SDavid du Colombier 		if(ep->ttype == Tiso)
1205154abd99SDavid du Colombier 			setmaxpkt(ep, "pollival");
1206154abd99SDavid du Colombier 		qunlock(ep);
1207154abd99SDavid du Colombier 		break;
1208154abd99SDavid du Colombier 	case CMsamplesz:
1209154abd99SDavid du Colombier 		if(ep->ttype != Tiso)
1210154abd99SDavid du Colombier 			error("not an iso endpoint");
1211154abd99SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
1212154abd99SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
1213154abd99SDavid du Colombier 		if(l <= 0 || l > 8)
1214154abd99SDavid du Colombier 			error("samplesz not in [1:8]");
1215154abd99SDavid du Colombier 		qlock(ep);
1216154abd99SDavid du Colombier 		ep->samplesz = l;
1217154abd99SDavid du Colombier 		setmaxpkt(ep, "samplesz");
1218154abd99SDavid du Colombier 		qunlock(ep);
1219154abd99SDavid du Colombier 		break;
1220154abd99SDavid du Colombier 	case CMhz:
1221154abd99SDavid du Colombier 		if(ep->ttype != Tiso)
1222154abd99SDavid du Colombier 			error("not an iso endpoint");
1223154abd99SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
1224154abd99SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
1225154abd99SDavid du Colombier 		if(l <= 0 || l > 100000)
1226154abd99SDavid du Colombier 			error("hz not in [1:100000]");
1227154abd99SDavid du Colombier 		qlock(ep);
1228154abd99SDavid du Colombier 		ep->hz = l;
1229154abd99SDavid du Colombier 		setmaxpkt(ep, "hz");
1230154abd99SDavid du Colombier 		qunlock(ep);
1231154abd99SDavid du Colombier 		break;
1232154abd99SDavid du Colombier 	case CMclrhalt:
1233154abd99SDavid du Colombier 		qlock(ep);
1234154abd99SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1235154abd99SDavid du Colombier 		ep->clrhalt = 1;
1236154abd99SDavid du Colombier 		qunlock(ep);
1237154abd99SDavid du Colombier 		break;
1238154abd99SDavid du Colombier 	case CMinfo:
1239154abd99SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1240154abd99SDavid du Colombier 		l = strlen(Info);
1241154abd99SDavid du Colombier 		s = a;
1242154abd99SDavid du Colombier 		if(n < l+2 || strncmp(Info, s, l) != 0)
1243154abd99SDavid du Colombier 			error(Ebadctl);
1244154abd99SDavid du Colombier 		if(n > 1024)
1245154abd99SDavid du Colombier 			n = 1024;
1246154abd99SDavid du Colombier 		b = smalloc(n);
1247154abd99SDavid du Colombier 		memmove(b, s+l, n-l);
1248154abd99SDavid du Colombier 		b[n-l] = 0;
1249154abd99SDavid du Colombier 		if(b[n-l-1] == '\n')
1250154abd99SDavid du Colombier 			b[n-l-1] = 0;
1251154abd99SDavid du Colombier 		qlock(ep);
1252154abd99SDavid du Colombier 		free(ep->info);
1253154abd99SDavid du Colombier 		ep->info = b;
1254154abd99SDavid du Colombier 		qunlock(ep);
1255154abd99SDavid du Colombier 		break;
1256154abd99SDavid du Colombier 	case CMaddress:
1257154abd99SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1258154abd99SDavid du Colombier 		ep->dev->state = Denabled;
1259154abd99SDavid du Colombier 		break;
1260154abd99SDavid du Colombier 	case CMdetach:
1261154abd99SDavid du Colombier 		if(ep->dev->isroot != 0)
1262154abd99SDavid du Colombier 			error("can't detach a root hub");
1263154abd99SDavid du Colombier 		deprint("usb epctl %s ep%d.%d\n",
1264154abd99SDavid du Colombier 			cb->f[0], ep->dev->nb, ep->nb);
1265154abd99SDavid du Colombier 		ep->dev->state = Ddetach;
1266154abd99SDavid du Colombier 		/* Release file system ref. for its endpoints */
1267154abd99SDavid du Colombier 		for(i = 0; i < nelem(ep->dev->eps); i++)
1268154abd99SDavid du Colombier 			putep(ep->dev->eps[i]);
1269154abd99SDavid du Colombier 		break;
1270154abd99SDavid du Colombier 	case CMdebugep:
1271154abd99SDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0)
1272154abd99SDavid du Colombier 			ep->debug = 1;
1273154abd99SDavid du Colombier 		else if(strcmp(cb->f[1], "off") == 0)
1274154abd99SDavid du Colombier 			ep->debug = 0;
1275154abd99SDavid du Colombier 		else
1276154abd99SDavid du Colombier 			ep->debug = strtoul(cb->f[1], nil, 0);
1277154abd99SDavid du Colombier 		print("usb: ep%d.%d debug %d\n",
1278154abd99SDavid du Colombier 			ep->dev->nb, ep->nb, ep->debug);
1279154abd99SDavid du Colombier 		break;
1280154abd99SDavid du Colombier 	case CMname:
1281154abd99SDavid du Colombier 		deprint("usb epctl %s %s\n", cb->f[0], cb->f[1]);
1282154abd99SDavid du Colombier 		validname(cb->f[1], 0);
1283154abd99SDavid du Colombier 		kstrdup(&ep->name, cb->f[1]);
1284154abd99SDavid du Colombier 		break;
1285cd52453fSDavid du Colombier 	case CMtmout:
1286cd52453fSDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1287cd52453fSDavid du Colombier 		if(ep->ttype == Tiso || ep->ttype == Tctl)
1288cd52453fSDavid du Colombier 			error("ctl ignored for this endpoint type");
1289cd52453fSDavid du Colombier 		ep->tmout = strtoul(cb->f[1], nil, 0);
1290cd52453fSDavid du Colombier 		if(ep->tmout != 0 && ep->tmout < Xfertmout)
1291cd52453fSDavid du Colombier 			ep->tmout = Xfertmout;
1292cd52453fSDavid du Colombier 		break;
1293cd52453fSDavid du Colombier 	case CMpreset:
1294cd52453fSDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1295cd52453fSDavid du Colombier 		if(ep->ttype != Tctl)
1296cd52453fSDavid du Colombier 			error("not a control endpoint");
1297cd52453fSDavid du Colombier 		if(ep->dev->state != Denabled)
1298cd52453fSDavid du Colombier 			error("forbidden on devices not enabled");
1299cd52453fSDavid du Colombier 		ep->dev->state = Dreset;
1300cd52453fSDavid du Colombier 		break;
1301154abd99SDavid du Colombier 	default:
1302154abd99SDavid du Colombier 		panic("usb: unknown epctl %d", ct->index);
1303154abd99SDavid du Colombier 	}
1304154abd99SDavid du Colombier 	free(cb);
1305154abd99SDavid du Colombier 	poperror();
1306154abd99SDavid du Colombier 	return n;
1307154abd99SDavid du Colombier }
1308154abd99SDavid du Colombier 
1309154abd99SDavid du Colombier static long
1310154abd99SDavid du Colombier usbctl(void *a, long n)
1311154abd99SDavid du Colombier {
1312154abd99SDavid du Colombier 	Cmdtab *ct;
1313154abd99SDavid du Colombier 	Cmdbuf *cb;
1314154abd99SDavid du Colombier 	Ep *ep;
1315154abd99SDavid du Colombier 	int i;
1316154abd99SDavid du Colombier 
1317154abd99SDavid du Colombier 	cb = parsecmd(a, n);
1318154abd99SDavid du Colombier 	if(waserror()){
1319154abd99SDavid du Colombier 		free(cb);
1320154abd99SDavid du Colombier 		nexterror();
1321154abd99SDavid du Colombier 	}
1322154abd99SDavid du Colombier 	ct = lookupcmd(cb, usbctls, nelem(usbctls));
1323154abd99SDavid du Colombier 	dprint("usb ctl %s\n", cb->f[0]);
1324154abd99SDavid du Colombier 	switch(ct->index){
1325154abd99SDavid du Colombier 	case CMdebug:
1326154abd99SDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0)
1327154abd99SDavid du Colombier 			debug = 1;
1328154abd99SDavid du Colombier 		else if(strcmp(cb->f[1], "off") == 0)
1329154abd99SDavid du Colombier 			debug = 0;
1330154abd99SDavid du Colombier 		else
1331154abd99SDavid du Colombier 			debug = strtol(cb->f[1], nil, 0);
1332154abd99SDavid du Colombier 		print("usb: debug %d\n", debug);
1333154abd99SDavid du Colombier 		for(i = 0; i < epmax; i++)
1334154abd99SDavid du Colombier 			if((ep = getep(i)) != nil){
1335154abd99SDavid du Colombier 				ep->hp->debug(ep->hp, debug);
1336154abd99SDavid du Colombier 				putep(ep);
1337154abd99SDavid du Colombier 			}
1338154abd99SDavid du Colombier 		break;
1339154abd99SDavid du Colombier 	case CMdump:
1340154abd99SDavid du Colombier 		dumpeps();
1341154abd99SDavid du Colombier 		break;
1342154abd99SDavid du Colombier 	}
1343154abd99SDavid du Colombier 	free(cb);
1344154abd99SDavid du Colombier 	poperror();
1345154abd99SDavid du Colombier 	return n;
1346154abd99SDavid du Colombier }
1347154abd99SDavid du Colombier 
1348154abd99SDavid du Colombier static long
1349154abd99SDavid du Colombier ctlwrite(Chan *c, void *a, long n)
1350154abd99SDavid du Colombier {
1351154abd99SDavid du Colombier 	int q;
1352154abd99SDavid du Colombier 	Ep *ep;
1353154abd99SDavid du Colombier 
1354154abd99SDavid du Colombier 	q = QID(c->qid);
1355154abd99SDavid du Colombier 	if(q == Qctl)
1356154abd99SDavid du Colombier 		return usbctl(a, n);
1357154abd99SDavid du Colombier 
1358154abd99SDavid du Colombier 	ep = getep(qid2epidx(q));
1359154abd99SDavid du Colombier 	if(ep == nil)
1360154abd99SDavid du Colombier 		error(Eio);
1361154abd99SDavid du Colombier 	if(waserror()){
1362154abd99SDavid du Colombier 		putep(ep);
1363154abd99SDavid du Colombier 		nexterror();
1364154abd99SDavid du Colombier 	}
1365154abd99SDavid du Colombier 	if(ep->dev->state == Ddetach)
1366154abd99SDavid du Colombier 		error(Edetach);
1367154abd99SDavid du Colombier 	if(isqtype(q, Qepctl) && c->aux != nil){
1368154abd99SDavid du Colombier 		/* Be sure we don't keep a cloned ep name */
1369154abd99SDavid du Colombier 		free(c->aux);
1370154abd99SDavid du Colombier 		c->aux = nil;
1371154abd99SDavid du Colombier 		error("read, not write, expected");
1372154abd99SDavid du Colombier 	}
1373154abd99SDavid du Colombier 	n = epctl(ep, c, a, n);
1374154abd99SDavid du Colombier 	putep(ep);
1375154abd99SDavid du Colombier 	poperror();
1376154abd99SDavid du Colombier 	return n;
1377154abd99SDavid du Colombier }
1378154abd99SDavid du Colombier 
1379154abd99SDavid du Colombier static long
1380154abd99SDavid du Colombier usbwrite(Chan *c, void *a, long n, vlong off)
1381154abd99SDavid du Colombier {
1382c8a340cdSDavid du Colombier 	int nr, q;
1383154abd99SDavid du Colombier 	Ep *ep;
1384154abd99SDavid du Colombier 
1385154abd99SDavid du Colombier 	if(c->qid.type == QTDIR)
1386154abd99SDavid du Colombier 		error(Eisdir);
1387154abd99SDavid du Colombier 
1388154abd99SDavid du Colombier 	q = QID(c->qid);
1389154abd99SDavid du Colombier 
1390154abd99SDavid du Colombier 	if(q == Qctl || isqtype(q, Qepctl))
1391154abd99SDavid du Colombier 		return ctlwrite(c, a, n);
1392154abd99SDavid du Colombier 
1393154abd99SDavid du Colombier 	ep = getep(qid2epidx(q));
1394154abd99SDavid du Colombier 	if(ep == nil)
1395154abd99SDavid du Colombier 		error(Eio);
1396154abd99SDavid du Colombier 	if(waserror()){
1397154abd99SDavid du Colombier 		putep(ep);
1398154abd99SDavid du Colombier 		nexterror();
1399154abd99SDavid du Colombier 	}
1400154abd99SDavid du Colombier 	if(ep->dev->state == Ddetach)
1401154abd99SDavid du Colombier 		error(Edetach);
1402154abd99SDavid du Colombier 	if(ep->mode == OREAD || ep->inuse == 0)
1403154abd99SDavid du Colombier 		error(Ebadusefd);
1404154abd99SDavid du Colombier 
1405154abd99SDavid du Colombier 	switch(ep->ttype){
1406154abd99SDavid du Colombier 	case Tnone:
1407154abd99SDavid du Colombier 		error("endpoint not configured");
1408154abd99SDavid du Colombier 	case Tctl:
1409154abd99SDavid du Colombier 		nr = rhubwrite(ep, a, n);
1410154abd99SDavid du Colombier 		if(nr >= 0){
1411154abd99SDavid du Colombier 			n = nr;
1412154abd99SDavid du Colombier 			break;
1413154abd99SDavid du Colombier 		}
1414154abd99SDavid du Colombier 		/* else fall */
1415154abd99SDavid du Colombier 	default:
1416154abd99SDavid du Colombier 		ddeprint("\nusbwrite q %#x fid %d cnt %ld off %lld\n",q, c->fid, n, off);
1417154abd99SDavid du Colombier 		ep->hp->epwrite(ep, a, n);
1418154abd99SDavid du Colombier 	}
1419154abd99SDavid du Colombier 	putep(ep);
1420154abd99SDavid du Colombier 	poperror();
1421154abd99SDavid du Colombier 	return n;
1422154abd99SDavid du Colombier }
1423154abd99SDavid du Colombier 
1424154abd99SDavid du Colombier void
1425154abd99SDavid du Colombier usbshutdown(void)
1426154abd99SDavid du Colombier {
1427cd52453fSDavid du Colombier 	Hci *hp;
1428154abd99SDavid du Colombier 	int i;
1429154abd99SDavid du Colombier 
1430154abd99SDavid du Colombier 	for(i = 0; i < Nhcis; i++){
1431cd52453fSDavid du Colombier 		hp = hcis[i];
1432cd52453fSDavid du Colombier 		if(hp == nil)
1433154abd99SDavid du Colombier 			continue;
1434cd52453fSDavid du Colombier 		if(hp->shutdown == nil)
1435cd52453fSDavid du Colombier 			print("#u: no shutdown function for %s\n", hp->type);
1436cd52453fSDavid du Colombier 		else
1437cd52453fSDavid du Colombier 			hp->shutdown(hp);
1438154abd99SDavid du Colombier 	}
1439154abd99SDavid du Colombier }
1440154abd99SDavid du Colombier 
1441154abd99SDavid du Colombier Dev usbdevtab = {
1442154abd99SDavid du Colombier 	L'u',
1443154abd99SDavid du Colombier 	"usb",
1444154abd99SDavid du Colombier 
1445154abd99SDavid du Colombier 	usbreset,
1446154abd99SDavid du Colombier 	usbinit,
1447154abd99SDavid du Colombier 	usbshutdown,
1448154abd99SDavid du Colombier 	usbattach,
1449154abd99SDavid du Colombier 	usbwalk,
1450154abd99SDavid du Colombier 	usbstat,
1451154abd99SDavid du Colombier 	usbopen,
1452154abd99SDavid du Colombier 	devcreate,
1453154abd99SDavid du Colombier 	usbclose,
1454154abd99SDavid du Colombier 	usbread,
1455154abd99SDavid du Colombier 	devbread,
1456154abd99SDavid du Colombier 	usbwrite,
1457154abd99SDavid du Colombier 	devbwrite,
1458154abd99SDavid du Colombier 	devremove,
1459154abd99SDavid du Colombier 	devwstat,
1460154abd99SDavid du Colombier };
1461