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