xref: /plan9-contrib/sys/src/9/loongson/devusb.c (revision a81c3ea0c7f009a3088ab7fe55ea9013d9d77a74)
1*a81c3ea0SDavid du Colombier /*
2*a81c3ea0SDavid du Colombier  * USB device driver framework.
3*a81c3ea0SDavid du Colombier  *
4*a81c3ea0SDavid du Colombier  * This is in charge of providing access to actual HCIs
5*a81c3ea0SDavid du Colombier  * and providing I/O to the various endpoints of devices.
6*a81c3ea0SDavid du Colombier  * A separate user program (usbd) is in charge of
7*a81c3ea0SDavid du Colombier  * enumerating the bus, setting up endpoints and
8*a81c3ea0SDavid du Colombier  * starting devices (also user programs).
9*a81c3ea0SDavid du Colombier  *
10*a81c3ea0SDavid du Colombier  * The interface provided is a violation of the standard:
11*a81c3ea0SDavid du Colombier  * you're welcome.
12*a81c3ea0SDavid du Colombier  *
13*a81c3ea0SDavid du Colombier  * The interface consists of a root directory with several files
14*a81c3ea0SDavid du Colombier  * plus a directory (epN.M) with two files per endpoint.
15*a81c3ea0SDavid du Colombier  * A device is represented by its first endpoint, which
16*a81c3ea0SDavid du Colombier  * is a control endpoint automatically allocated for each device.
17*a81c3ea0SDavid du Colombier  * Device control endpoints may be used to create new endpoints.
18*a81c3ea0SDavid du Colombier  * Devices corresponding to hubs may also allocate new devices,
19*a81c3ea0SDavid du Colombier  * perhaps also hubs. Initially, a hub device is allocated for
20*a81c3ea0SDavid du Colombier  * each controller present, to represent its root hub. Those can
21*a81c3ea0SDavid du Colombier  * never be removed.
22*a81c3ea0SDavid du Colombier  *
23*a81c3ea0SDavid du Colombier  * All endpoints refer to the first endpoint (epN.0) of the device,
24*a81c3ea0SDavid du Colombier  * which keeps per-device information, and also to the HCI used
25*a81c3ea0SDavid du Colombier  * to reach them. Although all endpoints cache that information.
26*a81c3ea0SDavid du Colombier  *
27*a81c3ea0SDavid du Colombier  * epN.M/data files permit I/O and are considered DMEXCL.
28*a81c3ea0SDavid du Colombier  * epN.M/ctl files provide status info and accept control requests.
29*a81c3ea0SDavid du Colombier  *
30*a81c3ea0SDavid du Colombier  * Endpoints may be given file names to be listed also at #u,
31*a81c3ea0SDavid du Colombier  * for those drivers that have nothing to do after configuring the
32*a81c3ea0SDavid du Colombier  * device and its endpoints.
33*a81c3ea0SDavid du Colombier  *
34*a81c3ea0SDavid du Colombier  * Drivers for different controllers are kept at usb[oue]hci.c
35*a81c3ea0SDavid du Colombier  * It's likely we could factor out much from controllers into
36*a81c3ea0SDavid du Colombier  * a generic controller driver, the problem is that details
37*a81c3ea0SDavid du Colombier  * regarding how to handle toggles, tokens, Tds, etc. will
38*a81c3ea0SDavid du Colombier  * get in the way. Thus, code is probably easier the way it is.
39*a81c3ea0SDavid du Colombier  */
40*a81c3ea0SDavid du Colombier 
41*a81c3ea0SDavid du Colombier #include	"u.h"
42*a81c3ea0SDavid du Colombier #include	"../port/lib.h"
43*a81c3ea0SDavid du Colombier #include	"mem.h"
44*a81c3ea0SDavid du Colombier #include	"dat.h"
45*a81c3ea0SDavid du Colombier #include	"fns.h"
46*a81c3ea0SDavid du Colombier #include	"io.h"
47*a81c3ea0SDavid du Colombier #include	"../port/error.h"
48*a81c3ea0SDavid du Colombier #include	"../port/usb.h"
49*a81c3ea0SDavid du Colombier 
50*a81c3ea0SDavid du Colombier typedef struct Hcitype Hcitype;
51*a81c3ea0SDavid du Colombier 
52*a81c3ea0SDavid du Colombier enum
53*a81c3ea0SDavid du Colombier {
54*a81c3ea0SDavid du Colombier 	/* Qid numbers */
55*a81c3ea0SDavid du Colombier 	Qdir = 0,		/* #u */
56*a81c3ea0SDavid du Colombier 	Qusbdir,			/* #u/usb */
57*a81c3ea0SDavid du Colombier 	Qctl,			/* #u/usb/ctl - control requests */
58*a81c3ea0SDavid du Colombier 
59*a81c3ea0SDavid du Colombier 	Qep0dir,			/* #u/usb/ep0.0 - endpoint 0 dir */
60*a81c3ea0SDavid du Colombier 	Qep0io,			/* #u/usb/ep0.0/data - endpoint 0 I/O */
61*a81c3ea0SDavid du Colombier 	Qep0ctl,		/* #u/usb/ep0.0/ctl - endpoint 0 ctl. */
62*a81c3ea0SDavid du Colombier 	Qep0dummy,		/* give 4 qids to each endpoint */
63*a81c3ea0SDavid du Colombier 
64*a81c3ea0SDavid du Colombier 	Qepdir = 0,		/* (qid-qep0dir)&3 is one of these */
65*a81c3ea0SDavid du Colombier 	Qepio,			/* to identify which file for the endpoint */
66*a81c3ea0SDavid du Colombier 	Qepctl,
67*a81c3ea0SDavid du Colombier 
68*a81c3ea0SDavid du Colombier 	/* ... */
69*a81c3ea0SDavid du Colombier 
70*a81c3ea0SDavid du Colombier 	/* Usb ctls. */
71*a81c3ea0SDavid du Colombier 	CMdebug = 0,		/* debug on|off */
72*a81c3ea0SDavid du Colombier 	CMdump,			/* dump (data structures for debug) */
73*a81c3ea0SDavid du Colombier 
74*a81c3ea0SDavid du Colombier 	/* Ep. ctls */
75*a81c3ea0SDavid du Colombier 	CMnew = 0,		/* new nb ctl|bulk|intr|iso r|w|rw (endpoint) */
76*a81c3ea0SDavid du Colombier 	CMnewdev,		/* newdev full|low|high portnb (allocate new devices) */
77*a81c3ea0SDavid du Colombier 	CMhub,			/* hub (set the device as a hub) */
78*a81c3ea0SDavid du Colombier 	CMspeed,		/* speed full|low|high|no */
79*a81c3ea0SDavid du Colombier 	CMmaxpkt,		/* maxpkt size */
80*a81c3ea0SDavid du Colombier 	CMntds,			/* ntds nb (max nb. of tds per µframe) */
81*a81c3ea0SDavid du Colombier 	CMclrhalt,		/* clrhalt (halt was cleared on endpoint) */
82*a81c3ea0SDavid du Colombier 	CMpollival,		/* pollival interval (interrupt/iso) */
83*a81c3ea0SDavid du Colombier 	CMhz,			/* hz n (samples/sec; iso) */
84*a81c3ea0SDavid du Colombier 	CMsamplesz,		/* samplesz n (sample size; iso) */
85*a81c3ea0SDavid du Colombier 	CMinfo,			/* info infostr (ke.ep info for humans) */
86*a81c3ea0SDavid du Colombier 	CMdetach,		/* detach (abort I/O forever on this ep). */
87*a81c3ea0SDavid du Colombier 	CMaddress,		/* address (address is assigned) */
88*a81c3ea0SDavid du Colombier 	CMdebugep,		/* debug n (set/clear debug for this ep) */
89*a81c3ea0SDavid du Colombier 	CMname,			/* name str (show up as #u/name as well) */
90*a81c3ea0SDavid du Colombier 	CMtmout,		/* timeout n (activate timeouts for ep) */
91*a81c3ea0SDavid du Colombier 	CMpreset,		/* reset the port */
92*a81c3ea0SDavid du Colombier 
93*a81c3ea0SDavid du Colombier 	/* Hub feature selectors */
94*a81c3ea0SDavid du Colombier 	Rportenable	= 1,
95*a81c3ea0SDavid du Colombier 	Rportreset	= 4,
96*a81c3ea0SDavid du Colombier 
97*a81c3ea0SDavid du Colombier };
98*a81c3ea0SDavid du Colombier 
99*a81c3ea0SDavid du Colombier struct Hcitype
100*a81c3ea0SDavid du Colombier {
101*a81c3ea0SDavid du Colombier 	char*	type;
102*a81c3ea0SDavid du Colombier 	int	(*reset)(Hci*);
103*a81c3ea0SDavid du Colombier };
104*a81c3ea0SDavid du Colombier 
105*a81c3ea0SDavid du Colombier #define QID(q)	((int)(q).path)
106*a81c3ea0SDavid du Colombier 
107*a81c3ea0SDavid du Colombier static Cmdtab usbctls[] =
108*a81c3ea0SDavid du Colombier {
109*a81c3ea0SDavid du Colombier 	{CMdebug,	"debug",	2},
110*a81c3ea0SDavid du Colombier 	{CMdump,	"dump",		1},
111*a81c3ea0SDavid du Colombier };
112*a81c3ea0SDavid du Colombier 
113*a81c3ea0SDavid du Colombier static Cmdtab epctls[] =
114*a81c3ea0SDavid du Colombier {
115*a81c3ea0SDavid du Colombier 	{CMnew,		"new",		4},
116*a81c3ea0SDavid du Colombier 	{CMnewdev,	"newdev",	3},
117*a81c3ea0SDavid du Colombier 	{CMhub,		"hub",		1},
118*a81c3ea0SDavid du Colombier 	{CMspeed,	"speed",	2},
119*a81c3ea0SDavid du Colombier 	{CMmaxpkt,	"maxpkt",	2},
120*a81c3ea0SDavid du Colombier 	{CMntds,	"ntds",		2},
121*a81c3ea0SDavid du Colombier 	{CMpollival,	"pollival",	2},
122*a81c3ea0SDavid du Colombier 	{CMsamplesz,	"samplesz",	2},
123*a81c3ea0SDavid du Colombier 	{CMhz,		"hz",		2},
124*a81c3ea0SDavid du Colombier 	{CMinfo,	"info",		0},
125*a81c3ea0SDavid du Colombier 	{CMdetach,	"detach",	1},
126*a81c3ea0SDavid du Colombier 	{CMaddress,	"address",	1},
127*a81c3ea0SDavid du Colombier 	{CMdebugep,	"debug",	2},
128*a81c3ea0SDavid du Colombier 	{CMclrhalt,	"clrhalt",	1},
129*a81c3ea0SDavid du Colombier 	{CMname,	"name",		2},
130*a81c3ea0SDavid du Colombier 	{CMtmout,	"timeout",	2},
131*a81c3ea0SDavid du Colombier 	{CMpreset,	"reset",	1},
132*a81c3ea0SDavid du Colombier };
133*a81c3ea0SDavid du Colombier 
134*a81c3ea0SDavid du Colombier static Dirtab usbdir[] =
135*a81c3ea0SDavid du Colombier {
136*a81c3ea0SDavid du Colombier 	"ctl",		{Qctl},		0,	0666,
137*a81c3ea0SDavid du Colombier };
138*a81c3ea0SDavid du Colombier 
139*a81c3ea0SDavid du Colombier char *usbmodename[] =
140*a81c3ea0SDavid du Colombier {
141*a81c3ea0SDavid du Colombier 	[OREAD]	"r",
142*a81c3ea0SDavid du Colombier 	[OWRITE]	"w",
143*a81c3ea0SDavid du Colombier 	[ORDWR]	"rw",
144*a81c3ea0SDavid du Colombier };
145*a81c3ea0SDavid du Colombier 
146*a81c3ea0SDavid du Colombier static char *ttname[] =
147*a81c3ea0SDavid du Colombier {
148*a81c3ea0SDavid du Colombier 	[Tnone]	"none",
149*a81c3ea0SDavid du Colombier 	[Tctl]	"control",
150*a81c3ea0SDavid du Colombier 	[Tiso]	"iso",
151*a81c3ea0SDavid du Colombier 	[Tintr]	"interrupt",
152*a81c3ea0SDavid du Colombier 	[Tbulk]	"bulk",
153*a81c3ea0SDavid du Colombier };
154*a81c3ea0SDavid du Colombier 
155*a81c3ea0SDavid du Colombier static char *spname[] =
156*a81c3ea0SDavid du Colombier {
157*a81c3ea0SDavid du Colombier 	[Fullspeed]	"full",
158*a81c3ea0SDavid du Colombier 	[Lowspeed]	"low",
159*a81c3ea0SDavid du Colombier 	[Highspeed]	"high",
160*a81c3ea0SDavid du Colombier 	[Nospeed]	"no",
161*a81c3ea0SDavid du Colombier };
162*a81c3ea0SDavid du Colombier 
163*a81c3ea0SDavid du Colombier static int	debug;
164*a81c3ea0SDavid du Colombier static Hcitype	hcitypes[Nhcis];
165*a81c3ea0SDavid du Colombier static Hci*	hcis[Nhcis];
166*a81c3ea0SDavid du Colombier static QLock	epslck;		/* add, del, lookup endpoints */
167*a81c3ea0SDavid du Colombier static Ep*	eps[Neps];	/* all endpoints known */
168*a81c3ea0SDavid du Colombier static int	epmax;		/* 1 + last endpoint index used  */
169*a81c3ea0SDavid du Colombier static int	usbidgen;	/* device address generator */
170*a81c3ea0SDavid du Colombier 
171*a81c3ea0SDavid du Colombier /*
172*a81c3ea0SDavid du Colombier  * Is there something like this in a library? should it be?
173*a81c3ea0SDavid du Colombier  */
174*a81c3ea0SDavid du Colombier char*
seprintdata(char * s,char * se,uchar * d,int n)175*a81c3ea0SDavid du Colombier seprintdata(char *s, char *se, uchar *d, int n)
176*a81c3ea0SDavid du Colombier {
177*a81c3ea0SDavid du Colombier 	int i, l;
178*a81c3ea0SDavid du Colombier 
179*a81c3ea0SDavid du Colombier 	s = seprint(s, se, " %#p[%d]: ", d, n);
180*a81c3ea0SDavid du Colombier 	l = n;
181*a81c3ea0SDavid du Colombier 	if(l > 10)
182*a81c3ea0SDavid du Colombier 		l = 10;
183*a81c3ea0SDavid du Colombier 	for(i=0; i<l; i++)
184*a81c3ea0SDavid du Colombier 		s = seprint(s, se, " %2.2ux", d[i]);
185*a81c3ea0SDavid du Colombier 	if(l < n)
186*a81c3ea0SDavid du Colombier 		s = seprint(s, se, "...");
187*a81c3ea0SDavid du Colombier 	return s;
188*a81c3ea0SDavid du Colombier }
189*a81c3ea0SDavid du Colombier 
190*a81c3ea0SDavid du Colombier static int
name2speed(char * name)191*a81c3ea0SDavid du Colombier name2speed(char *name)
192*a81c3ea0SDavid du Colombier {
193*a81c3ea0SDavid du Colombier 	int i;
194*a81c3ea0SDavid du Colombier 
195*a81c3ea0SDavid du Colombier 	for(i = 0; i < nelem(spname); i++)
196*a81c3ea0SDavid du Colombier 		if(strcmp(name, spname[i]) == 0)
197*a81c3ea0SDavid du Colombier 			return i;
198*a81c3ea0SDavid du Colombier 	return Nospeed;
199*a81c3ea0SDavid du Colombier }
200*a81c3ea0SDavid du Colombier 
201*a81c3ea0SDavid du Colombier static int
name2ttype(char * name)202*a81c3ea0SDavid du Colombier name2ttype(char *name)
203*a81c3ea0SDavid du Colombier {
204*a81c3ea0SDavid du Colombier 	int i;
205*a81c3ea0SDavid du Colombier 
206*a81c3ea0SDavid du Colombier 	for(i = 0; i < nelem(ttname); i++)
207*a81c3ea0SDavid du Colombier 		if(strcmp(name, ttname[i]) == 0)
208*a81c3ea0SDavid du Colombier 			return i;
209*a81c3ea0SDavid du Colombier 	/* may be a std. USB ep. type */
210*a81c3ea0SDavid du Colombier 	i = strtol(name, nil, 0);
211*a81c3ea0SDavid du Colombier 	switch(i+1){
212*a81c3ea0SDavid du Colombier 	case Tctl:
213*a81c3ea0SDavid du Colombier 	case Tiso:
214*a81c3ea0SDavid du Colombier 	case Tbulk:
215*a81c3ea0SDavid du Colombier 	case Tintr:
216*a81c3ea0SDavid du Colombier 		return i+1;
217*a81c3ea0SDavid du Colombier 	default:
218*a81c3ea0SDavid du Colombier 		return Tnone;
219*a81c3ea0SDavid du Colombier 	}
220*a81c3ea0SDavid du Colombier }
221*a81c3ea0SDavid du Colombier 
222*a81c3ea0SDavid du Colombier static int
name2mode(char * mode)223*a81c3ea0SDavid du Colombier name2mode(char *mode)
224*a81c3ea0SDavid du Colombier {
225*a81c3ea0SDavid du Colombier 	int i;
226*a81c3ea0SDavid du Colombier 
227*a81c3ea0SDavid du Colombier 	for(i = 0; i < nelem(usbmodename); i++)
228*a81c3ea0SDavid du Colombier 		if(strcmp(mode, usbmodename[i]) == 0)
229*a81c3ea0SDavid du Colombier 			return i;
230*a81c3ea0SDavid du Colombier 	return -1;
231*a81c3ea0SDavid du Colombier }
232*a81c3ea0SDavid du Colombier 
233*a81c3ea0SDavid du Colombier static int
qid2epidx(int q)234*a81c3ea0SDavid du Colombier qid2epidx(int q)
235*a81c3ea0SDavid du Colombier {
236*a81c3ea0SDavid du Colombier 	q = (q-Qep0dir)/4;
237*a81c3ea0SDavid du Colombier 	if(q < 0 || q >= epmax || eps[q] == nil)
238*a81c3ea0SDavid du Colombier 		return -1;
239*a81c3ea0SDavid du Colombier 	return q;
240*a81c3ea0SDavid du Colombier }
241*a81c3ea0SDavid du Colombier 
242*a81c3ea0SDavid du Colombier static int
isqtype(int q,int type)243*a81c3ea0SDavid du Colombier isqtype(int q, int type)
244*a81c3ea0SDavid du Colombier {
245*a81c3ea0SDavid du Colombier 	if(q < Qep0dir)
246*a81c3ea0SDavid du Colombier 		return 0;
247*a81c3ea0SDavid du Colombier 	q -= Qep0dir;
248*a81c3ea0SDavid du Colombier 	return (q & 3) == type;
249*a81c3ea0SDavid du Colombier }
250*a81c3ea0SDavid du Colombier 
251*a81c3ea0SDavid du Colombier void
addhcitype(char * t,int (* r)(Hci *))252*a81c3ea0SDavid du Colombier addhcitype(char* t, int (*r)(Hci*))
253*a81c3ea0SDavid du Colombier {
254*a81c3ea0SDavid du Colombier 	static int ntype;
255*a81c3ea0SDavid du Colombier 
256*a81c3ea0SDavid du Colombier 	if(ntype == Nhcis)
257*a81c3ea0SDavid du Colombier 		panic("too many USB host interface types");
258*a81c3ea0SDavid du Colombier 	hcitypes[ntype].type = t;
259*a81c3ea0SDavid du Colombier 	hcitypes[ntype].reset = r;
260*a81c3ea0SDavid du Colombier 	ntype++;
261*a81c3ea0SDavid du Colombier }
262*a81c3ea0SDavid du Colombier 
263*a81c3ea0SDavid du Colombier static char*
seprintep(char * s,char * se,Ep * ep,int all)264*a81c3ea0SDavid du Colombier seprintep(char *s, char *se, Ep *ep, int all)
265*a81c3ea0SDavid du Colombier {
266*a81c3ea0SDavid du Colombier 	static char* dsnames[] = { "config", "enabled", "detached", "reset" };
267*a81c3ea0SDavid du Colombier 	Udev *d;
268*a81c3ea0SDavid du Colombier 	int i;
269*a81c3ea0SDavid du Colombier 	int di;
270*a81c3ea0SDavid du Colombier 
271*a81c3ea0SDavid du Colombier 	d = ep->dev;
272*a81c3ea0SDavid du Colombier 
273*a81c3ea0SDavid du Colombier 	qlock(ep);
274*a81c3ea0SDavid du Colombier 	if(waserror()){
275*a81c3ea0SDavid du Colombier 		qunlock(ep);
276*a81c3ea0SDavid du Colombier 		nexterror();
277*a81c3ea0SDavid du Colombier 	}
278*a81c3ea0SDavid du Colombier 	di = ep->dev->nb;
279*a81c3ea0SDavid du Colombier 	if(all)
280*a81c3ea0SDavid du Colombier 		s = seprint(s, se, "dev %d ep %d ", di, ep->nb);
281*a81c3ea0SDavid du Colombier 	s = seprint(s, se, "%s", dsnames[ep->dev->state]);
282*a81c3ea0SDavid du Colombier 	s = seprint(s, se, " %s", ttname[ep->ttype]);
283*a81c3ea0SDavid du Colombier 	assert(ep->mode == OREAD || ep->mode == OWRITE || ep->mode == ORDWR);
284*a81c3ea0SDavid du Colombier 	s = seprint(s, se, " %s", usbmodename[ep->mode]);
285*a81c3ea0SDavid du Colombier 	s = seprint(s, se, " speed %s", spname[d->speed]);
286*a81c3ea0SDavid du Colombier 	s = seprint(s, se, " maxpkt %ld", ep->maxpkt);
287*a81c3ea0SDavid du Colombier 	s = seprint(s, se, " pollival %ld", ep->pollival);
288*a81c3ea0SDavid du Colombier 	s = seprint(s, se, " samplesz %ld", ep->samplesz);
289*a81c3ea0SDavid du Colombier 	s = seprint(s, se, " hz %ld", ep->hz);
290*a81c3ea0SDavid du Colombier 	s = seprint(s, se, " hub %d", ep->dev->hub);
291*a81c3ea0SDavid du Colombier 	s = seprint(s, se, " port %d", ep->dev->port);
292*a81c3ea0SDavid du Colombier 	if(ep->inuse)
293*a81c3ea0SDavid du Colombier 		s = seprint(s, se, " busy");
294*a81c3ea0SDavid du Colombier 	else
295*a81c3ea0SDavid du Colombier 		s = seprint(s, se, " idle");
296*a81c3ea0SDavid du Colombier 	if(all){
297*a81c3ea0SDavid du Colombier 		s = seprint(s, se, " load %uld", ep->load);
298*a81c3ea0SDavid du Colombier 		s = seprint(s, se, " ref %ld addr %#p", ep->ref, ep);
299*a81c3ea0SDavid du Colombier 		s = seprint(s, se, " idx %d", ep->idx);
300*a81c3ea0SDavid du Colombier 		if(ep->name != nil)
301*a81c3ea0SDavid du Colombier 			s = seprint(s, se, " name '%s'", ep->name);
302*a81c3ea0SDavid du Colombier 		if(ep->tmout != 0)
303*a81c3ea0SDavid du Colombier 			s = seprint(s, se, " tmout");
304*a81c3ea0SDavid du Colombier 		if(ep == ep->ep0){
305*a81c3ea0SDavid du Colombier 			s = seprint(s, se, " ctlrno %#x", ep->hp->ctlrno);
306*a81c3ea0SDavid du Colombier 			s = seprint(s, se, " eps:");
307*a81c3ea0SDavid du Colombier 			for(i = 0; i < nelem(d->eps); i++)
308*a81c3ea0SDavid du Colombier 				if(d->eps[i] != nil)
309*a81c3ea0SDavid du Colombier 					s = seprint(s, se, " ep%d.%d", di, i);
310*a81c3ea0SDavid du Colombier 		}
311*a81c3ea0SDavid du Colombier 	}
312*a81c3ea0SDavid du Colombier 	if(ep->info != nil)
313*a81c3ea0SDavid du Colombier 		s = seprint(s, se, "\n%s %s\n", ep->info, ep->hp->type);
314*a81c3ea0SDavid du Colombier 	else
315*a81c3ea0SDavid du Colombier 		s = seprint(s, se, "\n");
316*a81c3ea0SDavid du Colombier 	qunlock(ep);
317*a81c3ea0SDavid du Colombier 	poperror();
318*a81c3ea0SDavid du Colombier 	return s;
319*a81c3ea0SDavid du Colombier }
320*a81c3ea0SDavid du Colombier 
321*a81c3ea0SDavid du Colombier static Ep*
epalloc(Hci * hp)322*a81c3ea0SDavid du Colombier epalloc(Hci *hp)
323*a81c3ea0SDavid du Colombier {
324*a81c3ea0SDavid du Colombier 	Ep *ep;
325*a81c3ea0SDavid du Colombier 	int i;
326*a81c3ea0SDavid du Colombier 
327*a81c3ea0SDavid du Colombier 	ep = smalloc(sizeof(Ep));
328*a81c3ea0SDavid du Colombier 	ep->ref = 1;
329*a81c3ea0SDavid du Colombier 	qlock(&epslck);
330*a81c3ea0SDavid du Colombier 	for(i = 0; i < Neps; i++)
331*a81c3ea0SDavid du Colombier 		if(eps[i] == nil)
332*a81c3ea0SDavid du Colombier 			break;
333*a81c3ea0SDavid du Colombier 	if(i == Neps){
334*a81c3ea0SDavid du Colombier 		qunlock(&epslck);
335*a81c3ea0SDavid du Colombier 		free(ep);
336*a81c3ea0SDavid du Colombier 		panic("usb: epalloc: too few endpoints (%d)", Neps);
337*a81c3ea0SDavid du Colombier 	}
338*a81c3ea0SDavid du Colombier 	ep->idx = i;
339*a81c3ea0SDavid du Colombier 	if(epmax <= i)
340*a81c3ea0SDavid du Colombier 		epmax = i+1;
341*a81c3ea0SDavid du Colombier 	eps[i] = ep;
342*a81c3ea0SDavid du Colombier 	ep->hp = hp;
343*a81c3ea0SDavid du Colombier 	ep->maxpkt = 8;
344*a81c3ea0SDavid du Colombier 	ep->ntds = 1;
345*a81c3ea0SDavid du Colombier 	ep->samplesz = ep->pollival = ep->hz = 0; /* make them void */
346*a81c3ea0SDavid du Colombier 	qunlock(&epslck);
347*a81c3ea0SDavid du Colombier 	return ep;
348*a81c3ea0SDavid du Colombier }
349*a81c3ea0SDavid du Colombier 
350*a81c3ea0SDavid du Colombier static Ep*
getep(int i)351*a81c3ea0SDavid du Colombier getep(int i)
352*a81c3ea0SDavid du Colombier {
353*a81c3ea0SDavid du Colombier 	Ep *ep;
354*a81c3ea0SDavid du Colombier 
355*a81c3ea0SDavid du Colombier 	if(i < 0 || i >= epmax || eps[i] == nil)
356*a81c3ea0SDavid du Colombier 		return nil;
357*a81c3ea0SDavid du Colombier 	qlock(&epslck);
358*a81c3ea0SDavid du Colombier 	ep = eps[i];
359*a81c3ea0SDavid du Colombier 	if(ep != nil)
360*a81c3ea0SDavid du Colombier 		incref(ep);
361*a81c3ea0SDavid du Colombier 	qunlock(&epslck);
362*a81c3ea0SDavid du Colombier 	return ep;
363*a81c3ea0SDavid du Colombier }
364*a81c3ea0SDavid du Colombier 
365*a81c3ea0SDavid du Colombier static void
putep(Ep * ep)366*a81c3ea0SDavid du Colombier putep(Ep *ep)
367*a81c3ea0SDavid du Colombier {
368*a81c3ea0SDavid du Colombier 	Udev *d;
369*a81c3ea0SDavid du Colombier 
370*a81c3ea0SDavid du Colombier 	if(ep != nil && decref(ep) == 0){
371*a81c3ea0SDavid du Colombier 		d = ep->dev;
372*a81c3ea0SDavid du Colombier 		deprint("usb: ep%d.%d %#p released\n", d->nb, ep->nb, ep);
373*a81c3ea0SDavid du Colombier 		qlock(&epslck);
374*a81c3ea0SDavid du Colombier 		eps[ep->idx] = nil;
375*a81c3ea0SDavid du Colombier 		if(ep->idx == epmax-1)
376*a81c3ea0SDavid du Colombier 			epmax--;
377*a81c3ea0SDavid du Colombier 		if(ep == ep->ep0 && ep->dev != nil && ep->dev->nb == usbidgen)
378*a81c3ea0SDavid du Colombier 			usbidgen--;
379*a81c3ea0SDavid du Colombier 		qunlock(&epslck);
380*a81c3ea0SDavid du Colombier 		if(d != nil){
381*a81c3ea0SDavid du Colombier 			qlock(ep->ep0);
382*a81c3ea0SDavid du Colombier 			d->eps[ep->nb] = nil;
383*a81c3ea0SDavid du Colombier 			qunlock(ep->ep0);
384*a81c3ea0SDavid du Colombier 		}
385*a81c3ea0SDavid du Colombier 		if(ep->ep0 != ep){
386*a81c3ea0SDavid du Colombier 			putep(ep->ep0);
387*a81c3ea0SDavid du Colombier 			ep->ep0 = nil;
388*a81c3ea0SDavid du Colombier 		}
389*a81c3ea0SDavid du Colombier 		free(ep->info);
390*a81c3ea0SDavid du Colombier 		free(ep->name);
391*a81c3ea0SDavid du Colombier 		free(ep);
392*a81c3ea0SDavid du Colombier 	}
393*a81c3ea0SDavid du Colombier }
394*a81c3ea0SDavid du Colombier 
395*a81c3ea0SDavid du Colombier static void
dumpeps(void)396*a81c3ea0SDavid du Colombier dumpeps(void)
397*a81c3ea0SDavid du Colombier {
398*a81c3ea0SDavid du Colombier 	int i;
399*a81c3ea0SDavid du Colombier 	static char buf[512];
400*a81c3ea0SDavid du Colombier 	char *s;
401*a81c3ea0SDavid du Colombier 	char *e;
402*a81c3ea0SDavid du Colombier 	Ep *ep;
403*a81c3ea0SDavid du Colombier 
404*a81c3ea0SDavid du Colombier 	print("usb dump eps: epmax %d Neps %d (ref=1+ for dump):\n", epmax, Neps);
405*a81c3ea0SDavid du Colombier 	for(i = 0; i < epmax; i++){
406*a81c3ea0SDavid du Colombier 		s = buf;
407*a81c3ea0SDavid du Colombier 		e = buf+sizeof(buf);
408*a81c3ea0SDavid du Colombier 		ep = getep(i);
409*a81c3ea0SDavid du Colombier 		if(ep != nil){
410*a81c3ea0SDavid du Colombier 			if(waserror()){
411*a81c3ea0SDavid du Colombier 				putep(ep);
412*a81c3ea0SDavid du Colombier 				nexterror();
413*a81c3ea0SDavid du Colombier 			}
414*a81c3ea0SDavid du Colombier 			s = seprint(s, e, "ep%d.%d ", ep->dev->nb, ep->nb);
415*a81c3ea0SDavid du Colombier 			seprintep(s, e, ep, 1);
416*a81c3ea0SDavid du Colombier 			print("%s", buf);
417*a81c3ea0SDavid du Colombier 			ep->hp->seprintep(buf, e, ep);
418*a81c3ea0SDavid du Colombier 			print("%s", buf);
419*a81c3ea0SDavid du Colombier 			poperror();
420*a81c3ea0SDavid du Colombier 			putep(ep);
421*a81c3ea0SDavid du Colombier 		}
422*a81c3ea0SDavid du Colombier 	}
423*a81c3ea0SDavid du Colombier 	print("usb dump hcis:\n");
424*a81c3ea0SDavid du Colombier 	for(i = 0; i < Nhcis; i++)
425*a81c3ea0SDavid du Colombier 		if(hcis[i] != nil)
426*a81c3ea0SDavid du Colombier 			hcis[i]->dump(hcis[i]);
427*a81c3ea0SDavid du Colombier }
428*a81c3ea0SDavid du Colombier 
429*a81c3ea0SDavid du Colombier static int
newusbid(Hci *)430*a81c3ea0SDavid du Colombier newusbid(Hci *)
431*a81c3ea0SDavid du Colombier {
432*a81c3ea0SDavid du Colombier 	int id;
433*a81c3ea0SDavid du Colombier 
434*a81c3ea0SDavid du Colombier 	qlock(&epslck);
435*a81c3ea0SDavid du Colombier 	id = ++usbidgen;
436*a81c3ea0SDavid du Colombier 	if(id >= 0x7F)
437*a81c3ea0SDavid du Colombier 		print("#u: too many device addresses; reuse them more\n");
438*a81c3ea0SDavid du Colombier 	qunlock(&epslck);
439*a81c3ea0SDavid du Colombier 	return id;
440*a81c3ea0SDavid du Colombier }
441*a81c3ea0SDavid du Colombier 
442*a81c3ea0SDavid du Colombier /*
443*a81c3ea0SDavid du Colombier  * Create endpoint 0 for a new device
444*a81c3ea0SDavid du Colombier  */
445*a81c3ea0SDavid du Colombier static Ep*
newdev(Hci * hp,int ishub,int isroot)446*a81c3ea0SDavid du Colombier newdev(Hci *hp, int ishub, int isroot)
447*a81c3ea0SDavid du Colombier {
448*a81c3ea0SDavid du Colombier 	Ep *ep;
449*a81c3ea0SDavid du Colombier 	Udev *d;
450*a81c3ea0SDavid du Colombier 
451*a81c3ea0SDavid du Colombier 	ep = epalloc(hp);
452*a81c3ea0SDavid du Colombier 	d = ep->dev = smalloc(sizeof(Udev));
453*a81c3ea0SDavid du Colombier 	d->nb = newusbid(hp);
454*a81c3ea0SDavid du Colombier 	d->eps[0] = ep;
455*a81c3ea0SDavid du Colombier 	ep->nb = 0;
456*a81c3ea0SDavid du Colombier 	ep->toggle[0] = ep->toggle[1] = 0;
457*a81c3ea0SDavid du Colombier 	d->ishub = ishub;
458*a81c3ea0SDavid du Colombier 	d->isroot = isroot;
459*a81c3ea0SDavid du Colombier 	if(hp->highspeed != 0)
460*a81c3ea0SDavid du Colombier 		d->speed = Highspeed;
461*a81c3ea0SDavid du Colombier 	else
462*a81c3ea0SDavid du Colombier 		d->speed = Fullspeed;
463*a81c3ea0SDavid du Colombier 	d->state = Dconfig;		/* address not yet set */
464*a81c3ea0SDavid du Colombier 	ep->dev = d;
465*a81c3ea0SDavid du Colombier 	ep->ep0 = ep;			/* no ref counted here */
466*a81c3ea0SDavid du Colombier 	ep->ttype = Tctl;
467*a81c3ea0SDavid du Colombier 	ep->tmout = Xfertmout;
468*a81c3ea0SDavid du Colombier 	ep->mode = ORDWR;
469*a81c3ea0SDavid du Colombier 	dprint("newdev %#p ep%d.%d %#p\n", d, d->nb, ep->nb, ep);
470*a81c3ea0SDavid du Colombier 	return ep;
471*a81c3ea0SDavid du Colombier }
472*a81c3ea0SDavid du Colombier 
473*a81c3ea0SDavid du Colombier /*
474*a81c3ea0SDavid du Colombier  * Create a new endpoint for the device
475*a81c3ea0SDavid du Colombier  * accessed via the given endpoint 0.
476*a81c3ea0SDavid du Colombier  */
477*a81c3ea0SDavid du Colombier static Ep*
newdevep(Ep * ep,int i,int tt,int mode)478*a81c3ea0SDavid du Colombier newdevep(Ep *ep, int i, int tt, int mode)
479*a81c3ea0SDavid du Colombier {
480*a81c3ea0SDavid du Colombier 	Ep *nep;
481*a81c3ea0SDavid du Colombier 	Udev *d;
482*a81c3ea0SDavid du Colombier 
483*a81c3ea0SDavid du Colombier 	d = ep->dev;
484*a81c3ea0SDavid du Colombier 	if(d->eps[i] != nil)
485*a81c3ea0SDavid du Colombier 		error("endpoint already in use");
486*a81c3ea0SDavid du Colombier 	nep = epalloc(ep->hp);
487*a81c3ea0SDavid du Colombier 	incref(ep);
488*a81c3ea0SDavid du Colombier 	d->eps[i] = nep;
489*a81c3ea0SDavid du Colombier 	nep->nb = i;
490*a81c3ea0SDavid du Colombier 	nep->toggle[0] = nep->toggle[1] = 0;
491*a81c3ea0SDavid du Colombier 	nep->ep0 = ep;
492*a81c3ea0SDavid du Colombier 	nep->dev = ep->dev;
493*a81c3ea0SDavid du Colombier 	nep->mode = mode;
494*a81c3ea0SDavid du Colombier 	nep->ttype = tt;
495*a81c3ea0SDavid du Colombier 	nep->debug = ep->debug;
496*a81c3ea0SDavid du Colombier 	/* set defaults */
497*a81c3ea0SDavid du Colombier 	switch(tt){
498*a81c3ea0SDavid du Colombier 	case Tctl:
499*a81c3ea0SDavid du Colombier 		nep->tmout = Xfertmout;
500*a81c3ea0SDavid du Colombier 		break;
501*a81c3ea0SDavid du Colombier 	case Tintr:
502*a81c3ea0SDavid du Colombier 		nep->pollival = 10;
503*a81c3ea0SDavid du Colombier 		break;
504*a81c3ea0SDavid du Colombier 	case Tiso:
505*a81c3ea0SDavid du Colombier 		nep->tmout = Xfertmout;
506*a81c3ea0SDavid du Colombier 		nep->pollival = 10;
507*a81c3ea0SDavid du Colombier 		nep->samplesz = 4;
508*a81c3ea0SDavid du Colombier 		nep->hz = 44100;
509*a81c3ea0SDavid du Colombier 		break;
510*a81c3ea0SDavid du Colombier 	}
511*a81c3ea0SDavid du Colombier 	deprint("newdevep ep%d.%d %#p\n", d->nb, nep->nb, nep);
512*a81c3ea0SDavid du Colombier 	return ep;
513*a81c3ea0SDavid du Colombier }
514*a81c3ea0SDavid du Colombier 
515*a81c3ea0SDavid du Colombier static int
epdataperm(int mode)516*a81c3ea0SDavid du Colombier epdataperm(int mode)
517*a81c3ea0SDavid du Colombier {
518*a81c3ea0SDavid du Colombier 
519*a81c3ea0SDavid du Colombier 	switch(mode){
520*a81c3ea0SDavid du Colombier 	case OREAD:
521*a81c3ea0SDavid du Colombier 		return 0440|DMEXCL;
522*a81c3ea0SDavid du Colombier 		break;
523*a81c3ea0SDavid du Colombier 	case OWRITE:
524*a81c3ea0SDavid du Colombier 		return 0220|DMEXCL;
525*a81c3ea0SDavid du Colombier 		break;
526*a81c3ea0SDavid du Colombier 	default:
527*a81c3ea0SDavid du Colombier 		return 0660|DMEXCL;
528*a81c3ea0SDavid du Colombier 	}
529*a81c3ea0SDavid du Colombier }
530*a81c3ea0SDavid du Colombier 
531*a81c3ea0SDavid du Colombier static int
usbgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)532*a81c3ea0SDavid du Colombier usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
533*a81c3ea0SDavid du Colombier {
534*a81c3ea0SDavid du Colombier 	Qid q;
535*a81c3ea0SDavid du Colombier 	Dirtab *dir;
536*a81c3ea0SDavid du Colombier 	int perm;
537*a81c3ea0SDavid du Colombier 	char *se;
538*a81c3ea0SDavid du Colombier 	Ep *ep;
539*a81c3ea0SDavid du Colombier 	int nb;
540*a81c3ea0SDavid du Colombier 	int mode;
541*a81c3ea0SDavid du Colombier 
542*a81c3ea0SDavid du Colombier //	if(0)ddprint("usbgen q %#x s %d...", QID(c->qid), s);
543*a81c3ea0SDavid du Colombier 	if(s == DEVDOTDOT){
544*a81c3ea0SDavid du Colombier 		if(QID(c->qid) <= Qusbdir){
545*a81c3ea0SDavid du Colombier 			mkqid(&q, Qdir, 0, QTDIR);
546*a81c3ea0SDavid du Colombier 			devdir(c, q, "#u", 0, eve, 0555, dp);
547*a81c3ea0SDavid du Colombier 		}else{
548*a81c3ea0SDavid du Colombier 			mkqid(&q, Qusbdir, 0, QTDIR);
549*a81c3ea0SDavid du Colombier 			devdir(c, q, "usb", 0, eve, 0555, dp);
550*a81c3ea0SDavid du Colombier 		}
551*a81c3ea0SDavid du Colombier //		if(0)ddprint("ok\n");
552*a81c3ea0SDavid du Colombier 		return 1;
553*a81c3ea0SDavid du Colombier 	}
554*a81c3ea0SDavid du Colombier 
555*a81c3ea0SDavid du Colombier 	switch(QID(c->qid)){
556*a81c3ea0SDavid du Colombier 	case Qdir:				/* list #u */
557*a81c3ea0SDavid du Colombier 		if(s == 0){
558*a81c3ea0SDavid du Colombier 			mkqid(&q, Qusbdir, 0, QTDIR);
559*a81c3ea0SDavid du Colombier 			devdir(c, q, "usb", 0, eve, 0555, dp);
560*a81c3ea0SDavid du Colombier //			if(0)ddprint("ok\n");
561*a81c3ea0SDavid du Colombier 			return 1;
562*a81c3ea0SDavid du Colombier 		}
563*a81c3ea0SDavid du Colombier 		s--;
564*a81c3ea0SDavid du Colombier 		if(s < 0 || s >= epmax)
565*a81c3ea0SDavid du Colombier 			goto Fail;
566*a81c3ea0SDavid du Colombier 		ep = getep(s);
567*a81c3ea0SDavid du Colombier 		if(ep == nil || ep->name == nil){
568*a81c3ea0SDavid du Colombier 			if(ep != nil)
569*a81c3ea0SDavid du Colombier 				putep(ep);
570*a81c3ea0SDavid du Colombier //			if(0)ddprint("skip\n");
571*a81c3ea0SDavid du Colombier 			return 0;
572*a81c3ea0SDavid du Colombier 		}
573*a81c3ea0SDavid du Colombier 		if(waserror()){
574*a81c3ea0SDavid du Colombier 			putep(ep);
575*a81c3ea0SDavid du Colombier 			nexterror();
576*a81c3ea0SDavid du Colombier 		}
577*a81c3ea0SDavid du Colombier 		mkqid(&q, Qep0io+s*4, 0, QTFILE);
578*a81c3ea0SDavid du Colombier 		devdir(c, q, ep->name, 0, eve, epdataperm(ep->mode), dp);
579*a81c3ea0SDavid du Colombier 		putep(ep);
580*a81c3ea0SDavid du Colombier 		poperror();
581*a81c3ea0SDavid du Colombier //		if(0)ddprint("ok\n");
582*a81c3ea0SDavid du Colombier 		return 1;
583*a81c3ea0SDavid du Colombier 
584*a81c3ea0SDavid du Colombier 	case Qusbdir:				/* list #u/usb */
585*a81c3ea0SDavid du Colombier 	Usbdir:
586*a81c3ea0SDavid du Colombier 		if(s < nelem(usbdir)){
587*a81c3ea0SDavid du Colombier 			dir = &usbdir[s];
588*a81c3ea0SDavid du Colombier 			mkqid(&q, dir->qid.path, 0, QTFILE);
589*a81c3ea0SDavid du Colombier 			devdir(c, q, dir->name, dir->length, eve, dir->perm, dp);
590*a81c3ea0SDavid du Colombier //			if(0)ddprint("ok\n");
591*a81c3ea0SDavid du Colombier 			return 1;
592*a81c3ea0SDavid du Colombier 		}
593*a81c3ea0SDavid du Colombier 		s -= nelem(usbdir);
594*a81c3ea0SDavid du Colombier 		if(s < 0 || s >= epmax)
595*a81c3ea0SDavid du Colombier 			goto Fail;
596*a81c3ea0SDavid du Colombier 		ep = getep(s);
597*a81c3ea0SDavid du Colombier 		if(ep == nil){
598*a81c3ea0SDavid du Colombier //			if(0)ddprint("skip\n");
599*a81c3ea0SDavid du Colombier 			return 0;
600*a81c3ea0SDavid du Colombier 		}
601*a81c3ea0SDavid du Colombier 		if(waserror()){
602*a81c3ea0SDavid du Colombier 			putep(ep);
603*a81c3ea0SDavid du Colombier 			nexterror();
604*a81c3ea0SDavid du Colombier 		}
605*a81c3ea0SDavid du Colombier 		se = up->genbuf+sizeof(up->genbuf);
606*a81c3ea0SDavid du Colombier 		seprint(up->genbuf, se, "ep%d.%d", ep->dev->nb, ep->nb);
607*a81c3ea0SDavid du Colombier 		mkqid(&q, Qep0dir+4*s, 0, QTDIR);
608*a81c3ea0SDavid du Colombier 		putep(ep);
609*a81c3ea0SDavid du Colombier 		poperror();
610*a81c3ea0SDavid du Colombier 		devdir(c, q, up->genbuf, 0, eve, 0755, dp);
611*a81c3ea0SDavid du Colombier //		if(0)ddprint("ok\n");
612*a81c3ea0SDavid du Colombier 		return 1;
613*a81c3ea0SDavid du Colombier 
614*a81c3ea0SDavid du Colombier 	case Qctl:
615*a81c3ea0SDavid du Colombier 		s = 0;
616*a81c3ea0SDavid du Colombier 		goto Usbdir;
617*a81c3ea0SDavid du Colombier 
618*a81c3ea0SDavid du Colombier 	default:				/* list #u/usb/epN.M */
619*a81c3ea0SDavid du Colombier 		nb = qid2epidx(QID(c->qid));
620*a81c3ea0SDavid du Colombier 		ep = getep(nb);
621*a81c3ea0SDavid du Colombier 		if(ep == nil)
622*a81c3ea0SDavid du Colombier 			goto Fail;
623*a81c3ea0SDavid du Colombier 		mode = ep->mode;
624*a81c3ea0SDavid du Colombier 		putep(ep);
625*a81c3ea0SDavid du Colombier 		if(isqtype(QID(c->qid), Qepdir)){
626*a81c3ea0SDavid du Colombier 		Epdir:
627*a81c3ea0SDavid du Colombier 			switch(s){
628*a81c3ea0SDavid du Colombier 			case 0:
629*a81c3ea0SDavid du Colombier 				mkqid(&q, Qep0io+nb*4, 0, QTFILE);
630*a81c3ea0SDavid du Colombier 				perm = epdataperm(mode);
631*a81c3ea0SDavid du Colombier 				devdir(c, q, "data", 0, eve, perm, dp);
632*a81c3ea0SDavid du Colombier 				break;
633*a81c3ea0SDavid du Colombier 			case 1:
634*a81c3ea0SDavid du Colombier 				mkqid(&q, Qep0ctl+nb*4, 0, QTFILE);
635*a81c3ea0SDavid du Colombier 				devdir(c, q, "ctl", 0, eve, 0664, dp);
636*a81c3ea0SDavid du Colombier 				break;
637*a81c3ea0SDavid du Colombier 			default:
638*a81c3ea0SDavid du Colombier 				goto Fail;
639*a81c3ea0SDavid du Colombier 			}
640*a81c3ea0SDavid du Colombier 		}else if(isqtype(QID(c->qid), Qepctl)){
641*a81c3ea0SDavid du Colombier 			s = 1;
642*a81c3ea0SDavid du Colombier 			goto Epdir;
643*a81c3ea0SDavid du Colombier 		}else{
644*a81c3ea0SDavid du Colombier 			s = 0;
645*a81c3ea0SDavid du Colombier 			goto Epdir;
646*a81c3ea0SDavid du Colombier 		}
647*a81c3ea0SDavid du Colombier //		if(0)ddprint("ok\n");
648*a81c3ea0SDavid du Colombier 		return 1;
649*a81c3ea0SDavid du Colombier 	}
650*a81c3ea0SDavid du Colombier Fail:
651*a81c3ea0SDavid du Colombier //	if(0)ddprint("fail\n");
652*a81c3ea0SDavid du Colombier 	return -1;
653*a81c3ea0SDavid du Colombier }
654*a81c3ea0SDavid du Colombier 
655*a81c3ea0SDavid du Colombier static Hci*
hciprobe(int cardno,int ctlrno)656*a81c3ea0SDavid du Colombier hciprobe(int cardno, int ctlrno)
657*a81c3ea0SDavid du Colombier {
658*a81c3ea0SDavid du Colombier 	Hci *hp;
659*a81c3ea0SDavid du Colombier 	char *type;
660*a81c3ea0SDavid du Colombier 	char name[64];
661*a81c3ea0SDavid du Colombier 	static int epnb = 1;	/* guess the endpoint nb. for the controller */
662*a81c3ea0SDavid du Colombier 
663*a81c3ea0SDavid du Colombier 	ddprint("hciprobe %d %d\n", cardno, ctlrno);
664*a81c3ea0SDavid du Colombier 	hp = smalloc(sizeof(Hci));
665*a81c3ea0SDavid du Colombier 	hp->ctlrno = ctlrno;
666*a81c3ea0SDavid du Colombier 	hp->tbdf = BUSUNKNOWN;
667*a81c3ea0SDavid du Colombier 
668*a81c3ea0SDavid du Colombier 	if(cardno < 0)
669*a81c3ea0SDavid du Colombier 		for(cardno = 0; cardno < Nhcis; cardno++){
670*a81c3ea0SDavid du Colombier 			if(hcitypes[cardno].type == nil)
671*a81c3ea0SDavid du Colombier 				break;
672*a81c3ea0SDavid du Colombier 			type = hp->type;
673*a81c3ea0SDavid du Colombier 			if(type==nil || *type==0)
674*a81c3ea0SDavid du Colombier 				type = "uhci";
675*a81c3ea0SDavid du Colombier 			if(cistrcmp(hcitypes[cardno].type, type) == 0)
676*a81c3ea0SDavid du Colombier 				break;
677*a81c3ea0SDavid du Colombier 		}
678*a81c3ea0SDavid du Colombier 
679*a81c3ea0SDavid du Colombier 	if(cardno >= Nhcis || hcitypes[cardno].type == nil){
680*a81c3ea0SDavid du Colombier 		free(hp);
681*a81c3ea0SDavid du Colombier 		return nil;
682*a81c3ea0SDavid du Colombier 	}
683*a81c3ea0SDavid du Colombier 	dprint("%s...", hcitypes[cardno].type);
684*a81c3ea0SDavid du Colombier 	if(hcitypes[cardno].reset(hp) < 0){
685*a81c3ea0SDavid du Colombier 		free(hp);
686*a81c3ea0SDavid du Colombier 		return nil;
687*a81c3ea0SDavid du Colombier 	}
688*a81c3ea0SDavid du Colombier 
689*a81c3ea0SDavid du Colombier 	snprint(name, sizeof(name), "usb%s", hcitypes[cardno].type);
690*a81c3ea0SDavid du Colombier 	intrenable(hp->irq, hp->interrupt, hp, pcisubirq(hp->tbdf));
691*a81c3ea0SDavid du Colombier 
692*a81c3ea0SDavid du Colombier 	print("#u/usb/ep%d.0: %s: port 0x%lux irq %d\n",
693*a81c3ea0SDavid du Colombier 		epnb, hcitypes[cardno].type, hp->port, hp->irq);
694*a81c3ea0SDavid du Colombier 	epnb++;
695*a81c3ea0SDavid du Colombier 	return hp;
696*a81c3ea0SDavid du Colombier }
697*a81c3ea0SDavid du Colombier 
698*a81c3ea0SDavid du Colombier static void
usbreset(void)699*a81c3ea0SDavid du Colombier usbreset(void)
700*a81c3ea0SDavid du Colombier {
701*a81c3ea0SDavid du Colombier 	int cardno, ctlrno;
702*a81c3ea0SDavid du Colombier 	Hci *hp;
703*a81c3ea0SDavid du Colombier 
704*a81c3ea0SDavid du Colombier 	if(getconf("*nousbprobe"))
705*a81c3ea0SDavid du Colombier 		return;
706*a81c3ea0SDavid du Colombier 	dprint("usbreset\n");
707*a81c3ea0SDavid du Colombier 
708*a81c3ea0SDavid du Colombier 	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++)
709*a81c3ea0SDavid du Colombier 		if((hp = hciprobe(-1, ctlrno)) != nil)
710*a81c3ea0SDavid du Colombier 			hcis[ctlrno] = hp;
711*a81c3ea0SDavid du Colombier 	cardno = ctlrno = 0;
712*a81c3ea0SDavid du Colombier 	while(cardno < Nhcis && ctlrno < Nhcis && hcitypes[cardno].type != nil)
713*a81c3ea0SDavid du Colombier 		if(hcis[ctlrno] != nil)
714*a81c3ea0SDavid du Colombier 			ctlrno++;
715*a81c3ea0SDavid du Colombier 		else{
716*a81c3ea0SDavid du Colombier 			hp = hciprobe(cardno, ctlrno);
717*a81c3ea0SDavid du Colombier 			if(hp == nil)
718*a81c3ea0SDavid du Colombier 				cardno++;
719*a81c3ea0SDavid du Colombier 			hcis[ctlrno++] = hp;
720*a81c3ea0SDavid du Colombier 		}
721*a81c3ea0SDavid du Colombier 	if(hcis[Nhcis-1] != nil)
722*a81c3ea0SDavid du Colombier 		print("usbreset: bug: Nhcis (%d) too small\n", Nhcis);
723*a81c3ea0SDavid du Colombier }
724*a81c3ea0SDavid du Colombier 
725*a81c3ea0SDavid du Colombier static void
usbinit(void)726*a81c3ea0SDavid du Colombier usbinit(void)
727*a81c3ea0SDavid du Colombier {
728*a81c3ea0SDavid du Colombier 	Hci *hp;
729*a81c3ea0SDavid du Colombier 	int ctlrno;
730*a81c3ea0SDavid du Colombier 	Ep *d;
731*a81c3ea0SDavid du Colombier 	char info[40];
732*a81c3ea0SDavid du Colombier 
733*a81c3ea0SDavid du Colombier 	dprint("usbinit\n");
734*a81c3ea0SDavid du Colombier 	for(ctlrno = 0; ctlrno < Nhcis; ctlrno++){
735*a81c3ea0SDavid du Colombier 		hp = hcis[ctlrno];
736*a81c3ea0SDavid du Colombier 		if(hp != nil){
737*a81c3ea0SDavid du Colombier 			if(hp->init != nil)
738*a81c3ea0SDavid du Colombier 				hp->init(hp);
739*a81c3ea0SDavid du Colombier 			d = newdev(hp, 1, 1);		/* new root hub */
740*a81c3ea0SDavid du Colombier 			d->dev->state = Denabled;	/* although addr == 0 */
741*a81c3ea0SDavid du Colombier 			d->maxpkt = 64;
742*a81c3ea0SDavid du Colombier 			snprint(info, sizeof(info), "ports %d", hp->nports);
743*a81c3ea0SDavid du Colombier 			kstrdup(&d->info, info);
744*a81c3ea0SDavid du Colombier 		}
745*a81c3ea0SDavid du Colombier 	}
746*a81c3ea0SDavid du Colombier }
747*a81c3ea0SDavid du Colombier 
748*a81c3ea0SDavid du Colombier static Chan*
usbattach(char * spec)749*a81c3ea0SDavid du Colombier usbattach(char *spec)
750*a81c3ea0SDavid du Colombier {
751*a81c3ea0SDavid du Colombier 	return devattach(L'u', spec);
752*a81c3ea0SDavid du Colombier }
753*a81c3ea0SDavid du Colombier 
754*a81c3ea0SDavid du Colombier static Walkqid*
usbwalk(Chan * c,Chan * nc,char ** name,int nname)755*a81c3ea0SDavid du Colombier usbwalk(Chan *c, Chan *nc, char **name, int nname)
756*a81c3ea0SDavid du Colombier {
757*a81c3ea0SDavid du Colombier 	return devwalk(c, nc, name, nname, nil, 0, usbgen);
758*a81c3ea0SDavid du Colombier }
759*a81c3ea0SDavid du Colombier 
760*a81c3ea0SDavid du Colombier static int
usbstat(Chan * c,uchar * db,int n)761*a81c3ea0SDavid du Colombier usbstat(Chan *c, uchar *db, int n)
762*a81c3ea0SDavid du Colombier {
763*a81c3ea0SDavid du Colombier 	return devstat(c, db, n, nil, 0, usbgen);
764*a81c3ea0SDavid du Colombier }
765*a81c3ea0SDavid du Colombier 
766*a81c3ea0SDavid du Colombier /*
767*a81c3ea0SDavid du Colombier  * µs for the given transfer, for bandwidth allocation.
768*a81c3ea0SDavid du Colombier  * This is a very rough worst case for what 5.11.3
769*a81c3ea0SDavid du Colombier  * of the usb 2.0 spec says.
770*a81c3ea0SDavid du Colombier  * Also, we are using maxpkt and not actual transfer sizes.
771*a81c3ea0SDavid du Colombier  * Only when we are sure we
772*a81c3ea0SDavid du Colombier  * are not exceeding b/w might we consider adjusting it.
773*a81c3ea0SDavid du Colombier  */
774*a81c3ea0SDavid du Colombier static ulong
usbload(int speed,int maxpkt)775*a81c3ea0SDavid du Colombier usbload(int speed, int maxpkt)
776*a81c3ea0SDavid du Colombier {
777*a81c3ea0SDavid du Colombier 	enum{ Hostns = 1000, Hubns = 333 };
778*a81c3ea0SDavid du Colombier 	ulong l;
779*a81c3ea0SDavid du Colombier 	ulong bs;
780*a81c3ea0SDavid du Colombier 
781*a81c3ea0SDavid du Colombier 	l = 0;
782*a81c3ea0SDavid du Colombier 	bs = 10UL * maxpkt;
783*a81c3ea0SDavid du Colombier 	switch(speed){
784*a81c3ea0SDavid du Colombier 	case Highspeed:
785*a81c3ea0SDavid du Colombier 		l = 55*8*2 + 2 * (3 + bs) + Hostns;
786*a81c3ea0SDavid du Colombier 		break;
787*a81c3ea0SDavid du Colombier 	case Fullspeed:
788*a81c3ea0SDavid du Colombier 		l = 9107 + 84 * (4 + bs) + Hostns;
789*a81c3ea0SDavid du Colombier 		break;
790*a81c3ea0SDavid du Colombier 	case Lowspeed:
791*a81c3ea0SDavid du Colombier 		l = 64107 + 2 * Hubns + 667 * (3 + bs) + Hostns;
792*a81c3ea0SDavid du Colombier 		break;
793*a81c3ea0SDavid du Colombier 	default:
794*a81c3ea0SDavid du Colombier 		print("usbload: bad speed %d\n", speed);
795*a81c3ea0SDavid du Colombier 		/* let it run */
796*a81c3ea0SDavid du Colombier 	}
797*a81c3ea0SDavid du Colombier 	return l / 1000UL;	/* in µs */
798*a81c3ea0SDavid du Colombier }
799*a81c3ea0SDavid du Colombier 
800*a81c3ea0SDavid du Colombier static Chan*
usbopen(Chan * c,int omode)801*a81c3ea0SDavid du Colombier usbopen(Chan *c, int omode)
802*a81c3ea0SDavid du Colombier {
803*a81c3ea0SDavid du Colombier 	int q;
804*a81c3ea0SDavid du Colombier 	Ep *ep;
805*a81c3ea0SDavid du Colombier 	int mode;
806*a81c3ea0SDavid du Colombier 
807*a81c3ea0SDavid du Colombier 	mode = openmode(omode);
808*a81c3ea0SDavid du Colombier 	q = QID(c->qid);
809*a81c3ea0SDavid du Colombier 
810*a81c3ea0SDavid du Colombier 	if(q >= Qep0dir && qid2epidx(q) < 0)
811*a81c3ea0SDavid du Colombier 		error(Eio);
812*a81c3ea0SDavid du Colombier 	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
813*a81c3ea0SDavid du Colombier 		return devopen(c, omode, nil, 0, usbgen);
814*a81c3ea0SDavid du Colombier 
815*a81c3ea0SDavid du Colombier 	ep = getep(qid2epidx(q));
816*a81c3ea0SDavid du Colombier 	if(ep == nil)
817*a81c3ea0SDavid du Colombier 		error(Eio);
818*a81c3ea0SDavid du Colombier 	deprint("usbopen q %#x fid %d omode %d\n", q, c->fid, mode);
819*a81c3ea0SDavid du Colombier 	if(waserror()){
820*a81c3ea0SDavid du Colombier 		putep(ep);
821*a81c3ea0SDavid du Colombier 		nexterror();
822*a81c3ea0SDavid du Colombier 	}
823*a81c3ea0SDavid du Colombier 	qlock(ep);
824*a81c3ea0SDavid du Colombier 	if(ep->inuse){
825*a81c3ea0SDavid du Colombier 		qunlock(ep);
826*a81c3ea0SDavid du Colombier 		error(Einuse);
827*a81c3ea0SDavid du Colombier 	}
828*a81c3ea0SDavid du Colombier 	ep->inuse = 1;
829*a81c3ea0SDavid du Colombier 	qunlock(ep);
830*a81c3ea0SDavid du Colombier 	if(waserror()){
831*a81c3ea0SDavid du Colombier 		ep->inuse = 0;
832*a81c3ea0SDavid du Colombier 		nexterror();
833*a81c3ea0SDavid du Colombier 	}
834*a81c3ea0SDavid du Colombier 	if(mode != OREAD && ep->mode == OREAD)
835*a81c3ea0SDavid du Colombier 		error(Eperm);
836*a81c3ea0SDavid du Colombier 	if(mode != OWRITE && ep->mode == OWRITE)
837*a81c3ea0SDavid du Colombier 		error(Eperm);
838*a81c3ea0SDavid du Colombier 	if(ep->ttype == Tnone)
839*a81c3ea0SDavid du Colombier 		error(Enotconf);
840*a81c3ea0SDavid du Colombier 	ep->clrhalt = 0;
841*a81c3ea0SDavid du Colombier 	ep->rhrepl = -1;
842*a81c3ea0SDavid du Colombier 	if(ep->load == 0)
843*a81c3ea0SDavid du Colombier 		ep->load = usbload(ep->dev->speed, ep->maxpkt);
844*a81c3ea0SDavid du Colombier 	ep->hp->epopen(ep);
845*a81c3ea0SDavid du Colombier 
846*a81c3ea0SDavid du Colombier 	poperror();	/* ep->inuse */
847*a81c3ea0SDavid du Colombier 	poperror();	/* don't putep(): ref kept for fid using the ep. */
848*a81c3ea0SDavid du Colombier 
849*a81c3ea0SDavid du Colombier 	c->mode = mode;
850*a81c3ea0SDavid du Colombier 	c->flag |= COPEN;
851*a81c3ea0SDavid du Colombier 	c->offset = 0;
852*a81c3ea0SDavid du Colombier 	c->aux = nil;	/* paranoia */
853*a81c3ea0SDavid du Colombier 	return c;
854*a81c3ea0SDavid du Colombier }
855*a81c3ea0SDavid du Colombier 
856*a81c3ea0SDavid du Colombier static void
epclose(Ep * ep)857*a81c3ea0SDavid du Colombier epclose(Ep *ep)
858*a81c3ea0SDavid du Colombier {
859*a81c3ea0SDavid du Colombier 	qlock(ep);
860*a81c3ea0SDavid du Colombier 	if(waserror()){
861*a81c3ea0SDavid du Colombier 		qunlock(ep);
862*a81c3ea0SDavid du Colombier 		nexterror();
863*a81c3ea0SDavid du Colombier 	}
864*a81c3ea0SDavid du Colombier 	if(ep->inuse){
865*a81c3ea0SDavid du Colombier 		ep->hp->epclose(ep);
866*a81c3ea0SDavid du Colombier 		ep->inuse = 0;
867*a81c3ea0SDavid du Colombier 	}
868*a81c3ea0SDavid du Colombier 	qunlock(ep);
869*a81c3ea0SDavid du Colombier 	poperror();
870*a81c3ea0SDavid du Colombier }
871*a81c3ea0SDavid du Colombier 
872*a81c3ea0SDavid du Colombier static void
usbclose(Chan * c)873*a81c3ea0SDavid du Colombier usbclose(Chan *c)
874*a81c3ea0SDavid du Colombier {
875*a81c3ea0SDavid du Colombier 	int q;
876*a81c3ea0SDavid du Colombier 	Ep *ep;
877*a81c3ea0SDavid du Colombier 
878*a81c3ea0SDavid du Colombier 	q = QID(c->qid);
879*a81c3ea0SDavid du Colombier 	if(q < Qep0dir || isqtype(q, Qepctl) || isqtype(q, Qepdir))
880*a81c3ea0SDavid du Colombier 		return;
881*a81c3ea0SDavid du Colombier 
882*a81c3ea0SDavid du Colombier 	ep = getep(qid2epidx(q));
883*a81c3ea0SDavid du Colombier 	if(ep == nil)
884*a81c3ea0SDavid du Colombier 		return;
885*a81c3ea0SDavid du Colombier 	deprint("usbclose q %#x fid %d ref %ld\n", q, c->fid, ep->ref);
886*a81c3ea0SDavid du Colombier 	if(waserror()){
887*a81c3ea0SDavid du Colombier 		putep(ep);
888*a81c3ea0SDavid du Colombier 		nexterror();
889*a81c3ea0SDavid du Colombier 	}
890*a81c3ea0SDavid du Colombier 	if(c->flag & COPEN){
891*a81c3ea0SDavid du Colombier 		free(c->aux);
892*a81c3ea0SDavid du Colombier 		c->aux = nil;
893*a81c3ea0SDavid du Colombier 		epclose(ep);
894*a81c3ea0SDavid du Colombier 		putep(ep);	/* release ref kept since usbopen */
895*a81c3ea0SDavid du Colombier 		c->flag &= ~COPEN;
896*a81c3ea0SDavid du Colombier 	}
897*a81c3ea0SDavid du Colombier 	poperror();
898*a81c3ea0SDavid du Colombier 	putep(ep);
899*a81c3ea0SDavid du Colombier }
900*a81c3ea0SDavid du Colombier 
901*a81c3ea0SDavid du Colombier static long
ctlread(Chan * c,void * a,long n,vlong offset)902*a81c3ea0SDavid du Colombier ctlread(Chan *c, void *a, long n, vlong offset)
903*a81c3ea0SDavid du Colombier {
904*a81c3ea0SDavid du Colombier 	int q;
905*a81c3ea0SDavid du Colombier 	char *s;
906*a81c3ea0SDavid du Colombier 	char *us;
907*a81c3ea0SDavid du Colombier 	char *se;
908*a81c3ea0SDavid du Colombier 	Ep *ep;
909*a81c3ea0SDavid du Colombier 	int i;
910*a81c3ea0SDavid du Colombier 
911*a81c3ea0SDavid du Colombier 	q = QID(c->qid);
912*a81c3ea0SDavid du Colombier 	us = s = smalloc(READSTR);
913*a81c3ea0SDavid du Colombier 	se = s + READSTR;
914*a81c3ea0SDavid du Colombier 	if(waserror()){
915*a81c3ea0SDavid du Colombier 		free(us);
916*a81c3ea0SDavid du Colombier 		nexterror();
917*a81c3ea0SDavid du Colombier 	}
918*a81c3ea0SDavid du Colombier 	if(q == Qctl)
919*a81c3ea0SDavid du Colombier 		for(i = 0; i < epmax; i++){
920*a81c3ea0SDavid du Colombier 			ep = getep(i);
921*a81c3ea0SDavid du Colombier 			if(ep != nil){
922*a81c3ea0SDavid du Colombier 				if(waserror()){
923*a81c3ea0SDavid du Colombier 					putep(ep);
924*a81c3ea0SDavid du Colombier 					nexterror();
925*a81c3ea0SDavid du Colombier 				}
926*a81c3ea0SDavid du Colombier 				s = seprint(s, se, "ep%d.%d ", ep->dev->nb, ep->nb);
927*a81c3ea0SDavid du Colombier 				s = seprintep(s, se, ep, 0);
928*a81c3ea0SDavid du Colombier 				poperror();
929*a81c3ea0SDavid du Colombier 			}
930*a81c3ea0SDavid du Colombier 			putep(ep);
931*a81c3ea0SDavid du Colombier 		}
932*a81c3ea0SDavid du Colombier 	else{
933*a81c3ea0SDavid du Colombier 		ep = getep(qid2epidx(q));
934*a81c3ea0SDavid du Colombier 		if(ep == nil)
935*a81c3ea0SDavid du Colombier 			error(Eio);
936*a81c3ea0SDavid du Colombier 		if(waserror()){
937*a81c3ea0SDavid du Colombier 			putep(ep);
938*a81c3ea0SDavid du Colombier 			nexterror();
939*a81c3ea0SDavid du Colombier 		}
940*a81c3ea0SDavid du Colombier 		if(c->aux != nil){
941*a81c3ea0SDavid du Colombier 			/* After a new endpoint request we read
942*a81c3ea0SDavid du Colombier 			 * the new endpoint name back.
943*a81c3ea0SDavid du Colombier 			 */
944*a81c3ea0SDavid du Colombier 			strecpy(s, se, c->aux);
945*a81c3ea0SDavid du Colombier 			free(c->aux);
946*a81c3ea0SDavid du Colombier 			c->aux = nil;
947*a81c3ea0SDavid du Colombier 		}else
948*a81c3ea0SDavid du Colombier 			seprintep(s, se, ep, 0);
949*a81c3ea0SDavid du Colombier 		poperror();
950*a81c3ea0SDavid du Colombier 		putep(ep);
951*a81c3ea0SDavid du Colombier 	}
952*a81c3ea0SDavid du Colombier 	n = readstr(offset, a, n, us);
953*a81c3ea0SDavid du Colombier 	poperror();
954*a81c3ea0SDavid du Colombier 	free(us);
955*a81c3ea0SDavid du Colombier 	return n;
956*a81c3ea0SDavid du Colombier }
957*a81c3ea0SDavid du Colombier 
958*a81c3ea0SDavid du Colombier /*
959*a81c3ea0SDavid du Colombier  * Fake root hub emulation.
960*a81c3ea0SDavid du Colombier  */
961*a81c3ea0SDavid du Colombier static long
rhubread(Ep * ep,void * a,long n)962*a81c3ea0SDavid du Colombier rhubread(Ep *ep, void *a, long n)
963*a81c3ea0SDavid du Colombier {
964*a81c3ea0SDavid du Colombier 	char *b;
965*a81c3ea0SDavid du Colombier 
966*a81c3ea0SDavid du Colombier 	if(ep->dev->isroot == 0 || ep->nb != 0 || n < 2)
967*a81c3ea0SDavid du Colombier 		return -1;
968*a81c3ea0SDavid du Colombier 	if(ep->rhrepl < 0)
969*a81c3ea0SDavid du Colombier 		return -1;
970*a81c3ea0SDavid du Colombier 
971*a81c3ea0SDavid du Colombier 	b = a;
972*a81c3ea0SDavid du Colombier 	memset(b, 0, n);
973*a81c3ea0SDavid du Colombier 	PUT2(b, ep->rhrepl);
974*a81c3ea0SDavid du Colombier 	ep->rhrepl = -1;
975*a81c3ea0SDavid du Colombier 	return n;
976*a81c3ea0SDavid du Colombier }
977*a81c3ea0SDavid du Colombier 
978*a81c3ea0SDavid du Colombier static long
rhubwrite(Ep * ep,void * a,long n)979*a81c3ea0SDavid du Colombier rhubwrite(Ep *ep, void *a, long n)
980*a81c3ea0SDavid du Colombier {
981*a81c3ea0SDavid du Colombier 	uchar *s;
982*a81c3ea0SDavid du Colombier 	int cmd;
983*a81c3ea0SDavid du Colombier 	int feature;
984*a81c3ea0SDavid du Colombier 	int port;
985*a81c3ea0SDavid du Colombier 	Hci *hp;
986*a81c3ea0SDavid du Colombier 
987*a81c3ea0SDavid du Colombier 	if(ep->dev == nil || ep->dev->isroot == 0 || ep->nb != 0)
988*a81c3ea0SDavid du Colombier 		return -1;
989*a81c3ea0SDavid du Colombier 	if(n != Rsetuplen)
990*a81c3ea0SDavid du Colombier 		error("root hub is a toy hub");
991*a81c3ea0SDavid du Colombier 	ep->rhrepl = -1;
992*a81c3ea0SDavid du Colombier 	s = a;
993*a81c3ea0SDavid du Colombier 	if(s[Rtype] != (Rh2d|Rclass|Rother) && s[Rtype] != (Rd2h|Rclass|Rother))
994*a81c3ea0SDavid du Colombier 		error("root hub is a toy hub");
995*a81c3ea0SDavid du Colombier 	hp = ep->hp;
996*a81c3ea0SDavid du Colombier 	cmd = s[Rreq];
997*a81c3ea0SDavid du Colombier 	feature = GET2(s+Rvalue);
998*a81c3ea0SDavid du Colombier 	port = GET2(s+Rindex);
999*a81c3ea0SDavid du Colombier 	if(port < 1 || port > hp->nports)
1000*a81c3ea0SDavid du Colombier 		error("bad hub port number");
1001*a81c3ea0SDavid du Colombier 	switch(feature){
1002*a81c3ea0SDavid du Colombier 	case Rportenable:
1003*a81c3ea0SDavid du Colombier 		ep->rhrepl = hp->portenable(hp, port, cmd == Rsetfeature);
1004*a81c3ea0SDavid du Colombier 		break;
1005*a81c3ea0SDavid du Colombier 	case Rportreset:
1006*a81c3ea0SDavid du Colombier 		ep->rhrepl = hp->portreset(hp, port, cmd == Rsetfeature);
1007*a81c3ea0SDavid du Colombier 		break;
1008*a81c3ea0SDavid du Colombier 	case Rgetstatus:
1009*a81c3ea0SDavid du Colombier 		ep->rhrepl = hp->portstatus(hp, port);
1010*a81c3ea0SDavid du Colombier 		break;
1011*a81c3ea0SDavid du Colombier 	default:
1012*a81c3ea0SDavid du Colombier 		ep->rhrepl = 0;
1013*a81c3ea0SDavid du Colombier 	}
1014*a81c3ea0SDavid du Colombier 	return n;
1015*a81c3ea0SDavid du Colombier }
1016*a81c3ea0SDavid du Colombier 
1017*a81c3ea0SDavid du Colombier static long
usbread(Chan * c,void * a,long n,vlong offset)1018*a81c3ea0SDavid du Colombier usbread(Chan *c, void *a, long n, vlong offset)
1019*a81c3ea0SDavid du Colombier {
1020*a81c3ea0SDavid du Colombier 	int q;
1021*a81c3ea0SDavid du Colombier 	Ep *ep;
1022*a81c3ea0SDavid du Colombier 	int nr;
1023*a81c3ea0SDavid du Colombier 
1024*a81c3ea0SDavid du Colombier 	q = QID(c->qid);
1025*a81c3ea0SDavid du Colombier 
1026*a81c3ea0SDavid du Colombier 	if(c->qid.type == QTDIR)
1027*a81c3ea0SDavid du Colombier 		return devdirread(c, a, n, nil, 0, usbgen);
1028*a81c3ea0SDavid du Colombier 
1029*a81c3ea0SDavid du Colombier 	if(q == Qctl || isqtype(q, Qepctl))
1030*a81c3ea0SDavid du Colombier 		return ctlread(c, a, n, offset);
1031*a81c3ea0SDavid du Colombier 
1032*a81c3ea0SDavid du Colombier 	ep = getep(qid2epidx(q));
1033*a81c3ea0SDavid du Colombier 	if(ep == nil)
1034*a81c3ea0SDavid du Colombier 		error(Eio);
1035*a81c3ea0SDavid du Colombier 	if(waserror()){
1036*a81c3ea0SDavid du Colombier 		putep(ep);
1037*a81c3ea0SDavid du Colombier 		nexterror();
1038*a81c3ea0SDavid du Colombier 	}
1039*a81c3ea0SDavid du Colombier 	if(ep->dev->state == Ddetach)
1040*a81c3ea0SDavid du Colombier 		error(Edetach);
1041*a81c3ea0SDavid du Colombier 	if(ep->mode == OWRITE || ep->inuse == 0)
1042*a81c3ea0SDavid du Colombier 		error(Ebadusefd);
1043*a81c3ea0SDavid du Colombier 	switch(ep->ttype){
1044*a81c3ea0SDavid du Colombier 	case Tnone:
1045*a81c3ea0SDavid du Colombier 		error("endpoint not configured");
1046*a81c3ea0SDavid du Colombier 	case Tctl:
1047*a81c3ea0SDavid du Colombier 		nr = rhubread(ep, a, n);
1048*a81c3ea0SDavid du Colombier 		if(nr >= 0){
1049*a81c3ea0SDavid du Colombier 			n = nr;
1050*a81c3ea0SDavid du Colombier 			break;
1051*a81c3ea0SDavid du Colombier 		}
1052*a81c3ea0SDavid du Colombier 		/* else fall */
1053*a81c3ea0SDavid du Colombier 	default:
1054*a81c3ea0SDavid du Colombier 		ddeprint("\nusbread q %#x fid %d cnt %ld off %lld\n",q,c->fid,n,offset);
1055*a81c3ea0SDavid du Colombier 		n = ep->hp->epread(ep, a, n);
1056*a81c3ea0SDavid du Colombier 		break;
1057*a81c3ea0SDavid du Colombier 	}
1058*a81c3ea0SDavid du Colombier 	poperror();
1059*a81c3ea0SDavid du Colombier 	putep(ep);
1060*a81c3ea0SDavid du Colombier 	return n;
1061*a81c3ea0SDavid du Colombier }
1062*a81c3ea0SDavid du Colombier 
1063*a81c3ea0SDavid du Colombier static long
pow2(int n)1064*a81c3ea0SDavid du Colombier pow2(int n)
1065*a81c3ea0SDavid du Colombier {
1066*a81c3ea0SDavid du Colombier 	return 1 << n;
1067*a81c3ea0SDavid du Colombier }
1068*a81c3ea0SDavid du Colombier 
1069*a81c3ea0SDavid du Colombier static void
setmaxpkt(Ep * ep,char * s)1070*a81c3ea0SDavid du Colombier setmaxpkt(Ep *ep, char* s)
1071*a81c3ea0SDavid du Colombier {
1072*a81c3ea0SDavid du Colombier 	long spp;	/* samples per packet */
1073*a81c3ea0SDavid du Colombier 
1074*a81c3ea0SDavid du Colombier 	if(ep->dev->speed == Highspeed)
1075*a81c3ea0SDavid du Colombier 		spp = (ep->hz * ep->pollival * ep->ntds + 7999) / 8000;
1076*a81c3ea0SDavid du Colombier 	else
1077*a81c3ea0SDavid du Colombier 		spp = (ep->hz * ep->pollival + 999) / 1000;
1078*a81c3ea0SDavid du Colombier 	ep->maxpkt = spp * ep->samplesz;
1079*a81c3ea0SDavid du Colombier 	deprint("usb: %s: setmaxpkt: hz %ld poll %ld"
1080*a81c3ea0SDavid du Colombier 		" ntds %d %s speed -> spp %ld maxpkt %ld\n", s,
1081*a81c3ea0SDavid du Colombier 		ep->hz, ep->pollival, ep->ntds, spname[ep->dev->speed],
1082*a81c3ea0SDavid du Colombier 		spp, ep->maxpkt);
1083*a81c3ea0SDavid du Colombier 	if(ep->maxpkt > 1024){
1084*a81c3ea0SDavid du Colombier 		print("usb: %s: maxpkt %ld > 1024. truncating\n", s, ep->maxpkt);
1085*a81c3ea0SDavid du Colombier 		ep->maxpkt = 1024;
1086*a81c3ea0SDavid du Colombier 	}
1087*a81c3ea0SDavid du Colombier }
1088*a81c3ea0SDavid du Colombier 
1089*a81c3ea0SDavid du Colombier /*
1090*a81c3ea0SDavid du Colombier  * Many endpoint ctls. simply update the portable representation
1091*a81c3ea0SDavid du Colombier  * of the endpoint. The actual controller driver will look
1092*a81c3ea0SDavid du Colombier  * at them to setup the endpoints as dictated.
1093*a81c3ea0SDavid du Colombier  */
1094*a81c3ea0SDavid du Colombier static long
epctl(Ep * ep,Chan * c,void * a,long n)1095*a81c3ea0SDavid du Colombier epctl(Ep *ep, Chan *c, void *a, long n)
1096*a81c3ea0SDavid du Colombier {
1097*a81c3ea0SDavid du Colombier 	int i, l, mode, nb, tt;
1098*a81c3ea0SDavid du Colombier 	char *b, *s;
1099*a81c3ea0SDavid du Colombier 	Cmdbuf *cb;
1100*a81c3ea0SDavid du Colombier 	Cmdtab *ct;
1101*a81c3ea0SDavid du Colombier 	Ep *nep;
1102*a81c3ea0SDavid du Colombier 	Udev *d;
1103*a81c3ea0SDavid du Colombier 	static char *Info = "info ";
1104*a81c3ea0SDavid du Colombier 
1105*a81c3ea0SDavid du Colombier 	d = ep->dev;
1106*a81c3ea0SDavid du Colombier 
1107*a81c3ea0SDavid du Colombier 	cb = parsecmd(a, n);
1108*a81c3ea0SDavid du Colombier 	if(waserror()){
1109*a81c3ea0SDavid du Colombier 		free(cb);
1110*a81c3ea0SDavid du Colombier 		nexterror();
1111*a81c3ea0SDavid du Colombier 	}
1112*a81c3ea0SDavid du Colombier 	ct = lookupcmd(cb, epctls, nelem(epctls));
1113*a81c3ea0SDavid du Colombier 	if(ct == nil)
1114*a81c3ea0SDavid du Colombier 		error(Ebadctl);
1115*a81c3ea0SDavid du Colombier 	i = ct->index;
1116*a81c3ea0SDavid du Colombier 	if(i == CMnew || i == CMspeed || i == CMhub || i == CMpreset)
1117*a81c3ea0SDavid du Colombier 		if(ep != ep->ep0)
1118*a81c3ea0SDavid du Colombier 			error("allowed only on a setup endpoint");
1119*a81c3ea0SDavid du Colombier 	if(i != CMclrhalt && i != CMdetach && i != CMdebugep && i != CMname)
1120*a81c3ea0SDavid du Colombier 		if(ep != ep->ep0 && ep->inuse != 0)
1121*a81c3ea0SDavid du Colombier 			error("must configure before using");
1122*a81c3ea0SDavid du Colombier 	switch(i){
1123*a81c3ea0SDavid du Colombier 	case CMnew:
1124*a81c3ea0SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1125*a81c3ea0SDavid du Colombier 		nb = strtol(cb->f[1], nil, 0);
1126*a81c3ea0SDavid du Colombier 		if(nb < 0 || nb >= Ndeveps)
1127*a81c3ea0SDavid du Colombier 			error("bad endpoint number");
1128*a81c3ea0SDavid du Colombier 		tt = name2ttype(cb->f[2]);
1129*a81c3ea0SDavid du Colombier 		if(tt == Tnone)
1130*a81c3ea0SDavid du Colombier 			error("unknown endpoint type");
1131*a81c3ea0SDavid du Colombier 		mode = name2mode(cb->f[3]);
1132*a81c3ea0SDavid du Colombier 		if(mode < 0)
1133*a81c3ea0SDavid du Colombier 			error("unknown i/o mode");
1134*a81c3ea0SDavid du Colombier 		newdevep(ep, nb, tt, mode);
1135*a81c3ea0SDavid du Colombier 		break;
1136*a81c3ea0SDavid du Colombier 	case CMnewdev:
1137*a81c3ea0SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1138*a81c3ea0SDavid du Colombier 		if(ep != ep->ep0 || d->ishub == 0)
1139*a81c3ea0SDavid du Colombier 			error("not a hub setup endpoint");
1140*a81c3ea0SDavid du Colombier 		l = name2speed(cb->f[1]);
1141*a81c3ea0SDavid du Colombier 		if(l == Nospeed)
1142*a81c3ea0SDavid du Colombier 			error("speed must be full|low|high");
1143*a81c3ea0SDavid du Colombier 		nep = newdev(ep->hp, 0, 0);
1144*a81c3ea0SDavid du Colombier 		nep->dev->speed = l;
1145*a81c3ea0SDavid du Colombier 		if(nep->dev->speed  != Lowspeed)
1146*a81c3ea0SDavid du Colombier 			nep->maxpkt = 64;	/* assume full speed */
1147*a81c3ea0SDavid du Colombier 		nep->dev->hub = d->nb;
1148*a81c3ea0SDavid du Colombier 		nep->dev->port = atoi(cb->f[2]);
1149*a81c3ea0SDavid du Colombier 		/* next read request will read
1150*a81c3ea0SDavid du Colombier 		 * the name for the new endpoint
1151*a81c3ea0SDavid du Colombier 		 */
1152*a81c3ea0SDavid du Colombier 		l = sizeof(up->genbuf);
1153*a81c3ea0SDavid du Colombier 		snprint(up->genbuf, l, "ep%d.%d", nep->dev->nb, nep->nb);
1154*a81c3ea0SDavid du Colombier 		kstrdup(&c->aux, up->genbuf);
1155*a81c3ea0SDavid du Colombier 		break;
1156*a81c3ea0SDavid du Colombier 	case CMhub:
1157*a81c3ea0SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1158*a81c3ea0SDavid du Colombier 		d->ishub = 1;
1159*a81c3ea0SDavid du Colombier 		break;
1160*a81c3ea0SDavid du Colombier 	case CMspeed:
1161*a81c3ea0SDavid du Colombier 		l = name2speed(cb->f[1]);
1162*a81c3ea0SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
1163*a81c3ea0SDavid du Colombier 		if(l == Nospeed)
1164*a81c3ea0SDavid du Colombier 			error("speed must be full|low|high");
1165*a81c3ea0SDavid du Colombier 		qlock(ep->ep0);
1166*a81c3ea0SDavid du Colombier 		d->speed = l;
1167*a81c3ea0SDavid du Colombier 		qunlock(ep->ep0);
1168*a81c3ea0SDavid du Colombier 		break;
1169*a81c3ea0SDavid du Colombier 	case CMmaxpkt:
1170*a81c3ea0SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
1171*a81c3ea0SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
1172*a81c3ea0SDavid du Colombier 		if(l < 1 || l > 1024)
1173*a81c3ea0SDavid du Colombier 			error("maxpkt not in [1:1024]");
1174*a81c3ea0SDavid du Colombier 		qlock(ep);
1175*a81c3ea0SDavid du Colombier 		ep->maxpkt = l;
1176*a81c3ea0SDavid du Colombier 		qunlock(ep);
1177*a81c3ea0SDavid du Colombier 		break;
1178*a81c3ea0SDavid du Colombier 	case CMntds:
1179*a81c3ea0SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
1180*a81c3ea0SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
1181*a81c3ea0SDavid du Colombier 		if(l < 1 || l > 3)
1182*a81c3ea0SDavid du Colombier 			error("ntds not in [1:3]");
1183*a81c3ea0SDavid du Colombier 		qlock(ep);
1184*a81c3ea0SDavid du Colombier 		ep->ntds = l;
1185*a81c3ea0SDavid du Colombier 		qunlock(ep);
1186*a81c3ea0SDavid du Colombier 		break;
1187*a81c3ea0SDavid du Colombier 	case CMpollival:
1188*a81c3ea0SDavid du Colombier 		if(ep->ttype != Tintr && ep->ttype != Tiso)
1189*a81c3ea0SDavid du Colombier 			error("not an intr or iso endpoint");
1190*a81c3ea0SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
1191*a81c3ea0SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
1192*a81c3ea0SDavid du Colombier 		if(ep->ttype == Tiso ||
1193*a81c3ea0SDavid du Colombier 		   (ep->ttype == Tintr && ep->dev->speed == Highspeed)){
1194*a81c3ea0SDavid du Colombier 			if(l < 1 || l > 16)
1195*a81c3ea0SDavid du Colombier 				error("pollival power not in [1:16]");
1196*a81c3ea0SDavid du Colombier 			l = pow2(l-1);
1197*a81c3ea0SDavid du Colombier 		}else
1198*a81c3ea0SDavid du Colombier 			if(l < 1 || l > 255)
1199*a81c3ea0SDavid du Colombier 				error("pollival not in [1:255]");
1200*a81c3ea0SDavid du Colombier 		qlock(ep);
1201*a81c3ea0SDavid du Colombier 		ep->pollival = l;
1202*a81c3ea0SDavid du Colombier 		if(ep->ttype == Tiso)
1203*a81c3ea0SDavid du Colombier 			setmaxpkt(ep, "pollival");
1204*a81c3ea0SDavid du Colombier 		qunlock(ep);
1205*a81c3ea0SDavid du Colombier 		break;
1206*a81c3ea0SDavid du Colombier 	case CMsamplesz:
1207*a81c3ea0SDavid du Colombier 		if(ep->ttype != Tiso)
1208*a81c3ea0SDavid du Colombier 			error("not an iso endpoint");
1209*a81c3ea0SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
1210*a81c3ea0SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
1211*a81c3ea0SDavid du Colombier 		if(l <= 0 || l > 8)
1212*a81c3ea0SDavid du Colombier 			error("samplesz not in [1:8]");
1213*a81c3ea0SDavid du Colombier 		qlock(ep);
1214*a81c3ea0SDavid du Colombier 		ep->samplesz = l;
1215*a81c3ea0SDavid du Colombier 		setmaxpkt(ep, "samplesz");
1216*a81c3ea0SDavid du Colombier 		qunlock(ep);
1217*a81c3ea0SDavid du Colombier 		break;
1218*a81c3ea0SDavid du Colombier 	case CMhz:
1219*a81c3ea0SDavid du Colombier 		if(ep->ttype != Tiso)
1220*a81c3ea0SDavid du Colombier 			error("not an iso endpoint");
1221*a81c3ea0SDavid du Colombier 		l = strtoul(cb->f[1], nil, 0);
1222*a81c3ea0SDavid du Colombier 		deprint("usb epctl %s %d\n", cb->f[0], l);
1223*a81c3ea0SDavid du Colombier 		if(l <= 0 || l > 100000)
1224*a81c3ea0SDavid du Colombier 			error("hz not in [1:100000]");
1225*a81c3ea0SDavid du Colombier 		qlock(ep);
1226*a81c3ea0SDavid du Colombier 		ep->hz = l;
1227*a81c3ea0SDavid du Colombier 		setmaxpkt(ep, "hz");
1228*a81c3ea0SDavid du Colombier 		qunlock(ep);
1229*a81c3ea0SDavid du Colombier 		break;
1230*a81c3ea0SDavid du Colombier 	case CMclrhalt:
1231*a81c3ea0SDavid du Colombier 		qlock(ep);
1232*a81c3ea0SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1233*a81c3ea0SDavid du Colombier 		ep->clrhalt = 1;
1234*a81c3ea0SDavid du Colombier 		qunlock(ep);
1235*a81c3ea0SDavid du Colombier 		break;
1236*a81c3ea0SDavid du Colombier 	case CMinfo:
1237*a81c3ea0SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1238*a81c3ea0SDavid du Colombier 		l = strlen(Info);
1239*a81c3ea0SDavid du Colombier 		s = a;
1240*a81c3ea0SDavid du Colombier 		if(n < l+2 || strncmp(Info, s, l) != 0)
1241*a81c3ea0SDavid du Colombier 			error(Ebadctl);
1242*a81c3ea0SDavid du Colombier 		if(n > 1024)
1243*a81c3ea0SDavid du Colombier 			n = 1024;
1244*a81c3ea0SDavid du Colombier 		b = smalloc(n);
1245*a81c3ea0SDavid du Colombier 		memmove(b, s+l, n-l);
1246*a81c3ea0SDavid du Colombier 		b[n-l] = 0;
1247*a81c3ea0SDavid du Colombier 		if(b[n-l-1] == '\n')
1248*a81c3ea0SDavid du Colombier 			b[n-l-1] = 0;
1249*a81c3ea0SDavid du Colombier 		qlock(ep);
1250*a81c3ea0SDavid du Colombier 		free(ep->info);
1251*a81c3ea0SDavid du Colombier 		ep->info = b;
1252*a81c3ea0SDavid du Colombier 		qunlock(ep);
1253*a81c3ea0SDavid du Colombier 		break;
1254*a81c3ea0SDavid du Colombier 	case CMaddress:
1255*a81c3ea0SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1256*a81c3ea0SDavid du Colombier 		ep->dev->state = Denabled;
1257*a81c3ea0SDavid du Colombier 		break;
1258*a81c3ea0SDavid du Colombier 	case CMdetach:
1259*a81c3ea0SDavid du Colombier 		if(ep->dev->isroot != 0)
1260*a81c3ea0SDavid du Colombier 			error("can't detach a root hub");
1261*a81c3ea0SDavid du Colombier 		deprint("usb epctl %s ep%d.%d\n",
1262*a81c3ea0SDavid du Colombier 			cb->f[0], ep->dev->nb, ep->nb);
1263*a81c3ea0SDavid du Colombier 		ep->dev->state = Ddetach;
1264*a81c3ea0SDavid du Colombier 		/* Release file system ref. for its endpoints */
1265*a81c3ea0SDavid du Colombier 		for(i = 0; i < nelem(ep->dev->eps); i++)
1266*a81c3ea0SDavid du Colombier 			putep(ep->dev->eps[i]);
1267*a81c3ea0SDavid du Colombier 		break;
1268*a81c3ea0SDavid du Colombier 	case CMdebugep:
1269*a81c3ea0SDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0)
1270*a81c3ea0SDavid du Colombier 			ep->debug = 1;
1271*a81c3ea0SDavid du Colombier 		else if(strcmp(cb->f[1], "off") == 0)
1272*a81c3ea0SDavid du Colombier 			ep->debug = 0;
1273*a81c3ea0SDavid du Colombier 		else
1274*a81c3ea0SDavid du Colombier 			ep->debug = strtoul(cb->f[1], nil, 0);
1275*a81c3ea0SDavid du Colombier 		print("usb: ep%d.%d debug %d\n",
1276*a81c3ea0SDavid du Colombier 			ep->dev->nb, ep->nb, ep->debug);
1277*a81c3ea0SDavid du Colombier 		break;
1278*a81c3ea0SDavid du Colombier 	case CMname:
1279*a81c3ea0SDavid du Colombier 		deprint("usb epctl %s %s\n", cb->f[0], cb->f[1]);
1280*a81c3ea0SDavid du Colombier 		validname(cb->f[1], 0);
1281*a81c3ea0SDavid du Colombier 		kstrdup(&ep->name, cb->f[1]);
1282*a81c3ea0SDavid du Colombier 		break;
1283*a81c3ea0SDavid du Colombier 	case CMtmout:
1284*a81c3ea0SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1285*a81c3ea0SDavid du Colombier 		if(ep->ttype == Tiso || ep->ttype == Tctl)
1286*a81c3ea0SDavid du Colombier 			error("ctl ignored for this endpoint type");
1287*a81c3ea0SDavid du Colombier 		ep->tmout = strtoul(cb->f[1], nil, 0);
1288*a81c3ea0SDavid du Colombier 		if(ep->tmout != 0 && ep->tmout < Xfertmout)
1289*a81c3ea0SDavid du Colombier 			ep->tmout = Xfertmout;
1290*a81c3ea0SDavid du Colombier 		break;
1291*a81c3ea0SDavid du Colombier 	case CMpreset:
1292*a81c3ea0SDavid du Colombier 		deprint("usb epctl %s\n", cb->f[0]);
1293*a81c3ea0SDavid du Colombier 		if(ep->ttype != Tctl)
1294*a81c3ea0SDavid du Colombier 			error("not a control endpoint");
1295*a81c3ea0SDavid du Colombier 		if(ep->dev->state != Denabled)
1296*a81c3ea0SDavid du Colombier 			error("forbidden on devices not enabled");
1297*a81c3ea0SDavid du Colombier 		ep->dev->state = Dreset;
1298*a81c3ea0SDavid du Colombier 		break;
1299*a81c3ea0SDavid du Colombier 	default:
1300*a81c3ea0SDavid du Colombier 		panic("usb: unknown epctl %d", ct->index);
1301*a81c3ea0SDavid du Colombier 	}
1302*a81c3ea0SDavid du Colombier 	free(cb);
1303*a81c3ea0SDavid du Colombier 	poperror();
1304*a81c3ea0SDavid du Colombier 	return n;
1305*a81c3ea0SDavid du Colombier }
1306*a81c3ea0SDavid du Colombier 
1307*a81c3ea0SDavid du Colombier static long
usbctl(void * a,long n)1308*a81c3ea0SDavid du Colombier usbctl(void *a, long n)
1309*a81c3ea0SDavid du Colombier {
1310*a81c3ea0SDavid du Colombier 	Cmdtab *ct;
1311*a81c3ea0SDavid du Colombier 	Cmdbuf *cb;
1312*a81c3ea0SDavid du Colombier 	Ep *ep;
1313*a81c3ea0SDavid du Colombier 	int i;
1314*a81c3ea0SDavid du Colombier 
1315*a81c3ea0SDavid du Colombier 	cb = parsecmd(a, n);
1316*a81c3ea0SDavid du Colombier 	if(waserror()){
1317*a81c3ea0SDavid du Colombier 		free(cb);
1318*a81c3ea0SDavid du Colombier 		nexterror();
1319*a81c3ea0SDavid du Colombier 	}
1320*a81c3ea0SDavid du Colombier 	ct = lookupcmd(cb, usbctls, nelem(usbctls));
1321*a81c3ea0SDavid du Colombier 	dprint("usb ctl %s\n", cb->f[0]);
1322*a81c3ea0SDavid du Colombier 	switch(ct->index){
1323*a81c3ea0SDavid du Colombier 	case CMdebug:
1324*a81c3ea0SDavid du Colombier 		if(strcmp(cb->f[1], "on") == 0)
1325*a81c3ea0SDavid du Colombier 			debug = 1;
1326*a81c3ea0SDavid du Colombier 		else if(strcmp(cb->f[1], "off") == 0)
1327*a81c3ea0SDavid du Colombier 			debug = 0;
1328*a81c3ea0SDavid du Colombier 		else
1329*a81c3ea0SDavid du Colombier 			debug = strtol(cb->f[1], nil, 0);
1330*a81c3ea0SDavid du Colombier 		print("usb: debug %d\n", debug);
1331*a81c3ea0SDavid du Colombier 		for(i = 0; i < epmax; i++)
1332*a81c3ea0SDavid du Colombier 			if((ep = getep(i)) != nil){
1333*a81c3ea0SDavid du Colombier 				ep->hp->debug(ep->hp, debug);
1334*a81c3ea0SDavid du Colombier 				putep(ep);
1335*a81c3ea0SDavid du Colombier 			}
1336*a81c3ea0SDavid du Colombier 		break;
1337*a81c3ea0SDavid du Colombier 	case CMdump:
1338*a81c3ea0SDavid du Colombier 		dumpeps();
1339*a81c3ea0SDavid du Colombier 		break;
1340*a81c3ea0SDavid du Colombier 	}
1341*a81c3ea0SDavid du Colombier 	free(cb);
1342*a81c3ea0SDavid du Colombier 	poperror();
1343*a81c3ea0SDavid du Colombier 	return n;
1344*a81c3ea0SDavid du Colombier }
1345*a81c3ea0SDavid du Colombier 
1346*a81c3ea0SDavid du Colombier static long
ctlwrite(Chan * c,void * a,long n)1347*a81c3ea0SDavid du Colombier ctlwrite(Chan *c, void *a, long n)
1348*a81c3ea0SDavid du Colombier {
1349*a81c3ea0SDavid du Colombier 	int q;
1350*a81c3ea0SDavid du Colombier 	Ep *ep;
1351*a81c3ea0SDavid du Colombier 
1352*a81c3ea0SDavid du Colombier 	q = QID(c->qid);
1353*a81c3ea0SDavid du Colombier 	if(q == Qctl)
1354*a81c3ea0SDavid du Colombier 		return usbctl(a, n);
1355*a81c3ea0SDavid du Colombier 
1356*a81c3ea0SDavid du Colombier 	ep = getep(qid2epidx(q));
1357*a81c3ea0SDavid du Colombier 	if(ep == nil)
1358*a81c3ea0SDavid du Colombier 		error(Eio);
1359*a81c3ea0SDavid du Colombier 	if(waserror()){
1360*a81c3ea0SDavid du Colombier 		putep(ep);
1361*a81c3ea0SDavid du Colombier 		nexterror();
1362*a81c3ea0SDavid du Colombier 	}
1363*a81c3ea0SDavid du Colombier 	if(ep->dev->state == Ddetach)
1364*a81c3ea0SDavid du Colombier 		error(Edetach);
1365*a81c3ea0SDavid du Colombier 	if(isqtype(q, Qepctl) && c->aux != nil){
1366*a81c3ea0SDavid du Colombier 		/* Be sure we don't keep a cloned ep name */
1367*a81c3ea0SDavid du Colombier 		free(c->aux);
1368*a81c3ea0SDavid du Colombier 		c->aux = nil;
1369*a81c3ea0SDavid du Colombier 		error("read, not write, expected");
1370*a81c3ea0SDavid du Colombier 	}
1371*a81c3ea0SDavid du Colombier 	n = epctl(ep, c, a, n);
1372*a81c3ea0SDavid du Colombier 	putep(ep);
1373*a81c3ea0SDavid du Colombier 	poperror();
1374*a81c3ea0SDavid du Colombier 	return n;
1375*a81c3ea0SDavid du Colombier }
1376*a81c3ea0SDavid du Colombier 
1377*a81c3ea0SDavid du Colombier static long
usbwrite(Chan * c,void * a,long n,vlong off)1378*a81c3ea0SDavid du Colombier usbwrite(Chan *c, void *a, long n, vlong off)
1379*a81c3ea0SDavid du Colombier {
1380*a81c3ea0SDavid du Colombier 	int nr, q;
1381*a81c3ea0SDavid du Colombier 	Ep *ep;
1382*a81c3ea0SDavid du Colombier 
1383*a81c3ea0SDavid du Colombier 	if(c->qid.type == QTDIR)
1384*a81c3ea0SDavid du Colombier 		error(Eisdir);
1385*a81c3ea0SDavid du Colombier 
1386*a81c3ea0SDavid du Colombier 	q = QID(c->qid);
1387*a81c3ea0SDavid du Colombier 
1388*a81c3ea0SDavid du Colombier 	if(q == Qctl || isqtype(q, Qepctl))
1389*a81c3ea0SDavid du Colombier 		return ctlwrite(c, a, n);
1390*a81c3ea0SDavid du Colombier 
1391*a81c3ea0SDavid du Colombier 	ep = getep(qid2epidx(q));
1392*a81c3ea0SDavid du Colombier 	if(ep == nil)
1393*a81c3ea0SDavid du Colombier 		error(Eio);
1394*a81c3ea0SDavid du Colombier 	if(waserror()){
1395*a81c3ea0SDavid du Colombier 		putep(ep);
1396*a81c3ea0SDavid du Colombier 		nexterror();
1397*a81c3ea0SDavid du Colombier 	}
1398*a81c3ea0SDavid du Colombier 	if(ep->dev->state == Ddetach)
1399*a81c3ea0SDavid du Colombier 		error(Edetach);
1400*a81c3ea0SDavid du Colombier 	if(ep->mode == OREAD || ep->inuse == 0)
1401*a81c3ea0SDavid du Colombier 		error(Ebadusefd);
1402*a81c3ea0SDavid du Colombier 
1403*a81c3ea0SDavid du Colombier 	switch(ep->ttype){
1404*a81c3ea0SDavid du Colombier 	case Tnone:
1405*a81c3ea0SDavid du Colombier 		error("endpoint not configured");
1406*a81c3ea0SDavid du Colombier 	case Tctl:
1407*a81c3ea0SDavid du Colombier 		nr = rhubwrite(ep, a, n);
1408*a81c3ea0SDavid du Colombier 		if(nr >= 0){
1409*a81c3ea0SDavid du Colombier 			n = nr;
1410*a81c3ea0SDavid du Colombier 			break;
1411*a81c3ea0SDavid du Colombier 		}
1412*a81c3ea0SDavid du Colombier 		/* else fall */
1413*a81c3ea0SDavid du Colombier 	default:
1414*a81c3ea0SDavid du Colombier 		ddeprint("\nusbwrite q %#x fid %d cnt %ld off %lld\n",q, c->fid, n, off);
1415*a81c3ea0SDavid du Colombier 		ep->hp->epwrite(ep, a, n);
1416*a81c3ea0SDavid du Colombier 	}
1417*a81c3ea0SDavid du Colombier 	putep(ep);
1418*a81c3ea0SDavid du Colombier 	poperror();
1419*a81c3ea0SDavid du Colombier 	return n;
1420*a81c3ea0SDavid du Colombier }
1421*a81c3ea0SDavid du Colombier 
1422*a81c3ea0SDavid du Colombier void
usbshutdown(void)1423*a81c3ea0SDavid du Colombier usbshutdown(void)
1424*a81c3ea0SDavid du Colombier {
1425*a81c3ea0SDavid du Colombier 	Hci *hp;
1426*a81c3ea0SDavid du Colombier 	int i;
1427*a81c3ea0SDavid du Colombier 
1428*a81c3ea0SDavid du Colombier 	for(i = 0; i < Nhcis; i++){
1429*a81c3ea0SDavid du Colombier 		hp = hcis[i];
1430*a81c3ea0SDavid du Colombier 		if(hp == nil)
1431*a81c3ea0SDavid du Colombier 			continue;
1432*a81c3ea0SDavid du Colombier 		if(hp->shutdown == nil)
1433*a81c3ea0SDavid du Colombier 			print("#u: no shutdown function for %s\n", hp->type);
1434*a81c3ea0SDavid du Colombier 		else
1435*a81c3ea0SDavid du Colombier 			hp->shutdown(hp);
1436*a81c3ea0SDavid du Colombier 	}
1437*a81c3ea0SDavid du Colombier }
1438*a81c3ea0SDavid du Colombier 
1439*a81c3ea0SDavid du Colombier Dev usbdevtab = {
1440*a81c3ea0SDavid du Colombier 	L'u',
1441*a81c3ea0SDavid du Colombier 	"usb",
1442*a81c3ea0SDavid du Colombier 
1443*a81c3ea0SDavid du Colombier 	usbreset,
1444*a81c3ea0SDavid du Colombier 	usbinit,
1445*a81c3ea0SDavid du Colombier 	usbshutdown,
1446*a81c3ea0SDavid du Colombier 	usbattach,
1447*a81c3ea0SDavid du Colombier 	usbwalk,
1448*a81c3ea0SDavid du Colombier 	usbstat,
1449*a81c3ea0SDavid du Colombier 	usbopen,
1450*a81c3ea0SDavid du Colombier 	devcreate,
1451*a81c3ea0SDavid du Colombier 	usbclose,
1452*a81c3ea0SDavid du Colombier 	usbread,
1453*a81c3ea0SDavid du Colombier 	devbread,
1454*a81c3ea0SDavid du Colombier 	usbwrite,
1455*a81c3ea0SDavid du Colombier 	devbwrite,
1456*a81c3ea0SDavid du Colombier 	devremove,
1457*a81c3ea0SDavid du Colombier 	devwstat,
1458*a81c3ea0SDavid du Colombier };
1459