xref: /plan9/sys/src/cmd/usb/ether/ether.c (revision a1c7250fee76ae3457375b15c3e9ab06f81bcf8e)
1906943f9SDavid du Colombier /*
2906943f9SDavid du Colombier  * usb/ether - usb ethernet adapter.
3906943f9SDavid du Colombier  * BUG: This should use /dev/etherfile to
4906943f9SDavid du Colombier  * use the kernel ether device code.
5906943f9SDavid du Colombier  */
6906943f9SDavid du Colombier #include <u.h>
7906943f9SDavid du Colombier #include <libc.h>
8906943f9SDavid du Colombier #include <fcall.h>
9906943f9SDavid du Colombier #include <thread.h>
10906943f9SDavid du Colombier #include "usb.h"
11906943f9SDavid du Colombier #include "usbfs.h"
12906943f9SDavid du Colombier #include "ether.h"
13906943f9SDavid du Colombier 
14906943f9SDavid du Colombier typedef struct Dirtab Dirtab;
15906943f9SDavid du Colombier 
16906943f9SDavid du Colombier enum
17906943f9SDavid du Colombier {
18906943f9SDavid du Colombier 	/* Qids. Maintain order (relative to dirtabs structs) */
19906943f9SDavid du Colombier 	Qroot	= 0,
20906943f9SDavid du Colombier 	Qclone,
21906943f9SDavid du Colombier 	Qaddr,
22906943f9SDavid du Colombier 	Qifstats,
23906943f9SDavid du Colombier 	Qstats,
24906943f9SDavid du Colombier 	Qndir,
25906943f9SDavid du Colombier 	Qndata,
26906943f9SDavid du Colombier 	Qnctl,
27906943f9SDavid du Colombier 	Qnifstats,
28906943f9SDavid du Colombier 	Qnstats,
29906943f9SDavid du Colombier 	Qntype,
30906943f9SDavid du Colombier 	Qmax,
31906943f9SDavid du Colombier };
32906943f9SDavid du Colombier 
33906943f9SDavid du Colombier struct Dirtab
34906943f9SDavid du Colombier {
35906943f9SDavid du Colombier 	char	*name;
36906943f9SDavid du Colombier 	int	qid;
37906943f9SDavid du Colombier 	int	mode;
38906943f9SDavid du Colombier };
39906943f9SDavid du Colombier 
40906943f9SDavid du Colombier typedef int (*Resetf)(Ether*);
41906943f9SDavid du Colombier 
42906943f9SDavid du Colombier /*
43906943f9SDavid du Colombier  * Controllers by vid/vid. Used to locate
44906943f9SDavid du Colombier  * specific adapters that do not implement cdc ethernet
45906943f9SDavid du Colombier  * Keep null terminated.
46906943f9SDavid du Colombier  */
47906943f9SDavid du Colombier Cinfo cinfo[] =
48906943f9SDavid du Colombier {
49906943f9SDavid du Colombier 	/* Asix controllers.
50*a1c7250fSDavid du Colombier 	 * Only A88178 and A88772 are implemented.
51906943f9SDavid du Colombier 	 * Others are easy to add by borrowing code
52906943f9SDavid du Colombier 	 * from other systems.
53906943f9SDavid du Colombier 	 */
54*a1c7250fSDavid du Colombier 	{0x0411, 0x003d, A8817x},	/* buffalo */
55906943f9SDavid du Colombier 	{0x0411, 0x006e, A88178},
56906943f9SDavid du Colombier 	{0x04f1, 0x3008, A8817x},
57906943f9SDavid du Colombier 	{0x050d, 0x5055, A88178},
58*a1c7250fSDavid du Colombier 	{0x0557, 0x2009, A8817x},
59906943f9SDavid du Colombier 	{0x05ac, 0x1402, A88772},	/* Apple */
60*a1c7250fSDavid du Colombier 	{0x077b, 0x2226, A8817x},
61*a1c7250fSDavid du Colombier 	{0x07aa, 0x0017, A8817x},
62*a1c7250fSDavid du Colombier 	{0x07d1, 0x3c05, A88772},
63*a1c7250fSDavid du Colombier 	{0x0b95, 0x1720, A8817x},	/* asix */
64*a1c7250fSDavid du Colombier 	{0x0b95, 0x1780, A88178},	/* Geoff */
65*a1c7250fSDavid du Colombier 	{0x0b95, 0x7720, A88772},
66906943f9SDavid du Colombier 	{0x0b95, 0x772a, A88772},
67906943f9SDavid du Colombier 	{0x0db0, 0xa877, A88772},
68*a1c7250fSDavid du Colombier 	{0x1189, 0x0893, A8817x},
69*a1c7250fSDavid du Colombier 	{0x13b1, 0x0018, A88772},
70*a1c7250fSDavid du Colombier 	{0x14ea, 0xab11, A88178},
71*a1c7250fSDavid du Colombier 	{0x1557, 0x7720, A88772},
72*a1c7250fSDavid du Colombier 	{0x1631, 0x6200, A8817x},
73*a1c7250fSDavid du Colombier 	{0x1737, 0x0039, A88178},
74*a1c7250fSDavid du Colombier 	{0x2001, 0x3c05, A88772},
75*a1c7250fSDavid du Colombier 	{0x6189, 0x182d, A8817x},
76906943f9SDavid du Colombier 	{0, 0, 0},
77906943f9SDavid du Colombier };
78906943f9SDavid du Colombier 
79906943f9SDavid du Colombier /*
80906943f9SDavid du Colombier  * Each etherU%d is the root of our file system,
81906943f9SDavid du Colombier  * which is added to the usb root directory. We only
82906943f9SDavid du Colombier  * have to concern ourselfs with each /etherU%d subtree.
83906943f9SDavid du Colombier  *
84906943f9SDavid du Colombier  * NB: Maintain order in dirtabs, relative to the Qids enum.
85906943f9SDavid du Colombier  */
86906943f9SDavid du Colombier 
87906943f9SDavid du Colombier static Dirtab rootdirtab[] =
88906943f9SDavid du Colombier {
89906943f9SDavid du Colombier 	"/",		Qroot,		DMDIR|0555,	/* etherU%d */
90906943f9SDavid du Colombier 	"clone",	Qclone,		0666,
91906943f9SDavid du Colombier 	"addr",		Qaddr,		0444,
92906943f9SDavid du Colombier 	"ifstats",	Qifstats,	0444,
93906943f9SDavid du Colombier 	"stats",	Qstats,		0444,
94906943f9SDavid du Colombier 	/* one dir per connection here */
95906943f9SDavid du Colombier 	nil, 0, 0,
96906943f9SDavid du Colombier };
97906943f9SDavid du Colombier 
98906943f9SDavid du Colombier static Dirtab conndirtab[] =
99906943f9SDavid du Colombier {
100906943f9SDavid du Colombier 	"%d",		Qndir,		DMDIR|0555,
101906943f9SDavid du Colombier 	"data",		Qndata,		0666,
102906943f9SDavid du Colombier 	"ctl",		Qnctl,		0666,
103906943f9SDavid du Colombier 	"ifstats",	Qnifstats,	0444,
104906943f9SDavid du Colombier 	"stats",	Qnstats,	0444,
105906943f9SDavid du Colombier 	"type",		Qntype,		0444,
106906943f9SDavid du Colombier 	nil, 0,
107906943f9SDavid du Colombier };
108906943f9SDavid du Colombier 
109906943f9SDavid du Colombier int etherdebug;
110906943f9SDavid du Colombier 
111906943f9SDavid du Colombier Resetf ethers[] =
112906943f9SDavid du Colombier {
113906943f9SDavid du Colombier 	asixreset,
114906943f9SDavid du Colombier 	cdcreset,	/* keep last */
115906943f9SDavid du Colombier };
116906943f9SDavid du Colombier 
117906943f9SDavid du Colombier static int
118906943f9SDavid du Colombier qtype(vlong q)
119906943f9SDavid du Colombier {
120906943f9SDavid du Colombier 	return q&0xFF;
121906943f9SDavid du Colombier }
122906943f9SDavid du Colombier 
123906943f9SDavid du Colombier static int
124906943f9SDavid du Colombier qnum(vlong q)
125906943f9SDavid du Colombier {
126906943f9SDavid du Colombier 	return (q >> 8) & 0xFFFFFF;
127906943f9SDavid du Colombier }
128906943f9SDavid du Colombier 
129906943f9SDavid du Colombier static uvlong
130906943f9SDavid du Colombier mkqid(int n, int t)
131906943f9SDavid du Colombier {
132906943f9SDavid du Colombier 	uvlong q;
133906943f9SDavid du Colombier 
134906943f9SDavid du Colombier 	q = (n&0xFFFFFF) << 8 | t&0xFF;
135906943f9SDavid du Colombier 	return q;
136906943f9SDavid du Colombier }
137906943f9SDavid du Colombier 
138906943f9SDavid du Colombier static void
139906943f9SDavid du Colombier freebuf(Ether *e, Buf *bp)
140906943f9SDavid du Colombier {
141906943f9SDavid du Colombier 	if(0)deprint(2, "%s: freebuf %#p\n", argv0, bp);
142906943f9SDavid du Colombier 	if(bp != nil){
143906943f9SDavid du Colombier 		qlock(e);
144906943f9SDavid du Colombier 		e->nbufs--;
145906943f9SDavid du Colombier 		qunlock(e);
146906943f9SDavid du Colombier 		sendp(e->bc, bp);
147906943f9SDavid du Colombier 	}
148906943f9SDavid du Colombier }
149906943f9SDavid du Colombier 
150906943f9SDavid du Colombier static Buf*
151906943f9SDavid du Colombier allocbuf(Ether *e)
152906943f9SDavid du Colombier {
153906943f9SDavid du Colombier 	Buf *bp;
154906943f9SDavid du Colombier 
155906943f9SDavid du Colombier 	bp = nbrecvp(e->bc);
156906943f9SDavid du Colombier 	if(bp == nil){
157906943f9SDavid du Colombier 		qlock(e);
158906943f9SDavid du Colombier 		if(e->nabufs < Nconns){
159906943f9SDavid du Colombier 			bp = emallocz(sizeof(Buf), 1);
160906943f9SDavid du Colombier 			e->nabufs++;
161906943f9SDavid du Colombier 			setmalloctag(bp, getcallerpc(&e));
162906943f9SDavid du Colombier 			deprint(2, "%s: %d buffers\n", argv0, e->nabufs);
163906943f9SDavid du Colombier 		}
164906943f9SDavid du Colombier 		qunlock(e);
165906943f9SDavid du Colombier 	}
166906943f9SDavid du Colombier 	if(bp == nil)
167906943f9SDavid du Colombier 		bp = recvp(e->bc);
168906943f9SDavid du Colombier 	bp->rp = bp->data + Hdrsize;
169906943f9SDavid du Colombier 	bp->ndata = 0;
170906943f9SDavid du Colombier 	if(0)deprint(2, "%s: allocbuf %#p\n", argv0, bp);
171906943f9SDavid du Colombier 	qlock(e);
172906943f9SDavid du Colombier 	e->nbufs++;
173906943f9SDavid du Colombier 	qunlock(e);
174906943f9SDavid du Colombier 	return bp;
175906943f9SDavid du Colombier }
176906943f9SDavid du Colombier 
177906943f9SDavid du Colombier static Conn*
178906943f9SDavid du Colombier newconn(Ether *e)
179906943f9SDavid du Colombier {
180906943f9SDavid du Colombier 	int i;
181906943f9SDavid du Colombier 	Conn *c;
182906943f9SDavid du Colombier 
183906943f9SDavid du Colombier 	qlock(e);
184906943f9SDavid du Colombier 	for(i = 0; i < nelem(e->conns); i++){
185906943f9SDavid du Colombier 		c = e->conns[i];
186906943f9SDavid du Colombier 		if(c == nil || c->ref == 0){
187906943f9SDavid du Colombier 			if(c == nil){
188906943f9SDavid du Colombier 				c = emallocz(sizeof(Conn), 1);
189906943f9SDavid du Colombier 				c->rc = chancreate(sizeof(Buf*), 2);
190906943f9SDavid du Colombier 				c->nb = i;
191906943f9SDavid du Colombier 			}
192906943f9SDavid du Colombier 			c->ref = 1;
193906943f9SDavid du Colombier 			if(i == e->nconns)
194906943f9SDavid du Colombier 				e->nconns++;
195906943f9SDavid du Colombier 			e->conns[i] = c;
196906943f9SDavid du Colombier 			deprint(2, "%s: newconn %d\n", argv0, i);
197906943f9SDavid du Colombier 			qunlock(e);
198906943f9SDavid du Colombier 			return c;
199906943f9SDavid du Colombier 		}
200906943f9SDavid du Colombier 	}
201906943f9SDavid du Colombier 	qunlock(e);
202906943f9SDavid du Colombier 	return nil;
203906943f9SDavid du Colombier }
204906943f9SDavid du Colombier 
205906943f9SDavid du Colombier static char*
206906943f9SDavid du Colombier seprintaddr(char *s, char *se, uchar *addr)
207906943f9SDavid du Colombier {
208906943f9SDavid du Colombier 	int i;
209906943f9SDavid du Colombier 
210906943f9SDavid du Colombier 	for(i = 0; i < Eaddrlen; i++)
211906943f9SDavid du Colombier 		s = seprint(s, se, "%02x", addr[i]);
212906943f9SDavid du Colombier 	return s;
213906943f9SDavid du Colombier }
214906943f9SDavid du Colombier 
215906943f9SDavid du Colombier void
216906943f9SDavid du Colombier dumpframe(char *tag, void *p, int n)
217906943f9SDavid du Colombier {
218906943f9SDavid du Colombier 	Etherpkt *ep;
219906943f9SDavid du Colombier 	char buf[128];
220906943f9SDavid du Colombier 	char *s, *se;
221906943f9SDavid du Colombier 	int i;
222906943f9SDavid du Colombier 
223906943f9SDavid du Colombier 	ep = p;
224906943f9SDavid du Colombier 	if(n < Eaddrlen * 2 + 2){
225906943f9SDavid du Colombier 		fprint(2, "short packet (%d bytes)\n", n);
226906943f9SDavid du Colombier 		return;
227906943f9SDavid du Colombier 	}
228906943f9SDavid du Colombier 	se = buf+sizeof(buf);
229906943f9SDavid du Colombier 	s = seprint(buf, se, "%s [%d]: ", tag, n);
230906943f9SDavid du Colombier 	s = seprintaddr(s, se, ep->s);
231906943f9SDavid du Colombier 	s = seprint(s, se, " -> ");
232906943f9SDavid du Colombier 	s = seprintaddr(s, se, ep->d);
233906943f9SDavid du Colombier 	s = seprint(s, se, " type 0x%02ux%02ux ", ep->type[0], ep->type[1]);
234906943f9SDavid du Colombier 	n -= Eaddrlen * 2 + 2;
235906943f9SDavid du Colombier 	for(i = 0; i < n && i < 16; i++)
236906943f9SDavid du Colombier 		s = seprint(s, se, "%02x", ep->data[i]);
237906943f9SDavid du Colombier 	if(n >= 16)
238906943f9SDavid du Colombier 		fprint(2, "%s...\n", buf);
239906943f9SDavid du Colombier 	else
240906943f9SDavid du Colombier 		fprint(2, "%s\n", buf);
241906943f9SDavid du Colombier }
242906943f9SDavid du Colombier 
243906943f9SDavid du Colombier static char*
244906943f9SDavid du Colombier seprintstats(char *s, char *se, Ether *e)
245906943f9SDavid du Colombier {
246906943f9SDavid du Colombier 	qlock(e);
247906943f9SDavid du Colombier 	s = seprint(s, se, "in: %ld\n", e->nin);
248906943f9SDavid du Colombier 	s = seprint(s, se, "out: %ld\n", e->nout);
249906943f9SDavid du Colombier 	s = seprint(s, se, "input errs: %ld\n", e->nierrs);
250906943f9SDavid du Colombier 	s = seprint(s, se, "output errs: %ld\n", e->noerrs);
251906943f9SDavid du Colombier 	s = seprint(s, se, "mbps: %d\n", e->mbps);
252906943f9SDavid du Colombier 	s = seprint(s, se, "prom: %ld\n", e->prom.ref);
253906943f9SDavid du Colombier 	s = seprint(s, se, "addr: ");
254906943f9SDavid du Colombier 	s = seprintaddr(s, se, e->addr);
255906943f9SDavid du Colombier 	s = seprint(s, se, "\n");
256906943f9SDavid du Colombier 	qunlock(e);
257906943f9SDavid du Colombier 	return s;
258906943f9SDavid du Colombier }
259906943f9SDavid du Colombier 
260906943f9SDavid du Colombier static char*
261906943f9SDavid du Colombier seprintifstats(char *s, char *se, Ether *e)
262906943f9SDavid du Colombier {
263906943f9SDavid du Colombier 	int i;
264906943f9SDavid du Colombier 	Conn *c;
265906943f9SDavid du Colombier 
266906943f9SDavid du Colombier 	qlock(e);
267906943f9SDavid du Colombier 	s = seprint(s, se, "ctlr id: %#x\n", e->cid);
268906943f9SDavid du Colombier 	s = seprint(s, se, "phy: %#x\n", e->phy);
269d37e33ffSDavid du Colombier 	s = seprint(s, se, "exiting: %s\n", e->exiting ? "y" : "n");
270906943f9SDavid du Colombier 	s = seprint(s, se, "conns: %d\n", e->nconns);
271906943f9SDavid du Colombier 	s = seprint(s, se, "allocated bufs: %d\n", e->nabufs);
272906943f9SDavid du Colombier 	s = seprint(s, se, "used bufs: %d\n", e->nbufs);
273906943f9SDavid du Colombier 	for(i = 0; i < nelem(e->conns); i++){
274906943f9SDavid du Colombier 		c = e->conns[i];
275906943f9SDavid du Colombier 		if(c == nil)
276906943f9SDavid du Colombier 			continue;
277906943f9SDavid du Colombier 		if(c->ref == 0)
278906943f9SDavid du Colombier 			s = seprint(s, se, "c[%d]: free\n", i);
279906943f9SDavid du Colombier 		else{
280906943f9SDavid du Colombier 			s = seprint(s, se, "c[%d]: refs %ld t %#x h %d p %d\n",
281906943f9SDavid du Colombier 				c->nb, c->ref, c->type, c->headersonly, c->prom);
282906943f9SDavid du Colombier 		}
283906943f9SDavid du Colombier 	}
284906943f9SDavid du Colombier 	qunlock(e);
285906943f9SDavid du Colombier 	return s;
286906943f9SDavid du Colombier }
287906943f9SDavid du Colombier 
288906943f9SDavid du Colombier static void
289906943f9SDavid du Colombier etherdump(Ether *e)
290906943f9SDavid du Colombier {
291906943f9SDavid du Colombier 	char buf[256];
292906943f9SDavid du Colombier 
293906943f9SDavid du Colombier 	if(etherdebug == 0)
294906943f9SDavid du Colombier 		return;
295906943f9SDavid du Colombier 	seprintifstats(buf, buf+sizeof(buf), e);
296906943f9SDavid du Colombier 	fprint(2, "%s: ether %#p:\n%s\n", argv0, e, buf);
297906943f9SDavid du Colombier }
298906943f9SDavid du Colombier 
299906943f9SDavid du Colombier static Conn*
300906943f9SDavid du Colombier getconn(Ether *e, int i, int idleok)
301906943f9SDavid du Colombier {
302906943f9SDavid du Colombier 	Conn *c;
303906943f9SDavid du Colombier 
304906943f9SDavid du Colombier 	qlock(e);
305906943f9SDavid du Colombier 	if(i < 0 || i >= e->nconns)
306906943f9SDavid du Colombier 		c = nil;
307906943f9SDavid du Colombier 	else{
308906943f9SDavid du Colombier 		c = e->conns[i];
309906943f9SDavid du Colombier 		if(idleok == 0 && c != nil && c->ref == 0)
310906943f9SDavid du Colombier 			c = nil;
311906943f9SDavid du Colombier 	}
312906943f9SDavid du Colombier 	qunlock(e);
313906943f9SDavid du Colombier 	return c;
314906943f9SDavid du Colombier }
315906943f9SDavid du Colombier 
316906943f9SDavid du Colombier static void
317906943f9SDavid du Colombier filldir(Usbfs *fs, Dir *d, Dirtab *tab, int cn)
318906943f9SDavid du Colombier {
319906943f9SDavid du Colombier 	d->qid.path = mkqid(cn, tab->qid);
320906943f9SDavid du Colombier 	d->qid.path |= fs->qid;
321906943f9SDavid du Colombier 	d->mode = tab->mode;
322906943f9SDavid du Colombier 	if((d->mode & DMDIR) != 0)
323906943f9SDavid du Colombier 		d->qid.type = QTDIR;
324906943f9SDavid du Colombier 	else
325906943f9SDavid du Colombier 		d->qid.type = QTFILE;
326906943f9SDavid du Colombier 	if(tab->qid == Qndir)
327e9b54818SDavid du Colombier 		snprint(d->name, Namesz, "%d", cn);
328906943f9SDavid du Colombier 	else
329906943f9SDavid du Colombier 		d->name = tab->name;
330906943f9SDavid du Colombier }
331906943f9SDavid du Colombier 
332906943f9SDavid du Colombier static int
333906943f9SDavid du Colombier rootdirgen(Usbfs *fs, Qid, int i, Dir *d, void *)
334906943f9SDavid du Colombier {
335906943f9SDavid du Colombier 	Ether *e;
336906943f9SDavid du Colombier 	Dirtab *tab;
337906943f9SDavid du Colombier 	int cn;
338906943f9SDavid du Colombier 
339906943f9SDavid du Colombier 	e = fs->aux;
340906943f9SDavid du Colombier 	i++;				/* skip root */
341906943f9SDavid du Colombier 	cn = 0;
342906943f9SDavid du Colombier 	if(i < nelem(rootdirtab) - 1)	/* null terminated */
343906943f9SDavid du Colombier 		tab = &rootdirtab[i];
344906943f9SDavid du Colombier 	else{
345906943f9SDavid du Colombier 		cn = i - nelem(rootdirtab) + 1;
346906943f9SDavid du Colombier 		if(cn < e->nconns)
347906943f9SDavid du Colombier 			tab = &conndirtab[0];
348906943f9SDavid du Colombier 		else
349906943f9SDavid du Colombier 			return -1;
350906943f9SDavid du Colombier 	}
351906943f9SDavid du Colombier 	filldir(fs, d, tab, cn);
352906943f9SDavid du Colombier 	return 0;
353906943f9SDavid du Colombier }
354906943f9SDavid du Colombier 
355906943f9SDavid du Colombier static int
356906943f9SDavid du Colombier conndirgen(Usbfs *fs, Qid q, int i, Dir *d, void *)
357906943f9SDavid du Colombier {
358906943f9SDavid du Colombier 	Dirtab *tab;
359906943f9SDavid du Colombier 
360906943f9SDavid du Colombier 	i++;				/* skip root */
361906943f9SDavid du Colombier 	if(i < nelem(conndirtab) - 1)	/* null terminated */
362906943f9SDavid du Colombier 		tab = &conndirtab[i];
363906943f9SDavid du Colombier 	else
364906943f9SDavid du Colombier 		return -1;
365906943f9SDavid du Colombier 	filldir(fs, d, tab, qnum(q.path));
366906943f9SDavid du Colombier 	return 0;
367906943f9SDavid du Colombier }
368906943f9SDavid du Colombier 
369906943f9SDavid du Colombier static int
370906943f9SDavid du Colombier fswalk(Usbfs *fs, Fid *fid, char *name)
371906943f9SDavid du Colombier {
372d37e33ffSDavid du Colombier 	int cn, i;
373906943f9SDavid du Colombier 	char *es;
374906943f9SDavid du Colombier 	Dirtab *tab;
375d37e33ffSDavid du Colombier 	Ether *e;
376d37e33ffSDavid du Colombier 	Qid qid;
377906943f9SDavid du Colombier 
378906943f9SDavid du Colombier 	e = fs->aux;
379906943f9SDavid du Colombier 	qid = fid->qid;
380906943f9SDavid du Colombier 	qid.path &= ~fs->qid;
381906943f9SDavid du Colombier 	if((qid.type & QTDIR) == 0){
382906943f9SDavid du Colombier 		werrstr("walk in non-directory");
383906943f9SDavid du Colombier 		return -1;
384906943f9SDavid du Colombier 	}
385906943f9SDavid du Colombier 
386906943f9SDavid du Colombier 	if(strcmp(name, "..") == 0){
387906943f9SDavid du Colombier 		/* must be /etherU%d; i.e. our root dir. */
388906943f9SDavid du Colombier 		fid->qid.path = mkqid(0, Qroot) | fs->qid;
389906943f9SDavid du Colombier 		fid->qid.vers = 0;
390906943f9SDavid du Colombier 		fid->qid.type = QTDIR;
391906943f9SDavid du Colombier 		return 0;
392906943f9SDavid du Colombier 	}
393906943f9SDavid du Colombier 	switch(qtype(qid.path)){
394906943f9SDavid du Colombier 	case Qroot:
395906943f9SDavid du Colombier 		if(name[0] >= '0' && name[0] <= '9'){
396906943f9SDavid du Colombier 			es = name;
397906943f9SDavid du Colombier 			cn = strtoul(name, &es, 10);
398906943f9SDavid du Colombier 			if(cn >= e->nconns || *es != 0){
399906943f9SDavid du Colombier 				werrstr(Enotfound);
400906943f9SDavid du Colombier 				return -1;
401906943f9SDavid du Colombier 			}
402906943f9SDavid du Colombier 			fid->qid.path = mkqid(cn, Qndir) | fs->qid;
403906943f9SDavid du Colombier 			fid->qid.vers = 0;
404906943f9SDavid du Colombier 			return 0;
405906943f9SDavid du Colombier 		}
406906943f9SDavid du Colombier 		/* fall */
407906943f9SDavid du Colombier 	case Qndir:
408906943f9SDavid du Colombier 		if(qtype(qid.path) == Qroot)
409906943f9SDavid du Colombier 			tab = rootdirtab;
410906943f9SDavid du Colombier 		else
411906943f9SDavid du Colombier 			tab = conndirtab;
412906943f9SDavid du Colombier 		cn = qnum(qid.path);
413906943f9SDavid du Colombier 		for(i = 0; tab[i].name != nil; tab++)
414906943f9SDavid du Colombier 			if(strcmp(tab[i].name, name) == 0){
415906943f9SDavid du Colombier 				fid->qid.path = mkqid(cn, tab[i].qid)|fs->qid;
416906943f9SDavid du Colombier 				fid->qid.vers = 0;
417906943f9SDavid du Colombier 				if((tab[i].mode & DMDIR) != 0)
418906943f9SDavid du Colombier 					fid->qid.type = QTDIR;
419906943f9SDavid du Colombier 				else
420906943f9SDavid du Colombier 					fid->qid.type = QTFILE;
421906943f9SDavid du Colombier 				return 0;
422906943f9SDavid du Colombier 			}
423906943f9SDavid du Colombier 		break;
424906943f9SDavid du Colombier 	default:
425906943f9SDavid du Colombier 		sysfatal("usb: ether: fswalk bug");
426906943f9SDavid du Colombier 	}
427906943f9SDavid du Colombier 	return -1;
428906943f9SDavid du Colombier }
429906943f9SDavid du Colombier 
430906943f9SDavid du Colombier static Dirtab*
431906943f9SDavid du Colombier qdirtab(vlong q)
432906943f9SDavid du Colombier {
433d37e33ffSDavid du Colombier 	int i, qt;
434906943f9SDavid du Colombier 	Dirtab *tab;
435906943f9SDavid du Colombier 
436906943f9SDavid du Colombier 	qt = qtype(q);
437906943f9SDavid du Colombier 	if(qt < nelem(rootdirtab) - 1){	/* null terminated */
438906943f9SDavid du Colombier 		tab = rootdirtab;
439906943f9SDavid du Colombier 		i = qt;
440906943f9SDavid du Colombier 	}else{
441906943f9SDavid du Colombier 		tab = conndirtab;
442906943f9SDavid du Colombier 		i = qt - (nelem(rootdirtab) - 1);
443906943f9SDavid du Colombier 		assert(i < nelem(conndirtab) - 1);
444906943f9SDavid du Colombier 	}
445906943f9SDavid du Colombier 	return &tab[i];
446906943f9SDavid du Colombier }
447906943f9SDavid du Colombier 
448906943f9SDavid du Colombier static int
449906943f9SDavid du Colombier fsstat(Usbfs *fs, Qid qid, Dir *d)
450906943f9SDavid du Colombier {
451906943f9SDavid du Colombier 	filldir(fs, d, qdirtab(qid.path), qnum(qid.path));
452906943f9SDavid du Colombier 	return 0;
453906943f9SDavid du Colombier }
454906943f9SDavid du Colombier 
455906943f9SDavid du Colombier static int
456906943f9SDavid du Colombier fsopen(Usbfs *fs, Fid *fid, int omode)
457906943f9SDavid du Colombier {
458906943f9SDavid du Colombier 	int qt;
459906943f9SDavid du Colombier 	vlong qid;
460d37e33ffSDavid du Colombier 	Conn *c;
461d37e33ffSDavid du Colombier 	Dirtab *tab;
462d37e33ffSDavid du Colombier 	Ether *e;
463906943f9SDavid du Colombier 
464906943f9SDavid du Colombier 	qid = fid->qid.path & ~fs->qid;
465906943f9SDavid du Colombier 	e = fs->aux;
466906943f9SDavid du Colombier 	qt = qtype(qid);
467906943f9SDavid du Colombier 	tab = qdirtab(qid);
468906943f9SDavid du Colombier 	omode &= 3;
469906943f9SDavid du Colombier 	if(omode != OREAD && (tab->mode&0222) == 0){
470906943f9SDavid du Colombier 		werrstr(Eperm);
471906943f9SDavid du Colombier 		return -1;
472906943f9SDavid du Colombier 	}
473906943f9SDavid du Colombier 	switch(qt){
474906943f9SDavid du Colombier 	case Qclone:
475906943f9SDavid du Colombier 		c = newconn(e);
476906943f9SDavid du Colombier 		if(c == nil){
477906943f9SDavid du Colombier 			werrstr("no more connections");
478906943f9SDavid du Colombier 			return -1;
479906943f9SDavid du Colombier 		}
480906943f9SDavid du Colombier 		fid->qid.type = QTFILE;
481906943f9SDavid du Colombier 		fid->qid.path = mkqid(c->nb, Qnctl)|fs->qid;
482906943f9SDavid du Colombier 		fid->qid.vers = 0;
483906943f9SDavid du Colombier 		break;
484906943f9SDavid du Colombier 	case Qndata:
485906943f9SDavid du Colombier 	case Qnctl:
486906943f9SDavid du Colombier 	case Qnifstats:
487906943f9SDavid du Colombier 	case Qnstats:
488906943f9SDavid du Colombier 	case Qntype:
489906943f9SDavid du Colombier 		c = getconn(e, qnum(qid), 1);
490906943f9SDavid du Colombier 		if(c == nil)
491906943f9SDavid du Colombier 			sysfatal("usb: ether: fsopen bug");
492906943f9SDavid du Colombier 		incref(c);
493906943f9SDavid du Colombier 		break;
494906943f9SDavid du Colombier 	}
495906943f9SDavid du Colombier 	etherdump(e);
496906943f9SDavid du Colombier 	return 0;
497906943f9SDavid du Colombier }
498906943f9SDavid du Colombier 
499906943f9SDavid du Colombier static int
500906943f9SDavid du Colombier prom(Ether *e, int set)
501906943f9SDavid du Colombier {
502906943f9SDavid du Colombier 	if(e->promiscuous != nil)
503906943f9SDavid du Colombier 		return e->promiscuous(e, set);
504906943f9SDavid du Colombier 	return 0;
505906943f9SDavid du Colombier }
506906943f9SDavid du Colombier 
507906943f9SDavid du Colombier static void
508906943f9SDavid du Colombier fsclunk(Usbfs *fs, Fid *fid)
509906943f9SDavid du Colombier {
510906943f9SDavid du Colombier 	int qt;
511906943f9SDavid du Colombier 	vlong qid;
512906943f9SDavid du Colombier 	Buf *bp;
513d37e33ffSDavid du Colombier 	Conn *c;
514d37e33ffSDavid du Colombier 	Ether *e;
515906943f9SDavid du Colombier 
516906943f9SDavid du Colombier 	e = fs->aux;
517906943f9SDavid du Colombier 	qid = fid->qid.path & ~fs->qid;
518906943f9SDavid du Colombier 	qt = qtype(qid);
519906943f9SDavid du Colombier 	switch(qt){
520906943f9SDavid du Colombier 	case Qndata:
521906943f9SDavid du Colombier 	case Qnctl:
522906943f9SDavid du Colombier 	case Qnifstats:
523906943f9SDavid du Colombier 	case Qnstats:
524906943f9SDavid du Colombier 	case Qntype:
525906943f9SDavid du Colombier 		if(fid->omode != ONONE){
526906943f9SDavid du Colombier 			c = getconn(e, qnum(qid), 0);
527906943f9SDavid du Colombier 			if(c == nil)
528906943f9SDavid du Colombier 				sysfatal("usb: ether: fsopen bug");
529906943f9SDavid du Colombier 			if(decref(c) == 0){
530906943f9SDavid du Colombier 				while((bp = nbrecvp(c->rc)) != nil)
531906943f9SDavid du Colombier 					freebuf(e, bp);
532906943f9SDavid du Colombier 				qlock(e);
533906943f9SDavid du Colombier 				if(c->prom != 0)
534906943f9SDavid du Colombier 					if(decref(&e->prom) == 0)
535906943f9SDavid du Colombier 						prom(e, 0);
536906943f9SDavid du Colombier 				c->prom = c->type = 0;
537906943f9SDavid du Colombier 				qunlock(e);
538906943f9SDavid du Colombier 			}
539906943f9SDavid du Colombier 		}
540906943f9SDavid du Colombier 		break;
541906943f9SDavid du Colombier 	}
542906943f9SDavid du Colombier 	etherdump(e);
543906943f9SDavid du Colombier }
544906943f9SDavid du Colombier 
545906943f9SDavid du Colombier int
546906943f9SDavid du Colombier parseaddr(uchar *m, char *s)
547906943f9SDavid du Colombier {
548d37e33ffSDavid du Colombier 	int i, n;
549906943f9SDavid du Colombier 	uchar v;
550906943f9SDavid du Colombier 
551906943f9SDavid du Colombier 	if(strlen(s) < 12)
552906943f9SDavid du Colombier 		return -1;
553906943f9SDavid du Colombier 	if(strlen(s) > 12 && strlen(s) < 17)
554906943f9SDavid du Colombier 		return -1;
555906943f9SDavid du Colombier 	for(i = n = 0; i < strlen(s); i++){
556906943f9SDavid du Colombier 		if(s[i] == ':')
557906943f9SDavid du Colombier 			continue;
558906943f9SDavid du Colombier 		if(s[i] >= 'A' && s[i] <= 'F')
559906943f9SDavid du Colombier 			v = 10 + s[i] - 'A';
560906943f9SDavid du Colombier 		else if(s[i] >= 'a' && s[i] <= 'f')
561906943f9SDavid du Colombier 			v = 10 + s[i] - 'a';
562906943f9SDavid du Colombier 		else if(s[i] >= '0' && s[i] <= '9')
563906943f9SDavid du Colombier 			v = s[i] - '0';
564906943f9SDavid du Colombier 		else
565906943f9SDavid du Colombier 			return -1;
566906943f9SDavid du Colombier 		if(n&1)
567906943f9SDavid du Colombier 			m[n/2] |= v;
568906943f9SDavid du Colombier 		else
569906943f9SDavid du Colombier 			m[n/2] = v<<4;
570906943f9SDavid du Colombier 		n++;
571906943f9SDavid du Colombier 	}
572906943f9SDavid du Colombier 	return 0;
573906943f9SDavid du Colombier }
574906943f9SDavid du Colombier 
575906943f9SDavid du Colombier static long
576906943f9SDavid du Colombier fsread(Usbfs *fs, Fid *fid, void *data, long count, vlong offset)
577906943f9SDavid du Colombier {
578d37e33ffSDavid du Colombier 	int cn, qt;
579d37e33ffSDavid du Colombier 	char *s, *se;
580ed868a7cSDavid du Colombier 	char buf[2048];		/* keep this large for ifstats */
581d37e33ffSDavid du Colombier 	Buf *bp;
582906943f9SDavid du Colombier 	Conn *c;
583d37e33ffSDavid du Colombier 	Ether *e;
584d37e33ffSDavid du Colombier 	Qid q;
585906943f9SDavid du Colombier 
586906943f9SDavid du Colombier 	q = fid->qid;
587906943f9SDavid du Colombier 	q.path &= ~fs->qid;
588906943f9SDavid du Colombier 	e = fs->aux;
589906943f9SDavid du Colombier 	s = buf;
590906943f9SDavid du Colombier 	se = buf+sizeof(buf);
591906943f9SDavid du Colombier 	qt = qtype(q.path);
592906943f9SDavid du Colombier 	cn = qnum(q.path);
593906943f9SDavid du Colombier 	switch(qt){
594906943f9SDavid du Colombier 	case Qroot:
595906943f9SDavid du Colombier 		count = usbdirread(fs, q, data, count, offset, rootdirgen, nil);
596906943f9SDavid du Colombier 		break;
597906943f9SDavid du Colombier 	case Qaddr:
598906943f9SDavid du Colombier 		s = seprintaddr(s, se, e->addr);
599906943f9SDavid du Colombier 		count = usbreadbuf(data, count, offset, buf, s - buf);
600906943f9SDavid du Colombier 		break;
601906943f9SDavid du Colombier 	case Qnifstats:
602906943f9SDavid du Colombier 		/* BUG */
603906943f9SDavid du Colombier 	case Qifstats:
604906943f9SDavid du Colombier 		s = seprintifstats(s, se, e);
605906943f9SDavid du Colombier 		if(e->seprintstats != nil)
606906943f9SDavid du Colombier 			s = e->seprintstats(s, se, e);
607906943f9SDavid du Colombier 		count = usbreadbuf(data, count, offset, buf, s - buf);
608906943f9SDavid du Colombier 		break;
609906943f9SDavid du Colombier 	case Qnstats:
610906943f9SDavid du Colombier 		/* BUG */
611906943f9SDavid du Colombier 	case Qstats:
612906943f9SDavid du Colombier 		s = seprintstats(s, se, e);
613906943f9SDavid du Colombier 		count = usbreadbuf(data, count, offset, buf, s - buf);
614906943f9SDavid du Colombier 		break;
615906943f9SDavid du Colombier 
616906943f9SDavid du Colombier 	case Qndir:
617906943f9SDavid du Colombier 		count = usbdirread(fs, q, data, count, offset, conndirgen, nil);
618906943f9SDavid du Colombier 		break;
619906943f9SDavid du Colombier 	case Qndata:
620906943f9SDavid du Colombier 		c = getconn(e, cn, 0);
621906943f9SDavid du Colombier 		if(c == nil){
622906943f9SDavid du Colombier 			werrstr(Eio);
623906943f9SDavid du Colombier 			return -1;
624906943f9SDavid du Colombier 		}
625906943f9SDavid du Colombier 		bp = recvp(c->rc);
626906943f9SDavid du Colombier 		if(bp == nil)
627906943f9SDavid du Colombier 			return -1;
628906943f9SDavid du Colombier 		if(etherdebug > 1)
629906943f9SDavid du Colombier 			dumpframe("etherin", bp->rp, bp->ndata);
630906943f9SDavid du Colombier 		count = usbreadbuf(data, count, 0LL, bp->rp, bp->ndata);
631906943f9SDavid du Colombier 		freebuf(e, bp);
632906943f9SDavid du Colombier 		break;
633906943f9SDavid du Colombier 	case Qnctl:
634906943f9SDavid du Colombier 		s = seprint(s, se, "%11d ", cn);
635906943f9SDavid du Colombier 		count = usbreadbuf(data, count, offset, buf, s - buf);
636906943f9SDavid du Colombier 		break;
637906943f9SDavid du Colombier 	case Qntype:
638906943f9SDavid du Colombier 		c = getconn(e, cn, 0);
639906943f9SDavid du Colombier 		if(c == nil)
640906943f9SDavid du Colombier 			s = seprint(s, se, "%11d ", 0);
641906943f9SDavid du Colombier 		else
642906943f9SDavid du Colombier 			s = seprint(s, se, "%11d ", c->type);
643906943f9SDavid du Colombier 		count = usbreadbuf(data, count, offset, buf, s - buf);
644906943f9SDavid du Colombier 		break;
645906943f9SDavid du Colombier 	default:
646906943f9SDavid du Colombier 		sysfatal("usb: ether: fsread bug");
647906943f9SDavid du Colombier 	}
648906943f9SDavid du Colombier 	return count;
649906943f9SDavid du Colombier }
650906943f9SDavid du Colombier 
651906943f9SDavid du Colombier static int
652906943f9SDavid du Colombier typeinuse(Ether *e, int t)
653906943f9SDavid du Colombier {
654906943f9SDavid du Colombier 	int i;
655906943f9SDavid du Colombier 
656906943f9SDavid du Colombier 	for(i = 0; i < e->nconns; i++)
657906943f9SDavid du Colombier 		if(e->conns[i]->ref > 0 && e->conns[i]->type == t)
658906943f9SDavid du Colombier 			return 1;
659906943f9SDavid du Colombier 	return 0;
660906943f9SDavid du Colombier }
661906943f9SDavid du Colombier 
662906943f9SDavid du Colombier static int
663906943f9SDavid du Colombier isloopback(Ether *e, Buf *)
664906943f9SDavid du Colombier {
665906943f9SDavid du Colombier 	return e->prom.ref > 0; /* BUG: also loopbacks and broadcasts */
666906943f9SDavid du Colombier }
667906943f9SDavid du Colombier 
668906943f9SDavid du Colombier static int
669906943f9SDavid du Colombier etherctl(Ether *e, Conn *c, char *buf)
670906943f9SDavid du Colombier {
671906943f9SDavid du Colombier 	uchar addr[Eaddrlen];
672906943f9SDavid du Colombier 	int t;
673906943f9SDavid du Colombier 
674906943f9SDavid du Colombier 	deprint(2, "%s: etherctl: %s\n", argv0, buf);
675906943f9SDavid du Colombier 	if(strncmp(buf, "connect ", 8) == 0){
676906943f9SDavid du Colombier 		t = atoi(buf+8);
677906943f9SDavid du Colombier 		qlock(e);
678906943f9SDavid du Colombier 		if(typeinuse(e, t)){
679906943f9SDavid du Colombier 			werrstr("type already in use");
680906943f9SDavid du Colombier 			qunlock(e);
681906943f9SDavid du Colombier 			return -1;
682906943f9SDavid du Colombier 		}
683906943f9SDavid du Colombier 		c->type = atoi(buf+8);
684906943f9SDavid du Colombier 		qunlock(e);
685906943f9SDavid du Colombier 		return 0;
686906943f9SDavid du Colombier 	}
687906943f9SDavid du Colombier 	if(strncmp(buf, "nonblocking", 11) == 0){
688906943f9SDavid du Colombier 		if(buf[11] == '\n' || buf[11] == 0)
689906943f9SDavid du Colombier 			e->nblock = 1;
690906943f9SDavid du Colombier 		else
691906943f9SDavid du Colombier 			e->nblock = atoi(buf + 12);
692906943f9SDavid du Colombier 		deprint(2, "%s: nblock %d\n", argv0, e->nblock);
693906943f9SDavid du Colombier 		return 0;
694906943f9SDavid du Colombier 	}
695906943f9SDavid du Colombier 	if(strncmp(buf, "promiscuous", 11) == 0){
696906943f9SDavid du Colombier 		if(c->prom == 0)
697906943f9SDavid du Colombier 			incref(&e->prom);
698906943f9SDavid du Colombier 		c->prom = 1;
699906943f9SDavid du Colombier 		return prom(e, 1);
700906943f9SDavid du Colombier 	}
701906943f9SDavid du Colombier 	if(strncmp(buf, "headersonly", 11) == 0){
702906943f9SDavid du Colombier 		c->headersonly = 1;
703906943f9SDavid du Colombier 		return 0;
704906943f9SDavid du Colombier 	}
705d37e33ffSDavid du Colombier 	if(strncmp(buf, "addmulti ", 9) == 0 || strncmp(buf, "remmulti ", 9) == 0){
706906943f9SDavid du Colombier 		if(parseaddr(addr, buf+9) < 0){
707906943f9SDavid du Colombier 			werrstr("bad address");
708906943f9SDavid du Colombier 			return -1;
709906943f9SDavid du Colombier 		}
710906943f9SDavid du Colombier 		if(e->multicast == nil)
711906943f9SDavid du Colombier 			return 0;
712906943f9SDavid du Colombier 		if(strncmp(buf, "add", 3) == 0){
713906943f9SDavid du Colombier 			e->nmcasts++;
714906943f9SDavid du Colombier 			return e->multicast(e, addr, 1);
715906943f9SDavid du Colombier 		}else{
716906943f9SDavid du Colombier 			e->nmcasts--;
717906943f9SDavid du Colombier 			return e->multicast(e, addr, 0);
718906943f9SDavid du Colombier 		}
719906943f9SDavid du Colombier 	}
720906943f9SDavid du Colombier 
721906943f9SDavid du Colombier 	if(e->ctl != nil)
722906943f9SDavid du Colombier 		return e->ctl(e, buf);
723906943f9SDavid du Colombier 	werrstr(Ebadctl);
724906943f9SDavid du Colombier 	return -1;
725906943f9SDavid du Colombier }
726906943f9SDavid du Colombier 
727906943f9SDavid du Colombier static long
728906943f9SDavid du Colombier etherbread(Ether *e, Buf *bp)
729906943f9SDavid du Colombier {
730906943f9SDavid du Colombier 	deprint(2, "%s: etherbread\n", argv0);
731906943f9SDavid du Colombier 	bp->rp = bp->data + Hdrsize;
732d37e33ffSDavid du Colombier 	bp->ndata = -1;
733906943f9SDavid du Colombier 	bp->ndata = read(e->epin->dfd, bp->rp, sizeof(bp->data)-Hdrsize);
734a23bc242SDavid du Colombier 	if(bp->ndata < 0){
735a23bc242SDavid du Colombier 		deprint(2, "%s: etherbread: %r\n", argv0);	/* keep { and }  */
736a23bc242SDavid du Colombier 	}else
737d37e33ffSDavid du Colombier 		deprint(2, "%s: etherbread: got %d bytes\n", argv0, bp->ndata);
738906943f9SDavid du Colombier 	return bp->ndata;
739906943f9SDavid du Colombier }
740906943f9SDavid du Colombier 
741906943f9SDavid du Colombier static long
742906943f9SDavid du Colombier etherbwrite(Ether *e, Buf *bp)
743906943f9SDavid du Colombier {
744906943f9SDavid du Colombier 	long n;
745906943f9SDavid du Colombier 
746906943f9SDavid du Colombier 	deprint(2, "%s: etherbwrite %d bytes\n", argv0, bp->ndata);
747906943f9SDavid du Colombier 	n = write(e->epout->dfd, bp->rp, bp->ndata);
748a23bc242SDavid du Colombier 	if(n < 0){
749a23bc242SDavid du Colombier 		deprint(2, "%s: etherbwrite: %r\n", argv0);	/* keep { and }  */
750a23bc242SDavid du Colombier 	}else
751906943f9SDavid du Colombier 		deprint(2, "%s: etherbwrite wrote %ld bytes\n", argv0, n);
752906943f9SDavid du Colombier 	if(n <= 0)
753906943f9SDavid du Colombier 		return n;
754906943f9SDavid du Colombier 	if((bp->ndata % e->epout->maxpkt) == 0){
755906943f9SDavid du Colombier 		deprint(2, "%s: short pkt write\n", argv0);
756d37e33ffSDavid du Colombier 		write(e->epout->dfd, "", 1);
757906943f9SDavid du Colombier 	}
758906943f9SDavid du Colombier 	return n;
759906943f9SDavid du Colombier }
760906943f9SDavid du Colombier 
761906943f9SDavid du Colombier static long
762906943f9SDavid du Colombier fswrite(Usbfs *fs, Fid *fid, void *data, long count, vlong)
763906943f9SDavid du Colombier {
764d37e33ffSDavid du Colombier 	int cn, qt;
765906943f9SDavid du Colombier 	char buf[128];
766906943f9SDavid du Colombier 	Buf *bp;
767d37e33ffSDavid du Colombier 	Conn *c;
768d37e33ffSDavid du Colombier 	Ether *e;
769d37e33ffSDavid du Colombier 	Qid q;
770906943f9SDavid du Colombier 
771906943f9SDavid du Colombier 	q = fid->qid;
772906943f9SDavid du Colombier 	q.path &= ~fs->qid;
773906943f9SDavid du Colombier 	e = fs->aux;
774906943f9SDavid du Colombier 	qt = qtype(q.path);
775906943f9SDavid du Colombier 	cn = qnum(q.path);
776906943f9SDavid du Colombier 	switch(qt){
777906943f9SDavid du Colombier 	case Qndata:
778906943f9SDavid du Colombier 		c = getconn(e, cn, 0);
779906943f9SDavid du Colombier 		if(c == nil){
780906943f9SDavid du Colombier 			werrstr(Eio);
781906943f9SDavid du Colombier 			return -1;
782906943f9SDavid du Colombier 		}
783906943f9SDavid du Colombier 		bp = allocbuf(e);
784906943f9SDavid du Colombier 		if(count > sizeof(bp->data)-Hdrsize)
785906943f9SDavid du Colombier 			count = sizeof(bp->data)-Hdrsize;
786906943f9SDavid du Colombier 		memmove(bp->rp, data, count);
787906943f9SDavid du Colombier 		bp->ndata = count;
788906943f9SDavid du Colombier 		if(etherdebug > 1)
789906943f9SDavid du Colombier 			dumpframe("etherout", bp->rp, bp->ndata);
790906943f9SDavid du Colombier 		if(e->nblock == 0)
791906943f9SDavid du Colombier 			sendp(e->wc, bp);
792a23bc242SDavid du Colombier 		else if(nbsendp(e->wc, bp) == 0){
793906943f9SDavid du Colombier 			deprint(2, "%s: (out) packet lost\n", argv0);
794906943f9SDavid du Colombier 			freebuf(e, bp);
795906943f9SDavid du Colombier 		}
796906943f9SDavid du Colombier 		break;
797906943f9SDavid du Colombier 	case Qnctl:
798906943f9SDavid du Colombier 		c = getconn(e, cn, 0);
799906943f9SDavid du Colombier 		if(c == nil){
800906943f9SDavid du Colombier 			werrstr(Eio);
801906943f9SDavid du Colombier 			return -1;
802906943f9SDavid du Colombier 		}
803906943f9SDavid du Colombier 		if(count > sizeof(buf) - 1)
804906943f9SDavid du Colombier 			count = sizeof(buf) - 1;
805906943f9SDavid du Colombier 		memmove(buf, data, count);
806906943f9SDavid du Colombier 		buf[count] = 0;
807906943f9SDavid du Colombier 		if(etherctl(e, c, buf) < 0)
808906943f9SDavid du Colombier 			return -1;
809906943f9SDavid du Colombier 		break;
810906943f9SDavid du Colombier 	default:
811906943f9SDavid du Colombier 		sysfatal("usb: ether: fsread bug");
812906943f9SDavid du Colombier 	}
813906943f9SDavid du Colombier 	return count;
814906943f9SDavid du Colombier }
815906943f9SDavid du Colombier 
816906943f9SDavid du Colombier static int
817906943f9SDavid du Colombier openeps(Ether *e, int epin, int epout)
818906943f9SDavid du Colombier {
819906943f9SDavid du Colombier 	e->epin = openep(e->dev, epin);
820906943f9SDavid du Colombier 	if(e->epin == nil){
821906943f9SDavid du Colombier 		fprint(2, "ether: in: openep %d: %r\n", epin);
822906943f9SDavid du Colombier 		return -1;
823906943f9SDavid du Colombier 	}
824906943f9SDavid du Colombier 	if(epout == epin){
825906943f9SDavid du Colombier 		incref(e->epin);
826906943f9SDavid du Colombier 		e->epout = e->epin;
827906943f9SDavid du Colombier 	}else
828906943f9SDavid du Colombier 		e->epout = openep(e->dev, epout);
829906943f9SDavid du Colombier 	if(e->epout == nil){
830906943f9SDavid du Colombier 		fprint(2, "ether: out: openep %d: %r\n", epout);
831906943f9SDavid du Colombier 		closedev(e->epin);
832906943f9SDavid du Colombier 		return -1;
833906943f9SDavid du Colombier 	}
834906943f9SDavid du Colombier 	if(e->epin == e->epout)
835906943f9SDavid du Colombier 		opendevdata(e->epin, ORDWR);
836906943f9SDavid du Colombier 	else{
837906943f9SDavid du Colombier 		opendevdata(e->epin, OREAD);
838906943f9SDavid du Colombier 		opendevdata(e->epout, OWRITE);
839906943f9SDavid du Colombier 	}
840906943f9SDavid du Colombier 	if(e->epin->dfd < 0 || e->epout->dfd < 0){
841906943f9SDavid du Colombier 		fprint(2, "ether: open i/o ep data: %r\n");
842906943f9SDavid du Colombier 		closedev(e->epin);
843906943f9SDavid du Colombier 		closedev(e->epout);
844906943f9SDavid du Colombier 		return -1;
845906943f9SDavid du Colombier 	}
846d37e33ffSDavid du Colombier 	dprint(2, "ether: ep in %s maxpkt %d; ep out %s maxpkt %d\n",
847d37e33ffSDavid du Colombier 		e->epin->dir, e->epin->maxpkt, e->epout->dir, e->epout->maxpkt);
848d37e33ffSDavid du Colombier 
849d37e33ffSDavid du Colombier 	/* time outs are not activated for I/O endpoints */
850906943f9SDavid du Colombier 
851906943f9SDavid du Colombier 	if(usbdebug > 2 || etherdebug > 2){
852906943f9SDavid du Colombier 		devctl(e->epin, "debug 1");
853906943f9SDavid du Colombier 		devctl(e->epout, "debug 1");
854906943f9SDavid du Colombier 		devctl(e->dev, "debug 1");
855906943f9SDavid du Colombier 	}
856d37e33ffSDavid du Colombier 
857906943f9SDavid du Colombier 	return 0;
858906943f9SDavid du Colombier }
859906943f9SDavid du Colombier 
860906943f9SDavid du Colombier static int
861906943f9SDavid du Colombier usage(void)
862906943f9SDavid du Colombier {
863ed868a7cSDavid du Colombier 	werrstr("usage: usb/ether [-d] [-N nb]");
864906943f9SDavid du Colombier 	return -1;
865906943f9SDavid du Colombier }
866906943f9SDavid du Colombier 
867906943f9SDavid du Colombier static Usbfs etherfs = {
868906943f9SDavid du Colombier 	.walk = fswalk,
869906943f9SDavid du Colombier 	.open =	 fsopen,
870906943f9SDavid du Colombier 	.read =	 fsread,
871906943f9SDavid du Colombier 	.write = fswrite,
872906943f9SDavid du Colombier 	.stat =	 fsstat,
873906943f9SDavid du Colombier 	.clunk = fsclunk,
874906943f9SDavid du Colombier };
875906943f9SDavid du Colombier 
876906943f9SDavid du Colombier static void
877d37e33ffSDavid du Colombier shutdownchan(Channel *c)
878d37e33ffSDavid du Colombier {
879d37e33ffSDavid du Colombier 	Buf *bp;
880d37e33ffSDavid du Colombier 
881d37e33ffSDavid du Colombier 	while((bp=nbrecvp(c)) != nil)
882d37e33ffSDavid du Colombier 		free(bp);
883d37e33ffSDavid du Colombier 	chanfree(c);
884d37e33ffSDavid du Colombier }
885d37e33ffSDavid du Colombier 
886d37e33ffSDavid du Colombier static void
887906943f9SDavid du Colombier etherfree(Ether *e)
888906943f9SDavid du Colombier {
889906943f9SDavid du Colombier 	int i;
890906943f9SDavid du Colombier 	Buf *bp;
891906943f9SDavid du Colombier 
892906943f9SDavid du Colombier 	if(e->free != nil)
893906943f9SDavid du Colombier 		e->free(e);
894906943f9SDavid du Colombier 	closedev(e->epin);
895906943f9SDavid du Colombier 	closedev(e->epout);
896d37e33ffSDavid du Colombier 	if(e->rc == nil){	/* not really started */
897906943f9SDavid du Colombier 		free(e);
898906943f9SDavid du Colombier 		return;
899906943f9SDavid du Colombier 	}
900906943f9SDavid du Colombier 	for(i = 0; i < e->nconns; i++)
901906943f9SDavid du Colombier 		if(e->conns[i] != nil){
902906943f9SDavid du Colombier 			while((bp = nbrecvp(e->conns[i]->rc)) != nil)
903906943f9SDavid du Colombier 				free(bp);
904906943f9SDavid du Colombier 			chanfree(e->conns[i]->rc);
905906943f9SDavid du Colombier 			free(e->conns[i]);
906906943f9SDavid du Colombier 		}
907d37e33ffSDavid du Colombier 	shutdownchan(e->bc);
908d37e33ffSDavid du Colombier 	shutdownchan(e->rc);
909d37e33ffSDavid du Colombier 	shutdownchan(e->wc);
910906943f9SDavid du Colombier 	e->epin = e->epout = nil;
911d5789509SDavid du Colombier 	devctl(e->dev, "detach");
912906943f9SDavid du Colombier 	free(e);
913906943f9SDavid du Colombier }
914906943f9SDavid du Colombier 
915d37e33ffSDavid du Colombier static void
916d37e33ffSDavid du Colombier etherdevfree(void *a)
917d37e33ffSDavid du Colombier {
918d37e33ffSDavid du Colombier 	Ether *e = a;
919d37e33ffSDavid du Colombier 
920d37e33ffSDavid du Colombier 	if(e != nil)
921d37e33ffSDavid du Colombier 		etherfree(e);
922d37e33ffSDavid du Colombier }
923d37e33ffSDavid du Colombier 
924906943f9SDavid du Colombier /* must return 1 if c wants bp; 0 if not */
925906943f9SDavid du Colombier static int
926906943f9SDavid du Colombier cwantsbp(Conn *c, Buf *bp)
927906943f9SDavid du Colombier {
928906943f9SDavid du Colombier 	if(c->ref != 0 && (c->prom != 0 || c->type < 0 || c->type == bp->type))
929906943f9SDavid du Colombier 		return 1;
930906943f9SDavid du Colombier 	return 0;
931906943f9SDavid du Colombier }
932906943f9SDavid du Colombier 
933906943f9SDavid du Colombier static void
934906943f9SDavid du Colombier etherwriteproc(void *a)
935906943f9SDavid du Colombier {
936906943f9SDavid du Colombier 	Ether *e = a;
937906943f9SDavid du Colombier 	Buf *bp;
938906943f9SDavid du Colombier 	Channel *wc;
939906943f9SDavid du Colombier 
940906943f9SDavid du Colombier 	wc = e->wc;
941906943f9SDavid du Colombier 	while(e->exiting == 0){
942906943f9SDavid du Colombier 		bp = recvp(wc);
943a23bc242SDavid du Colombier 		if(bp == nil || e->exiting != 0){
944a23bc242SDavid du Colombier 			free(bp);
945906943f9SDavid du Colombier 			break;
946a23bc242SDavid du Colombier 		}
947906943f9SDavid du Colombier 		e->nout++;
948906943f9SDavid du Colombier 		if(e->bwrite(e, bp) < 0)
949906943f9SDavid du Colombier 			e->noerrs++;
950d37e33ffSDavid du Colombier 		if(isloopback(e, bp) && e->exiting == 0)
951906943f9SDavid du Colombier 			sendp(e->rc, bp); /* send to input queue */
952906943f9SDavid du Colombier 		else
953906943f9SDavid du Colombier 			freebuf(e, bp);
954906943f9SDavid du Colombier 	}
955906943f9SDavid du Colombier 	deprint(2, "%s: writeproc exiting\n", argv0);
956d37e33ffSDavid du Colombier 	closedev(e->dev);
957d37e33ffSDavid du Colombier }
958d37e33ffSDavid du Colombier 
959d37e33ffSDavid du Colombier static void
960d37e33ffSDavid du Colombier setbuftype(Buf *bp)
961d37e33ffSDavid du Colombier {
962d37e33ffSDavid du Colombier 	uchar *p;
963d37e33ffSDavid du Colombier 
964d37e33ffSDavid du Colombier 	bp->type = 0;
965d37e33ffSDavid du Colombier 	if(bp->ndata >= Ehdrsize){
966d37e33ffSDavid du Colombier 		p = bp->rp + Eaddrlen*2;
967d37e33ffSDavid du Colombier 		bp->type = p[0]<<8 | p[1];
968d37e33ffSDavid du Colombier 	}
969d37e33ffSDavid du Colombier }
970d37e33ffSDavid du Colombier 
971d37e33ffSDavid du Colombier static void
972d37e33ffSDavid du Colombier etherexiting(Ether *e)
973d37e33ffSDavid du Colombier {
974d37e33ffSDavid du Colombier 	devctl(e->dev, "detach");
975d37e33ffSDavid du Colombier 	e->exiting = 1;
976d37e33ffSDavid du Colombier 	close(e->epin->dfd);
977d37e33ffSDavid du Colombier 	e->epin->dfd = -1;
978d37e33ffSDavid du Colombier 	close(e->epout->dfd);
979d37e33ffSDavid du Colombier 	e->epout->dfd = -1;
980d37e33ffSDavid du Colombier 	nbsend(e->wc, nil);
981906943f9SDavid du Colombier }
982906943f9SDavid du Colombier 
983906943f9SDavid du Colombier static void
984906943f9SDavid du Colombier etherreadproc(void *a)
985906943f9SDavid du Colombier {
986d37e33ffSDavid du Colombier 	int i, n, nwants;
987d37e33ffSDavid du Colombier 	Buf *bp, *dbp;
988906943f9SDavid du Colombier 	Ether *e = a;
989906943f9SDavid du Colombier 
990906943f9SDavid du Colombier 	while(e->exiting == 0){
991906943f9SDavid du Colombier 		bp = nbrecvp(e->rc);
992906943f9SDavid du Colombier 		if(bp == nil){
993d37e33ffSDavid du Colombier 			bp = allocbuf(e);	/* leak() may think we leak */
994906943f9SDavid du Colombier 			if(e->bread(e, bp) < 0){
995906943f9SDavid du Colombier 				freebuf(e, bp);
996906943f9SDavid du Colombier 				break;
997906943f9SDavid du Colombier 			}
998906943f9SDavid du Colombier 			if(bp->ndata == 0){
999906943f9SDavid du Colombier 				/* may be a short packet; continue */
1000906943f9SDavid du Colombier 				if(0)dprint(2, "%s: read: short\n", argv0);
1001906943f9SDavid du Colombier 				freebuf(e, bp);
1002906943f9SDavid du Colombier 				continue;
1003d37e33ffSDavid du Colombier 			}else
1004d37e33ffSDavid du Colombier 				setbuftype(bp);
1005906943f9SDavid du Colombier 		}
1006906943f9SDavid du Colombier 		e->nin++;
1007906943f9SDavid du Colombier 		nwants = 0;
1008906943f9SDavid du Colombier 		for(i = 0; i < e->nconns; i++)
1009906943f9SDavid du Colombier 			nwants += cwantsbp(e->conns[i], bp);
1010906943f9SDavid du Colombier 		for(i = 0; nwants > 0 && i < e->nconns; i++)
1011906943f9SDavid du Colombier 			if(cwantsbp(e->conns[i], bp)){
1012906943f9SDavid du Colombier 				n = bp->ndata;
1013906943f9SDavid du Colombier 				if(e->conns[i]->type == -2 && n > 64)
1014906943f9SDavid du Colombier 					n = 64;
1015906943f9SDavid du Colombier 				if(nwants-- == 1){
1016906943f9SDavid du Colombier 					bp->ndata = n;
1017906943f9SDavid du Colombier 					dbp = bp;
1018906943f9SDavid du Colombier 					bp = nil;
1019906943f9SDavid du Colombier 				}else{
1020906943f9SDavid du Colombier 					dbp = allocbuf(e);
1021906943f9SDavid du Colombier 					memmove(dbp->rp, bp->rp, n);
1022906943f9SDavid du Colombier 					dbp->ndata = n;
1023d37e33ffSDavid du Colombier 					dbp->type = bp->type;
1024906943f9SDavid du Colombier 				}
1025a23bc242SDavid du Colombier 				if(nbsendp(e->conns[i]->rc, dbp) == 0){
1026906943f9SDavid du Colombier 					e->nierrs++;
1027906943f9SDavid du Colombier 					freebuf(e, dbp);
1028906943f9SDavid du Colombier 				}
1029906943f9SDavid du Colombier 			}
1030906943f9SDavid du Colombier 		freebuf(e, bp);
1031906943f9SDavid du Colombier 	}
1032906943f9SDavid du Colombier 	deprint(2, "%s: writeproc exiting\n", argv0);
1033d37e33ffSDavid du Colombier 	etherexiting(e);
1034d37e33ffSDavid du Colombier 	closedev(e->dev);
1035d5789509SDavid du Colombier 	usbfsdel(&e->fs);
1036906943f9SDavid du Colombier }
1037906943f9SDavid du Colombier 
1038906943f9SDavid du Colombier static void
1039906943f9SDavid du Colombier setalt(Dev *d, int ifcid, int altid)
1040906943f9SDavid du Colombier {
1041d37e33ffSDavid du Colombier 	if(usbcmd(d, Rh2d|Rstd|Riface, Rsetiface, altid, ifcid, nil, 0) < 0)
1042906943f9SDavid du Colombier 		dprint(2, "%s: setalt ifc %d alt %d: %r\n", argv0, ifcid, altid);
1043906943f9SDavid du Colombier }
1044906943f9SDavid du Colombier 
1045906943f9SDavid du Colombier static int
1046906943f9SDavid du Colombier ifaceinit(Ether *e, Iface *ifc, int *ei, int *eo)
1047906943f9SDavid du Colombier {
1048906943f9SDavid du Colombier 	Ep *ep;
1049d37e33ffSDavid du Colombier 	int epin, epout, i;
1050906943f9SDavid du Colombier 
1051906943f9SDavid du Colombier 	if(ifc == nil)
1052906943f9SDavid du Colombier 		return -1;
1053906943f9SDavid du Colombier 
1054d37e33ffSDavid du Colombier 	epin = epout = -1;
1055906943f9SDavid du Colombier 	for(i = 0; (epin < 0 || epout < 0) && i < nelem(ifc->ep); i++)
1056906943f9SDavid du Colombier 		if((ep = ifc->ep[i]) != nil && ep->type == Ebulk){
1057906943f9SDavid du Colombier 			if(ep->dir == Eboth || ep->dir == Ein)
1058906943f9SDavid du Colombier 				if(epin == -1)
1059906943f9SDavid du Colombier 					epin =  ep->id;
1060906943f9SDavid du Colombier 			if(ep->dir == Eboth || ep->dir == Eout)
1061906943f9SDavid du Colombier 				if(epout == -1)
1062906943f9SDavid du Colombier 					epout = ep->id;
1063906943f9SDavid du Colombier 		}
1064906943f9SDavid du Colombier 	if(epin == -1 || epout == -1)
1065906943f9SDavid du Colombier 		return -1;
1066d37e33ffSDavid du Colombier 
1067906943f9SDavid du Colombier 	dprint(2, "ether: ep ids: in %d out %d\n", epin, epout);
1068906943f9SDavid du Colombier 	for(i = 0; i < nelem(ifc->altc); i++)
1069d37e33ffSDavid du Colombier 		if(ifc->altc[i] != nil)
1070d37e33ffSDavid du Colombier 			setalt(e->dev, ifc->id, i);
1071906943f9SDavid du Colombier 
1072906943f9SDavid du Colombier 	*ei = epin;
1073906943f9SDavid du Colombier 	*eo = epout;
1074906943f9SDavid du Colombier 	return 0;
1075906943f9SDavid du Colombier }
1076906943f9SDavid du Colombier 
1077906943f9SDavid du Colombier static int
1078906943f9SDavid du Colombier etherinit(Ether *e, int *ei, int *eo)
1079906943f9SDavid du Colombier {
1080d37e33ffSDavid du Colombier 	int ctlid, datid, i, j;
1081906943f9SDavid du Colombier 	Conf *c;
1082d37e33ffSDavid du Colombier 	Desc *desc;
1083d37e33ffSDavid du Colombier 	Iface *ctlif, *datif;
1084d37e33ffSDavid du Colombier 	Usbdev *ud;
1085906943f9SDavid du Colombier 
1086906943f9SDavid du Colombier 	*ei = *eo = -1;
1087906943f9SDavid du Colombier 	ud = e->dev->usb;
1088d37e33ffSDavid du Colombier 
1089d37e33ffSDavid du Colombier 	/* look for union descriptor with ethernet ctrl interface */
1090d37e33ffSDavid du Colombier 	for(i = 0; i < nelem(ud->ddesc); i++){
1091d37e33ffSDavid du Colombier 		if((desc = ud->ddesc[i]) == nil)
1092d37e33ffSDavid du Colombier 			continue;
1093d37e33ffSDavid du Colombier 		if(desc->data.bLength < 5 || desc->data.bbytes[0] != Cdcunion)
1094d37e33ffSDavid du Colombier 			continue;
1095d37e33ffSDavid du Colombier 
1096d37e33ffSDavid du Colombier 		ctlid = desc->data.bbytes[1];
1097d37e33ffSDavid du Colombier 		datid = desc->data.bbytes[2];
1098d37e33ffSDavid du Colombier 
1099d37e33ffSDavid du Colombier 		if((c = desc->conf) == nil)
1100d37e33ffSDavid du Colombier 			continue;
1101d37e33ffSDavid du Colombier 
1102d37e33ffSDavid du Colombier 		ctlif = datif = nil;
1103d37e33ffSDavid du Colombier 		for(j = 0; j < nelem(c->iface); j++){
1104d37e33ffSDavid du Colombier 			if(c->iface[j] == nil)
1105d37e33ffSDavid du Colombier 				continue;
1106d37e33ffSDavid du Colombier 			if(c->iface[j]->id == ctlid)
1107d37e33ffSDavid du Colombier 				ctlif = c->iface[j];
1108d37e33ffSDavid du Colombier 			if(c->iface[j]->id == datid)
1109d37e33ffSDavid du Colombier 				datif = c->iface[j];
1110d37e33ffSDavid du Colombier 
1111d37e33ffSDavid du Colombier 			if(datif != nil && ctlif != nil){
1112d37e33ffSDavid du Colombier 				if(Subclass(ctlif->csp) == Scether &&
1113d37e33ffSDavid du Colombier 				    ifaceinit(e, datif, ei, eo) != -1)
1114d37e33ffSDavid du Colombier 					return 0;
1115d37e33ffSDavid du Colombier 				break;
1116d37e33ffSDavid du Colombier 			}
1117d37e33ffSDavid du Colombier 		}
1118d37e33ffSDavid du Colombier 	}
1119d37e33ffSDavid du Colombier 	/* try any other one that seems to be ok */
1120906943f9SDavid du Colombier 	for(i = 0; i < nelem(ud->conf); i++)
1121906943f9SDavid du Colombier 		if((c = ud->conf[i]) != nil)
1122906943f9SDavid du Colombier 			for(j = 0; j < nelem(c->iface); j++)
1123906943f9SDavid du Colombier 				if(ifaceinit(e, c->iface[j], ei, eo) != -1)
1124906943f9SDavid du Colombier 					return 0;
1125906943f9SDavid du Colombier 	dprint(2, "%s: no valid endpoints", argv0);
1126906943f9SDavid du Colombier 	return -1;
1127906943f9SDavid du Colombier }
1128906943f9SDavid du Colombier 
1129906943f9SDavid du Colombier int
1130906943f9SDavid du Colombier ethermain(Dev *dev, int argc, char **argv)
1131906943f9SDavid du Colombier {
1132ed868a7cSDavid du Colombier 	int epin, epout, i, devid;
1133906943f9SDavid du Colombier 	Ether *e;
1134906943f9SDavid du Colombier 
1135ed868a7cSDavid du Colombier 	devid = dev->id;
1136906943f9SDavid du Colombier 	ARGBEGIN{
1137906943f9SDavid du Colombier 	case 'd':
1138906943f9SDavid du Colombier 		if(etherdebug == 0)
1139906943f9SDavid du Colombier 			fprint(2, "ether debug on\n");
1140906943f9SDavid du Colombier 		etherdebug++;
1141906943f9SDavid du Colombier 		break;
1142ed868a7cSDavid du Colombier 	case 'N':
1143ed868a7cSDavid du Colombier 		devid = atoi(EARGF(usage()));
1144ed868a7cSDavid du Colombier 		break;
1145906943f9SDavid du Colombier 	default:
1146906943f9SDavid du Colombier 		return usage();
1147906943f9SDavid du Colombier 	}ARGEND
1148ed868a7cSDavid du Colombier 	if(argc != 0) {
1149906943f9SDavid du Colombier 		return usage();
1150ed868a7cSDavid du Colombier 	}
1151906943f9SDavid du Colombier 	e = dev->aux = emallocz(sizeof(Ether), 1);
1152906943f9SDavid du Colombier 	e->dev = dev;
1153906943f9SDavid du Colombier 	dev->free = etherdevfree;
1154906943f9SDavid du Colombier 
1155906943f9SDavid du Colombier 	for(i = 0; i < nelem(ethers); i++)
1156906943f9SDavid du Colombier 		if(ethers[i](e) == 0)
1157906943f9SDavid du Colombier 			break;
1158906943f9SDavid du Colombier 	if(i == nelem(ethers))
1159906943f9SDavid du Colombier 		return -1;
1160906943f9SDavid du Colombier 	if(e->init == nil)
1161906943f9SDavid du Colombier 		e->init = etherinit;
1162906943f9SDavid du Colombier 	if(e->init(e, &epin, &epout) < 0)
1163906943f9SDavid du Colombier 		return -1;
1164906943f9SDavid du Colombier 	if(e->bwrite == nil)
1165906943f9SDavid du Colombier 		e->bwrite = etherbwrite;
1166906943f9SDavid du Colombier 	if(e->bread == nil)
1167906943f9SDavid du Colombier 		e->bread = etherbread;
1168906943f9SDavid du Colombier 
1169906943f9SDavid du Colombier 	if(openeps(e, epin, epout) < 0)
1170906943f9SDavid du Colombier 		return -1;
1171906943f9SDavid du Colombier 	e->fs = etherfs;
1172ed868a7cSDavid du Colombier 	snprint(e->fs.name, sizeof(e->fs.name), "etherU%d", devid);
1173906943f9SDavid du Colombier 	e->fs.dev = dev;
1174906943f9SDavid du Colombier 	e->fs.aux = e;
1175906943f9SDavid du Colombier 	e->bc = chancreate(sizeof(Buf*), Nconns);
1176906943f9SDavid du Colombier 	e->rc = chancreate(sizeof(Buf*), Nconns/2);
1177906943f9SDavid du Colombier 	e->wc = chancreate(sizeof(Buf*), Nconns*2);
1178d37e33ffSDavid du Colombier 	incref(e->dev);
1179906943f9SDavid du Colombier 	proccreate(etherwriteproc, e, 16*1024);
1180d37e33ffSDavid du Colombier 	incref(e->dev);
1181906943f9SDavid du Colombier 	proccreate(etherreadproc, e, 16*1024);
1182906943f9SDavid du Colombier 	deprint(2, "%s: dev ref %ld\n", argv0, dev->ref);
1183d5789509SDavid du Colombier 	incref(e->dev);
1184906943f9SDavid du Colombier 	usbfsadd(&e->fs);
1185906943f9SDavid du Colombier 	return 0;
1186906943f9SDavid du Colombier }
1187