xref: /inferno-os/os/pc/devusb.c (revision 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a)
1*74a4d8c2SCharles.Forsyth #include	"u.h"
2*74a4d8c2SCharles.Forsyth #include	"../port/lib.h"
3*74a4d8c2SCharles.Forsyth #include	"mem.h"
4*74a4d8c2SCharles.Forsyth #include	"dat.h"
5*74a4d8c2SCharles.Forsyth #include	"fns.h"
6*74a4d8c2SCharles.Forsyth #include	"io.h"
7*74a4d8c2SCharles.Forsyth #include	"../port/error.h"
8*74a4d8c2SCharles.Forsyth 
9*74a4d8c2SCharles.Forsyth #include	"usb.h"
10*74a4d8c2SCharles.Forsyth 
11*74a4d8c2SCharles.Forsyth static int debug = 0;
12*74a4d8c2SCharles.Forsyth 
13*74a4d8c2SCharles.Forsyth #define Chatty	1
14*74a4d8c2SCharles.Forsyth #define DPRINT if(Chatty)print
15*74a4d8c2SCharles.Forsyth #define XPRINT if(debug)iprint
16*74a4d8c2SCharles.Forsyth 
17*74a4d8c2SCharles.Forsyth Usbhost*	usbhost[MaxUsb];
18*74a4d8c2SCharles.Forsyth 
19*74a4d8c2SCharles.Forsyth static char *devstates[] = {
20*74a4d8c2SCharles.Forsyth 	[Disabled]		"Disabled",
21*74a4d8c2SCharles.Forsyth 	[Attached]	"Attached",
22*74a4d8c2SCharles.Forsyth 	[Enabled]		"Enabled",
23*74a4d8c2SCharles.Forsyth 	[Assigned]	"Assigned",
24*74a4d8c2SCharles.Forsyth 	[Configured]	"Configured",
25*74a4d8c2SCharles.Forsyth };
26*74a4d8c2SCharles.Forsyth 
27*74a4d8c2SCharles.Forsyth static	char	Ebadusbmsg[] = "invalid parameters to USB ctl message";
28*74a4d8c2SCharles.Forsyth 
29*74a4d8c2SCharles.Forsyth enum
30*74a4d8c2SCharles.Forsyth {
31*74a4d8c2SCharles.Forsyth 	Qtopdir = 0,
32*74a4d8c2SCharles.Forsyth 	Q2nd,
33*74a4d8c2SCharles.Forsyth 	Qnew,
34*74a4d8c2SCharles.Forsyth 	Qport,
35*74a4d8c2SCharles.Forsyth 	Q3rd,
36*74a4d8c2SCharles.Forsyth 	Qctl,
37*74a4d8c2SCharles.Forsyth 	Qstatus,
38*74a4d8c2SCharles.Forsyth 	Qep0,
39*74a4d8c2SCharles.Forsyth 	/* other endpoint files */
40*74a4d8c2SCharles.Forsyth };
41*74a4d8c2SCharles.Forsyth 
42*74a4d8c2SCharles.Forsyth /*
43*74a4d8c2SCharles.Forsyth  * Qid path is:
44*74a4d8c2SCharles.Forsyth  *	8 bits of file type (qids above)
45*74a4d8c2SCharles.Forsyth  *	8 bits of slot number; default address 0 used for per-controller files
46*74a4d8c2SCharles.Forsyth  *	4 bits of controller number
47*74a4d8c2SCharles.Forsyth  */
48*74a4d8c2SCharles.Forsyth enum {
49*74a4d8c2SCharles.Forsyth 	TYPEBITS	= 8,
50*74a4d8c2SCharles.Forsyth 	SLOTBITS	= 8,
51*74a4d8c2SCharles.Forsyth 	CTLRBITS	= 4,
52*74a4d8c2SCharles.Forsyth 
53*74a4d8c2SCharles.Forsyth 	SLOTSHIFT	= TYPEBITS,
54*74a4d8c2SCharles.Forsyth 	CTLRSHIFT	= SLOTSHIFT+SLOTBITS,
55*74a4d8c2SCharles.Forsyth 
56*74a4d8c2SCharles.Forsyth 	TYPEMASK	= (1<<TYPEBITS)-1,
57*74a4d8c2SCharles.Forsyth 	SLOTMASK	= (1<<SLOTBITS)-1,
58*74a4d8c2SCharles.Forsyth 	CTLRMASK	= (1<<CTLRBITS)-1,
59*74a4d8c2SCharles.Forsyth };
60*74a4d8c2SCharles.Forsyth 
61*74a4d8c2SCharles.Forsyth #define	TYPE(q)		(((ulong)(q).path)&TYPEMASK)
62*74a4d8c2SCharles.Forsyth #define	SLOT(q)		((((ulong)(q).path)>>SLOTSHIFT)&SLOTMASK)
63*74a4d8c2SCharles.Forsyth #define	CTLR(q)		((((ulong)(q).path)>>CTLRSHIFT)&CTLRMASK)
64*74a4d8c2SCharles.Forsyth #define	PATH(t, s, c)	((t)|((s)<<SLOTSHIFT)|((c)<<CTLRSHIFT))
65*74a4d8c2SCharles.Forsyth 
66*74a4d8c2SCharles.Forsyth static Dirtab usbdir2[] = {
67*74a4d8c2SCharles.Forsyth 	"new",	{Qnew},			0,	0666,
68*74a4d8c2SCharles.Forsyth 	"port",	{Qport},			0,	0666,
69*74a4d8c2SCharles.Forsyth };
70*74a4d8c2SCharles.Forsyth 
71*74a4d8c2SCharles.Forsyth static Dirtab usbdir3[]={
72*74a4d8c2SCharles.Forsyth 	"ctl",		{Qctl},			0,	0666,
73*74a4d8c2SCharles.Forsyth 	"status",	{Qstatus},			0,	0444,
74*74a4d8c2SCharles.Forsyth 	"setup",	{Qep0},			0,	0666,
75*74a4d8c2SCharles.Forsyth 	/* epNdata names are generated on demand */
76*74a4d8c2SCharles.Forsyth };
77*74a4d8c2SCharles.Forsyth 
78*74a4d8c2SCharles.Forsyth enum
79*74a4d8c2SCharles.Forsyth {
80*74a4d8c2SCharles.Forsyth 	PMdisable,
81*74a4d8c2SCharles.Forsyth 	PMenable,
82*74a4d8c2SCharles.Forsyth 	PMreset,
83*74a4d8c2SCharles.Forsyth };
84*74a4d8c2SCharles.Forsyth 
85*74a4d8c2SCharles.Forsyth enum
86*74a4d8c2SCharles.Forsyth {
87*74a4d8c2SCharles.Forsyth 	CMclass,
88*74a4d8c2SCharles.Forsyth 	CMdata,
89*74a4d8c2SCharles.Forsyth 	CMdebug,
90*74a4d8c2SCharles.Forsyth 	CMep,
91*74a4d8c2SCharles.Forsyth 	CMmaxpkt,
92*74a4d8c2SCharles.Forsyth 	CMadjust,
93*74a4d8c2SCharles.Forsyth 	CMspeed,
94*74a4d8c2SCharles.Forsyth 	CMunstall,
95*74a4d8c2SCharles.Forsyth };
96*74a4d8c2SCharles.Forsyth 
97*74a4d8c2SCharles.Forsyth static Cmdtab usbportmsg[] =
98*74a4d8c2SCharles.Forsyth {
99*74a4d8c2SCharles.Forsyth 	PMdisable,	"disable",	2,
100*74a4d8c2SCharles.Forsyth 	PMenable,		"enable",	2,
101*74a4d8c2SCharles.Forsyth 	PMreset,		"reset",	2,
102*74a4d8c2SCharles.Forsyth };
103*74a4d8c2SCharles.Forsyth 
104*74a4d8c2SCharles.Forsyth static Cmdtab usbctlmsg[] =
105*74a4d8c2SCharles.Forsyth {
106*74a4d8c2SCharles.Forsyth 	CMclass,		"class",	0,
107*74a4d8c2SCharles.Forsyth 	CMdata,		"data",	3,
108*74a4d8c2SCharles.Forsyth 	CMdebug,		"debug",	3,
109*74a4d8c2SCharles.Forsyth 	CMep,		"ep",		6,
110*74a4d8c2SCharles.Forsyth 	CMmaxpkt,	"maxpkt",	3,
111*74a4d8c2SCharles.Forsyth 	CMadjust,		"adjust",	3,
112*74a4d8c2SCharles.Forsyth 	CMspeed,		"speed",	2,
113*74a4d8c2SCharles.Forsyth 	CMunstall,	"unstall",	2,
114*74a4d8c2SCharles.Forsyth };
115*74a4d8c2SCharles.Forsyth 
116*74a4d8c2SCharles.Forsyth static struct
117*74a4d8c2SCharles.Forsyth {
118*74a4d8c2SCharles.Forsyth 	char*	type;
119*74a4d8c2SCharles.Forsyth 	int	(*reset)(Usbhost*);
120*74a4d8c2SCharles.Forsyth } usbtypes[MaxUsb+1];
121*74a4d8c2SCharles.Forsyth 
122*74a4d8c2SCharles.Forsyth void
addusbtype(char * t,int (* r)(Usbhost *))123*74a4d8c2SCharles.Forsyth addusbtype(char* t, int (*r)(Usbhost*))
124*74a4d8c2SCharles.Forsyth {
125*74a4d8c2SCharles.Forsyth 	static int ntype;
126*74a4d8c2SCharles.Forsyth 
127*74a4d8c2SCharles.Forsyth 	if(ntype == MaxUsb)
128*74a4d8c2SCharles.Forsyth 		panic("too many USB host interface types");
129*74a4d8c2SCharles.Forsyth 	usbtypes[ntype].type = t;
130*74a4d8c2SCharles.Forsyth 	usbtypes[ntype].reset = r;
131*74a4d8c2SCharles.Forsyth 	ntype++;
132*74a4d8c2SCharles.Forsyth }
133*74a4d8c2SCharles.Forsyth 
134*74a4d8c2SCharles.Forsyth static Udev*
usbdeviceofslot(Usbhost * uh,int s)135*74a4d8c2SCharles.Forsyth usbdeviceofslot(Usbhost *uh, int s)
136*74a4d8c2SCharles.Forsyth {
137*74a4d8c2SCharles.Forsyth 	if(s < 0 || s > nelem(uh->dev))
138*74a4d8c2SCharles.Forsyth 		return nil;
139*74a4d8c2SCharles.Forsyth 	return uh->dev[s];
140*74a4d8c2SCharles.Forsyth }
141*74a4d8c2SCharles.Forsyth 
142*74a4d8c2SCharles.Forsyth static Udev*
usbdevice(Chan * c)143*74a4d8c2SCharles.Forsyth usbdevice(Chan *c)
144*74a4d8c2SCharles.Forsyth {
145*74a4d8c2SCharles.Forsyth 	int bus;
146*74a4d8c2SCharles.Forsyth 	Udev *d;
147*74a4d8c2SCharles.Forsyth 	Usbhost *uh;
148*74a4d8c2SCharles.Forsyth 
149*74a4d8c2SCharles.Forsyth 	bus = CTLR(c->qid);
150*74a4d8c2SCharles.Forsyth 	if(bus > nelem(usbhost) || (uh = usbhost[bus]) == nil) {
151*74a4d8c2SCharles.Forsyth 		error(Egreg);
152*74a4d8c2SCharles.Forsyth 		return nil;		/* for compiler */
153*74a4d8c2SCharles.Forsyth 	}
154*74a4d8c2SCharles.Forsyth 	d = usbdeviceofslot(uh, SLOT(c->qid));
155*74a4d8c2SCharles.Forsyth 	if(d == nil || d->id != c->qid.vers || d->state == Disabled)
156*74a4d8c2SCharles.Forsyth 		error(Ehungup);
157*74a4d8c2SCharles.Forsyth 	return d;
158*74a4d8c2SCharles.Forsyth }
159*74a4d8c2SCharles.Forsyth 
160*74a4d8c2SCharles.Forsyth static Endpt *
devendpt(Udev * d,int id,int add)161*74a4d8c2SCharles.Forsyth devendpt(Udev *d, int id, int add)
162*74a4d8c2SCharles.Forsyth {
163*74a4d8c2SCharles.Forsyth 	Usbhost *uh;
164*74a4d8c2SCharles.Forsyth 	Endpt *e, **p;
165*74a4d8c2SCharles.Forsyth 
166*74a4d8c2SCharles.Forsyth 	p = &d->ep[id&0xF];
167*74a4d8c2SCharles.Forsyth 	lock(d);
168*74a4d8c2SCharles.Forsyth 	e = *p;
169*74a4d8c2SCharles.Forsyth 	if(e != nil){
170*74a4d8c2SCharles.Forsyth 		incref(e);
171*74a4d8c2SCharles.Forsyth 		XPRINT("incref(0x%p) in devendpt, new value %ld\n", e, e->ref);
172*74a4d8c2SCharles.Forsyth 		unlock(d);
173*74a4d8c2SCharles.Forsyth 		return e;
174*74a4d8c2SCharles.Forsyth 	}
175*74a4d8c2SCharles.Forsyth 	unlock(d);
176*74a4d8c2SCharles.Forsyth 	if(!add)
177*74a4d8c2SCharles.Forsyth 		return nil;
178*74a4d8c2SCharles.Forsyth 
179*74a4d8c2SCharles.Forsyth 	e = mallocz(sizeof(*e), 1);
180*74a4d8c2SCharles.Forsyth 	e->ref = 1;
181*74a4d8c2SCharles.Forsyth 	e->x = id&0xF;
182*74a4d8c2SCharles.Forsyth 	e->id = id;
183*74a4d8c2SCharles.Forsyth 	e->sched = -1;
184*74a4d8c2SCharles.Forsyth 	e->maxpkt = 8;
185*74a4d8c2SCharles.Forsyth 	e->nbuf = 1;
186*74a4d8c2SCharles.Forsyth 	e->dev = d;
187*74a4d8c2SCharles.Forsyth 	e->active = 0;
188*74a4d8c2SCharles.Forsyth 
189*74a4d8c2SCharles.Forsyth 	uh = d->uh;
190*74a4d8c2SCharles.Forsyth 	uh->epalloc(uh, e);
191*74a4d8c2SCharles.Forsyth 
192*74a4d8c2SCharles.Forsyth 	lock(d);
193*74a4d8c2SCharles.Forsyth 	if(*p != nil){
194*74a4d8c2SCharles.Forsyth 		incref(*p);
195*74a4d8c2SCharles.Forsyth 		XPRINT("incref(0x%p) in devendpt, new value %ld\n", *p, (*p)->ref);
196*74a4d8c2SCharles.Forsyth 		unlock(d);
197*74a4d8c2SCharles.Forsyth 		uh->epfree(uh, e);
198*74a4d8c2SCharles.Forsyth 		free(e);
199*74a4d8c2SCharles.Forsyth 		return *p;
200*74a4d8c2SCharles.Forsyth 	}
201*74a4d8c2SCharles.Forsyth 	*p = e;
202*74a4d8c2SCharles.Forsyth 	unlock(d);
203*74a4d8c2SCharles.Forsyth 	e->rq = qopen(8*1024, 0, nil, e);
204*74a4d8c2SCharles.Forsyth 	e->wq = qopen(8*1024, 0, nil, e);
205*74a4d8c2SCharles.Forsyth 	return e;
206*74a4d8c2SCharles.Forsyth }
207*74a4d8c2SCharles.Forsyth 
208*74a4d8c2SCharles.Forsyth static void
freept(Endpt * e)209*74a4d8c2SCharles.Forsyth freept(Endpt *e)
210*74a4d8c2SCharles.Forsyth {
211*74a4d8c2SCharles.Forsyth 	Usbhost *uh;
212*74a4d8c2SCharles.Forsyth 
213*74a4d8c2SCharles.Forsyth 	if(e != nil && decref(e) == 0){
214*74a4d8c2SCharles.Forsyth 		XPRINT("freept(%d,%d)\n", e->dev->x, e->x);
215*74a4d8c2SCharles.Forsyth 		uh = e->dev->uh;
216*74a4d8c2SCharles.Forsyth 		uh->epclose(uh, e);
217*74a4d8c2SCharles.Forsyth 		e->dev->ep[e->x] = nil;
218*74a4d8c2SCharles.Forsyth 		uh->epfree(uh, e);
219*74a4d8c2SCharles.Forsyth 		free(e);
220*74a4d8c2SCharles.Forsyth 	}
221*74a4d8c2SCharles.Forsyth }
222*74a4d8c2SCharles.Forsyth 
223*74a4d8c2SCharles.Forsyth static Udev*
usbnewdevice(Usbhost * uh)224*74a4d8c2SCharles.Forsyth usbnewdevice(Usbhost *uh)
225*74a4d8c2SCharles.Forsyth {
226*74a4d8c2SCharles.Forsyth 	int i;
227*74a4d8c2SCharles.Forsyth 	Udev *d;
228*74a4d8c2SCharles.Forsyth 	Endpt *e;
229*74a4d8c2SCharles.Forsyth 
230*74a4d8c2SCharles.Forsyth 	d = nil;
231*74a4d8c2SCharles.Forsyth 	qlock(uh);
232*74a4d8c2SCharles.Forsyth 	if(waserror()){
233*74a4d8c2SCharles.Forsyth 		qunlock(uh);
234*74a4d8c2SCharles.Forsyth 		nexterror();
235*74a4d8c2SCharles.Forsyth 	}
236*74a4d8c2SCharles.Forsyth 	for(i=0; i<nelem(uh->dev); i++)
237*74a4d8c2SCharles.Forsyth 		if(uh->dev[i] == nil){
238*74a4d8c2SCharles.Forsyth 			uh->idgen++;
239*74a4d8c2SCharles.Forsyth 			d = mallocz(sizeof(*d), 1);
240*74a4d8c2SCharles.Forsyth 			d->uh = uh;
241*74a4d8c2SCharles.Forsyth 			d->ref = 1;
242*74a4d8c2SCharles.Forsyth 			d->x = i;
243*74a4d8c2SCharles.Forsyth 			d->id = (uh->idgen << 8) | i;
244*74a4d8c2SCharles.Forsyth 			d->state = Enabled;
245*74a4d8c2SCharles.Forsyth 			XPRINT("calling devendpt in usbnewdevice\n");
246*74a4d8c2SCharles.Forsyth 			e = devendpt(d, 0, 1);	/* always provide control endpoint 0 */
247*74a4d8c2SCharles.Forsyth 			e->mode = ORDWR;
248*74a4d8c2SCharles.Forsyth 			e->iso = 0;
249*74a4d8c2SCharles.Forsyth 			e->sched = -1;
250*74a4d8c2SCharles.Forsyth 			uh->dev[i] = d;
251*74a4d8c2SCharles.Forsyth 			break;
252*74a4d8c2SCharles.Forsyth 		}
253*74a4d8c2SCharles.Forsyth 	poperror();
254*74a4d8c2SCharles.Forsyth 	qunlock(uh);
255*74a4d8c2SCharles.Forsyth 	return d;
256*74a4d8c2SCharles.Forsyth }
257*74a4d8c2SCharles.Forsyth 
258*74a4d8c2SCharles.Forsyth static void
freedev(Udev * d,int ept)259*74a4d8c2SCharles.Forsyth freedev(Udev *d, int ept)
260*74a4d8c2SCharles.Forsyth {
261*74a4d8c2SCharles.Forsyth 	int i;
262*74a4d8c2SCharles.Forsyth 	Endpt *e;
263*74a4d8c2SCharles.Forsyth 	Usbhost *uh;
264*74a4d8c2SCharles.Forsyth 
265*74a4d8c2SCharles.Forsyth 	uh = d->uh;
266*74a4d8c2SCharles.Forsyth 	if(decref(d) == 0){
267*74a4d8c2SCharles.Forsyth 		XPRINT("freedev 0x%p, 0\n", d);
268*74a4d8c2SCharles.Forsyth 		for(i=0; i<nelem(d->ep); i++)
269*74a4d8c2SCharles.Forsyth 			freept(d->ep[i]);
270*74a4d8c2SCharles.Forsyth 		if(d->x >= 0)
271*74a4d8c2SCharles.Forsyth 			uh->dev[d->x] = nil;
272*74a4d8c2SCharles.Forsyth 		free(d);
273*74a4d8c2SCharles.Forsyth 	} else {
274*74a4d8c2SCharles.Forsyth 		if(ept >= 0 && ept < nelem(d->ep)){
275*74a4d8c2SCharles.Forsyth 			e = d->ep[ept];
276*74a4d8c2SCharles.Forsyth 			XPRINT("freedev, freept 0x%p\n", e);
277*74a4d8c2SCharles.Forsyth 			if(e != nil)
278*74a4d8c2SCharles.Forsyth 				uh->epclose(uh, e);
279*74a4d8c2SCharles.Forsyth 		}
280*74a4d8c2SCharles.Forsyth 	}
281*74a4d8c2SCharles.Forsyth }
282*74a4d8c2SCharles.Forsyth 
283*74a4d8c2SCharles.Forsyth static int
usbgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)284*74a4d8c2SCharles.Forsyth usbgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
285*74a4d8c2SCharles.Forsyth {
286*74a4d8c2SCharles.Forsyth 	Qid q;
287*74a4d8c2SCharles.Forsyth 	Udev *d;
288*74a4d8c2SCharles.Forsyth 	Endpt *e;
289*74a4d8c2SCharles.Forsyth 	Dirtab *tab;
290*74a4d8c2SCharles.Forsyth 	Usbhost *uh;
291*74a4d8c2SCharles.Forsyth 	int t, bus, slot, perm;
292*74a4d8c2SCharles.Forsyth 
293*74a4d8c2SCharles.Forsyth 	/*
294*74a4d8c2SCharles.Forsyth 	 * Top level directory contains the controller names.
295*74a4d8c2SCharles.Forsyth 	 */
296*74a4d8c2SCharles.Forsyth 	if(c->qid.path == Qtopdir){
297*74a4d8c2SCharles.Forsyth 		if(s == DEVDOTDOT){
298*74a4d8c2SCharles.Forsyth 			mkqid(&q, Qtopdir, 0, QTDIR);
299*74a4d8c2SCharles.Forsyth 			devdir(c, q, "#U", 0, eve, 0555, dp);
300*74a4d8c2SCharles.Forsyth 			return 1;
301*74a4d8c2SCharles.Forsyth 		}
302*74a4d8c2SCharles.Forsyth 		if(s >= nelem(usbhost) || usbhost[s] == nil)
303*74a4d8c2SCharles.Forsyth 			return -1;
304*74a4d8c2SCharles.Forsyth 		mkqid(&q, PATH(Q2nd, 0, s), 0, QTDIR);
305*74a4d8c2SCharles.Forsyth 		snprint(up->genbuf, sizeof up->genbuf, "usb%d", s);
306*74a4d8c2SCharles.Forsyth 		devdir(c, q, up->genbuf, 0, eve, 0555, dp);
307*74a4d8c2SCharles.Forsyth 		return 1;
308*74a4d8c2SCharles.Forsyth 	}
309*74a4d8c2SCharles.Forsyth 	bus = CTLR(c->qid);
310*74a4d8c2SCharles.Forsyth 	if(bus >= nelem(usbhost) || (uh = usbhost[bus]) == nil)
311*74a4d8c2SCharles.Forsyth 			return -1;
312*74a4d8c2SCharles.Forsyth 
313*74a4d8c2SCharles.Forsyth 	/*
314*74a4d8c2SCharles.Forsyth 	 * Second level contains "new", "port", and a numbered
315*74a4d8c2SCharles.Forsyth 	 * directory for each enumerated device on the bus.
316*74a4d8c2SCharles.Forsyth 	 */
317*74a4d8c2SCharles.Forsyth 	t = TYPE(c->qid);
318*74a4d8c2SCharles.Forsyth 	if(t < Q3rd){
319*74a4d8c2SCharles.Forsyth 		if(s == DEVDOTDOT){
320*74a4d8c2SCharles.Forsyth 			mkqid(&q, Qtopdir, 0, QTDIR);
321*74a4d8c2SCharles.Forsyth 			devdir(c, q, "#U", 0, eve, 0555, dp);
322*74a4d8c2SCharles.Forsyth 			return 1;
323*74a4d8c2SCharles.Forsyth 		}
324*74a4d8c2SCharles.Forsyth 		if(s < nelem(usbdir2)){
325*74a4d8c2SCharles.Forsyth 			d = uh->dev[0];
326*74a4d8c2SCharles.Forsyth 			if(d == nil)
327*74a4d8c2SCharles.Forsyth 				return -1;
328*74a4d8c2SCharles.Forsyth 			tab = &usbdir2[s];
329*74a4d8c2SCharles.Forsyth 			mkqid(&q, PATH(tab->qid.path, 0, bus), d->id, QTFILE);
330*74a4d8c2SCharles.Forsyth 			devdir(c, q, tab->name, tab->length, eve, tab->perm, dp);
331*74a4d8c2SCharles.Forsyth 			return 1;
332*74a4d8c2SCharles.Forsyth 		}
333*74a4d8c2SCharles.Forsyth 		s -= nelem(usbdir2);
334*74a4d8c2SCharles.Forsyth 		if(s >= 0 && s < nelem(uh->dev)) {
335*74a4d8c2SCharles.Forsyth 			d = uh->dev[s];
336*74a4d8c2SCharles.Forsyth 			if(d == nil)
337*74a4d8c2SCharles.Forsyth 				return 0;
338*74a4d8c2SCharles.Forsyth 			sprint(up->genbuf, "%d", s);
339*74a4d8c2SCharles.Forsyth 			mkqid(&q, PATH(Q3rd, s, bus), d->id, QTDIR);
340*74a4d8c2SCharles.Forsyth 			devdir(c, q, up->genbuf, 0, eve, 0555, dp);
341*74a4d8c2SCharles.Forsyth 			return 1;
342*74a4d8c2SCharles.Forsyth 		}
343*74a4d8c2SCharles.Forsyth 		return -1;
344*74a4d8c2SCharles.Forsyth 	}
345*74a4d8c2SCharles.Forsyth 
346*74a4d8c2SCharles.Forsyth 	/*
347*74a4d8c2SCharles.Forsyth 	 * Third level.
348*74a4d8c2SCharles.Forsyth 	 */
349*74a4d8c2SCharles.Forsyth 	slot = SLOT(c->qid);
350*74a4d8c2SCharles.Forsyth 	if(s == DEVDOTDOT) {
351*74a4d8c2SCharles.Forsyth 		mkqid(&q, PATH(Q2nd, 0, bus), c->qid.vers, QTDIR);
352*74a4d8c2SCharles.Forsyth 		snprint(up->genbuf, sizeof up->genbuf, "usb%d", bus);
353*74a4d8c2SCharles.Forsyth 		devdir(c, q, up->genbuf, 0, eve, 0555, dp);
354*74a4d8c2SCharles.Forsyth 		return 1;
355*74a4d8c2SCharles.Forsyth 	}
356*74a4d8c2SCharles.Forsyth 	if(s < nelem(usbdir3)) {
357*74a4d8c2SCharles.Forsyth 		tab = &usbdir3[s];
358*74a4d8c2SCharles.Forsyth 		mkqid(&q, PATH(tab->qid.path, slot, bus), c->qid.vers, QTFILE);
359*74a4d8c2SCharles.Forsyth 		devdir(c, q, tab->name, tab->length, eve, tab->perm, dp);
360*74a4d8c2SCharles.Forsyth 		return 1;
361*74a4d8c2SCharles.Forsyth 	}
362*74a4d8c2SCharles.Forsyth 	s -= nelem(usbdir3);
363*74a4d8c2SCharles.Forsyth 
364*74a4d8c2SCharles.Forsyth 	/* active endpoints */
365*74a4d8c2SCharles.Forsyth 	d = usbdeviceofslot(uh, slot);
366*74a4d8c2SCharles.Forsyth 	if(d == nil || s >= nelem(d->ep))
367*74a4d8c2SCharles.Forsyth 		return -1;
368*74a4d8c2SCharles.Forsyth 	if(s == 0 || (e = d->ep[s]) == nil)	/* ep0data is called "setup" */
369*74a4d8c2SCharles.Forsyth 		return 0;
370*74a4d8c2SCharles.Forsyth 	sprint(up->genbuf, "ep%ddata", s);
371*74a4d8c2SCharles.Forsyth 	mkqid(&q, PATH(Qep0+s, slot, bus), c->qid.vers, QTFILE);
372*74a4d8c2SCharles.Forsyth 	switch(e->mode) {
373*74a4d8c2SCharles.Forsyth 	case OREAD:
374*74a4d8c2SCharles.Forsyth 		perm = 0444;
375*74a4d8c2SCharles.Forsyth 		break;
376*74a4d8c2SCharles.Forsyth 	case OWRITE:
377*74a4d8c2SCharles.Forsyth 		perm = 0222;
378*74a4d8c2SCharles.Forsyth 		break;
379*74a4d8c2SCharles.Forsyth 	default:
380*74a4d8c2SCharles.Forsyth 		perm = 0666;
381*74a4d8c2SCharles.Forsyth 		break;
382*74a4d8c2SCharles.Forsyth 	}
383*74a4d8c2SCharles.Forsyth 	devdir(c, q, up->genbuf, e->buffered, eve, perm, dp);
384*74a4d8c2SCharles.Forsyth 	return 1;
385*74a4d8c2SCharles.Forsyth }
386*74a4d8c2SCharles.Forsyth 
387*74a4d8c2SCharles.Forsyth static Usbhost*
usbprobe(int cardno,int ctlrno)388*74a4d8c2SCharles.Forsyth usbprobe(int cardno, int ctlrno)
389*74a4d8c2SCharles.Forsyth {
390*74a4d8c2SCharles.Forsyth 	Usbhost *uh;
391*74a4d8c2SCharles.Forsyth 	char buf[128], *ebuf, name[64], *p, *type;
392*74a4d8c2SCharles.Forsyth 
393*74a4d8c2SCharles.Forsyth 	uh = malloc(sizeof(Usbhost));
394*74a4d8c2SCharles.Forsyth 	memset(uh, 0, sizeof(Usbhost));
395*74a4d8c2SCharles.Forsyth 	uh->tbdf = BUSUNKNOWN;
396*74a4d8c2SCharles.Forsyth 
397*74a4d8c2SCharles.Forsyth 	if(cardno < 0){
398*74a4d8c2SCharles.Forsyth 		if(isaconfig("usb", ctlrno, uh) == 0){
399*74a4d8c2SCharles.Forsyth 			free(uh);
400*74a4d8c2SCharles.Forsyth 			return nil;
401*74a4d8c2SCharles.Forsyth 		}
402*74a4d8c2SCharles.Forsyth 		for(cardno = 0; usbtypes[cardno].type; cardno++){
403*74a4d8c2SCharles.Forsyth 			type = uh->type;
404*74a4d8c2SCharles.Forsyth 			if(type==nil || *type==0)
405*74a4d8c2SCharles.Forsyth 				type = "uhci";
406*74a4d8c2SCharles.Forsyth 			if(cistrcmp(usbtypes[cardno].type, type))
407*74a4d8c2SCharles.Forsyth 				continue;
408*74a4d8c2SCharles.Forsyth 			break;
409*74a4d8c2SCharles.Forsyth 		}
410*74a4d8c2SCharles.Forsyth 	}
411*74a4d8c2SCharles.Forsyth 
412*74a4d8c2SCharles.Forsyth 	if(cardno >= MaxUsb || usbtypes[cardno].type == nil){
413*74a4d8c2SCharles.Forsyth 		free(uh);
414*74a4d8c2SCharles.Forsyth 		return nil;
415*74a4d8c2SCharles.Forsyth 	}
416*74a4d8c2SCharles.Forsyth 	if(usbtypes[cardno].reset(uh) < 0){
417*74a4d8c2SCharles.Forsyth 		free(uh);
418*74a4d8c2SCharles.Forsyth 		return nil;
419*74a4d8c2SCharles.Forsyth 	}
420*74a4d8c2SCharles.Forsyth 
421*74a4d8c2SCharles.Forsyth 	/*
422*74a4d8c2SCharles.Forsyth 	 * IRQ2 doesn't really exist, it's used to gang the interrupt
423*74a4d8c2SCharles.Forsyth 	 * controllers together. A device set to IRQ2 will appear on
424*74a4d8c2SCharles.Forsyth 	 * the second interrupt controller as IRQ9.
425*74a4d8c2SCharles.Forsyth 	 */
426*74a4d8c2SCharles.Forsyth 	if(uh->irq == 2)
427*74a4d8c2SCharles.Forsyth 		uh->irq = 9;
428*74a4d8c2SCharles.Forsyth 	snprint(name, sizeof(name), "usb%d", ctlrno);
429*74a4d8c2SCharles.Forsyth 	intrenable(uh->irq, uh->interrupt, uh, uh->tbdf, name);
430*74a4d8c2SCharles.Forsyth 
431*74a4d8c2SCharles.Forsyth 	ebuf = buf + sizeof buf;
432*74a4d8c2SCharles.Forsyth 	p = seprint(buf, ebuf, "#U/usb%d: %s: port 0x%luX irq %d", ctlrno, usbtypes[cardno].type, uh->port, uh->irq);
433*74a4d8c2SCharles.Forsyth 	if(uh->mem)
434*74a4d8c2SCharles.Forsyth 		p = seprint(p, ebuf, " addr 0x%luX", PADDR(uh->mem));
435*74a4d8c2SCharles.Forsyth 	if(uh->size)
436*74a4d8c2SCharles.Forsyth 		seprint(p, ebuf, " size 0x%luX", uh->size);
437*74a4d8c2SCharles.Forsyth 	print("%s\n", buf);
438*74a4d8c2SCharles.Forsyth 
439*74a4d8c2SCharles.Forsyth 	return uh;
440*74a4d8c2SCharles.Forsyth }
441*74a4d8c2SCharles.Forsyth 
442*74a4d8c2SCharles.Forsyth static void
usbreset(void)443*74a4d8c2SCharles.Forsyth usbreset(void)
444*74a4d8c2SCharles.Forsyth {
445*74a4d8c2SCharles.Forsyth 	int cardno, ctlrno;
446*74a4d8c2SCharles.Forsyth 	Usbhost *uh;
447*74a4d8c2SCharles.Forsyth 
448*74a4d8c2SCharles.Forsyth 	for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){
449*74a4d8c2SCharles.Forsyth 		if((uh = usbprobe(-1, ctlrno)) == nil)
450*74a4d8c2SCharles.Forsyth 			continue;
451*74a4d8c2SCharles.Forsyth 		usbhost[ctlrno] = uh;
452*74a4d8c2SCharles.Forsyth 	}
453*74a4d8c2SCharles.Forsyth 
454*74a4d8c2SCharles.Forsyth 	if(getconf("*nousbprobe"))
455*74a4d8c2SCharles.Forsyth 		return;
456*74a4d8c2SCharles.Forsyth 
457*74a4d8c2SCharles.Forsyth 	cardno = ctlrno = 0;
458*74a4d8c2SCharles.Forsyth 	while(usbtypes[cardno].type != nil && ctlrno < MaxUsb){
459*74a4d8c2SCharles.Forsyth 		if(usbhost[ctlrno] != nil){
460*74a4d8c2SCharles.Forsyth 			ctlrno++;
461*74a4d8c2SCharles.Forsyth 			continue;
462*74a4d8c2SCharles.Forsyth 		}
463*74a4d8c2SCharles.Forsyth 		if((uh = usbprobe(cardno, ctlrno)) == nil){
464*74a4d8c2SCharles.Forsyth 			cardno++;
465*74a4d8c2SCharles.Forsyth 			continue;
466*74a4d8c2SCharles.Forsyth 		}
467*74a4d8c2SCharles.Forsyth 		usbhost[ctlrno] = uh;
468*74a4d8c2SCharles.Forsyth 		ctlrno++;
469*74a4d8c2SCharles.Forsyth 	}
470*74a4d8c2SCharles.Forsyth }
471*74a4d8c2SCharles.Forsyth 
472*74a4d8c2SCharles.Forsyth void
usbinit(void)473*74a4d8c2SCharles.Forsyth usbinit(void)
474*74a4d8c2SCharles.Forsyth {
475*74a4d8c2SCharles.Forsyth 	Udev *d;
476*74a4d8c2SCharles.Forsyth 	int ctlrno;
477*74a4d8c2SCharles.Forsyth 	Usbhost *uh;
478*74a4d8c2SCharles.Forsyth 
479*74a4d8c2SCharles.Forsyth 	for(ctlrno = 0; ctlrno < MaxUsb; ctlrno++){
480*74a4d8c2SCharles.Forsyth 		uh = usbhost[ctlrno];
481*74a4d8c2SCharles.Forsyth 		if(uh == nil)
482*74a4d8c2SCharles.Forsyth 			continue;
483*74a4d8c2SCharles.Forsyth 		if(uh->init != 0)
484*74a4d8c2SCharles.Forsyth 			uh->init(uh);
485*74a4d8c2SCharles.Forsyth 
486*74a4d8c2SCharles.Forsyth 		/* reserve device for configuration */
487*74a4d8c2SCharles.Forsyth 		d = usbnewdevice(uh);
488*74a4d8c2SCharles.Forsyth 		incref(d);
489*74a4d8c2SCharles.Forsyth 		d->state = Attached;
490*74a4d8c2SCharles.Forsyth 	}
491*74a4d8c2SCharles.Forsyth }
492*74a4d8c2SCharles.Forsyth 
493*74a4d8c2SCharles.Forsyth Chan *
usbattach(char * spec)494*74a4d8c2SCharles.Forsyth usbattach(char *spec)
495*74a4d8c2SCharles.Forsyth {
496*74a4d8c2SCharles.Forsyth 	return devattach('U', spec);
497*74a4d8c2SCharles.Forsyth }
498*74a4d8c2SCharles.Forsyth 
499*74a4d8c2SCharles.Forsyth static Walkqid*
usbwalk(Chan * c,Chan * nc,char ** name,int nname)500*74a4d8c2SCharles.Forsyth usbwalk(Chan *c, Chan *nc, char **name, int nname)
501*74a4d8c2SCharles.Forsyth {
502*74a4d8c2SCharles.Forsyth 	return devwalk(c, nc, name, nname, nil, 0, usbgen);
503*74a4d8c2SCharles.Forsyth }
504*74a4d8c2SCharles.Forsyth 
505*74a4d8c2SCharles.Forsyth static int
usbstat(Chan * c,uchar * db,int n)506*74a4d8c2SCharles.Forsyth usbstat(Chan *c, uchar *db, int n)
507*74a4d8c2SCharles.Forsyth {
508*74a4d8c2SCharles.Forsyth 	return devstat(c, db, n, nil, 0, usbgen);
509*74a4d8c2SCharles.Forsyth }
510*74a4d8c2SCharles.Forsyth 
511*74a4d8c2SCharles.Forsyth Chan*
usbopen(Chan * c,int omode)512*74a4d8c2SCharles.Forsyth usbopen(Chan *c, int omode)
513*74a4d8c2SCharles.Forsyth {
514*74a4d8c2SCharles.Forsyth 	Udev *d;
515*74a4d8c2SCharles.Forsyth 	Endpt *e;
516*74a4d8c2SCharles.Forsyth 	int f, s, type;
517*74a4d8c2SCharles.Forsyth 	Usbhost *uh;
518*74a4d8c2SCharles.Forsyth 
519*74a4d8c2SCharles.Forsyth 	if(c->qid.type == QTDIR)
520*74a4d8c2SCharles.Forsyth 		return devopen(c, omode, nil, 0, usbgen);
521*74a4d8c2SCharles.Forsyth 
522*74a4d8c2SCharles.Forsyth 	f = 0;
523*74a4d8c2SCharles.Forsyth 	type = TYPE(c->qid);
524*74a4d8c2SCharles.Forsyth 	if(type == Qnew){
525*74a4d8c2SCharles.Forsyth 		d = usbdevice(c);
526*74a4d8c2SCharles.Forsyth 		d = usbnewdevice(d->uh);
527*74a4d8c2SCharles.Forsyth 		XPRINT("usbopen, new dev 0x%p\n", d);
528*74a4d8c2SCharles.Forsyth 		if(d == nil) {
529*74a4d8c2SCharles.Forsyth 			XPRINT("usbopen failed (usbnewdevice)\n");
530*74a4d8c2SCharles.Forsyth 			error(Enodev);
531*74a4d8c2SCharles.Forsyth 		}
532*74a4d8c2SCharles.Forsyth 		type = Qctl;
533*74a4d8c2SCharles.Forsyth 		mkqid(&c->qid, PATH(type, d->x, CTLR(c->qid)), d->id, QTFILE);
534*74a4d8c2SCharles.Forsyth 		f = 1;
535*74a4d8c2SCharles.Forsyth 	}
536*74a4d8c2SCharles.Forsyth 
537*74a4d8c2SCharles.Forsyth 	if(type < Q3rd){
538*74a4d8c2SCharles.Forsyth 		XPRINT("usbopen, devopen < Q3rd\n");
539*74a4d8c2SCharles.Forsyth 		return devopen(c, omode, nil, 0, usbgen);
540*74a4d8c2SCharles.Forsyth 	}
541*74a4d8c2SCharles.Forsyth 
542*74a4d8c2SCharles.Forsyth 	d = usbdevice(c);
543*74a4d8c2SCharles.Forsyth 	uh = d->uh;
544*74a4d8c2SCharles.Forsyth 	qlock(uh);
545*74a4d8c2SCharles.Forsyth 	if(waserror()){
546*74a4d8c2SCharles.Forsyth 		qunlock(uh);
547*74a4d8c2SCharles.Forsyth 		nexterror();
548*74a4d8c2SCharles.Forsyth 	}
549*74a4d8c2SCharles.Forsyth 
550*74a4d8c2SCharles.Forsyth 	switch(type){
551*74a4d8c2SCharles.Forsyth 	case Qctl:
552*74a4d8c2SCharles.Forsyth 		if(0&&d->busy)
553*74a4d8c2SCharles.Forsyth 			error(Einuse);
554*74a4d8c2SCharles.Forsyth 		d->busy = 1;
555*74a4d8c2SCharles.Forsyth 		if(!f)
556*74a4d8c2SCharles.Forsyth 			incref(d);
557*74a4d8c2SCharles.Forsyth 		XPRINT("usbopen, Qctl 0x%p\n", d);
558*74a4d8c2SCharles.Forsyth 		break;
559*74a4d8c2SCharles.Forsyth 
560*74a4d8c2SCharles.Forsyth 	default:
561*74a4d8c2SCharles.Forsyth 		s = type - Qep0;
562*74a4d8c2SCharles.Forsyth 		XPRINT("usbopen, default 0x%p, %d\n", d, s);
563*74a4d8c2SCharles.Forsyth 		if(s >= 0 && s < nelem(d->ep)){
564*74a4d8c2SCharles.Forsyth 			if((e = d->ep[s]) == nil) {
565*74a4d8c2SCharles.Forsyth 				XPRINT("usbopen failed (endpoint)\n");
566*74a4d8c2SCharles.Forsyth 				error(Enodev);
567*74a4d8c2SCharles.Forsyth 			}
568*74a4d8c2SCharles.Forsyth 			XPRINT("usbopen: dev 0x%p, ept 0x%p\n", d, e);
569*74a4d8c2SCharles.Forsyth 			uh->epopen(uh, e);
570*74a4d8c2SCharles.Forsyth 			e->foffset = 0;
571*74a4d8c2SCharles.Forsyth 			e->toffset = 0;
572*74a4d8c2SCharles.Forsyth 			e->poffset = 0;
573*74a4d8c2SCharles.Forsyth 			e->buffered = 0;
574*74a4d8c2SCharles.Forsyth 		}
575*74a4d8c2SCharles.Forsyth 		incref(d);
576*74a4d8c2SCharles.Forsyth 		break;
577*74a4d8c2SCharles.Forsyth 	}
578*74a4d8c2SCharles.Forsyth 	poperror();
579*74a4d8c2SCharles.Forsyth 	qunlock(uh);
580*74a4d8c2SCharles.Forsyth 	c->mode = openmode(omode);
581*74a4d8c2SCharles.Forsyth 	c->flag |= COPEN;
582*74a4d8c2SCharles.Forsyth 	c->offset = 0;
583*74a4d8c2SCharles.Forsyth 	return c;
584*74a4d8c2SCharles.Forsyth }
585*74a4d8c2SCharles.Forsyth 
586*74a4d8c2SCharles.Forsyth void
usbclose(Chan * c)587*74a4d8c2SCharles.Forsyth usbclose(Chan *c)
588*74a4d8c2SCharles.Forsyth {
589*74a4d8c2SCharles.Forsyth 	Udev *d;
590*74a4d8c2SCharles.Forsyth 	int ept, type;
591*74a4d8c2SCharles.Forsyth 	Usbhost *uh;
592*74a4d8c2SCharles.Forsyth 
593*74a4d8c2SCharles.Forsyth 	type = TYPE(c->qid);
594*74a4d8c2SCharles.Forsyth 	if(c->qid.type == QTDIR || type < Q3rd)
595*74a4d8c2SCharles.Forsyth 		return;
596*74a4d8c2SCharles.Forsyth 	d = usbdevice(c);
597*74a4d8c2SCharles.Forsyth 	uh = d->uh;
598*74a4d8c2SCharles.Forsyth 	qlock(uh);
599*74a4d8c2SCharles.Forsyth 	if(waserror()){
600*74a4d8c2SCharles.Forsyth 		qunlock(uh);
601*74a4d8c2SCharles.Forsyth 		nexterror();
602*74a4d8c2SCharles.Forsyth 	}
603*74a4d8c2SCharles.Forsyth 	if(type == Qctl)
604*74a4d8c2SCharles.Forsyth 		d->busy = 0;
605*74a4d8c2SCharles.Forsyth 	XPRINT("usbclose: dev 0x%p\n", d);
606*74a4d8c2SCharles.Forsyth 	if(c->flag & COPEN){
607*74a4d8c2SCharles.Forsyth 		ept = (type != Qctl) ? type - Qep0 : -1;
608*74a4d8c2SCharles.Forsyth 		XPRINT("usbclose: freedev 0x%p\n", d);
609*74a4d8c2SCharles.Forsyth 		freedev(d, ept);
610*74a4d8c2SCharles.Forsyth 	}
611*74a4d8c2SCharles.Forsyth 	poperror();
612*74a4d8c2SCharles.Forsyth 	qunlock(uh);
613*74a4d8c2SCharles.Forsyth }
614*74a4d8c2SCharles.Forsyth 
615*74a4d8c2SCharles.Forsyth static char *
epstatus(char * s,char * se,Endpt * e,int i)616*74a4d8c2SCharles.Forsyth epstatus(char *s, char *se, Endpt *e, int i)
617*74a4d8c2SCharles.Forsyth {
618*74a4d8c2SCharles.Forsyth 	char *p;
619*74a4d8c2SCharles.Forsyth 
620*74a4d8c2SCharles.Forsyth 	p = seprint(s, se, "%2d %#6.6lux %10lud bytes %10lud blocks\n", i, e->csp, e->nbytes, e->nblocks);
621*74a4d8c2SCharles.Forsyth 	if(e->iso){
622*74a4d8c2SCharles.Forsyth 		p = seprint(p, se, "bufsize %6d buffered %6d", e->maxpkt, e->buffered);
623*74a4d8c2SCharles.Forsyth 		if(e->toffset)
624*74a4d8c2SCharles.Forsyth 			p = seprint(p, se, " offset  %10lud time %19lld\n", e->toffset, e->time);
625*74a4d8c2SCharles.Forsyth 		p = seprint(p, se, "\n");
626*74a4d8c2SCharles.Forsyth 	}
627*74a4d8c2SCharles.Forsyth 	return p;
628*74a4d8c2SCharles.Forsyth }
629*74a4d8c2SCharles.Forsyth 
630*74a4d8c2SCharles.Forsyth long
usbread(Chan * c,void * a,long n,vlong offset)631*74a4d8c2SCharles.Forsyth usbread(Chan *c, void *a, long n, vlong offset)
632*74a4d8c2SCharles.Forsyth {
633*74a4d8c2SCharles.Forsyth 	int t, i;
634*74a4d8c2SCharles.Forsyth 	Udev *d;
635*74a4d8c2SCharles.Forsyth 	Endpt *e;
636*74a4d8c2SCharles.Forsyth 	Usbhost *uh;
637*74a4d8c2SCharles.Forsyth 	char *s, *se, *p;
638*74a4d8c2SCharles.Forsyth 
639*74a4d8c2SCharles.Forsyth 	if(c->qid.type == QTDIR)
640*74a4d8c2SCharles.Forsyth 		return devdirread(c, a, n, nil, 0, usbgen);
641*74a4d8c2SCharles.Forsyth 
642*74a4d8c2SCharles.Forsyth 	d = usbdevice(c);
643*74a4d8c2SCharles.Forsyth 	uh = d->uh;
644*74a4d8c2SCharles.Forsyth 	t = TYPE(c->qid);
645*74a4d8c2SCharles.Forsyth 
646*74a4d8c2SCharles.Forsyth 	if(t >= Qep0) {
647*74a4d8c2SCharles.Forsyth 		t -= Qep0;
648*74a4d8c2SCharles.Forsyth 		if(t >= nelem(d->ep))
649*74a4d8c2SCharles.Forsyth 			error(Eio);
650*74a4d8c2SCharles.Forsyth 		e = d->ep[t];
651*74a4d8c2SCharles.Forsyth 		if(e == nil || e->mode == OWRITE)
652*74a4d8c2SCharles.Forsyth 			error(Egreg);
653*74a4d8c2SCharles.Forsyth 		if(t == 0) {
654*74a4d8c2SCharles.Forsyth 			if(e->iso)
655*74a4d8c2SCharles.Forsyth 				error(Egreg);
656*74a4d8c2SCharles.Forsyth 			e->data01 = 1;
657*74a4d8c2SCharles.Forsyth 			n = uh->read(uh, e, a, n, 0LL);
658*74a4d8c2SCharles.Forsyth 			if(e->setin){
659*74a4d8c2SCharles.Forsyth 				e->setin = 0;
660*74a4d8c2SCharles.Forsyth 				e->data01 = 1;
661*74a4d8c2SCharles.Forsyth 				uh->write(uh, e, "", 0, 0LL, TokOUT);
662*74a4d8c2SCharles.Forsyth 			}
663*74a4d8c2SCharles.Forsyth 			return n;
664*74a4d8c2SCharles.Forsyth 		}
665*74a4d8c2SCharles.Forsyth 		return uh->read(uh, e, a, n, offset);
666*74a4d8c2SCharles.Forsyth 	}
667*74a4d8c2SCharles.Forsyth 
668*74a4d8c2SCharles.Forsyth 	s = smalloc(READSTR);
669*74a4d8c2SCharles.Forsyth 	se = s+READSTR;
670*74a4d8c2SCharles.Forsyth 	if(waserror()){
671*74a4d8c2SCharles.Forsyth 		free(s);
672*74a4d8c2SCharles.Forsyth 		nexterror();
673*74a4d8c2SCharles.Forsyth 	}
674*74a4d8c2SCharles.Forsyth 	switch(t){
675*74a4d8c2SCharles.Forsyth 	case Qport:
676*74a4d8c2SCharles.Forsyth 		uh->portinfo(uh, s, se);
677*74a4d8c2SCharles.Forsyth 		break;
678*74a4d8c2SCharles.Forsyth 
679*74a4d8c2SCharles.Forsyth 	case Qctl:
680*74a4d8c2SCharles.Forsyth 		seprint(s, se, "%11d %11d\n", d->x, d->id);
681*74a4d8c2SCharles.Forsyth 		break;
682*74a4d8c2SCharles.Forsyth 
683*74a4d8c2SCharles.Forsyth 	case Qstatus:
684*74a4d8c2SCharles.Forsyth 		if (d->did || d->vid)
685*74a4d8c2SCharles.Forsyth 			p = seprint(s, se, "%s %#6.6lux %#4.4ux %#4.4ux\n", devstates[d->state], d->csp, d->vid, d->did);
686*74a4d8c2SCharles.Forsyth 		else
687*74a4d8c2SCharles.Forsyth 			p = seprint(s, se, "%s %#6.6lux\n", devstates[d->state], d->csp);
688*74a4d8c2SCharles.Forsyth 		for(i=0; i<nelem(d->ep); i++) {
689*74a4d8c2SCharles.Forsyth 			e = d->ep[i];
690*74a4d8c2SCharles.Forsyth 			if(e == nil)
691*74a4d8c2SCharles.Forsyth 				continue;
692*74a4d8c2SCharles.Forsyth 			/* TO DO: freeze e */
693*74a4d8c2SCharles.Forsyth 			p = epstatus(p, se, e, i);
694*74a4d8c2SCharles.Forsyth 		}
695*74a4d8c2SCharles.Forsyth 	}
696*74a4d8c2SCharles.Forsyth 	n = readstr(offset, a, n, s);
697*74a4d8c2SCharles.Forsyth 	poperror();
698*74a4d8c2SCharles.Forsyth 	free(s);
699*74a4d8c2SCharles.Forsyth 	return n;
700*74a4d8c2SCharles.Forsyth }
701*74a4d8c2SCharles.Forsyth 
702*74a4d8c2SCharles.Forsyth long
usbwrite(Chan * c,void * a,long n,vlong offset)703*74a4d8c2SCharles.Forsyth usbwrite(Chan *c, void *a, long n, vlong offset)
704*74a4d8c2SCharles.Forsyth {
705*74a4d8c2SCharles.Forsyth 	Udev *d;
706*74a4d8c2SCharles.Forsyth 	Endpt *e;
707*74a4d8c2SCharles.Forsyth 	Cmdtab *ct;
708*74a4d8c2SCharles.Forsyth 	Cmdbuf *cb;
709*74a4d8c2SCharles.Forsyth 	Usbhost *uh;
710*74a4d8c2SCharles.Forsyth 	int id, nw, t, i;
711*74a4d8c2SCharles.Forsyth 	char cmd[50];
712*74a4d8c2SCharles.Forsyth 
713*74a4d8c2SCharles.Forsyth 	if(c->qid.type == QTDIR)
714*74a4d8c2SCharles.Forsyth 		error(Egreg);
715*74a4d8c2SCharles.Forsyth 	d = usbdevice(c);
716*74a4d8c2SCharles.Forsyth 	uh = d->uh;
717*74a4d8c2SCharles.Forsyth 	t = TYPE(c->qid);
718*74a4d8c2SCharles.Forsyth 	switch(t){
719*74a4d8c2SCharles.Forsyth 	case Qport:
720*74a4d8c2SCharles.Forsyth 		cb = parsecmd(a, n);
721*74a4d8c2SCharles.Forsyth 		if(waserror()){
722*74a4d8c2SCharles.Forsyth 			free(cb);
723*74a4d8c2SCharles.Forsyth 			nexterror();
724*74a4d8c2SCharles.Forsyth 		}
725*74a4d8c2SCharles.Forsyth 
726*74a4d8c2SCharles.Forsyth 		ct = lookupcmd(cb, usbportmsg, nelem(usbportmsg));
727*74a4d8c2SCharles.Forsyth 		id = strtol(cb->f[1], nil, 0);
728*74a4d8c2SCharles.Forsyth 		switch(ct->index){
729*74a4d8c2SCharles.Forsyth 		case PMdisable:
730*74a4d8c2SCharles.Forsyth 			uh->portenable(uh, id, 0);
731*74a4d8c2SCharles.Forsyth 			break;
732*74a4d8c2SCharles.Forsyth 		case PMenable:
733*74a4d8c2SCharles.Forsyth 			uh->portenable(uh, id, 1);
734*74a4d8c2SCharles.Forsyth 			break;
735*74a4d8c2SCharles.Forsyth 		case PMreset:
736*74a4d8c2SCharles.Forsyth 			uh->portreset(uh, id);
737*74a4d8c2SCharles.Forsyth 			break;
738*74a4d8c2SCharles.Forsyth 		}
739*74a4d8c2SCharles.Forsyth 
740*74a4d8c2SCharles.Forsyth 		poperror();
741*74a4d8c2SCharles.Forsyth 		free(cb);
742*74a4d8c2SCharles.Forsyth 		return n;
743*74a4d8c2SCharles.Forsyth 	case Qctl:
744*74a4d8c2SCharles.Forsyth 		cb = parsecmd(a, n);
745*74a4d8c2SCharles.Forsyth 		if(waserror()){
746*74a4d8c2SCharles.Forsyth 			free(cb);
747*74a4d8c2SCharles.Forsyth 			nexterror();
748*74a4d8c2SCharles.Forsyth 		}
749*74a4d8c2SCharles.Forsyth 
750*74a4d8c2SCharles.Forsyth 		ct = lookupcmd(cb, usbctlmsg, nelem(usbctlmsg));
751*74a4d8c2SCharles.Forsyth 		switch(ct->index){
752*74a4d8c2SCharles.Forsyth 		case CMspeed:
753*74a4d8c2SCharles.Forsyth 			d->ls = strtoul(cb->f[1], nil, 0) == 0;
754*74a4d8c2SCharles.Forsyth 			break;
755*74a4d8c2SCharles.Forsyth 		case CMclass:
756*74a4d8c2SCharles.Forsyth 			if (cb->nf != 4 && cb->nf != 6)
757*74a4d8c2SCharles.Forsyth 				cmderror(cb, Ebadusbmsg);
758*74a4d8c2SCharles.Forsyth 			/* class #ifc ept csp ( == class subclass proto) [vendor product] */
759*74a4d8c2SCharles.Forsyth 			d->npt = strtoul(cb->f[1], nil, 0);	/* # of interfaces */
760*74a4d8c2SCharles.Forsyth 			i = strtoul(cb->f[2], nil, 0);		/* endpoint */
761*74a4d8c2SCharles.Forsyth 			if (i < 0 || i >= nelem(d->ep)
762*74a4d8c2SCharles.Forsyth 			 || d->npt > nelem(d->ep) || i >= d->npt)
763*74a4d8c2SCharles.Forsyth 				cmderror(cb, Ebadusbmsg);
764*74a4d8c2SCharles.Forsyth 			if (cb->nf == 6) {
765*74a4d8c2SCharles.Forsyth 				d->vid = strtoul(cb->f[4], nil, 0);
766*74a4d8c2SCharles.Forsyth 				d->did = strtoul(cb->f[5], nil, 0);
767*74a4d8c2SCharles.Forsyth 			}
768*74a4d8c2SCharles.Forsyth 			if (i == 0)
769*74a4d8c2SCharles.Forsyth 				d->csp = strtoul(cb->f[3], nil, 0);
770*74a4d8c2SCharles.Forsyth 			if(d->ep[i] == nil){
771*74a4d8c2SCharles.Forsyth 				XPRINT("calling devendpt in usbwrite (CMclass)\n");
772*74a4d8c2SCharles.Forsyth 				d->ep[i] = devendpt(d, i, 1);
773*74a4d8c2SCharles.Forsyth 			}
774*74a4d8c2SCharles.Forsyth 			d->ep[i]->csp = strtoul(cb->f[3], nil, 0);
775*74a4d8c2SCharles.Forsyth 			break;
776*74a4d8c2SCharles.Forsyth 		case CMdata:
777*74a4d8c2SCharles.Forsyth 			i = strtoul(cb->f[1], nil, 0);
778*74a4d8c2SCharles.Forsyth 			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
779*74a4d8c2SCharles.Forsyth 				error(Ebadusbmsg);
780*74a4d8c2SCharles.Forsyth 			e = d->ep[i];
781*74a4d8c2SCharles.Forsyth 			e->data01 = strtoul(cb->f[2], nil, 0) != 0;
782*74a4d8c2SCharles.Forsyth 			break;
783*74a4d8c2SCharles.Forsyth 		case CMmaxpkt:
784*74a4d8c2SCharles.Forsyth 			i = strtoul(cb->f[1], nil, 0);
785*74a4d8c2SCharles.Forsyth 			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
786*74a4d8c2SCharles.Forsyth 				error(Ebadusbmsg);
787*74a4d8c2SCharles.Forsyth 			e = d->ep[i];
788*74a4d8c2SCharles.Forsyth 			e->maxpkt = strtoul(cb->f[2], nil, 0);
789*74a4d8c2SCharles.Forsyth 			if(e->maxpkt > 1500)
790*74a4d8c2SCharles.Forsyth 				e->maxpkt = 1500;
791*74a4d8c2SCharles.Forsyth 			break;
792*74a4d8c2SCharles.Forsyth 		case CMadjust:
793*74a4d8c2SCharles.Forsyth 			i = strtoul(cb->f[1], nil, 0);
794*74a4d8c2SCharles.Forsyth 			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
795*74a4d8c2SCharles.Forsyth 				error(Ebadusbmsg);
796*74a4d8c2SCharles.Forsyth 			e = d->ep[i];
797*74a4d8c2SCharles.Forsyth 			if (e->iso == 0)
798*74a4d8c2SCharles.Forsyth 				error(Eperm);
799*74a4d8c2SCharles.Forsyth 			i = strtoul(cb->f[2], nil, 0);
800*74a4d8c2SCharles.Forsyth 			/* speed may not result in change of maxpkt */
801*74a4d8c2SCharles.Forsyth 			if (i < (e->maxpkt-1)/e->samplesz * 1000/e->pollms
802*74a4d8c2SCharles.Forsyth 			  || i > e->maxpkt/e->samplesz * 1000/e->pollms){
803*74a4d8c2SCharles.Forsyth 				snprint(cmd, sizeof(cmd), "%d < %d < %d?",
804*74a4d8c2SCharles.Forsyth 					(e->maxpkt-1)/e->samplesz * 1000/e->pollms,
805*74a4d8c2SCharles.Forsyth 					i,
806*74a4d8c2SCharles.Forsyth 					e->maxpkt/e->samplesz * 1000/e->pollms);
807*74a4d8c2SCharles.Forsyth 				error(cmd);
808*74a4d8c2SCharles.Forsyth 			}
809*74a4d8c2SCharles.Forsyth 			e->hz = i;
810*74a4d8c2SCharles.Forsyth 			break;
811*74a4d8c2SCharles.Forsyth 		case CMdebug:
812*74a4d8c2SCharles.Forsyth 			i = strtoul(cb->f[1], nil, 0);
813*74a4d8c2SCharles.Forsyth 			if(i < -1 || i >= nelem(d->ep) || d->ep[i] == nil)
814*74a4d8c2SCharles.Forsyth 				error(Ebadusbmsg);
815*74a4d8c2SCharles.Forsyth 			if (i == -1)
816*74a4d8c2SCharles.Forsyth 				debug = 0;
817*74a4d8c2SCharles.Forsyth 			else {
818*74a4d8c2SCharles.Forsyth 				debug = 1;
819*74a4d8c2SCharles.Forsyth 				e = d->ep[i];
820*74a4d8c2SCharles.Forsyth 				e->debug = strtoul(cb->f[2], nil, 0);
821*74a4d8c2SCharles.Forsyth 			}
822*74a4d8c2SCharles.Forsyth 			break;
823*74a4d8c2SCharles.Forsyth 		case CMunstall:
824*74a4d8c2SCharles.Forsyth 			i = strtoul(cb->f[1], nil, 0);
825*74a4d8c2SCharles.Forsyth 			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
826*74a4d8c2SCharles.Forsyth 				error(Ebadusbmsg);
827*74a4d8c2SCharles.Forsyth 			e = d->ep[i];
828*74a4d8c2SCharles.Forsyth 			e->err = nil;
829*74a4d8c2SCharles.Forsyth 			break;
830*74a4d8c2SCharles.Forsyth 		case CMep:
831*74a4d8c2SCharles.Forsyth 			/* ep n `bulk' mode maxpkt nbuf     OR
832*74a4d8c2SCharles.Forsyth 			 * ep n period mode samplesize Hz
833*74a4d8c2SCharles.Forsyth 			 */
834*74a4d8c2SCharles.Forsyth 			i = strtoul(cb->f[1], nil, 0);
835*74a4d8c2SCharles.Forsyth 			if(i < 0 || i >= nelem(d->ep)) {
836*74a4d8c2SCharles.Forsyth 				XPRINT("field 1: 0 <= %d < %d\n", i, nelem(d->ep));
837*74a4d8c2SCharles.Forsyth 				error(Ebadarg);
838*74a4d8c2SCharles.Forsyth 			}
839*74a4d8c2SCharles.Forsyth 			if((e = d->ep[i]) == nil){
840*74a4d8c2SCharles.Forsyth 				XPRINT("calling devendpt in usbwrite (CMep)\n");
841*74a4d8c2SCharles.Forsyth 				e = devendpt(d, i, 1);
842*74a4d8c2SCharles.Forsyth 			}
843*74a4d8c2SCharles.Forsyth 			qlock(uh);
844*74a4d8c2SCharles.Forsyth 			if(waserror()){
845*74a4d8c2SCharles.Forsyth 				freept(e);
846*74a4d8c2SCharles.Forsyth 				qunlock(uh);
847*74a4d8c2SCharles.Forsyth 				nexterror();
848*74a4d8c2SCharles.Forsyth 			}
849*74a4d8c2SCharles.Forsyth 			if(e->active)
850*74a4d8c2SCharles.Forsyth 				error(Eperm);
851*74a4d8c2SCharles.Forsyth 			if(strcmp(cb->f[2], "bulk") == 0){
852*74a4d8c2SCharles.Forsyth 				/* ep n `bulk' mode maxpkt nbuf */
853*74a4d8c2SCharles.Forsyth 				e->iso = 0;
854*74a4d8c2SCharles.Forsyth 				i = strtoul(cb->f[4], nil, 0);
855*74a4d8c2SCharles.Forsyth 				if(i < 8 || i > 1023)
856*74a4d8c2SCharles.Forsyth 					i = 8;
857*74a4d8c2SCharles.Forsyth 				e->maxpkt = i;
858*74a4d8c2SCharles.Forsyth 				i = strtoul(cb->f[5], nil, 0);
859*74a4d8c2SCharles.Forsyth 				if(i >= 1 && i <= 32)
860*74a4d8c2SCharles.Forsyth 					e->nbuf = i;
861*74a4d8c2SCharles.Forsyth 			} else {
862*74a4d8c2SCharles.Forsyth 				/* ep n period mode samplesize Hz */
863*74a4d8c2SCharles.Forsyth 				i = strtoul(cb->f[2], nil, 0);
864*74a4d8c2SCharles.Forsyth 				if(i > 0 && i <= 1000){
865*74a4d8c2SCharles.Forsyth 					e->pollms = i;
866*74a4d8c2SCharles.Forsyth 				}else {
867*74a4d8c2SCharles.Forsyth 					XPRINT("field 4: 0 <= %d <= 1000\n", i);
868*74a4d8c2SCharles.Forsyth 					error(Ebadarg);
869*74a4d8c2SCharles.Forsyth 				}
870*74a4d8c2SCharles.Forsyth 				i = strtoul(cb->f[4], nil, 0);
871*74a4d8c2SCharles.Forsyth 				if(i >= 1 && i <= 8){
872*74a4d8c2SCharles.Forsyth 					e->samplesz = i;
873*74a4d8c2SCharles.Forsyth 				}else {
874*74a4d8c2SCharles.Forsyth 					XPRINT("field 4: 0 < %d <= 8\n", i);
875*74a4d8c2SCharles.Forsyth 					error(Ebadarg);
876*74a4d8c2SCharles.Forsyth 				}
877*74a4d8c2SCharles.Forsyth 				i = strtoul(cb->f[5], nil, 0);
878*74a4d8c2SCharles.Forsyth 				if(i >= 1 && i*e->samplesz <= 12*1000*1000){
879*74a4d8c2SCharles.Forsyth 					/* Hz */
880*74a4d8c2SCharles.Forsyth 					e->hz = i;
881*74a4d8c2SCharles.Forsyth 					e->remain = 0;
882*74a4d8c2SCharles.Forsyth 				}else {
883*74a4d8c2SCharles.Forsyth 					XPRINT("field 5: 1 < %d <= 100000 Hz\n", i);
884*74a4d8c2SCharles.Forsyth 					error(Ebadarg);
885*74a4d8c2SCharles.Forsyth 				}
886*74a4d8c2SCharles.Forsyth 				e->maxpkt = (e->hz * e->pollms + 999)/1000 * e->samplesz;
887*74a4d8c2SCharles.Forsyth 				e->iso = 1;
888*74a4d8c2SCharles.Forsyth 			}
889*74a4d8c2SCharles.Forsyth 			e->mode = strcmp(cb->f[3],"r") == 0? OREAD :
890*74a4d8c2SCharles.Forsyth 				  	strcmp(cb->f[3],"w") == 0? OWRITE : ORDWR;
891*74a4d8c2SCharles.Forsyth 			uh->epmode(uh, e);
892*74a4d8c2SCharles.Forsyth 			poperror();
893*74a4d8c2SCharles.Forsyth 			qunlock(uh);
894*74a4d8c2SCharles.Forsyth 		}
895*74a4d8c2SCharles.Forsyth 
896*74a4d8c2SCharles.Forsyth 		poperror();
897*74a4d8c2SCharles.Forsyth 		free(cb);
898*74a4d8c2SCharles.Forsyth 		return n;
899*74a4d8c2SCharles.Forsyth 
900*74a4d8c2SCharles.Forsyth 	case Qep0:	/* SETUP endpoint 0 */
901*74a4d8c2SCharles.Forsyth 		/* should canqlock etc */
902*74a4d8c2SCharles.Forsyth 		e = d->ep[0];
903*74a4d8c2SCharles.Forsyth 		if(e == nil || e->iso)
904*74a4d8c2SCharles.Forsyth 			error(Egreg);
905*74a4d8c2SCharles.Forsyth 		if(n < 8)
906*74a4d8c2SCharles.Forsyth 			error(Eio);
907*74a4d8c2SCharles.Forsyth 		nw = *(uchar*)a & RD2H;
908*74a4d8c2SCharles.Forsyth 		e->data01 = 0;
909*74a4d8c2SCharles.Forsyth 		n = uh->write(uh, e, a, n, 0LL, TokSETUP);
910*74a4d8c2SCharles.Forsyth 		if(nw == 0) {	/* host to device: use IN[DATA1] to ack */
911*74a4d8c2SCharles.Forsyth 			e->data01 = 1;
912*74a4d8c2SCharles.Forsyth 			nw = uh->read(uh, e, cmd, 0LL, 8);
913*74a4d8c2SCharles.Forsyth 			if(nw != 0)
914*74a4d8c2SCharles.Forsyth 				error(Eio);	/* could provide more status */
915*74a4d8c2SCharles.Forsyth 		}else
916*74a4d8c2SCharles.Forsyth 			e->setin = 1;	/* two-phase */
917*74a4d8c2SCharles.Forsyth 		break;
918*74a4d8c2SCharles.Forsyth 
919*74a4d8c2SCharles.Forsyth 	default:	/* sends DATA[01] */
920*74a4d8c2SCharles.Forsyth 		t -= Qep0;
921*74a4d8c2SCharles.Forsyth 		if(t < 0 || t >= nelem(d->ep))
922*74a4d8c2SCharles.Forsyth 			error(Egreg);
923*74a4d8c2SCharles.Forsyth 		e = d->ep[t];
924*74a4d8c2SCharles.Forsyth 		if(e == nil || e->mode == OREAD)
925*74a4d8c2SCharles.Forsyth 			error(Egreg);
926*74a4d8c2SCharles.Forsyth 		n = uh->write(uh, e, a, n, offset, TokOUT);
927*74a4d8c2SCharles.Forsyth 		break;
928*74a4d8c2SCharles.Forsyth 	}
929*74a4d8c2SCharles.Forsyth 	return n;
930*74a4d8c2SCharles.Forsyth }
931*74a4d8c2SCharles.Forsyth 
932*74a4d8c2SCharles.Forsyth Dev usbdevtab = {
933*74a4d8c2SCharles.Forsyth 	'U',
934*74a4d8c2SCharles.Forsyth 	"usb",
935*74a4d8c2SCharles.Forsyth 
936*74a4d8c2SCharles.Forsyth 	usbreset,
937*74a4d8c2SCharles.Forsyth 	usbinit,
938*74a4d8c2SCharles.Forsyth 	devshutdown,
939*74a4d8c2SCharles.Forsyth 	usbattach,
940*74a4d8c2SCharles.Forsyth 	usbwalk,
941*74a4d8c2SCharles.Forsyth 	usbstat,
942*74a4d8c2SCharles.Forsyth 	usbopen,
943*74a4d8c2SCharles.Forsyth 	devcreate,
944*74a4d8c2SCharles.Forsyth 	usbclose,
945*74a4d8c2SCharles.Forsyth 	usbread,
946*74a4d8c2SCharles.Forsyth 	devbread,
947*74a4d8c2SCharles.Forsyth 	usbwrite,
948*74a4d8c2SCharles.Forsyth 	devbwrite,
949*74a4d8c2SCharles.Forsyth 	devremove,
950*74a4d8c2SCharles.Forsyth 	devwstat,
951*74a4d8c2SCharles.Forsyth };
952