xref: /plan9-contrib/sys/src/9/bcm/etherusb.c (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
15d9de2d3SDavid du Colombier /*
25d9de2d3SDavid du Colombier  * Kernel proxy for usb ethernet device
35d9de2d3SDavid du Colombier  */
45d9de2d3SDavid du Colombier 
55d9de2d3SDavid du Colombier #include "u.h"
65d9de2d3SDavid du Colombier #include "../port/lib.h"
75d9de2d3SDavid du Colombier #include "mem.h"
85d9de2d3SDavid du Colombier #include "dat.h"
95d9de2d3SDavid du Colombier #include "fns.h"
105d9de2d3SDavid du Colombier #include "io.h"
11*5c47fe09SDavid du Colombier #include "ureg.h"
125d9de2d3SDavid du Colombier #include "../port/error.h"
135d9de2d3SDavid du Colombier #include "../port/netif.h"
145d9de2d3SDavid du Colombier 
155d9de2d3SDavid du Colombier #include "etherif.h"
165d9de2d3SDavid du Colombier #include "../ip/ip.h"
175d9de2d3SDavid du Colombier 
185d9de2d3SDavid du Colombier #define	GET4(p)		((p)[3]<<24 | (p)[2]<<16 | (p)[1]<<8  | (p)[0])
195d9de2d3SDavid du Colombier #define	PUT4(p, v)	((p)[0] = (v), (p)[1] = (v)>>8, \
205d9de2d3SDavid du Colombier 			 (p)[2] = (v)>>16, (p)[3] = (v)>>24)
215d9de2d3SDavid du Colombier #define	dprint	if(debug) print
225d9de2d3SDavid du Colombier #define ddump	if(0) dump
235d9de2d3SDavid du Colombier 
245d9de2d3SDavid du Colombier static int debug = 0;
255d9de2d3SDavid du Colombier 
265d9de2d3SDavid du Colombier enum {
275d9de2d3SDavid du Colombier 	Bind	= 0,
285d9de2d3SDavid du Colombier 	Unbind,
295d9de2d3SDavid du Colombier 
305d9de2d3SDavid du Colombier 	SmscRxerror	= 0x8000,
315d9de2d3SDavid du Colombier 	SmscTxfirst	= 0x2000,
325d9de2d3SDavid du Colombier 	SmscTxlast	= 0x1000,
33*5c47fe09SDavid du Colombier 	Lan78Rxerror = 0x00400000,
34*5c47fe09SDavid du Colombier 	Lan78Txfcs	= 1<<22,
355d9de2d3SDavid du Colombier };
365d9de2d3SDavid du Colombier 
375d9de2d3SDavid du Colombier typedef struct Ctlr Ctlr;
385d9de2d3SDavid du Colombier typedef struct Udev Udev;
395d9de2d3SDavid du Colombier 
405d9de2d3SDavid du Colombier typedef int (Unpackfn)(Ether*, Block*);
415d9de2d3SDavid du Colombier typedef void (Transmitfn)(Ctlr*, Block*);
425d9de2d3SDavid du Colombier 
435d9de2d3SDavid du Colombier struct Ctlr {
445d9de2d3SDavid du Colombier 	Ether*	edev;
455d9de2d3SDavid du Colombier 	Udev*	udev;
465d9de2d3SDavid du Colombier 	Chan*	inchan;
475d9de2d3SDavid du Colombier 	Chan*	outchan;
485d9de2d3SDavid du Colombier 	char*	buf;
495d9de2d3SDavid du Colombier 	int	bufsize;
505d9de2d3SDavid du Colombier 	int	maxpkt;
515d9de2d3SDavid du Colombier 	uint	rxbuf;
525d9de2d3SDavid du Colombier 	uint	rxpkt;
535d9de2d3SDavid du Colombier 	uint	txbuf;
545d9de2d3SDavid du Colombier 	uint	txpkt;
555d9de2d3SDavid du Colombier 	QLock;
565d9de2d3SDavid du Colombier };
575d9de2d3SDavid du Colombier 
585d9de2d3SDavid du Colombier struct Udev {
595d9de2d3SDavid du Colombier 	char	*name;
605d9de2d3SDavid du Colombier 	Unpackfn *unpack;
615d9de2d3SDavid du Colombier 	Transmitfn *transmit;
625d9de2d3SDavid du Colombier };
635d9de2d3SDavid du Colombier 
645d9de2d3SDavid du Colombier static Cmdtab cmds[] = {
655d9de2d3SDavid du Colombier 	{ Bind,		"bind",		7, },
665d9de2d3SDavid du Colombier 	{ Unbind,	"unbind",	0, },
675d9de2d3SDavid du Colombier };
685d9de2d3SDavid du Colombier 
69*5c47fe09SDavid du Colombier static Unpackfn unpackcdc, unpackasix, unpacksmsc, unpacklan78;
70*5c47fe09SDavid du Colombier static Transmitfn transmitcdc, transmitasix, transmitsmsc, transmitlan78;
715d9de2d3SDavid du Colombier 
725d9de2d3SDavid du Colombier static Udev udevtab[] = {
735d9de2d3SDavid du Colombier 	{ "cdc",	unpackcdc,	transmitcdc, },
745d9de2d3SDavid du Colombier 	{ "asix",	unpackasix,	transmitasix, },
755d9de2d3SDavid du Colombier 	{ "smsc",	unpacksmsc,	transmitsmsc, },
76*5c47fe09SDavid du Colombier 	{ "lan78xx",	unpacklan78, transmitlan78, },
775d9de2d3SDavid du Colombier 	{ nil },
785d9de2d3SDavid du Colombier };
795d9de2d3SDavid du Colombier 
80*5c47fe09SDavid du Colombier static char nullea[Eaddrlen];
81*5c47fe09SDavid du Colombier 
825d9de2d3SDavid du Colombier static void
dump(int c,Block * b)835d9de2d3SDavid du Colombier dump(int c, Block *b)
845d9de2d3SDavid du Colombier {
855d9de2d3SDavid du Colombier 	int s, i;
865d9de2d3SDavid du Colombier 
875d9de2d3SDavid du Colombier 	s = splhi();
885d9de2d3SDavid du Colombier 	print("%c%ld:", c, BLEN(b));
895d9de2d3SDavid du Colombier 	for(i = 0; i < 32; i++)
905d9de2d3SDavid du Colombier 		print(" %2.2ux", b->rp[i]);
915d9de2d3SDavid du Colombier 	print("\n");
925d9de2d3SDavid du Colombier 	splx(s);
935d9de2d3SDavid du Colombier }
945d9de2d3SDavid du Colombier 
955d9de2d3SDavid du Colombier static int
unpack(Ether * edev,Block * b,int m)965d9de2d3SDavid du Colombier unpack(Ether *edev, Block *b, int m)
975d9de2d3SDavid du Colombier {
985d9de2d3SDavid du Colombier 	Block *nb;
995d9de2d3SDavid du Colombier 	Ctlr *ctlr;
1005d9de2d3SDavid du Colombier 
1015d9de2d3SDavid du Colombier 	ctlr = edev->ctlr;
1025d9de2d3SDavid du Colombier 	ddump('?', b);
1035d9de2d3SDavid du Colombier 	if(m == BLEN(b)){
1045d9de2d3SDavid du Colombier 		etheriq(edev, b, 1);
1055d9de2d3SDavid du Colombier 		ctlr->rxpkt++;
1065d9de2d3SDavid du Colombier 		return 1;
1075d9de2d3SDavid du Colombier 	}
1085d9de2d3SDavid du Colombier 	nb = iallocb(m);
1095d9de2d3SDavid du Colombier 	if(nb != nil){
1105d9de2d3SDavid du Colombier 		memmove(nb->wp, b->rp, m);
1115d9de2d3SDavid du Colombier 		nb->wp += m;
1125d9de2d3SDavid du Colombier 		etheriq(edev, nb, 1);
1135d9de2d3SDavid du Colombier 		ctlr->rxpkt++;
1145d9de2d3SDavid du Colombier 	}else
1155d9de2d3SDavid du Colombier 		edev->soverflows++;
1165d9de2d3SDavid du Colombier 	b->rp += m;
1175d9de2d3SDavid du Colombier 	return 0;
1185d9de2d3SDavid du Colombier }
1195d9de2d3SDavid du Colombier 
1205d9de2d3SDavid du Colombier static int
unpackcdc(Ether * edev,Block * b)1215d9de2d3SDavid du Colombier unpackcdc(Ether *edev, Block *b)
1225d9de2d3SDavid du Colombier {
1235d9de2d3SDavid du Colombier 	int m;
1245d9de2d3SDavid du Colombier 
1255d9de2d3SDavid du Colombier 	m = BLEN(b);
1265d9de2d3SDavid du Colombier 	if(m < 6)
1275d9de2d3SDavid du Colombier 		return -1;
1285d9de2d3SDavid du Colombier 	return unpack(edev, b, m);
1295d9de2d3SDavid du Colombier }
1305d9de2d3SDavid du Colombier 
1315d9de2d3SDavid du Colombier static int
unpackasix(Ether * edev,Block * b)1325d9de2d3SDavid du Colombier unpackasix(Ether *edev, Block *b)
1335d9de2d3SDavid du Colombier {
1345d9de2d3SDavid du Colombier 	ulong hd;
1355d9de2d3SDavid du Colombier 	int m;
1365d9de2d3SDavid du Colombier 	uchar *wp;
1375d9de2d3SDavid du Colombier 
1385d9de2d3SDavid du Colombier 	if(BLEN(b) < 4)
1395d9de2d3SDavid du Colombier 		return -1;
1405d9de2d3SDavid du Colombier 	hd = GET4(b->rp);
1415d9de2d3SDavid du Colombier 	b->rp += 4;
1425d9de2d3SDavid du Colombier 	m = hd & 0xFFFF;
1435d9de2d3SDavid du Colombier 	hd >>= 16;
1445d9de2d3SDavid du Colombier 	if(m != (~hd & 0xFFFF))
1455d9de2d3SDavid du Colombier 		return -1;
1465d9de2d3SDavid du Colombier 	m = ROUND(m, 2);
1475d9de2d3SDavid du Colombier 	if(m < 6 || m > BLEN(b))
1485d9de2d3SDavid du Colombier 		return -1;
1495d9de2d3SDavid du Colombier 	if((wp = b->rp + m) != b->wp && b->wp - wp < 4)
1505d9de2d3SDavid du Colombier 		b->wp = wp;
1515d9de2d3SDavid du Colombier 	return unpack(edev, b, m);
1525d9de2d3SDavid du Colombier }
1535d9de2d3SDavid du Colombier 
1545d9de2d3SDavid du Colombier static int
unpacksmsc(Ether * edev,Block * b)1555d9de2d3SDavid du Colombier unpacksmsc(Ether *edev, Block *b)
1565d9de2d3SDavid du Colombier {
1575d9de2d3SDavid du Colombier 	ulong hd;
1585d9de2d3SDavid du Colombier 	int m;
1595d9de2d3SDavid du Colombier 
1605d9de2d3SDavid du Colombier 	ddump('@', b);
1615d9de2d3SDavid du Colombier 	if(BLEN(b) < 4)
1625d9de2d3SDavid du Colombier 		return -1;
1635d9de2d3SDavid du Colombier 	hd = GET4(b->rp);
1645d9de2d3SDavid du Colombier 	b->rp += 4;
1655d9de2d3SDavid du Colombier 	m = hd >> 16;
1665d9de2d3SDavid du Colombier 	if(m < 6 || m > BLEN(b))
1675d9de2d3SDavid du Colombier 		return -1;
1685d9de2d3SDavid du Colombier 	if(BLEN(b) - m < 4)
1695d9de2d3SDavid du Colombier 		b->wp = b->rp + m;
1705d9de2d3SDavid du Colombier 	if(hd & SmscRxerror){
1715d9de2d3SDavid du Colombier 		edev->frames++;
1725d9de2d3SDavid du Colombier 		b->rp += m;
1735d9de2d3SDavid du Colombier 		if(BLEN(b) == 0){
1745d9de2d3SDavid du Colombier 			freeb(b);
1755d9de2d3SDavid du Colombier 			return 1;
1765d9de2d3SDavid du Colombier 		}
1775d9de2d3SDavid du Colombier 	}else if(unpack(edev, b, m) == 1)
1785d9de2d3SDavid du Colombier 		return 1;
1795d9de2d3SDavid du Colombier 	if((m &= 3) != 0)
1805d9de2d3SDavid du Colombier 		b->rp += 4 - m;
1815d9de2d3SDavid du Colombier 	return 0;
1825d9de2d3SDavid du Colombier }
1835d9de2d3SDavid du Colombier 
184*5c47fe09SDavid du Colombier static int
unpacklan78(Ether * edev,Block * b)185*5c47fe09SDavid du Colombier unpacklan78(Ether *edev, Block *b)
186*5c47fe09SDavid du Colombier {
187*5c47fe09SDavid du Colombier 	ulong hd;
188*5c47fe09SDavid du Colombier 	int m;
189*5c47fe09SDavid du Colombier 
190*5c47fe09SDavid du Colombier 	if(BLEN(b) < 10)
191*5c47fe09SDavid du Colombier 		return -1;
192*5c47fe09SDavid du Colombier 	hd = GET4(b->rp);
193*5c47fe09SDavid du Colombier 	b->rp += 10;
194*5c47fe09SDavid du Colombier 	m = hd & 0x3FFF;
195*5c47fe09SDavid du Colombier 	if(m < 6 || m > BLEN(b))
196*5c47fe09SDavid du Colombier 		return -1;
197*5c47fe09SDavid du Colombier 	if(hd & Lan78Rxerror){
198*5c47fe09SDavid du Colombier 		edev->frames++;
199*5c47fe09SDavid du Colombier 		b->rp += m;
200*5c47fe09SDavid du Colombier 		if(BLEN(b) == 0){
201*5c47fe09SDavid du Colombier 			freeb(b);
202*5c47fe09SDavid du Colombier 			return 1;
203*5c47fe09SDavid du Colombier 		}
204*5c47fe09SDavid du Colombier 	}else if(unpack(edev, b, m) == 1)
205*5c47fe09SDavid du Colombier 		return 1;
206*5c47fe09SDavid du Colombier 	if(BLEN(b) > 0)
207*5c47fe09SDavid du Colombier 		b->rp = (uchar*)((((uintptr)b->rp)+3)&~3);
208*5c47fe09SDavid du Colombier 	return 0;
209*5c47fe09SDavid du Colombier }
210*5c47fe09SDavid du Colombier 
2115d9de2d3SDavid du Colombier static void
transmit(Ctlr * ctlr,Block * b)2125d9de2d3SDavid du Colombier transmit(Ctlr *ctlr, Block *b)
2135d9de2d3SDavid du Colombier {
2145d9de2d3SDavid du Colombier 	Chan *c;
2155d9de2d3SDavid du Colombier 
2165d9de2d3SDavid du Colombier 	ddump('!', b);
2175d9de2d3SDavid du Colombier 	c = ctlr->outchan;
2185d9de2d3SDavid du Colombier 	devtab[c->type]->bwrite(c, b, 0);
2195d9de2d3SDavid du Colombier }
2205d9de2d3SDavid du Colombier 
2215d9de2d3SDavid du Colombier static void
transmitcdc(Ctlr * ctlr,Block * b)2225d9de2d3SDavid du Colombier transmitcdc(Ctlr *ctlr, Block *b)
2235d9de2d3SDavid du Colombier {
2245d9de2d3SDavid du Colombier 	transmit(ctlr, b);
2255d9de2d3SDavid du Colombier }
2265d9de2d3SDavid du Colombier 
2275d9de2d3SDavid du Colombier static void
transmitasix(Ctlr * ctlr,Block * b)2285d9de2d3SDavid du Colombier transmitasix(Ctlr *ctlr, Block *b)
2295d9de2d3SDavid du Colombier {
2305d9de2d3SDavid du Colombier 	int n;
2315d9de2d3SDavid du Colombier 
2325d9de2d3SDavid du Colombier 	n = BLEN(b) & 0xFFFF;
233b4d1cf41SDavid du Colombier 	n |= ~n << 16;
234*5c47fe09SDavid du Colombier 	b = padblock(b, 4);
2355d9de2d3SDavid du Colombier 	PUT4(b->rp, n);
2365d9de2d3SDavid du Colombier 	if(BLEN(b) % ctlr->maxpkt == 0){
237*5c47fe09SDavid du Colombier 		b = padblock(b, -4);
2385d9de2d3SDavid du Colombier 		PUT4(b->wp, 0xFFFF0000);
2395d9de2d3SDavid du Colombier 		b->wp += 4;
2405d9de2d3SDavid du Colombier 	}
2415d9de2d3SDavid du Colombier 	transmit(ctlr, b);
2425d9de2d3SDavid du Colombier }
2435d9de2d3SDavid du Colombier 
2445d9de2d3SDavid du Colombier static void
transmitsmsc(Ctlr * ctlr,Block * b)2455d9de2d3SDavid du Colombier transmitsmsc(Ctlr *ctlr, Block *b)
2465d9de2d3SDavid du Colombier {
2475d9de2d3SDavid du Colombier 	int n;
2485d9de2d3SDavid du Colombier 
2495d9de2d3SDavid du Colombier 	n = BLEN(b) & 0x7FF;
250*5c47fe09SDavid du Colombier 	b = padblock(b, 8);
2515d9de2d3SDavid du Colombier 	PUT4(b->rp, n | SmscTxfirst | SmscTxlast);
2525d9de2d3SDavid du Colombier 	PUT4(b->rp+4, n);
2535d9de2d3SDavid du Colombier 	transmit(ctlr, b);
2545d9de2d3SDavid du Colombier }
2555d9de2d3SDavid du Colombier 
2565d9de2d3SDavid du Colombier static void
transmitlan78(Ctlr * ctlr,Block * b)257*5c47fe09SDavid du Colombier transmitlan78(Ctlr *ctlr, Block *b)
258*5c47fe09SDavid du Colombier {
259*5c47fe09SDavid du Colombier 	int n;
260*5c47fe09SDavid du Colombier 
261*5c47fe09SDavid du Colombier 	n = BLEN(b) & 0xFFFFF;
262*5c47fe09SDavid du Colombier 	b = padblock(b, 8);
263*5c47fe09SDavid du Colombier 	PUT4(b->rp, n | Lan78Txfcs);
264*5c47fe09SDavid du Colombier 	PUT4(b->rp+4, n);
265*5c47fe09SDavid du Colombier 	transmit(ctlr, b);
266*5c47fe09SDavid du Colombier }
267*5c47fe09SDavid du Colombier 
268*5c47fe09SDavid du Colombier static void
etherusbproc(void * a)2695d9de2d3SDavid du Colombier etherusbproc(void *a)
2705d9de2d3SDavid du Colombier {
2715d9de2d3SDavid du Colombier 	Ether *edev;
2725d9de2d3SDavid du Colombier 	Ctlr *ctlr;
2735d9de2d3SDavid du Colombier 	Chan *c;
2745d9de2d3SDavid du Colombier 	Block *b;
2755d9de2d3SDavid du Colombier 
2765d9de2d3SDavid du Colombier 	edev = a;
2775d9de2d3SDavid du Colombier 	ctlr = edev->ctlr;
2785d9de2d3SDavid du Colombier 	c = ctlr->inchan;
2795d9de2d3SDavid du Colombier 	b = nil;
2805d9de2d3SDavid du Colombier 	if(waserror()){
2815d9de2d3SDavid du Colombier 		print("etherusbproc: error exit %s\n", up->errstr);
2825d9de2d3SDavid du Colombier 		pexit(up->errstr, 1);
2835d9de2d3SDavid du Colombier 		return;
2845d9de2d3SDavid du Colombier 	}
2855d9de2d3SDavid du Colombier 	for(;;){
2865d9de2d3SDavid du Colombier 		if(b == nil){
2875d9de2d3SDavid du Colombier 			b = devtab[c->type]->bread(c, ctlr->bufsize, 0);
2885d9de2d3SDavid du Colombier 			ctlr->rxbuf++;
2895d9de2d3SDavid du Colombier 		}
2905d9de2d3SDavid du Colombier 		switch(ctlr->udev->unpack(edev, b)){
2915d9de2d3SDavid du Colombier 		case -1:
2925d9de2d3SDavid du Colombier 			edev->buffs++;
2935d9de2d3SDavid du Colombier 			freeb(b);
2945d9de2d3SDavid du Colombier 			/* fall through */
2955d9de2d3SDavid du Colombier 		case 1:
2965d9de2d3SDavid du Colombier 			b = nil;
2975d9de2d3SDavid du Colombier 			break;
2985d9de2d3SDavid du Colombier 		}
2995d9de2d3SDavid du Colombier 	}
3005d9de2d3SDavid du Colombier }
3015d9de2d3SDavid du Colombier 
3025d9de2d3SDavid du Colombier /*
3035d9de2d3SDavid du Colombier  * bind type indev outdev mac bufsize maxpkt
3045d9de2d3SDavid du Colombier  */
3055d9de2d3SDavid du Colombier static void
bind(Ctlr * ctlr,Udev * udev,Cmdbuf * cb)3065d9de2d3SDavid du Colombier bind(Ctlr *ctlr, Udev *udev, Cmdbuf *cb)
3075d9de2d3SDavid du Colombier {
3085d9de2d3SDavid du Colombier 	Chan *inchan, *outchan;
3095d9de2d3SDavid du Colombier 	char *buf;
3105d9de2d3SDavid du Colombier 	uint bufsize, maxpkt;
311*5c47fe09SDavid du Colombier 	uchar ea[Eaddrlen];
3125d9de2d3SDavid du Colombier 
3135d9de2d3SDavid du Colombier 	qlock(ctlr);
3145d9de2d3SDavid du Colombier 	inchan = outchan = nil;
3155d9de2d3SDavid du Colombier 	buf = nil;
3165d9de2d3SDavid du Colombier 	if(waserror()){
3175d9de2d3SDavid du Colombier 		free(buf);
3185d9de2d3SDavid du Colombier 		if(inchan)
3195d9de2d3SDavid du Colombier 			cclose(inchan);
3205d9de2d3SDavid du Colombier 		if(outchan)
3215d9de2d3SDavid du Colombier 			cclose(outchan);
3225d9de2d3SDavid du Colombier 		qunlock(ctlr);
3235d9de2d3SDavid du Colombier 		nexterror();
3245d9de2d3SDavid du Colombier 	}
3255d9de2d3SDavid du Colombier 	if(ctlr->buf != nil)
3265d9de2d3SDavid du Colombier 		cmderror(cb, "already bound to a device");
3275d9de2d3SDavid du Colombier 	maxpkt = strtol(cb->f[6], 0, 0);
3285d9de2d3SDavid du Colombier 	if(maxpkt < 8 || maxpkt > 512)
3295d9de2d3SDavid du Colombier 		cmderror(cb, "bad maxpkt");
3305d9de2d3SDavid du Colombier 	bufsize = strtol(cb->f[5], 0, 0);
3315d9de2d3SDavid du Colombier 	if(bufsize < maxpkt || bufsize > 32*1024)
3325d9de2d3SDavid du Colombier 		cmderror(cb, "bad bufsize");
3335d9de2d3SDavid du Colombier 	buf = smalloc(bufsize);
3345d9de2d3SDavid du Colombier 	inchan = namec(cb->f[2], Aopen, OREAD, 0);
3355d9de2d3SDavid du Colombier 	outchan = namec(cb->f[3], Aopen, OWRITE, 0);
3365d9de2d3SDavid du Colombier 	assert(inchan != nil && outchan != nil);
337*5c47fe09SDavid du Colombier 	if(parsemac(ea, cb->f[4], Eaddrlen) != Eaddrlen)
3385d9de2d3SDavid du Colombier 		cmderror(cb, "bad etheraddr");
339*5c47fe09SDavid du Colombier 	if(memcmp(ctlr->edev->ea, nullea, Eaddrlen) == 0)
340*5c47fe09SDavid du Colombier 		memmove(ctlr->edev->ea, ea, Eaddrlen);
341*5c47fe09SDavid du Colombier 	else if(memcmp(ctlr->edev->ea, ea, Eaddrlen) != 0)
342*5c47fe09SDavid du Colombier 		cmderror(cb, "wrong ether address");
3435d9de2d3SDavid du Colombier 	ctlr->buf = buf;
3445d9de2d3SDavid du Colombier 	ctlr->inchan = inchan;
3455d9de2d3SDavid du Colombier 	ctlr->outchan = outchan;
3465d9de2d3SDavid du Colombier 	ctlr->bufsize = bufsize;
3475d9de2d3SDavid du Colombier 	ctlr->maxpkt = maxpkt;
3485d9de2d3SDavid du Colombier 	ctlr->udev = udev;
3495d9de2d3SDavid du Colombier 	kproc("etherusb", etherusbproc, ctlr->edev);
350*5c47fe09SDavid du Colombier 	memmove(ctlr->edev->addr, ea, Eaddrlen);
351*5c47fe09SDavid du Colombier 	print("\netherusb %s: %E\n", udev->name, ctlr->edev->addr);
3525d9de2d3SDavid du Colombier 	poperror();
3535d9de2d3SDavid du Colombier 	qunlock(ctlr);
3545d9de2d3SDavid du Colombier }
3555d9de2d3SDavid du Colombier 
3565d9de2d3SDavid du Colombier static void
unbind(Ctlr * ctlr)3575d9de2d3SDavid du Colombier unbind(Ctlr *ctlr)
3585d9de2d3SDavid du Colombier {
3595d9de2d3SDavid du Colombier 	qlock(ctlr);
3605d9de2d3SDavid du Colombier 	if(ctlr->buf != nil){
3615d9de2d3SDavid du Colombier 		free(ctlr->buf);
3625d9de2d3SDavid du Colombier 		ctlr->buf = nil;
3635d9de2d3SDavid du Colombier 		if(ctlr->inchan)
3645d9de2d3SDavid du Colombier 			cclose(ctlr->inchan);
3655d9de2d3SDavid du Colombier 		if(ctlr->outchan)
3665d9de2d3SDavid du Colombier 			cclose(ctlr->outchan);
3675d9de2d3SDavid du Colombier 		ctlr->inchan = ctlr->outchan = nil;
3685d9de2d3SDavid du Colombier 	}
3695d9de2d3SDavid du Colombier 	qunlock(ctlr);
3705d9de2d3SDavid du Colombier }
3715d9de2d3SDavid du Colombier 
3725d9de2d3SDavid du Colombier static long
etherusbifstat(Ether * edev,void * a,long n,ulong offset)3735d9de2d3SDavid du Colombier etherusbifstat(Ether* edev, void* a, long n, ulong offset)
3745d9de2d3SDavid du Colombier {
3755d9de2d3SDavid du Colombier 	Ctlr *ctlr;
3765d9de2d3SDavid du Colombier 	char *p;
3775d9de2d3SDavid du Colombier 	int l;
3785d9de2d3SDavid du Colombier 
3795d9de2d3SDavid du Colombier 	ctlr = edev->ctlr;
3805d9de2d3SDavid du Colombier 	p = malloc(READSTR);
3815d9de2d3SDavid du Colombier 	l = 0;
3825d9de2d3SDavid du Colombier 
3835d9de2d3SDavid du Colombier 	l += snprint(p+l, READSTR-l, "rxbuf: %ud\n", ctlr->rxbuf);
3845d9de2d3SDavid du Colombier 	l += snprint(p+l, READSTR-l, "rxpkt: %ud\n", ctlr->rxpkt);
3855d9de2d3SDavid du Colombier 	l += snprint(p+l, READSTR-l, "txbuf: %ud\n", ctlr->txbuf);
3865d9de2d3SDavid du Colombier 	l += snprint(p+l, READSTR-l, "txpkt: %ud\n", ctlr->txpkt);
3875d9de2d3SDavid du Colombier 	USED(l);
3885d9de2d3SDavid du Colombier 
3895d9de2d3SDavid du Colombier 	n = readstr(offset, a, n, p);
3905d9de2d3SDavid du Colombier 	free(p);
3915d9de2d3SDavid du Colombier 	return n;
3925d9de2d3SDavid du Colombier }
3935d9de2d3SDavid du Colombier 
3945d9de2d3SDavid du Colombier static void
etherusbtransmit(Ether * edev)3955d9de2d3SDavid du Colombier etherusbtransmit(Ether *edev)
3965d9de2d3SDavid du Colombier {
3975d9de2d3SDavid du Colombier 	Ctlr *ctlr;
3985d9de2d3SDavid du Colombier 	Block *b;
3995d9de2d3SDavid du Colombier 
4005d9de2d3SDavid du Colombier 	ctlr = edev->ctlr;
4015d9de2d3SDavid du Colombier 	while((b = qget(edev->oq)) != nil){
4025d9de2d3SDavid du Colombier 		ctlr->txpkt++;
4035d9de2d3SDavid du Colombier 		if(ctlr->buf == nil)
4045d9de2d3SDavid du Colombier 			freeb(b);
4055d9de2d3SDavid du Colombier 		else{
4065d9de2d3SDavid du Colombier 			ctlr->udev->transmit(ctlr, b);
4075d9de2d3SDavid du Colombier 			ctlr->txbuf++;
4085d9de2d3SDavid du Colombier 		}
4095d9de2d3SDavid du Colombier 	}
4105d9de2d3SDavid du Colombier }
4115d9de2d3SDavid du Colombier 
4125d9de2d3SDavid du Colombier static long
etherusbctl(Ether * edev,void * buf,long n)4135d9de2d3SDavid du Colombier etherusbctl(Ether* edev, void* buf, long n)
4145d9de2d3SDavid du Colombier {
4155d9de2d3SDavid du Colombier 	Ctlr *ctlr;
4165d9de2d3SDavid du Colombier 	Cmdbuf *cb;
4175d9de2d3SDavid du Colombier 	Cmdtab *ct;
4185d9de2d3SDavid du Colombier 	Udev *udev;
4195d9de2d3SDavid du Colombier 
4205d9de2d3SDavid du Colombier 	if((ctlr = edev->ctlr) == nil)
4215d9de2d3SDavid du Colombier 		error(Enonexist);
4225d9de2d3SDavid du Colombier 
4235d9de2d3SDavid du Colombier 	cb = parsecmd(buf, n);
4245d9de2d3SDavid du Colombier 	if(waserror()){
4255d9de2d3SDavid du Colombier 		free(cb);
4265d9de2d3SDavid du Colombier 		nexterror();
4275d9de2d3SDavid du Colombier 	}
4285d9de2d3SDavid du Colombier 	ct = lookupcmd(cb, cmds, nelem(cmds));
4295d9de2d3SDavid du Colombier 	switch(ct->index){
4305d9de2d3SDavid du Colombier 	case Bind:
4315d9de2d3SDavid du Colombier 		for(udev = udevtab; udev->name; udev++)
4325d9de2d3SDavid du Colombier 			if(strcmp(cb->f[1], udev->name) == 0)
4335d9de2d3SDavid du Colombier 				break;
4345d9de2d3SDavid du Colombier 		if(udev->name == nil)
4355d9de2d3SDavid du Colombier 			cmderror(cb, "unknown etherusb type");
4365d9de2d3SDavid du Colombier 		bind(ctlr, udev, cb);
4375d9de2d3SDavid du Colombier 		break;
4385d9de2d3SDavid du Colombier 	case Unbind:
4395d9de2d3SDavid du Colombier 		unbind(ctlr);
4405d9de2d3SDavid du Colombier 		break;
4415d9de2d3SDavid du Colombier 	default:
4425d9de2d3SDavid du Colombier 		cmderror(cb, "unknown etherusb control message");
4435d9de2d3SDavid du Colombier 	}
4445d9de2d3SDavid du Colombier 	poperror();
4455d9de2d3SDavid du Colombier 	free(cb);
4465d9de2d3SDavid du Colombier 	return n;
4475d9de2d3SDavid du Colombier }
4485d9de2d3SDavid du Colombier 
4495d9de2d3SDavid du Colombier static void
etherusbmulticast(void *,uchar *,int)450*5c47fe09SDavid du Colombier etherusbmulticast(void*, uchar*, int)
451*5c47fe09SDavid du Colombier {
452*5c47fe09SDavid du Colombier 	/* nothing to do, we allow all multicast packets in */
453*5c47fe09SDavid du Colombier }
454*5c47fe09SDavid du Colombier 
455*5c47fe09SDavid du Colombier static void
etherusbshutdown(Ether *)456*5c47fe09SDavid du Colombier etherusbshutdown(Ether*)
457*5c47fe09SDavid du Colombier {
458*5c47fe09SDavid du Colombier }
459*5c47fe09SDavid du Colombier 
460*5c47fe09SDavid du Colombier static void
etherusbattach(Ether * edev)4615d9de2d3SDavid du Colombier etherusbattach(Ether* edev)
4625d9de2d3SDavid du Colombier {
4635d9de2d3SDavid du Colombier 	Ctlr *ctlr;
4645d9de2d3SDavid du Colombier 
4655d9de2d3SDavid du Colombier 	ctlr = edev->ctlr;
466*5c47fe09SDavid du Colombier 	if(ctlr->edev == 0){
467*5c47fe09SDavid du Colombier 		/*
468*5c47fe09SDavid du Colombier 		 * Don't let boot process access etherusb until
469*5c47fe09SDavid du Colombier 		 * usbether driver has assigned an address.
470*5c47fe09SDavid du Colombier 		 */
471*5c47fe09SDavid du Colombier 		if(up->pid == 1 && strcmp(up->text, "boot") == 0)
472*5c47fe09SDavid du Colombier 			while(memcmp(edev->ea, nullea, Eaddrlen) == 0)
473*5c47fe09SDavid du Colombier 				tsleep(&up->sleep, return0, 0, 100);
4745d9de2d3SDavid du Colombier 		ctlr->edev = edev;
4755d9de2d3SDavid du Colombier 	}
476*5c47fe09SDavid du Colombier }
4775d9de2d3SDavid du Colombier 
4785d9de2d3SDavid du Colombier static int
etherusbpnp(Ether * edev)4795d9de2d3SDavid du Colombier etherusbpnp(Ether* edev)
4805d9de2d3SDavid du Colombier {
4815d9de2d3SDavid du Colombier 	Ctlr *ctlr;
4825d9de2d3SDavid du Colombier 
4835d9de2d3SDavid du Colombier 	ctlr = malloc(sizeof(Ctlr));
4845d9de2d3SDavid du Colombier 	edev->ctlr = ctlr;
4855d9de2d3SDavid du Colombier 	edev->irq = -1;
4865d9de2d3SDavid du Colombier 	edev->mbps = 100;	/* TODO: get this from usbether */
487853458f3SDavid du Colombier 
488853458f3SDavid du Colombier 	/*
489853458f3SDavid du Colombier 	 * Linkage to the generic ethernet driver.
490853458f3SDavid du Colombier 	 */
4915d9de2d3SDavid du Colombier 	edev->attach = etherusbattach;
4925d9de2d3SDavid du Colombier 	edev->transmit = etherusbtransmit;
493853458f3SDavid du Colombier 	edev->interrupt = nil;
4945d9de2d3SDavid du Colombier 	edev->ifstat = etherusbifstat;
4955d9de2d3SDavid du Colombier 	edev->ctl = etherusbctl;
496853458f3SDavid du Colombier 
497853458f3SDavid du Colombier 	edev->arg = edev;
498853458f3SDavid du Colombier 	/* TODO: promiscuous, multicast (for ipv6), shutdown (for reboot) */
499853458f3SDavid du Colombier //	edev->promiscuous = etherusbpromiscuous;
500*5c47fe09SDavid du Colombier 	edev->shutdown = etherusbshutdown;
501*5c47fe09SDavid du Colombier 	edev->multicast = etherusbmulticast;
502853458f3SDavid du Colombier 
5035d9de2d3SDavid du Colombier 	return 0;
5045d9de2d3SDavid du Colombier }
5055d9de2d3SDavid du Colombier 
5065d9de2d3SDavid du Colombier void
etherusblink(void)5075d9de2d3SDavid du Colombier etherusblink(void)
5085d9de2d3SDavid du Colombier {
5095d9de2d3SDavid du Colombier 	addethercard("usb", etherusbpnp);
5105d9de2d3SDavid du Colombier }
511