xref: /plan9-contrib/sys/src/9/bcm/etherusb.c (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
1 /*
2  * Kernel proxy for usb ethernet device
3  */
4 
5 #include "u.h"
6 #include "../port/lib.h"
7 #include "mem.h"
8 #include "dat.h"
9 #include "fns.h"
10 #include "io.h"
11 #include "ureg.h"
12 #include "../port/error.h"
13 #include "../port/netif.h"
14 
15 #include "etherif.h"
16 #include "../ip/ip.h"
17 
18 #define	GET4(p)		((p)[3]<<24 | (p)[2]<<16 | (p)[1]<<8  | (p)[0])
19 #define	PUT4(p, v)	((p)[0] = (v), (p)[1] = (v)>>8, \
20 			 (p)[2] = (v)>>16, (p)[3] = (v)>>24)
21 #define	dprint	if(debug) print
22 #define ddump	if(0) dump
23 
24 static int debug = 0;
25 
26 enum {
27 	Bind	= 0,
28 	Unbind,
29 
30 	SmscRxerror	= 0x8000,
31 	SmscTxfirst	= 0x2000,
32 	SmscTxlast	= 0x1000,
33 	Lan78Rxerror = 0x00400000,
34 	Lan78Txfcs	= 1<<22,
35 };
36 
37 typedef struct Ctlr Ctlr;
38 typedef struct Udev Udev;
39 
40 typedef int (Unpackfn)(Ether*, Block*);
41 typedef void (Transmitfn)(Ctlr*, Block*);
42 
43 struct Ctlr {
44 	Ether*	edev;
45 	Udev*	udev;
46 	Chan*	inchan;
47 	Chan*	outchan;
48 	char*	buf;
49 	int	bufsize;
50 	int	maxpkt;
51 	uint	rxbuf;
52 	uint	rxpkt;
53 	uint	txbuf;
54 	uint	txpkt;
55 	QLock;
56 };
57 
58 struct Udev {
59 	char	*name;
60 	Unpackfn *unpack;
61 	Transmitfn *transmit;
62 };
63 
64 static Cmdtab cmds[] = {
65 	{ Bind,		"bind",		7, },
66 	{ Unbind,	"unbind",	0, },
67 };
68 
69 static Unpackfn unpackcdc, unpackasix, unpacksmsc, unpacklan78;
70 static Transmitfn transmitcdc, transmitasix, transmitsmsc, transmitlan78;
71 
72 static Udev udevtab[] = {
73 	{ "cdc",	unpackcdc,	transmitcdc, },
74 	{ "asix",	unpackasix,	transmitasix, },
75 	{ "smsc",	unpacksmsc,	transmitsmsc, },
76 	{ "lan78xx",	unpacklan78, transmitlan78, },
77 	{ nil },
78 };
79 
80 static char nullea[Eaddrlen];
81 
82 static void
dump(int c,Block * b)83 dump(int c, Block *b)
84 {
85 	int s, i;
86 
87 	s = splhi();
88 	print("%c%ld:", c, BLEN(b));
89 	for(i = 0; i < 32; i++)
90 		print(" %2.2ux", b->rp[i]);
91 	print("\n");
92 	splx(s);
93 }
94 
95 static int
unpack(Ether * edev,Block * b,int m)96 unpack(Ether *edev, Block *b, int m)
97 {
98 	Block *nb;
99 	Ctlr *ctlr;
100 
101 	ctlr = edev->ctlr;
102 	ddump('?', b);
103 	if(m == BLEN(b)){
104 		etheriq(edev, b, 1);
105 		ctlr->rxpkt++;
106 		return 1;
107 	}
108 	nb = iallocb(m);
109 	if(nb != nil){
110 		memmove(nb->wp, b->rp, m);
111 		nb->wp += m;
112 		etheriq(edev, nb, 1);
113 		ctlr->rxpkt++;
114 	}else
115 		edev->soverflows++;
116 	b->rp += m;
117 	return 0;
118 }
119 
120 static int
unpackcdc(Ether * edev,Block * b)121 unpackcdc(Ether *edev, Block *b)
122 {
123 	int m;
124 
125 	m = BLEN(b);
126 	if(m < 6)
127 		return -1;
128 	return unpack(edev, b, m);
129 }
130 
131 static int
unpackasix(Ether * edev,Block * b)132 unpackasix(Ether *edev, Block *b)
133 {
134 	ulong hd;
135 	int m;
136 	uchar *wp;
137 
138 	if(BLEN(b) < 4)
139 		return -1;
140 	hd = GET4(b->rp);
141 	b->rp += 4;
142 	m = hd & 0xFFFF;
143 	hd >>= 16;
144 	if(m != (~hd & 0xFFFF))
145 		return -1;
146 	m = ROUND(m, 2);
147 	if(m < 6 || m > BLEN(b))
148 		return -1;
149 	if((wp = b->rp + m) != b->wp && b->wp - wp < 4)
150 		b->wp = wp;
151 	return unpack(edev, b, m);
152 }
153 
154 static int
unpacksmsc(Ether * edev,Block * b)155 unpacksmsc(Ether *edev, Block *b)
156 {
157 	ulong hd;
158 	int m;
159 
160 	ddump('@', b);
161 	if(BLEN(b) < 4)
162 		return -1;
163 	hd = GET4(b->rp);
164 	b->rp += 4;
165 	m = hd >> 16;
166 	if(m < 6 || m > BLEN(b))
167 		return -1;
168 	if(BLEN(b) - m < 4)
169 		b->wp = b->rp + m;
170 	if(hd & SmscRxerror){
171 		edev->frames++;
172 		b->rp += m;
173 		if(BLEN(b) == 0){
174 			freeb(b);
175 			return 1;
176 		}
177 	}else if(unpack(edev, b, m) == 1)
178 		return 1;
179 	if((m &= 3) != 0)
180 		b->rp += 4 - m;
181 	return 0;
182 }
183 
184 static int
unpacklan78(Ether * edev,Block * b)185 unpacklan78(Ether *edev, Block *b)
186 {
187 	ulong hd;
188 	int m;
189 
190 	if(BLEN(b) < 10)
191 		return -1;
192 	hd = GET4(b->rp);
193 	b->rp += 10;
194 	m = hd & 0x3FFF;
195 	if(m < 6 || m > BLEN(b))
196 		return -1;
197 	if(hd & Lan78Rxerror){
198 		edev->frames++;
199 		b->rp += m;
200 		if(BLEN(b) == 0){
201 			freeb(b);
202 			return 1;
203 		}
204 	}else if(unpack(edev, b, m) == 1)
205 		return 1;
206 	if(BLEN(b) > 0)
207 		b->rp = (uchar*)((((uintptr)b->rp)+3)&~3);
208 	return 0;
209 }
210 
211 static void
transmit(Ctlr * ctlr,Block * b)212 transmit(Ctlr *ctlr, Block *b)
213 {
214 	Chan *c;
215 
216 	ddump('!', b);
217 	c = ctlr->outchan;
218 	devtab[c->type]->bwrite(c, b, 0);
219 }
220 
221 static void
transmitcdc(Ctlr * ctlr,Block * b)222 transmitcdc(Ctlr *ctlr, Block *b)
223 {
224 	transmit(ctlr, b);
225 }
226 
227 static void
transmitasix(Ctlr * ctlr,Block * b)228 transmitasix(Ctlr *ctlr, Block *b)
229 {
230 	int n;
231 
232 	n = BLEN(b) & 0xFFFF;
233 	n |= ~n << 16;
234 	b = padblock(b, 4);
235 	PUT4(b->rp, n);
236 	if(BLEN(b) % ctlr->maxpkt == 0){
237 		b = padblock(b, -4);
238 		PUT4(b->wp, 0xFFFF0000);
239 		b->wp += 4;
240 	}
241 	transmit(ctlr, b);
242 }
243 
244 static void
transmitsmsc(Ctlr * ctlr,Block * b)245 transmitsmsc(Ctlr *ctlr, Block *b)
246 {
247 	int n;
248 
249 	n = BLEN(b) & 0x7FF;
250 	b = padblock(b, 8);
251 	PUT4(b->rp, n | SmscTxfirst | SmscTxlast);
252 	PUT4(b->rp+4, n);
253 	transmit(ctlr, b);
254 }
255 
256 static void
transmitlan78(Ctlr * ctlr,Block * b)257 transmitlan78(Ctlr *ctlr, Block *b)
258 {
259 	int n;
260 
261 	n = BLEN(b) & 0xFFFFF;
262 	b = padblock(b, 8);
263 	PUT4(b->rp, n | Lan78Txfcs);
264 	PUT4(b->rp+4, n);
265 	transmit(ctlr, b);
266 }
267 
268 static void
etherusbproc(void * a)269 etherusbproc(void *a)
270 {
271 	Ether *edev;
272 	Ctlr *ctlr;
273 	Chan *c;
274 	Block *b;
275 
276 	edev = a;
277 	ctlr = edev->ctlr;
278 	c = ctlr->inchan;
279 	b = nil;
280 	if(waserror()){
281 		print("etherusbproc: error exit %s\n", up->errstr);
282 		pexit(up->errstr, 1);
283 		return;
284 	}
285 	for(;;){
286 		if(b == nil){
287 			b = devtab[c->type]->bread(c, ctlr->bufsize, 0);
288 			ctlr->rxbuf++;
289 		}
290 		switch(ctlr->udev->unpack(edev, b)){
291 		case -1:
292 			edev->buffs++;
293 			freeb(b);
294 			/* fall through */
295 		case 1:
296 			b = nil;
297 			break;
298 		}
299 	}
300 }
301 
302 /*
303  * bind type indev outdev mac bufsize maxpkt
304  */
305 static void
bind(Ctlr * ctlr,Udev * udev,Cmdbuf * cb)306 bind(Ctlr *ctlr, Udev *udev, Cmdbuf *cb)
307 {
308 	Chan *inchan, *outchan;
309 	char *buf;
310 	uint bufsize, maxpkt;
311 	uchar ea[Eaddrlen];
312 
313 	qlock(ctlr);
314 	inchan = outchan = nil;
315 	buf = nil;
316 	if(waserror()){
317 		free(buf);
318 		if(inchan)
319 			cclose(inchan);
320 		if(outchan)
321 			cclose(outchan);
322 		qunlock(ctlr);
323 		nexterror();
324 	}
325 	if(ctlr->buf != nil)
326 		cmderror(cb, "already bound to a device");
327 	maxpkt = strtol(cb->f[6], 0, 0);
328 	if(maxpkt < 8 || maxpkt > 512)
329 		cmderror(cb, "bad maxpkt");
330 	bufsize = strtol(cb->f[5], 0, 0);
331 	if(bufsize < maxpkt || bufsize > 32*1024)
332 		cmderror(cb, "bad bufsize");
333 	buf = smalloc(bufsize);
334 	inchan = namec(cb->f[2], Aopen, OREAD, 0);
335 	outchan = namec(cb->f[3], Aopen, OWRITE, 0);
336 	assert(inchan != nil && outchan != nil);
337 	if(parsemac(ea, cb->f[4], Eaddrlen) != Eaddrlen)
338 		cmderror(cb, "bad etheraddr");
339 	if(memcmp(ctlr->edev->ea, nullea, Eaddrlen) == 0)
340 		memmove(ctlr->edev->ea, ea, Eaddrlen);
341 	else if(memcmp(ctlr->edev->ea, ea, Eaddrlen) != 0)
342 		cmderror(cb, "wrong ether address");
343 	ctlr->buf = buf;
344 	ctlr->inchan = inchan;
345 	ctlr->outchan = outchan;
346 	ctlr->bufsize = bufsize;
347 	ctlr->maxpkt = maxpkt;
348 	ctlr->udev = udev;
349 	kproc("etherusb", etherusbproc, ctlr->edev);
350 	memmove(ctlr->edev->addr, ea, Eaddrlen);
351 	print("\netherusb %s: %E\n", udev->name, ctlr->edev->addr);
352 	poperror();
353 	qunlock(ctlr);
354 }
355 
356 static void
unbind(Ctlr * ctlr)357 unbind(Ctlr *ctlr)
358 {
359 	qlock(ctlr);
360 	if(ctlr->buf != nil){
361 		free(ctlr->buf);
362 		ctlr->buf = nil;
363 		if(ctlr->inchan)
364 			cclose(ctlr->inchan);
365 		if(ctlr->outchan)
366 			cclose(ctlr->outchan);
367 		ctlr->inchan = ctlr->outchan = nil;
368 	}
369 	qunlock(ctlr);
370 }
371 
372 static long
etherusbifstat(Ether * edev,void * a,long n,ulong offset)373 etherusbifstat(Ether* edev, void* a, long n, ulong offset)
374 {
375 	Ctlr *ctlr;
376 	char *p;
377 	int l;
378 
379 	ctlr = edev->ctlr;
380 	p = malloc(READSTR);
381 	l = 0;
382 
383 	l += snprint(p+l, READSTR-l, "rxbuf: %ud\n", ctlr->rxbuf);
384 	l += snprint(p+l, READSTR-l, "rxpkt: %ud\n", ctlr->rxpkt);
385 	l += snprint(p+l, READSTR-l, "txbuf: %ud\n", ctlr->txbuf);
386 	l += snprint(p+l, READSTR-l, "txpkt: %ud\n", ctlr->txpkt);
387 	USED(l);
388 
389 	n = readstr(offset, a, n, p);
390 	free(p);
391 	return n;
392 }
393 
394 static void
etherusbtransmit(Ether * edev)395 etherusbtransmit(Ether *edev)
396 {
397 	Ctlr *ctlr;
398 	Block *b;
399 
400 	ctlr = edev->ctlr;
401 	while((b = qget(edev->oq)) != nil){
402 		ctlr->txpkt++;
403 		if(ctlr->buf == nil)
404 			freeb(b);
405 		else{
406 			ctlr->udev->transmit(ctlr, b);
407 			ctlr->txbuf++;
408 		}
409 	}
410 }
411 
412 static long
etherusbctl(Ether * edev,void * buf,long n)413 etherusbctl(Ether* edev, void* buf, long n)
414 {
415 	Ctlr *ctlr;
416 	Cmdbuf *cb;
417 	Cmdtab *ct;
418 	Udev *udev;
419 
420 	if((ctlr = edev->ctlr) == nil)
421 		error(Enonexist);
422 
423 	cb = parsecmd(buf, n);
424 	if(waserror()){
425 		free(cb);
426 		nexterror();
427 	}
428 	ct = lookupcmd(cb, cmds, nelem(cmds));
429 	switch(ct->index){
430 	case Bind:
431 		for(udev = udevtab; udev->name; udev++)
432 			if(strcmp(cb->f[1], udev->name) == 0)
433 				break;
434 		if(udev->name == nil)
435 			cmderror(cb, "unknown etherusb type");
436 		bind(ctlr, udev, cb);
437 		break;
438 	case Unbind:
439 		unbind(ctlr);
440 		break;
441 	default:
442 		cmderror(cb, "unknown etherusb control message");
443 	}
444 	poperror();
445 	free(cb);
446 	return n;
447 }
448 
449 static void
etherusbmulticast(void *,uchar *,int)450 etherusbmulticast(void*, uchar*, int)
451 {
452 	/* nothing to do, we allow all multicast packets in */
453 }
454 
455 static void
etherusbshutdown(Ether *)456 etherusbshutdown(Ether*)
457 {
458 }
459 
460 static void
etherusbattach(Ether * edev)461 etherusbattach(Ether* edev)
462 {
463 	Ctlr *ctlr;
464 
465 	ctlr = edev->ctlr;
466 	if(ctlr->edev == 0){
467 		/*
468 		 * Don't let boot process access etherusb until
469 		 * usbether driver has assigned an address.
470 		 */
471 		if(up->pid == 1 && strcmp(up->text, "boot") == 0)
472 			while(memcmp(edev->ea, nullea, Eaddrlen) == 0)
473 				tsleep(&up->sleep, return0, 0, 100);
474 		ctlr->edev = edev;
475 	}
476 }
477 
478 static int
etherusbpnp(Ether * edev)479 etherusbpnp(Ether* edev)
480 {
481 	Ctlr *ctlr;
482 
483 	ctlr = malloc(sizeof(Ctlr));
484 	edev->ctlr = ctlr;
485 	edev->irq = -1;
486 	edev->mbps = 100;	/* TODO: get this from usbether */
487 
488 	/*
489 	 * Linkage to the generic ethernet driver.
490 	 */
491 	edev->attach = etherusbattach;
492 	edev->transmit = etherusbtransmit;
493 	edev->interrupt = nil;
494 	edev->ifstat = etherusbifstat;
495 	edev->ctl = etherusbctl;
496 
497 	edev->arg = edev;
498 	/* TODO: promiscuous, multicast (for ipv6), shutdown (for reboot) */
499 //	edev->promiscuous = etherusbpromiscuous;
500 	edev->shutdown = etherusbshutdown;
501 	edev->multicast = etherusbmulticast;
502 
503 	return 0;
504 }
505 
506 void
etherusblink(void)507 etherusblink(void)
508 {
509 	addethercard("usb", etherusbpnp);
510 }
511