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