xref: /plan9/sys/src/cmd/usb/ether/ether.c (revision 906943f9f6b8411972abb5e3a03ed19f74be7ccc)
1*906943f9SDavid du Colombier /*
2*906943f9SDavid du Colombier  * usb/ether - usb ethernet adapter.
3*906943f9SDavid du Colombier  * BUG: This should use /dev/etherfile to
4*906943f9SDavid du Colombier  * use the kernel ether device code.
5*906943f9SDavid du Colombier  */
6*906943f9SDavid du Colombier #include <u.h>
7*906943f9SDavid du Colombier #include <libc.h>
8*906943f9SDavid du Colombier #include <fcall.h>
9*906943f9SDavid du Colombier #include <thread.h>
10*906943f9SDavid du Colombier #include "usb.h"
11*906943f9SDavid du Colombier #include "usbfs.h"
12*906943f9SDavid du Colombier #include "ether.h"
13*906943f9SDavid du Colombier 
14*906943f9SDavid du Colombier typedef struct Dirtab Dirtab;
15*906943f9SDavid du Colombier 
16*906943f9SDavid du Colombier enum
17*906943f9SDavid du Colombier {
18*906943f9SDavid du Colombier 	/* Qids. Maintain order (relative to dirtabs structs) */
19*906943f9SDavid du Colombier 	Qroot	= 0,
20*906943f9SDavid du Colombier 	Qclone,
21*906943f9SDavid du Colombier 	Qaddr,
22*906943f9SDavid du Colombier 	Qifstats,
23*906943f9SDavid du Colombier 	Qstats,
24*906943f9SDavid du Colombier 	Qndir,
25*906943f9SDavid du Colombier 	Qndata,
26*906943f9SDavid du Colombier 	Qnctl,
27*906943f9SDavid du Colombier 	Qnifstats,
28*906943f9SDavid du Colombier 	Qnstats,
29*906943f9SDavid du Colombier 	Qntype,
30*906943f9SDavid du Colombier 	Qmax,
31*906943f9SDavid du Colombier };
32*906943f9SDavid du Colombier 
33*906943f9SDavid du Colombier struct Dirtab
34*906943f9SDavid du Colombier {
35*906943f9SDavid du Colombier 	char	*name;
36*906943f9SDavid du Colombier 	int	qid;
37*906943f9SDavid du Colombier 	int	mode;
38*906943f9SDavid du Colombier };
39*906943f9SDavid du Colombier 
40*906943f9SDavid du Colombier typedef int (*Resetf)(Ether*);
41*906943f9SDavid du Colombier 
42*906943f9SDavid du Colombier /*
43*906943f9SDavid du Colombier  * Controllers by vid/vid. Used to locate
44*906943f9SDavid du Colombier  * specific adapters that do not implement cdc ethernet
45*906943f9SDavid du Colombier  * Keep null terminated.
46*906943f9SDavid du Colombier  */
47*906943f9SDavid du Colombier Cinfo cinfo[] =
48*906943f9SDavid du Colombier {
49*906943f9SDavid du Colombier 	/* Asix controllers.
50*906943f9SDavid du Colombier 	 * Only A88178 and A881772 are implemented.
51*906943f9SDavid du Colombier 	 * Others are easy to add by borrowing code
52*906943f9SDavid du Colombier 	 * from other systems.
53*906943f9SDavid du Colombier 	 */
54*906943f9SDavid du Colombier 	{0x077b, 0x2226, A8817x},
55*906943f9SDavid du Colombier 	{0x0b95, 0x1720, A8817x},
56*906943f9SDavid du Colombier 	{0x0557, 0x2009, A8817x},
57*906943f9SDavid du Colombier 	{0x0411, 0x003d, A8817x},
58*906943f9SDavid du Colombier 	{0x0411, 0x006e, A88178},
59*906943f9SDavid du Colombier 	{0x6189, 0x182d, A8817x},
60*906943f9SDavid du Colombier 	{0x07aa, 0x0017, A8817x},
61*906943f9SDavid du Colombier 	{0x1189, 0x0893, A8817x},
62*906943f9SDavid du Colombier 	{0x1631, 0x6200, A8817x},
63*906943f9SDavid du Colombier 	{0x04f1, 0x3008, A8817x},
64*906943f9SDavid du Colombier 	{0x0b95, 0x1780, A88178},	/* Geoff */
65*906943f9SDavid du Colombier 	{0x13b1, 0x0018, A88772},
66*906943f9SDavid du Colombier 	{0x1557, 0x7720, A88772},
67*906943f9SDavid du Colombier 	{0x07d1, 0x3c05, A88772},
68*906943f9SDavid du Colombier 	{0x2001, 0x3c05, A88772},
69*906943f9SDavid du Colombier 	{0x1737, 0x0039, A88178},
70*906943f9SDavid du Colombier 	{0x050d, 0x5055, A88178},
71*906943f9SDavid du Colombier 	{0x05ac, 0x1402, A88772},	/* Apple */
72*906943f9SDavid du Colombier 	{0x0b95, 0x772a, A88772},
73*906943f9SDavid du Colombier 	{0x14ea, 0xab11, A88178},
74*906943f9SDavid du Colombier 	{0x0db0, 0xa877, A88772},
75*906943f9SDavid du Colombier 	{0, 0, 0},
76*906943f9SDavid du Colombier };
77*906943f9SDavid du Colombier 
78*906943f9SDavid du Colombier /*
79*906943f9SDavid du Colombier  * Each etherU%d is the root of our file system,
80*906943f9SDavid du Colombier  * which is added to the usb root directory. We only
81*906943f9SDavid du Colombier  * have to concern ourselfs with each /etherU%d subtree.
82*906943f9SDavid du Colombier  *
83*906943f9SDavid du Colombier  * NB: Maintain order in dirtabs, relative to the Qids enum.
84*906943f9SDavid du Colombier  */
85*906943f9SDavid du Colombier 
86*906943f9SDavid du Colombier static Dirtab rootdirtab[] =
87*906943f9SDavid du Colombier {
88*906943f9SDavid du Colombier 	"/",		Qroot,		DMDIR|0555,	/* etherU%d */
89*906943f9SDavid du Colombier 	"clone",	Qclone,		0666,
90*906943f9SDavid du Colombier 	"addr",		Qaddr,		0444,
91*906943f9SDavid du Colombier 	"ifstats",	Qifstats,	0444,
92*906943f9SDavid du Colombier 	"stats",	Qstats,		0444,
93*906943f9SDavid du Colombier 	/* one dir per connection here */
94*906943f9SDavid du Colombier 	nil, 0, 0,
95*906943f9SDavid du Colombier };
96*906943f9SDavid du Colombier 
97*906943f9SDavid du Colombier static Dirtab conndirtab[] =
98*906943f9SDavid du Colombier {
99*906943f9SDavid du Colombier 	"%d",		Qndir,		DMDIR|0555,
100*906943f9SDavid du Colombier 	"data",		Qndata,		0666,
101*906943f9SDavid du Colombier 	"ctl",		Qnctl,		0666,
102*906943f9SDavid du Colombier 	"ifstats",	Qnifstats,	0444,
103*906943f9SDavid du Colombier 	"stats",	Qnstats,	0444,
104*906943f9SDavid du Colombier 	"type",		Qntype,		0444,
105*906943f9SDavid du Colombier 	nil, 0,
106*906943f9SDavid du Colombier };
107*906943f9SDavid du Colombier 
108*906943f9SDavid du Colombier int etherdebug;
109*906943f9SDavid du Colombier 
110*906943f9SDavid du Colombier Resetf ethers[] =
111*906943f9SDavid du Colombier {
112*906943f9SDavid du Colombier 	asixreset,
113*906943f9SDavid du Colombier 	cdcreset,	/* keep last */
114*906943f9SDavid du Colombier };
115*906943f9SDavid du Colombier 
116*906943f9SDavid du Colombier static int
117*906943f9SDavid du Colombier qtype(vlong q)
118*906943f9SDavid du Colombier {
119*906943f9SDavid du Colombier 	return q&0xFF;
120*906943f9SDavid du Colombier }
121*906943f9SDavid du Colombier 
122*906943f9SDavid du Colombier static int
123*906943f9SDavid du Colombier qnum(vlong q)
124*906943f9SDavid du Colombier {
125*906943f9SDavid du Colombier 	return (q >> 8) & 0xFFFFFF;
126*906943f9SDavid du Colombier }
127*906943f9SDavid du Colombier 
128*906943f9SDavid du Colombier static uvlong
129*906943f9SDavid du Colombier mkqid(int n, int t)
130*906943f9SDavid du Colombier {
131*906943f9SDavid du Colombier 	uvlong q;
132*906943f9SDavid du Colombier 
133*906943f9SDavid du Colombier 	q =  (n&0xFFFFFF) << 8 | t&0xFF;
134*906943f9SDavid du Colombier 	return q;
135*906943f9SDavid du Colombier }
136*906943f9SDavid du Colombier 
137*906943f9SDavid du Colombier static void
138*906943f9SDavid du Colombier freebuf(Ether *e, Buf *bp)
139*906943f9SDavid du Colombier {
140*906943f9SDavid du Colombier 	if(0)deprint(2, "%s: freebuf %#p\n", argv0, bp);
141*906943f9SDavid du Colombier 	if(bp != nil){
142*906943f9SDavid du Colombier 		qlock(e);
143*906943f9SDavid du Colombier 		e->nbufs--;
144*906943f9SDavid du Colombier 		qunlock(e);
145*906943f9SDavid du Colombier 		sendp(e->bc, bp);
146*906943f9SDavid du Colombier 	}
147*906943f9SDavid du Colombier }
148*906943f9SDavid du Colombier 
149*906943f9SDavid du Colombier static Buf*
150*906943f9SDavid du Colombier allocbuf(Ether *e)
151*906943f9SDavid du Colombier {
152*906943f9SDavid du Colombier 	Buf *bp;
153*906943f9SDavid du Colombier 
154*906943f9SDavid du Colombier 	bp = nbrecvp(e->bc);
155*906943f9SDavid du Colombier 	if(bp == nil){
156*906943f9SDavid du Colombier 		qlock(e);
157*906943f9SDavid du Colombier 		if(e->nabufs < Nconns){
158*906943f9SDavid du Colombier 			bp = emallocz(sizeof(Buf), 1);
159*906943f9SDavid du Colombier 			e->nabufs++;
160*906943f9SDavid du Colombier 			setmalloctag(bp, getcallerpc(&e));
161*906943f9SDavid du Colombier 			deprint(2, "%s: %d buffers\n", argv0, e->nabufs);
162*906943f9SDavid du Colombier 		}
163*906943f9SDavid du Colombier 		qunlock(e);
164*906943f9SDavid du Colombier 	}
165*906943f9SDavid du Colombier 	if(bp == nil)
166*906943f9SDavid du Colombier 		bp = recvp(e->bc);
167*906943f9SDavid du Colombier 	bp->rp = bp->data + Hdrsize;
168*906943f9SDavid du Colombier 	bp->ndata = 0;
169*906943f9SDavid du Colombier 	if(0)deprint(2, "%s: allocbuf %#p\n", argv0, bp);
170*906943f9SDavid du Colombier 	qlock(e);
171*906943f9SDavid du Colombier 	e->nbufs++;
172*906943f9SDavid du Colombier 	qunlock(e);
173*906943f9SDavid du Colombier 	return bp;
174*906943f9SDavid du Colombier }
175*906943f9SDavid du Colombier 
176*906943f9SDavid du Colombier static Conn*
177*906943f9SDavid du Colombier newconn(Ether *e)
178*906943f9SDavid du Colombier {
179*906943f9SDavid du Colombier 	int i;
180*906943f9SDavid du Colombier 	Conn *c;
181*906943f9SDavid du Colombier 
182*906943f9SDavid du Colombier 	qlock(e);
183*906943f9SDavid du Colombier 	for(i = 0; i < nelem(e->conns); i++){
184*906943f9SDavid du Colombier 		c = e->conns[i];
185*906943f9SDavid du Colombier 		if(c == nil || c->ref == 0){
186*906943f9SDavid du Colombier 			if(c == nil){
187*906943f9SDavid du Colombier 				c = emallocz(sizeof(Conn), 1);
188*906943f9SDavid du Colombier 				c->rc = chancreate(sizeof(Buf*), 2);
189*906943f9SDavid du Colombier 				c->nb = i;
190*906943f9SDavid du Colombier 			}
191*906943f9SDavid du Colombier 			c->ref = 1;
192*906943f9SDavid du Colombier 			if(i == e->nconns)
193*906943f9SDavid du Colombier 				e->nconns++;
194*906943f9SDavid du Colombier 			e->conns[i] = c;
195*906943f9SDavid du Colombier 			deprint(2, "%s: newconn %d\n", argv0, i);
196*906943f9SDavid du Colombier 			qunlock(e);
197*906943f9SDavid du Colombier 			return c;
198*906943f9SDavid du Colombier 		}
199*906943f9SDavid du Colombier 	}
200*906943f9SDavid du Colombier 	qunlock(e);
201*906943f9SDavid du Colombier 	return nil;
202*906943f9SDavid du Colombier }
203*906943f9SDavid du Colombier 
204*906943f9SDavid du Colombier static char*
205*906943f9SDavid du Colombier seprintaddr(char *s, char *se, uchar *addr)
206*906943f9SDavid du Colombier {
207*906943f9SDavid du Colombier 	int i;
208*906943f9SDavid du Colombier 
209*906943f9SDavid du Colombier 	for(i = 0; i < Eaddrlen; i++)
210*906943f9SDavid du Colombier 		s = seprint(s, se, "%02x", addr[i]);
211*906943f9SDavid du Colombier 	return s;
212*906943f9SDavid du Colombier }
213*906943f9SDavid du Colombier 
214*906943f9SDavid du Colombier void
215*906943f9SDavid du Colombier dumpframe(char *tag, void *p, int n)
216*906943f9SDavid du Colombier {
217*906943f9SDavid du Colombier 	Etherpkt *ep;
218*906943f9SDavid du Colombier 	char buf[128];
219*906943f9SDavid du Colombier 	char *s, *se;
220*906943f9SDavid du Colombier 	int i;
221*906943f9SDavid du Colombier 
222*906943f9SDavid du Colombier 	ep = p;
223*906943f9SDavid du Colombier 	if(n < Eaddrlen * 2 + 2){
224*906943f9SDavid du Colombier 		fprint(2, "short packet (%d bytes)\n", n);
225*906943f9SDavid du Colombier 		return;
226*906943f9SDavid du Colombier 	}
227*906943f9SDavid du Colombier 	se = buf+sizeof(buf);
228*906943f9SDavid du Colombier 	s = seprint(buf, se, "%s [%d]: ", tag, n);
229*906943f9SDavid du Colombier 	s = seprintaddr(s, se, ep->s);
230*906943f9SDavid du Colombier 	s = seprint(s, se, " -> ");
231*906943f9SDavid du Colombier 	s = seprintaddr(s, se, ep->d);
232*906943f9SDavid du Colombier 	s = seprint(s, se, " type 0x%02ux%02ux ", ep->type[0], ep->type[1]);
233*906943f9SDavid du Colombier 	n -= Eaddrlen * 2 + 2;
234*906943f9SDavid du Colombier 	for(i = 0; i < n && i < 16; i++)
235*906943f9SDavid du Colombier 		s = seprint(s, se, "%02x", ep->data[i]);
236*906943f9SDavid du Colombier 	if(n >= 16)
237*906943f9SDavid du Colombier 		fprint(2, "%s...\n", buf);
238*906943f9SDavid du Colombier 	else
239*906943f9SDavid du Colombier 		fprint(2, "%s\n", buf);
240*906943f9SDavid du Colombier }
241*906943f9SDavid du Colombier 
242*906943f9SDavid du Colombier static char*
243*906943f9SDavid du Colombier seprintstats(char *s, char *se, Ether *e)
244*906943f9SDavid du Colombier {
245*906943f9SDavid du Colombier 	qlock(e);
246*906943f9SDavid du Colombier 	s = seprint(s, se, "in: %ld\n", e->nin);
247*906943f9SDavid du Colombier 	s = seprint(s, se, "out: %ld\n", e->nout);
248*906943f9SDavid du Colombier 	s = seprint(s, se, "input errs: %ld\n", e->nierrs);
249*906943f9SDavid du Colombier 	s = seprint(s, se, "output errs: %ld\n", e->noerrs);
250*906943f9SDavid du Colombier 	s = seprint(s, se, "mbps: %d\n", e->mbps);
251*906943f9SDavid du Colombier 	s = seprint(s, se, "prom: %ld\n", e->prom.ref);
252*906943f9SDavid du Colombier 	s = seprint(s, se, "addr: ");
253*906943f9SDavid du Colombier 	s = seprintaddr(s, se, e->addr);
254*906943f9SDavid du Colombier 	s = seprint(s, se, "\n");
255*906943f9SDavid du Colombier 	qunlock(e);
256*906943f9SDavid du Colombier 	return s;
257*906943f9SDavid du Colombier }
258*906943f9SDavid du Colombier 
259*906943f9SDavid du Colombier static char*
260*906943f9SDavid du Colombier seprintifstats(char *s, char *se, Ether *e)
261*906943f9SDavid du Colombier {
262*906943f9SDavid du Colombier 	int i;
263*906943f9SDavid du Colombier 	Conn *c;
264*906943f9SDavid du Colombier 
265*906943f9SDavid du Colombier 	qlock(e);
266*906943f9SDavid du Colombier 	s = seprint(s, se, "ctlr id: %#x\n", e->cid);
267*906943f9SDavid du Colombier 	s = seprint(s, se, "phy: %#x\n", e->phy);
268*906943f9SDavid du Colombier 	s = seprint(s, se, "exiting: %#x\n", e->exiting);
269*906943f9SDavid du Colombier 	s = seprint(s, se, "conns: %d\n", e->nconns);
270*906943f9SDavid du Colombier 	s = seprint(s, se, "allocated bufs: %d\n", e->nabufs);
271*906943f9SDavid du Colombier 	s = seprint(s, se, "used bufs: %d\n", e->nbufs);
272*906943f9SDavid du Colombier 	for(i = 0; i < nelem(e->conns); i++){
273*906943f9SDavid du Colombier 		c = e->conns[i];
274*906943f9SDavid du Colombier 		if(c == nil)
275*906943f9SDavid du Colombier 			continue;
276*906943f9SDavid du Colombier 		if(c->ref == 0)
277*906943f9SDavid du Colombier 			s = seprint(s, se, "c[%d]: free\n", i);
278*906943f9SDavid du Colombier 		else{
279*906943f9SDavid du Colombier 			s = seprint(s, se, "c[%d]: refs %ld t %#x h %d p %d\n",
280*906943f9SDavid du Colombier 				c->nb, c->ref, c->type, c->headersonly, c->prom);
281*906943f9SDavid du Colombier 		}
282*906943f9SDavid du Colombier 	}
283*906943f9SDavid du Colombier 	qunlock(e);
284*906943f9SDavid du Colombier 	return s;
285*906943f9SDavid du Colombier }
286*906943f9SDavid du Colombier 
287*906943f9SDavid du Colombier static void
288*906943f9SDavid du Colombier etherdump(Ether *e)
289*906943f9SDavid du Colombier {
290*906943f9SDavid du Colombier 	char buf[256];
291*906943f9SDavid du Colombier 
292*906943f9SDavid du Colombier 	if(etherdebug == 0)
293*906943f9SDavid du Colombier 		return;
294*906943f9SDavid du Colombier 	seprintifstats(buf, buf+sizeof(buf), e);
295*906943f9SDavid du Colombier 	fprint(2, "%s: ether %#p:\n%s\n", argv0, e, buf);
296*906943f9SDavid du Colombier }
297*906943f9SDavid du Colombier 
298*906943f9SDavid du Colombier static Conn*
299*906943f9SDavid du Colombier getconn(Ether *e, int i, int idleok)
300*906943f9SDavid du Colombier {
301*906943f9SDavid du Colombier 	Conn *c;
302*906943f9SDavid du Colombier 
303*906943f9SDavid du Colombier 	qlock(e);
304*906943f9SDavid du Colombier 	if(i < 0 || i >= e->nconns)
305*906943f9SDavid du Colombier 		c = nil;
306*906943f9SDavid du Colombier 	else{
307*906943f9SDavid du Colombier 		c = e->conns[i];
308*906943f9SDavid du Colombier 		if(idleok == 0 && c != nil && c->ref == 0)
309*906943f9SDavid du Colombier 			c = nil;
310*906943f9SDavid du Colombier 	}
311*906943f9SDavid du Colombier 	qunlock(e);
312*906943f9SDavid du Colombier 	return c;
313*906943f9SDavid du Colombier }
314*906943f9SDavid du Colombier 
315*906943f9SDavid du Colombier static void
316*906943f9SDavid du Colombier filldir(Usbfs *fs, Dir *d, Dirtab *tab, int cn)
317*906943f9SDavid du Colombier {
318*906943f9SDavid du Colombier 	d->qid.path = mkqid(cn, tab->qid);
319*906943f9SDavid du Colombier 	d->qid.path |= fs->qid;
320*906943f9SDavid du Colombier 	d->mode = tab->mode;
321*906943f9SDavid du Colombier 	if((d->mode & DMDIR) != 0)
322*906943f9SDavid du Colombier 		d->qid.type = QTDIR;
323*906943f9SDavid du Colombier 	else
324*906943f9SDavid du Colombier 		d->qid.type = QTFILE;
325*906943f9SDavid du Colombier 	if(tab->qid == Qndir)
326*906943f9SDavid du Colombier 		sprint(d->name, "%d", cn);
327*906943f9SDavid du Colombier 	else
328*906943f9SDavid du Colombier 		d->name = tab->name;
329*906943f9SDavid du Colombier }
330*906943f9SDavid du Colombier 
331*906943f9SDavid du Colombier static int
332*906943f9SDavid du Colombier rootdirgen(Usbfs *fs, Qid, int i, Dir *d, void *)
333*906943f9SDavid du Colombier {
334*906943f9SDavid du Colombier 	Ether *e;
335*906943f9SDavid du Colombier 	Dirtab *tab;
336*906943f9SDavid du Colombier 	int cn;
337*906943f9SDavid du Colombier 
338*906943f9SDavid du Colombier 	e = fs->aux;
339*906943f9SDavid du Colombier 	i++;				/* skip root */
340*906943f9SDavid du Colombier 	cn = 0;
341*906943f9SDavid du Colombier 	if(i < nelem(rootdirtab) - 1)	/* null terminated */
342*906943f9SDavid du Colombier 		tab = &rootdirtab[i];
343*906943f9SDavid du Colombier 	else{
344*906943f9SDavid du Colombier 		cn = i - nelem(rootdirtab) + 1;
345*906943f9SDavid du Colombier 		if(cn < e->nconns)
346*906943f9SDavid du Colombier 			tab = &conndirtab[0];
347*906943f9SDavid du Colombier 		else
348*906943f9SDavid du Colombier 			return -1;
349*906943f9SDavid du Colombier 	}
350*906943f9SDavid du Colombier 	filldir(fs, d, tab, cn);
351*906943f9SDavid du Colombier 	return 0;
352*906943f9SDavid du Colombier 
353*906943f9SDavid du Colombier }
354*906943f9SDavid du Colombier 
355*906943f9SDavid du Colombier static int
356*906943f9SDavid du Colombier conndirgen(Usbfs *fs, Qid q, int i, Dir *d, void *)
357*906943f9SDavid du Colombier {
358*906943f9SDavid du Colombier 	Dirtab *tab;
359*906943f9SDavid du Colombier 
360*906943f9SDavid du Colombier 	i++;				/* skip root */
361*906943f9SDavid du Colombier 	if(i < nelem(conndirtab) - 1)	/* null terminated */
362*906943f9SDavid du Colombier 		tab = &conndirtab[i];
363*906943f9SDavid du Colombier 	else
364*906943f9SDavid du Colombier 		return -1;
365*906943f9SDavid du Colombier 	filldir(fs, d, tab, qnum(q.path));
366*906943f9SDavid du Colombier 	return 0;
367*906943f9SDavid du Colombier }
368*906943f9SDavid du Colombier 
369*906943f9SDavid du Colombier static int
370*906943f9SDavid du Colombier fswalk(Usbfs *fs, Fid *fid, char *name)
371*906943f9SDavid du Colombier {
372*906943f9SDavid du Colombier 	Ether *e;
373*906943f9SDavid du Colombier 	int i;
374*906943f9SDavid du Colombier 	Qid qid;
375*906943f9SDavid du Colombier 	char *es;
376*906943f9SDavid du Colombier 	Dirtab *tab;
377*906943f9SDavid du Colombier 	int cn;
378*906943f9SDavid du Colombier 
379*906943f9SDavid du Colombier 	e = fs->aux;
380*906943f9SDavid du Colombier 	qid = fid->qid;
381*906943f9SDavid du Colombier 	qid.path &= ~fs->qid;
382*906943f9SDavid du Colombier 	if((qid.type & QTDIR) == 0){
383*906943f9SDavid du Colombier 		werrstr("walk in non-directory");
384*906943f9SDavid du Colombier 		return -1;
385*906943f9SDavid du Colombier 	}
386*906943f9SDavid du Colombier 
387*906943f9SDavid du Colombier 	if(strcmp(name, "..") == 0){
388*906943f9SDavid du Colombier 		/* must be /etherU%d; i.e. our root dir. */
389*906943f9SDavid du Colombier 		fid->qid.path = mkqid(0, Qroot) | fs->qid;
390*906943f9SDavid du Colombier 		fid->qid.vers = 0;
391*906943f9SDavid du Colombier 		fid->qid.type = QTDIR;
392*906943f9SDavid du Colombier 		return 0;
393*906943f9SDavid du Colombier 	}
394*906943f9SDavid du Colombier 	switch(qtype(qid.path)){
395*906943f9SDavid du Colombier 	case Qroot:
396*906943f9SDavid du Colombier 		if(name[0] >= '0' && name[0] <= '9'){
397*906943f9SDavid du Colombier 			es = name;
398*906943f9SDavid du Colombier 			cn = strtoul(name, &es, 10);
399*906943f9SDavid du Colombier 			if(cn >= e->nconns || *es != 0){
400*906943f9SDavid du Colombier 				werrstr(Enotfound);
401*906943f9SDavid du Colombier 				return -1;
402*906943f9SDavid du Colombier 			}
403*906943f9SDavid du Colombier 			fid->qid.path = mkqid(cn, Qndir) | fs->qid;
404*906943f9SDavid du Colombier 			fid->qid.vers = 0;
405*906943f9SDavid du Colombier 			return 0;
406*906943f9SDavid du Colombier 		}
407*906943f9SDavid du Colombier 		/* fall */
408*906943f9SDavid du Colombier 	case Qndir:
409*906943f9SDavid du Colombier 		if(qtype(qid.path) == Qroot)
410*906943f9SDavid du Colombier 			tab = rootdirtab;
411*906943f9SDavid du Colombier 		else
412*906943f9SDavid du Colombier 			tab = conndirtab;
413*906943f9SDavid du Colombier 		cn = qnum(qid.path);
414*906943f9SDavid du Colombier 		for(i = 0; tab[i].name != nil; tab++)
415*906943f9SDavid du Colombier 			if(strcmp(tab[i].name, name) == 0){
416*906943f9SDavid du Colombier 				fid->qid.path = mkqid(cn, tab[i].qid)|fs->qid;
417*906943f9SDavid du Colombier 				fid->qid.vers = 0;
418*906943f9SDavid du Colombier 				if((tab[i].mode & DMDIR) != 0)
419*906943f9SDavid du Colombier 					fid->qid.type = QTDIR;
420*906943f9SDavid du Colombier 				else
421*906943f9SDavid du Colombier 					fid->qid.type = QTFILE;
422*906943f9SDavid du Colombier 				return 0;
423*906943f9SDavid du Colombier 			}
424*906943f9SDavid du Colombier 		break;
425*906943f9SDavid du Colombier 	default:
426*906943f9SDavid du Colombier 		sysfatal("usb: ether: fswalk bug");
427*906943f9SDavid du Colombier 	}
428*906943f9SDavid du Colombier 	return -1;
429*906943f9SDavid du Colombier }
430*906943f9SDavid du Colombier 
431*906943f9SDavid du Colombier static Dirtab*
432*906943f9SDavid du Colombier qdirtab(vlong q)
433*906943f9SDavid du Colombier {
434*906943f9SDavid du Colombier 	int qt;
435*906943f9SDavid du Colombier 	Dirtab *tab;
436*906943f9SDavid du Colombier 	int i;
437*906943f9SDavid du Colombier 
438*906943f9SDavid du Colombier 	qt = qtype(q);
439*906943f9SDavid du Colombier 	if(qt < nelem(rootdirtab) - 1){	/* null terminated */
440*906943f9SDavid du Colombier 		tab = rootdirtab;
441*906943f9SDavid du Colombier 		i = qt;
442*906943f9SDavid du Colombier 	}else{
443*906943f9SDavid du Colombier 		tab = conndirtab;
444*906943f9SDavid du Colombier 		i = qt - (nelem(rootdirtab) - 1);
445*906943f9SDavid du Colombier 		assert(i < nelem(conndirtab) - 1);
446*906943f9SDavid du Colombier 	}
447*906943f9SDavid du Colombier 	return &tab[i];
448*906943f9SDavid du Colombier }
449*906943f9SDavid du Colombier 
450*906943f9SDavid du Colombier static int
451*906943f9SDavid du Colombier fsstat(Usbfs *fs, Qid qid, Dir *d)
452*906943f9SDavid du Colombier {
453*906943f9SDavid du Colombier 	filldir(fs, d, qdirtab(qid.path), qnum(qid.path));
454*906943f9SDavid du Colombier 	return 0;
455*906943f9SDavid du Colombier }
456*906943f9SDavid du Colombier 
457*906943f9SDavid du Colombier static int
458*906943f9SDavid du Colombier fsopen(Usbfs *fs, Fid *fid, int omode)
459*906943f9SDavid du Colombier {
460*906943f9SDavid du Colombier 	Ether *e;
461*906943f9SDavid du Colombier 	int qt;
462*906943f9SDavid du Colombier 	Dirtab *tab;
463*906943f9SDavid du Colombier 	Conn *c;
464*906943f9SDavid du Colombier 	vlong qid;
465*906943f9SDavid du Colombier 
466*906943f9SDavid du Colombier 	qid = fid->qid.path & ~fs->qid;
467*906943f9SDavid du Colombier 	e = fs->aux;
468*906943f9SDavid du Colombier 	qt = qtype(qid);
469*906943f9SDavid du Colombier 	tab = qdirtab(qid);
470*906943f9SDavid du Colombier 	omode &= 3;
471*906943f9SDavid du Colombier 	if(omode != OREAD && (tab->mode&0222) == 0){
472*906943f9SDavid du Colombier 		werrstr(Eperm);
473*906943f9SDavid du Colombier 		return -1;
474*906943f9SDavid du Colombier 	}
475*906943f9SDavid du Colombier 	switch(qt){
476*906943f9SDavid du Colombier 	case Qclone:
477*906943f9SDavid du Colombier 		c = newconn(e);
478*906943f9SDavid du Colombier 		if(c == nil){
479*906943f9SDavid du Colombier 			werrstr("no more connections");
480*906943f9SDavid du Colombier 			return -1;
481*906943f9SDavid du Colombier 		}
482*906943f9SDavid du Colombier 		fid->qid.type = QTFILE;
483*906943f9SDavid du Colombier 		fid->qid.path = mkqid(c->nb, Qnctl)|fs->qid;
484*906943f9SDavid du Colombier 		fid->qid.vers = 0;
485*906943f9SDavid du Colombier 		break;
486*906943f9SDavid du Colombier 	case Qndata:
487*906943f9SDavid du Colombier 	case Qnctl:
488*906943f9SDavid du Colombier 	case Qnifstats:
489*906943f9SDavid du Colombier 	case Qnstats:
490*906943f9SDavid du Colombier 	case Qntype:
491*906943f9SDavid du Colombier 		c = getconn(e, qnum(qid), 1);
492*906943f9SDavid du Colombier 		if(c == nil)
493*906943f9SDavid du Colombier 			sysfatal("usb: ether: fsopen bug");
494*906943f9SDavid du Colombier 		incref(c);
495*906943f9SDavid du Colombier 		break;
496*906943f9SDavid du Colombier 	}
497*906943f9SDavid du Colombier 	etherdump(e);
498*906943f9SDavid du Colombier 	return 0;
499*906943f9SDavid du Colombier }
500*906943f9SDavid du Colombier 
501*906943f9SDavid du Colombier static int
502*906943f9SDavid du Colombier prom(Ether *e, int set)
503*906943f9SDavid du Colombier {
504*906943f9SDavid du Colombier 	if(e->promiscuous != nil)
505*906943f9SDavid du Colombier 		return e->promiscuous(e, set);
506*906943f9SDavid du Colombier 	return 0;
507*906943f9SDavid du Colombier }
508*906943f9SDavid du Colombier 
509*906943f9SDavid du Colombier static void
510*906943f9SDavid du Colombier fsclunk(Usbfs *fs, Fid *fid)
511*906943f9SDavid du Colombier {
512*906943f9SDavid du Colombier 	Ether *e;
513*906943f9SDavid du Colombier 	int qt;
514*906943f9SDavid du Colombier 	Conn *c;
515*906943f9SDavid du Colombier 	vlong qid;
516*906943f9SDavid du Colombier 	Buf *bp;
517*906943f9SDavid du Colombier 
518*906943f9SDavid du Colombier 	e = fs->aux;
519*906943f9SDavid du Colombier 	qid = fid->qid.path & ~fs->qid;
520*906943f9SDavid du Colombier 	qt = qtype(qid);
521*906943f9SDavid du Colombier 	switch(qt){
522*906943f9SDavid du Colombier 	case Qndata:
523*906943f9SDavid du Colombier 	case Qnctl:
524*906943f9SDavid du Colombier 	case Qnifstats:
525*906943f9SDavid du Colombier 	case Qnstats:
526*906943f9SDavid du Colombier 	case Qntype:
527*906943f9SDavid du Colombier 		if(fid->omode != ONONE){
528*906943f9SDavid du Colombier 			c = getconn(e, qnum(qid), 0);
529*906943f9SDavid du Colombier 			if(c == nil)
530*906943f9SDavid du Colombier 				sysfatal("usb: ether: fsopen bug");
531*906943f9SDavid du Colombier 			if(decref(c) == 0){
532*906943f9SDavid du Colombier 				while((bp = nbrecvp(c->rc)) != nil)
533*906943f9SDavid du Colombier 					freebuf(e, bp);
534*906943f9SDavid du Colombier 				qlock(e);
535*906943f9SDavid du Colombier 				if(c->prom != 0)
536*906943f9SDavid du Colombier 					if(decref(&e->prom) == 0)
537*906943f9SDavid du Colombier 						prom(e, 0);
538*906943f9SDavid du Colombier 				c->prom = c->type = 0;
539*906943f9SDavid du Colombier 				qunlock(e);
540*906943f9SDavid du Colombier 			}
541*906943f9SDavid du Colombier 		}
542*906943f9SDavid du Colombier 		break;
543*906943f9SDavid du Colombier 	}
544*906943f9SDavid du Colombier 	etherdump(e);
545*906943f9SDavid du Colombier }
546*906943f9SDavid du Colombier 
547*906943f9SDavid du Colombier int
548*906943f9SDavid du Colombier parseaddr(uchar *m, char *s)
549*906943f9SDavid du Colombier {
550*906943f9SDavid du Colombier 	int i;
551*906943f9SDavid du Colombier 	int n;
552*906943f9SDavid du Colombier 	uchar v;
553*906943f9SDavid du Colombier 
554*906943f9SDavid du Colombier 	if(strlen(s) < 12)
555*906943f9SDavid du Colombier 		return -1;
556*906943f9SDavid du Colombier 	if(strlen(s) > 12 && strlen(s) < 17)
557*906943f9SDavid du Colombier 		return -1;
558*906943f9SDavid du Colombier 	for(i = n = 0; i < strlen(s); i++){
559*906943f9SDavid du Colombier 		if(s[i] == ':')
560*906943f9SDavid du Colombier 			continue;
561*906943f9SDavid du Colombier 		if(s[i] >= 'A' && s[i] <= 'F')
562*906943f9SDavid du Colombier 			v = 10 + s[i] - 'A';
563*906943f9SDavid du Colombier 		else if(s[i] >= 'a' && s[i] <= 'f')
564*906943f9SDavid du Colombier 			v = 10 + s[i] - 'a';
565*906943f9SDavid du Colombier 		else if(s[i] >= '0' && s[i] <= '9')
566*906943f9SDavid du Colombier 			v = s[i] - '0';
567*906943f9SDavid du Colombier 		else
568*906943f9SDavid du Colombier 			return -1;
569*906943f9SDavid du Colombier 		if(n&1)
570*906943f9SDavid du Colombier 			m[n/2] |= v;
571*906943f9SDavid du Colombier 		else
572*906943f9SDavid du Colombier 			m[n/2] = v<<4;
573*906943f9SDavid du Colombier 		n++;
574*906943f9SDavid du Colombier 	}
575*906943f9SDavid du Colombier 	return 0;
576*906943f9SDavid du Colombier }
577*906943f9SDavid du Colombier 
578*906943f9SDavid du Colombier static long
579*906943f9SDavid du Colombier fsread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
580*906943f9SDavid du Colombier {
581*906943f9SDavid du Colombier 	Qid q;
582*906943f9SDavid du Colombier 	Buf *bp;
583*906943f9SDavid du Colombier 	Ether *e;
584*906943f9SDavid du Colombier 	int qt;
585*906943f9SDavid du Colombier 	int cn;
586*906943f9SDavid du Colombier 	char buf[128];
587*906943f9SDavid du Colombier 	char *s;
588*906943f9SDavid du Colombier 	char *se;
589*906943f9SDavid du Colombier 	Conn *c;
590*906943f9SDavid du Colombier 
591*906943f9SDavid du Colombier 	q = fid->qid;
592*906943f9SDavid du Colombier 	q.path &= ~fs->qid;
593*906943f9SDavid du Colombier 	e = fs->aux;
594*906943f9SDavid du Colombier 	s = buf;
595*906943f9SDavid du Colombier 	se = buf+sizeof(buf);
596*906943f9SDavid du Colombier 	qt = qtype(q.path);
597*906943f9SDavid du Colombier 	cn = qnum(q.path);
598*906943f9SDavid du Colombier 	switch(qt){
599*906943f9SDavid du Colombier 	case Qroot:
600*906943f9SDavid du Colombier 		count = usbdirread(fs, q, data, count, offset, rootdirgen, nil);
601*906943f9SDavid du Colombier 		break;
602*906943f9SDavid du Colombier 	case Qaddr:
603*906943f9SDavid du Colombier 		s = seprintaddr(s, se, e->addr);
604*906943f9SDavid du Colombier 		count = usbreadbuf(data, count, offset, buf, s - buf);
605*906943f9SDavid du Colombier 		break;
606*906943f9SDavid du Colombier 	case Qnifstats:
607*906943f9SDavid du Colombier 		/* BUG */
608*906943f9SDavid du Colombier 	case Qifstats:
609*906943f9SDavid du Colombier 		s = seprintifstats(s, se, e);
610*906943f9SDavid du Colombier 		if(e->seprintstats != nil)
611*906943f9SDavid du Colombier 			s = e->seprintstats(s, se, e);
612*906943f9SDavid du Colombier 		count = usbreadbuf(data, count, offset, buf, s - buf);
613*906943f9SDavid du Colombier 		break;
614*906943f9SDavid du Colombier 	case Qnstats:
615*906943f9SDavid du Colombier 		/* BUG */
616*906943f9SDavid du Colombier 	case Qstats:
617*906943f9SDavid du Colombier 		s = seprintstats(s, se, e);
618*906943f9SDavid du Colombier 		count = usbreadbuf(data, count, offset, buf, s - buf);
619*906943f9SDavid du Colombier 		break;
620*906943f9SDavid du Colombier 
621*906943f9SDavid du Colombier 	case Qndir:
622*906943f9SDavid du Colombier 		count = usbdirread(fs, q, data, count, offset, conndirgen, nil);
623*906943f9SDavid du Colombier 		break;
624*906943f9SDavid du Colombier 	case Qndata:
625*906943f9SDavid du Colombier 		c = getconn(e, cn, 0);
626*906943f9SDavid du Colombier 		if(c == nil){
627*906943f9SDavid du Colombier 			werrstr(Eio);
628*906943f9SDavid du Colombier 			return -1;
629*906943f9SDavid du Colombier 		}
630*906943f9SDavid du Colombier 		bp = recvp(c->rc);
631*906943f9SDavid du Colombier 		if(bp == nil)
632*906943f9SDavid du Colombier 			return -1;
633*906943f9SDavid du Colombier 		if(etherdebug > 1)
634*906943f9SDavid du Colombier 			dumpframe("etherin", bp->rp, bp->ndata);
635*906943f9SDavid du Colombier 		count = usbreadbuf(data, count, 0LL, bp->rp, bp->ndata);
636*906943f9SDavid du Colombier 		freebuf(e, bp);
637*906943f9SDavid du Colombier 		break;
638*906943f9SDavid du Colombier 	case Qnctl:
639*906943f9SDavid du Colombier 		s = seprint(s, se, "%11d ", cn);
640*906943f9SDavid du Colombier 		count = usbreadbuf(data, count, offset, buf, s - buf);
641*906943f9SDavid du Colombier 		break;
642*906943f9SDavid du Colombier 	case Qntype:
643*906943f9SDavid du Colombier 		c = getconn(e, cn, 0);
644*906943f9SDavid du Colombier 		if(c == nil)
645*906943f9SDavid du Colombier 			s = seprint(s, se, "%11d ", 0);
646*906943f9SDavid du Colombier 		else
647*906943f9SDavid du Colombier 			s = seprint(s, se, "%11d ", c->type);
648*906943f9SDavid du Colombier 		count = usbreadbuf(data, count, offset, buf, s - buf);
649*906943f9SDavid du Colombier 		break;
650*906943f9SDavid du Colombier 	default:
651*906943f9SDavid du Colombier 		sysfatal("usb: ether: fsread bug");
652*906943f9SDavid du Colombier 	}
653*906943f9SDavid du Colombier 	return count;
654*906943f9SDavid du Colombier }
655*906943f9SDavid du Colombier 
656*906943f9SDavid du Colombier static int
657*906943f9SDavid du Colombier typeinuse(Ether *e, int t)
658*906943f9SDavid du Colombier {
659*906943f9SDavid du Colombier 	int i;
660*906943f9SDavid du Colombier 
661*906943f9SDavid du Colombier 	for(i = 0; i < e->nconns; i++)
662*906943f9SDavid du Colombier 		if(e->conns[i]->ref > 0 && e->conns[i]->type == t)
663*906943f9SDavid du Colombier 			return 1;
664*906943f9SDavid du Colombier 	return 0;
665*906943f9SDavid du Colombier }
666*906943f9SDavid du Colombier 
667*906943f9SDavid du Colombier static int
668*906943f9SDavid du Colombier isloopback(Ether *e, Buf *)
669*906943f9SDavid du Colombier {
670*906943f9SDavid du Colombier 	return e->prom.ref > 0; /* BUG: also loopbacks and broadcasts */
671*906943f9SDavid du Colombier }
672*906943f9SDavid du Colombier 
673*906943f9SDavid du Colombier static int
674*906943f9SDavid du Colombier etherctl(Ether *e, Conn *c, char *buf)
675*906943f9SDavid du Colombier {
676*906943f9SDavid du Colombier 	uchar addr[Eaddrlen];
677*906943f9SDavid du Colombier 	int t;
678*906943f9SDavid du Colombier 
679*906943f9SDavid du Colombier 	deprint(2, "%s: etherctl: %s\n", argv0, buf);
680*906943f9SDavid du Colombier 	if(strncmp(buf, "connect ", 8) == 0){
681*906943f9SDavid du Colombier 		t = atoi(buf+8);
682*906943f9SDavid du Colombier 		qlock(e);
683*906943f9SDavid du Colombier 		if(typeinuse(e, t)){
684*906943f9SDavid du Colombier 			werrstr("type already in use");
685*906943f9SDavid du Colombier 			qunlock(e);
686*906943f9SDavid du Colombier 			return -1;
687*906943f9SDavid du Colombier 		}
688*906943f9SDavid du Colombier 		c->type = atoi(buf+8);
689*906943f9SDavid du Colombier 		qunlock(e);
690*906943f9SDavid du Colombier 		return 0;
691*906943f9SDavid du Colombier 	}
692*906943f9SDavid du Colombier 	if(strncmp(buf, "nonblocking", 11) == 0){
693*906943f9SDavid du Colombier 		if(buf[11] == '\n' || buf[11] == 0)
694*906943f9SDavid du Colombier 			e->nblock = 1;
695*906943f9SDavid du Colombier 		else
696*906943f9SDavid du Colombier 			e->nblock = atoi(buf + 12);
697*906943f9SDavid du Colombier 		deprint(2, "%s: nblock %d\n", argv0, e->nblock);
698*906943f9SDavid du Colombier 		return 0;
699*906943f9SDavid du Colombier 	}
700*906943f9SDavid du Colombier 	if(strncmp(buf, "promiscuous", 11) == 0){
701*906943f9SDavid du Colombier 		if(c->prom == 0)
702*906943f9SDavid du Colombier 			incref(&e->prom);
703*906943f9SDavid du Colombier 		c->prom = 1;
704*906943f9SDavid du Colombier 		return prom(e, 1);
705*906943f9SDavid du Colombier 	}
706*906943f9SDavid du Colombier 	if(strncmp(buf, "headersonly", 11) == 0){
707*906943f9SDavid du Colombier 		c->headersonly = 1;
708*906943f9SDavid du Colombier 		return 0;
709*906943f9SDavid du Colombier 	}
710*906943f9SDavid du Colombier 	if(!strncmp(buf, "addmulti ", 9) || !strncmp(buf, "remmulti ", 9)){
711*906943f9SDavid du Colombier 		if(parseaddr(addr, buf+9) < 0){
712*906943f9SDavid du Colombier 			werrstr("bad address");
713*906943f9SDavid du Colombier 			return -1;
714*906943f9SDavid du Colombier 		}
715*906943f9SDavid du Colombier 		if(e->multicast == nil)
716*906943f9SDavid du Colombier 			return 0;
717*906943f9SDavid du Colombier 		if(strncmp(buf, "add", 3) == 0){
718*906943f9SDavid du Colombier 			e->nmcasts++;
719*906943f9SDavid du Colombier 			return e->multicast(e, addr, 1);
720*906943f9SDavid du Colombier 		}else{
721*906943f9SDavid du Colombier 			e->nmcasts--;
722*906943f9SDavid du Colombier 			return e->multicast(e, addr, 0);
723*906943f9SDavid du Colombier 		}
724*906943f9SDavid du Colombier 	}
725*906943f9SDavid du Colombier 
726*906943f9SDavid du Colombier 	if(e->ctl != nil)
727*906943f9SDavid du Colombier 		return e->ctl(e, buf);
728*906943f9SDavid du Colombier 	werrstr(Ebadctl);
729*906943f9SDavid du Colombier 	return -1;
730*906943f9SDavid du Colombier }
731*906943f9SDavid du Colombier 
732*906943f9SDavid du Colombier static long
733*906943f9SDavid du Colombier etherbread(Ether *e, Buf *bp)
734*906943f9SDavid du Colombier {
735*906943f9SDavid du Colombier 	deprint(2, "%s: etherbread\n", argv0);
736*906943f9SDavid du Colombier 	bp->rp = bp->data + Hdrsize;
737*906943f9SDavid du Colombier 	bp->ndata = read(e->epin->dfd, bp->rp, sizeof(bp->data)-Hdrsize);
738*906943f9SDavid du Colombier 	deprint(2, "%s: etherbread got %d bytes\n", argv0, bp->ndata);
739*906943f9SDavid du Colombier 	return bp->ndata;
740*906943f9SDavid du Colombier }
741*906943f9SDavid du Colombier 
742*906943f9SDavid du Colombier static long
743*906943f9SDavid du Colombier etherbwrite(Ether *e, Buf *bp)
744*906943f9SDavid du Colombier {
745*906943f9SDavid du Colombier 	long n;
746*906943f9SDavid du Colombier 
747*906943f9SDavid du Colombier 	deprint(2, "%s: etherbwrite %d bytes\n", argv0, bp->ndata);
748*906943f9SDavid du Colombier 	n = write(e->epout->dfd, bp->rp, bp->ndata);
749*906943f9SDavid du Colombier 	deprint(2, "%s: etherbwrite wrote %ld bytes\n", argv0, n);
750*906943f9SDavid du Colombier 	if(n <= 0)
751*906943f9SDavid du Colombier 		return n;
752*906943f9SDavid du Colombier 	if((bp->ndata % e->epout->maxpkt) == 0){
753*906943f9SDavid du Colombier 		deprint(2, "%s: short pkt write\n", argv0);
754*906943f9SDavid du Colombier 		write(e->epout->dfd, "", 0);
755*906943f9SDavid du Colombier 	}
756*906943f9SDavid du Colombier 	return n;
757*906943f9SDavid du Colombier }
758*906943f9SDavid du Colombier 
759*906943f9SDavid du Colombier static long
760*906943f9SDavid du Colombier fswrite(Usbfs *fs, Fid *fid, void *data, long count, vlong)
761*906943f9SDavid du Colombier {
762*906943f9SDavid du Colombier 	Qid q;
763*906943f9SDavid du Colombier 	Ether *e;
764*906943f9SDavid du Colombier 	int qt;
765*906943f9SDavid du Colombier 	int cn;
766*906943f9SDavid du Colombier 	char buf[128];
767*906943f9SDavid du Colombier 	Conn *c;
768*906943f9SDavid du Colombier 	Buf *bp;
769*906943f9SDavid du Colombier 
770*906943f9SDavid du Colombier 	q = fid->qid;
771*906943f9SDavid du Colombier 	q.path &= ~fs->qid;
772*906943f9SDavid du Colombier 	e = fs->aux;
773*906943f9SDavid du Colombier 	qt = qtype(q.path);
774*906943f9SDavid du Colombier 	cn = qnum(q.path);
775*906943f9SDavid du Colombier 	switch(qt){
776*906943f9SDavid du Colombier 	case Qndata:
777*906943f9SDavid du Colombier 		c = getconn(e, cn, 0);
778*906943f9SDavid du Colombier 		if(c == nil){
779*906943f9SDavid du Colombier 			werrstr(Eio);
780*906943f9SDavid du Colombier 			return -1;
781*906943f9SDavid du Colombier 		}
782*906943f9SDavid du Colombier 		bp = allocbuf(e);
783*906943f9SDavid du Colombier 		if(count > sizeof(bp->data)-Hdrsize)
784*906943f9SDavid du Colombier 			count = sizeof(bp->data)-Hdrsize;
785*906943f9SDavid du Colombier 		memmove(bp->rp, data, count);
786*906943f9SDavid du Colombier 		bp->ndata = count;
787*906943f9SDavid du Colombier 		if(etherdebug > 1)
788*906943f9SDavid du Colombier 			dumpframe("etherout", bp->rp, bp->ndata);
789*906943f9SDavid du Colombier 		if(e->nblock == 0)
790*906943f9SDavid du Colombier 			sendp(e->wc, bp);
791*906943f9SDavid du Colombier 		else if(nbsendp(e->wc, bp) < 0){
792*906943f9SDavid du Colombier 			deprint(2, "%s: (out) packet lost\n", argv0);
793*906943f9SDavid du Colombier 			freebuf(e, bp);
794*906943f9SDavid du Colombier 		}
795*906943f9SDavid du Colombier 		break;
796*906943f9SDavid du Colombier 	case Qnctl:
797*906943f9SDavid du Colombier 		c = getconn(e, cn, 0);
798*906943f9SDavid du Colombier 		if(c == nil){
799*906943f9SDavid du Colombier 			werrstr(Eio);
800*906943f9SDavid du Colombier 			return -1;
801*906943f9SDavid du Colombier 		}
802*906943f9SDavid du Colombier 		if(count > sizeof(buf) - 1)
803*906943f9SDavid du Colombier 			count = sizeof(buf) - 1;
804*906943f9SDavid du Colombier 		memmove(buf, data, count);
805*906943f9SDavid du Colombier 		buf[count] = 0;
806*906943f9SDavid du Colombier 		if(etherctl(e, c, buf) < 0)
807*906943f9SDavid du Colombier 			return -1;
808*906943f9SDavid du Colombier 		break;
809*906943f9SDavid du Colombier 	default:
810*906943f9SDavid du Colombier 		sysfatal("usb: ether: fsread bug");
811*906943f9SDavid du Colombier 	}
812*906943f9SDavid du Colombier 	return count;
813*906943f9SDavid du Colombier }
814*906943f9SDavid du Colombier 
815*906943f9SDavid du Colombier static int
816*906943f9SDavid du Colombier openeps(Ether *e, int epin, int epout)
817*906943f9SDavid du Colombier {
818*906943f9SDavid du Colombier 	e->epin = openep(e->dev, epin);
819*906943f9SDavid du Colombier 	if(e->epin == nil){
820*906943f9SDavid du Colombier 		fprint(2, "ether: in: openep %d: %r\n", epin);
821*906943f9SDavid du Colombier 		return -1;
822*906943f9SDavid du Colombier 	}
823*906943f9SDavid du Colombier 	if(epout == epin){
824*906943f9SDavid du Colombier 		incref(e->epin);
825*906943f9SDavid du Colombier 		e->epout = e->epin;
826*906943f9SDavid du Colombier 	}else
827*906943f9SDavid du Colombier 		e->epout = openep(e->dev, epout);
828*906943f9SDavid du Colombier 	if(e->epout == nil){
829*906943f9SDavid du Colombier 		fprint(2, "ether: out: openep %d: %r\n", epout);
830*906943f9SDavid du Colombier 		closedev(e->epin);
831*906943f9SDavid du Colombier 		return -1;
832*906943f9SDavid du Colombier 	}
833*906943f9SDavid du Colombier 	if(e->epin == e->epout)
834*906943f9SDavid du Colombier 		opendevdata(e->epin, ORDWR);
835*906943f9SDavid du Colombier 	else{
836*906943f9SDavid du Colombier 		opendevdata(e->epin, OREAD);
837*906943f9SDavid du Colombier 		opendevdata(e->epout, OWRITE);
838*906943f9SDavid du Colombier 	}
839*906943f9SDavid du Colombier 	if(e->epin->dfd < 0 || e->epout->dfd < 0){
840*906943f9SDavid du Colombier 		fprint(2, "ether: open i/o ep data: %r\n");
841*906943f9SDavid du Colombier 		closedev(e->epin);
842*906943f9SDavid du Colombier 		closedev(e->epout);
843*906943f9SDavid du Colombier 		return -1;
844*906943f9SDavid du Colombier 	}
845*906943f9SDavid du Colombier 	dprint(2, "ether: ep in %s out %s\n", e->epin->dir, e->epout->dir);
846*906943f9SDavid du Colombier 
847*906943f9SDavid du Colombier 	if(usbdebug > 2 || etherdebug > 2){
848*906943f9SDavid du Colombier 		devctl(e->epin, "debug 1");
849*906943f9SDavid du Colombier 		devctl(e->epout, "debug 1");
850*906943f9SDavid du Colombier 		devctl(e->dev, "debug 1");
851*906943f9SDavid du Colombier 	}
852*906943f9SDavid du Colombier 	return 0;
853*906943f9SDavid du Colombier }
854*906943f9SDavid du Colombier 
855*906943f9SDavid du Colombier static int
856*906943f9SDavid du Colombier usage(void)
857*906943f9SDavid du Colombier {
858*906943f9SDavid du Colombier 	werrstr("usage: usb/ether [-d]");
859*906943f9SDavid du Colombier 	return -1;
860*906943f9SDavid du Colombier }
861*906943f9SDavid du Colombier 
862*906943f9SDavid du Colombier static Usbfs etherfs = {
863*906943f9SDavid du Colombier 	.walk = fswalk,
864*906943f9SDavid du Colombier 	.open =	 fsopen,
865*906943f9SDavid du Colombier 	.read =	 fsread,
866*906943f9SDavid du Colombier 	.write = fswrite,
867*906943f9SDavid du Colombier 	.stat =	 fsstat,
868*906943f9SDavid du Colombier 	.clunk = fsclunk,
869*906943f9SDavid du Colombier };
870*906943f9SDavid du Colombier 
871*906943f9SDavid du Colombier static void
872*906943f9SDavid du Colombier etherfree(Ether *e)
873*906943f9SDavid du Colombier {
874*906943f9SDavid du Colombier 	int i;
875*906943f9SDavid du Colombier 	Buf *bp;
876*906943f9SDavid du Colombier 
877*906943f9SDavid du Colombier 	if(e->free != nil)
878*906943f9SDavid du Colombier 		e->free(e);
879*906943f9SDavid du Colombier 	closedev(e->epin);
880*906943f9SDavid du Colombier 	closedev(e->epout);
881*906943f9SDavid du Colombier 	if(e->rc == nil){
882*906943f9SDavid du Colombier 		free(e);
883*906943f9SDavid du Colombier 		return;
884*906943f9SDavid du Colombier 	}
885*906943f9SDavid du Colombier 	for(i = 0; i < e->nconns; i++)
886*906943f9SDavid du Colombier 		if(e->conns[i] != nil){
887*906943f9SDavid du Colombier 			while((bp = nbrecvp(e->conns[i]->rc)) != nil)
888*906943f9SDavid du Colombier 				free(bp);
889*906943f9SDavid du Colombier 			chanfree(e->conns[i]->rc);
890*906943f9SDavid du Colombier 			free(e->conns[i]);
891*906943f9SDavid du Colombier 		}
892*906943f9SDavid du Colombier 	while((bp = nbrecvp(e->bc)) != nil)
893*906943f9SDavid du Colombier 		free(bp);
894*906943f9SDavid du Colombier 	chanfree(e->bc);
895*906943f9SDavid du Colombier 	chanfree(e->rc);
896*906943f9SDavid du Colombier 	/* chanfree(e->wc);	released by writeproc */
897*906943f9SDavid du Colombier 	e->epin = e->epout = nil;
898*906943f9SDavid du Colombier 	free(e);
899*906943f9SDavid du Colombier 
900*906943f9SDavid du Colombier }
901*906943f9SDavid du Colombier 
902*906943f9SDavid du Colombier /* must return 1 if c wants bp; 0 if not */
903*906943f9SDavid du Colombier static int
904*906943f9SDavid du Colombier cwantsbp(Conn *c, Buf *bp)
905*906943f9SDavid du Colombier {
906*906943f9SDavid du Colombier 	if(c->ref != 0 && (c->prom != 0 || c->type < 0 || c->type == bp->type))
907*906943f9SDavid du Colombier 		return 1;
908*906943f9SDavid du Colombier 	return 0;
909*906943f9SDavid du Colombier }
910*906943f9SDavid du Colombier 
911*906943f9SDavid du Colombier static void
912*906943f9SDavid du Colombier etherwriteproc(void *a)
913*906943f9SDavid du Colombier {
914*906943f9SDavid du Colombier 	Ether *e = a;
915*906943f9SDavid du Colombier 	Buf *bp;
916*906943f9SDavid du Colombier 	Channel *wc;
917*906943f9SDavid du Colombier 
918*906943f9SDavid du Colombier 	wc = e->wc;
919*906943f9SDavid du Colombier 	while(e->exiting == 0){
920*906943f9SDavid du Colombier 		bp = recvp(wc);
921*906943f9SDavid du Colombier 		if(bp == nil || e->exiting != 0)
922*906943f9SDavid du Colombier 			break;
923*906943f9SDavid du Colombier 		e->nout++;
924*906943f9SDavid du Colombier 		if(e->bwrite(e, bp) < 0)
925*906943f9SDavid du Colombier 			e->noerrs++;
926*906943f9SDavid du Colombier 		if(isloopback(e, bp))
927*906943f9SDavid du Colombier 			sendp(e->rc, bp); /* send to input queue */
928*906943f9SDavid du Colombier 		else
929*906943f9SDavid du Colombier 			freebuf(e, bp);
930*906943f9SDavid du Colombier 	}
931*906943f9SDavid du Colombier 	while((bp = nbrecvp(wc)) != nil)
932*906943f9SDavid du Colombier 		free(bp);
933*906943f9SDavid du Colombier 	chanfree(wc);
934*906943f9SDavid du Colombier 	deprint(2, "%s: writeproc exiting\n", argv0);
935*906943f9SDavid du Colombier }
936*906943f9SDavid du Colombier 
937*906943f9SDavid du Colombier static void
938*906943f9SDavid du Colombier etherreadproc(void *a)
939*906943f9SDavid du Colombier {
940*906943f9SDavid du Colombier 	Ether *e = a;
941*906943f9SDavid du Colombier 	int i;
942*906943f9SDavid du Colombier 	int n;
943*906943f9SDavid du Colombier 	Buf *bp;
944*906943f9SDavid du Colombier 	Buf *dbp;
945*906943f9SDavid du Colombier 	int nwants;
946*906943f9SDavid du Colombier 
947*906943f9SDavid du Colombier 	while(e->exiting == 0){
948*906943f9SDavid du Colombier 		bp = nbrecvp(e->rc);
949*906943f9SDavid du Colombier 		if(bp == nil){
950*906943f9SDavid du Colombier 			bp = allocbuf(e);	/* seems to leak bps kept at bc */
951*906943f9SDavid du Colombier 			if(e->bread(e, bp) < 0){
952*906943f9SDavid du Colombier 				freebuf(e, bp);
953*906943f9SDavid du Colombier 				break;
954*906943f9SDavid du Colombier 			}
955*906943f9SDavid du Colombier 			if(bp->ndata == 0){
956*906943f9SDavid du Colombier 				/* may be a short packet; continue */
957*906943f9SDavid du Colombier 				if(0)dprint(2, "%s: read: short\n", argv0);
958*906943f9SDavid du Colombier 				freebuf(e, bp);
959*906943f9SDavid du Colombier 				continue;
960*906943f9SDavid du Colombier 			}
961*906943f9SDavid du Colombier 		}
962*906943f9SDavid du Colombier 		e->nin++;
963*906943f9SDavid du Colombier 		nwants = 0;
964*906943f9SDavid du Colombier 		for(i = 0; i < e->nconns; i++)
965*906943f9SDavid du Colombier 			nwants += cwantsbp(e->conns[i], bp);
966*906943f9SDavid du Colombier 		for(i = 0; nwants > 0 && i < e->nconns; i++)
967*906943f9SDavid du Colombier 			if(cwantsbp(e->conns[i], bp)){
968*906943f9SDavid du Colombier 				n = bp->ndata;
969*906943f9SDavid du Colombier 				if(e->conns[i]->type == -2 && n > 64)
970*906943f9SDavid du Colombier 					n = 64;
971*906943f9SDavid du Colombier 				if(nwants-- == 1){
972*906943f9SDavid du Colombier 					bp->ndata = n;
973*906943f9SDavid du Colombier 					dbp = bp;
974*906943f9SDavid du Colombier 					bp = nil;
975*906943f9SDavid du Colombier 				}else{
976*906943f9SDavid du Colombier 					dbp = allocbuf(e);
977*906943f9SDavid du Colombier 					memmove(dbp->rp, bp->rp, n);
978*906943f9SDavid du Colombier 					dbp->ndata = n;
979*906943f9SDavid du Colombier 				}
980*906943f9SDavid du Colombier 				if(nbsendp(e->conns[i]->rc, dbp) < 0){
981*906943f9SDavid du Colombier 					e->nierrs++;
982*906943f9SDavid du Colombier 					freebuf(e, dbp);
983*906943f9SDavid du Colombier 				}
984*906943f9SDavid du Colombier 			}
985*906943f9SDavid du Colombier 		freebuf(e, bp);
986*906943f9SDavid du Colombier 	}
987*906943f9SDavid du Colombier 	while(e->exiting == 0)	/* give them time... */
988*906943f9SDavid du Colombier 		yield();
989*906943f9SDavid du Colombier 	while((bp = nbrecvp(e->rc)) != nil)
990*906943f9SDavid du Colombier 		free(bp);
991*906943f9SDavid du Colombier 	deprint(2, "%s: writeproc exiting\n", argv0);
992*906943f9SDavid du Colombier 	etherfree(e);
993*906943f9SDavid du Colombier }
994*906943f9SDavid du Colombier 
995*906943f9SDavid du Colombier static void
996*906943f9SDavid du Colombier etherdevfree(void *a)
997*906943f9SDavid du Colombier {
998*906943f9SDavid du Colombier 	Ether *e = a;
999*906943f9SDavid du Colombier 
1000*906943f9SDavid du Colombier 	if(e == nil)
1001*906943f9SDavid du Colombier 		return;
1002*906943f9SDavid du Colombier 	if(e->free != nil)
1003*906943f9SDavid du Colombier 		e->free(e);
1004*906943f9SDavid du Colombier 	if(e->rc == nil){
1005*906943f9SDavid du Colombier 		/* no readproc; free everything ourselves */
1006*906943f9SDavid du Colombier 		etherfree(e);
1007*906943f9SDavid du Colombier 		return;
1008*906943f9SDavid du Colombier 	}
1009*906943f9SDavid du Colombier 	/* ether resources released by etherreadproc
1010*906943f9SDavid du Colombier 	 * It will exit its main look for sure, because
1011*906943f9SDavid du Colombier 	 * the endpoints must be detached by now.
1012*906943f9SDavid du Colombier 	 */
1013*906943f9SDavid du Colombier 	close(e->epin->dfd);
1014*906943f9SDavid du Colombier 	e->epin->dfd = -1;
1015*906943f9SDavid du Colombier 	close(e->epout->dfd);
1016*906943f9SDavid du Colombier 	e->epout->dfd = -1;
1017*906943f9SDavid du Colombier 	e->exiting = 1;
1018*906943f9SDavid du Colombier }
1019*906943f9SDavid du Colombier 
1020*906943f9SDavid du Colombier static void
1021*906943f9SDavid du Colombier setalt(Dev *d, int ifcid, int altid)
1022*906943f9SDavid du Colombier {
1023*906943f9SDavid du Colombier 	int r;
1024*906943f9SDavid du Colombier 
1025*906943f9SDavid du Colombier 	r = Rh2d|Rstd|Riface;
1026*906943f9SDavid du Colombier 	if(usbcmd(d, r, Rsetiface, altid, ifcid, nil, 0) < 0)
1027*906943f9SDavid du Colombier 		dprint(2, "%s: setalt ifc %d alt %d: %r\n", argv0, ifcid, altid);
1028*906943f9SDavid du Colombier }
1029*906943f9SDavid du Colombier 
1030*906943f9SDavid du Colombier static int
1031*906943f9SDavid du Colombier ifaceinit(Ether *e, Iface *ifc, int *ei, int *eo)
1032*906943f9SDavid du Colombier {
1033*906943f9SDavid du Colombier 	int i;
1034*906943f9SDavid du Colombier 	Ep *ep;
1035*906943f9SDavid du Colombier 	int epin;
1036*906943f9SDavid du Colombier 	int epout;
1037*906943f9SDavid du Colombier 	int altid;
1038*906943f9SDavid du Colombier 
1039*906943f9SDavid du Colombier 	if(ifc == nil)
1040*906943f9SDavid du Colombier 		return -1;
1041*906943f9SDavid du Colombier 
1042*906943f9SDavid du Colombier 	altid = epin = epout = -1;
1043*906943f9SDavid du Colombier 	for(i = 0; (epin < 0 || epout < 0) && i < nelem(ifc->ep); i++)
1044*906943f9SDavid du Colombier 		if((ep = ifc->ep[i]) != nil && ep->type == Ebulk){
1045*906943f9SDavid du Colombier 			if(ep->dir == Eboth || ep->dir == Ein)
1046*906943f9SDavid du Colombier 				if(epin == -1)
1047*906943f9SDavid du Colombier 					epin =  ep->id;
1048*906943f9SDavid du Colombier 			if(ep->dir == Eboth || ep->dir == Eout)
1049*906943f9SDavid du Colombier 				if(epout == -1)
1050*906943f9SDavid du Colombier 					epout = ep->id;
1051*906943f9SDavid du Colombier 		}
1052*906943f9SDavid du Colombier 	if(epin == -1 || epout == -1)
1053*906943f9SDavid du Colombier 		return -1;
1054*906943f9SDavid du Colombier 	dprint(2, "ether: ep ids: in %d out %d\n", epin, epout);
1055*906943f9SDavid du Colombier 	for(i = 0; i < nelem(ifc->altc); i++)
1056*906943f9SDavid du Colombier 		if(ifc->altc[i] != nil){
1057*906943f9SDavid du Colombier 			altid = ifc->altc[i]->attrib;
1058*906943f9SDavid du Colombier 			break;
1059*906943f9SDavid du Colombier 		}
1060*906943f9SDavid du Colombier 	if(altid != -1)
1061*906943f9SDavid du Colombier 		setalt(e->dev, ifc->id, altid);
1062*906943f9SDavid du Colombier 
1063*906943f9SDavid du Colombier 	*ei = epin;
1064*906943f9SDavid du Colombier 	*eo = epout;
1065*906943f9SDavid du Colombier 	return 0;
1066*906943f9SDavid du Colombier }
1067*906943f9SDavid du Colombier 
1068*906943f9SDavid du Colombier static int
1069*906943f9SDavid du Colombier etherinit(Ether *e, int *ei, int *eo)
1070*906943f9SDavid du Colombier {
1071*906943f9SDavid du Colombier 	Usbdev *ud;
1072*906943f9SDavid du Colombier 	Conf *c;
1073*906943f9SDavid du Colombier 	int i;
1074*906943f9SDavid du Colombier 	int j;
1075*906943f9SDavid du Colombier 
1076*906943f9SDavid du Colombier 	*ei = *eo = -1;
1077*906943f9SDavid du Colombier 	ud = e->dev->usb;
1078*906943f9SDavid du Colombier 	for(i = 0; i < nelem(ud->conf); i++)
1079*906943f9SDavid du Colombier 		if((c = ud->conf[i]) != nil)
1080*906943f9SDavid du Colombier 			for(j = 0; j < nelem(c->iface); j++)
1081*906943f9SDavid du Colombier 				if(ifaceinit(e,c->iface[j],ei,eo) != -1)
1082*906943f9SDavid du Colombier 					return 0;
1083*906943f9SDavid du Colombier 	dprint(2, "%s: no valid endpoints", argv0);
1084*906943f9SDavid du Colombier 	return -1;
1085*906943f9SDavid du Colombier }
1086*906943f9SDavid du Colombier 
1087*906943f9SDavid du Colombier int
1088*906943f9SDavid du Colombier ethermain(Dev *dev, int argc, char **argv)
1089*906943f9SDavid du Colombier {
1090*906943f9SDavid du Colombier 	Ether *e;
1091*906943f9SDavid du Colombier 	int epin;
1092*906943f9SDavid du Colombier 	int epout;
1093*906943f9SDavid du Colombier 	int i;
1094*906943f9SDavid du Colombier 
1095*906943f9SDavid du Colombier 	ARGBEGIN{
1096*906943f9SDavid du Colombier 	case 'd':
1097*906943f9SDavid du Colombier 		if(etherdebug == 0)
1098*906943f9SDavid du Colombier 			fprint(2, "ether debug on\n");
1099*906943f9SDavid du Colombier 		etherdebug++;
1100*906943f9SDavid du Colombier 		break;
1101*906943f9SDavid du Colombier 	default:
1102*906943f9SDavid du Colombier 		return usage();
1103*906943f9SDavid du Colombier 	}ARGEND
1104*906943f9SDavid du Colombier 	if(argc != 0)
1105*906943f9SDavid du Colombier 		return usage();
1106*906943f9SDavid du Colombier 
1107*906943f9SDavid du Colombier 	e = dev->aux = emallocz(sizeof(Ether), 1);
1108*906943f9SDavid du Colombier 	e->dev = dev;
1109*906943f9SDavid du Colombier 	dev->free = etherdevfree;
1110*906943f9SDavid du Colombier 
1111*906943f9SDavid du Colombier 	for(i = 0; i < nelem(ethers); i++)
1112*906943f9SDavid du Colombier 		if(ethers[i](e) == 0)
1113*906943f9SDavid du Colombier 			break;
1114*906943f9SDavid du Colombier 	if(i == nelem(ethers))
1115*906943f9SDavid du Colombier 		return -1;
1116*906943f9SDavid du Colombier 	if(e->init == nil)
1117*906943f9SDavid du Colombier 		e->init = etherinit;
1118*906943f9SDavid du Colombier 	if(e->init(e, &epin, &epout) < 0)
1119*906943f9SDavid du Colombier 		return -1;
1120*906943f9SDavid du Colombier 	if(e->bwrite == nil)
1121*906943f9SDavid du Colombier 		e->bwrite = etherbwrite;
1122*906943f9SDavid du Colombier 	if(e->bread == nil)
1123*906943f9SDavid du Colombier 		e->bread = etherbread;
1124*906943f9SDavid du Colombier 
1125*906943f9SDavid du Colombier 	if(openeps(e, epin, epout) < 0)
1126*906943f9SDavid du Colombier 		return -1;
1127*906943f9SDavid du Colombier 	e->fs = etherfs;
1128*906943f9SDavid du Colombier 	snprint(e->fs.name, sizeof(e->fs.name), "etherU%d", dev->id);
1129*906943f9SDavid du Colombier 	e->fs.dev = dev;
1130*906943f9SDavid du Colombier 	e->fs.aux = e;
1131*906943f9SDavid du Colombier 	e->bc = chancreate(sizeof(Buf*), Nconns);
1132*906943f9SDavid du Colombier 	e->rc = chancreate(sizeof(Buf*), Nconns/2);
1133*906943f9SDavid du Colombier 	e->wc = chancreate(sizeof(Buf*), Nconns*2);
1134*906943f9SDavid du Colombier 	proccreate(etherwriteproc, e, 16*1024);
1135*906943f9SDavid du Colombier 	proccreate(etherreadproc, e, 16*1024);
1136*906943f9SDavid du Colombier 	deprint(2, "%s: dev ref %ld\n", argv0, dev->ref);
1137*906943f9SDavid du Colombier 	usbfsadd(&e->fs);
1138*906943f9SDavid du Colombier 	return 0;
1139*906943f9SDavid du Colombier }
1140