xref: /plan9/sys/src/9/bcm/etherusb.c (revision 853458f38e7eb3a48cfa3a36aefdb799375e398a)
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"
115d9de2d3SDavid du Colombier #include "../port/error.h"
125d9de2d3SDavid du Colombier #include "../port/netif.h"
135d9de2d3SDavid du Colombier 
145d9de2d3SDavid du Colombier #include "etherif.h"
155d9de2d3SDavid du Colombier #include "../ip/ip.h"
165d9de2d3SDavid du Colombier 
175d9de2d3SDavid du Colombier #define	GET4(p)		((p)[3]<<24 | (p)[2]<<16 | (p)[1]<<8  | (p)[0])
185d9de2d3SDavid du Colombier #define	PUT4(p, v)	((p)[0] = (v), (p)[1] = (v)>>8, \
195d9de2d3SDavid du Colombier 			 (p)[2] = (v)>>16, (p)[3] = (v)>>24)
205d9de2d3SDavid du Colombier #define	dprint	if(debug) print
215d9de2d3SDavid du Colombier #define ddump	if(0) dump
225d9de2d3SDavid du Colombier 
235d9de2d3SDavid du Colombier static int debug = 0;
245d9de2d3SDavid du Colombier 
255d9de2d3SDavid du Colombier enum {
265d9de2d3SDavid du Colombier 	Bind	= 0,
275d9de2d3SDavid du Colombier 	Unbind,
285d9de2d3SDavid du Colombier 
295d9de2d3SDavid du Colombier 	SmscRxerror	= 0x8000,
305d9de2d3SDavid du Colombier 	SmscTxfirst	= 0x2000,
315d9de2d3SDavid du Colombier 	SmscTxlast	= 0x1000,
325d9de2d3SDavid du Colombier };
335d9de2d3SDavid du Colombier 
345d9de2d3SDavid du Colombier typedef struct Ctlr Ctlr;
355d9de2d3SDavid du Colombier typedef struct Udev Udev;
365d9de2d3SDavid du Colombier 
375d9de2d3SDavid du Colombier typedef int (Unpackfn)(Ether*, Block*);
385d9de2d3SDavid du Colombier typedef void (Transmitfn)(Ctlr*, Block*);
395d9de2d3SDavid du Colombier 
405d9de2d3SDavid du Colombier struct Ctlr {
415d9de2d3SDavid du Colombier 	Ether*	edev;
425d9de2d3SDavid du Colombier 	Udev*	udev;
435d9de2d3SDavid du Colombier 	Chan*	inchan;
445d9de2d3SDavid du Colombier 	Chan*	outchan;
455d9de2d3SDavid du Colombier 	char*	buf;
465d9de2d3SDavid du Colombier 	int	bufsize;
475d9de2d3SDavid du Colombier 	int	maxpkt;
485d9de2d3SDavid du Colombier 	uint	rxbuf;
495d9de2d3SDavid du Colombier 	uint	rxpkt;
505d9de2d3SDavid du Colombier 	uint	txbuf;
515d9de2d3SDavid du Colombier 	uint	txpkt;
525d9de2d3SDavid du Colombier 	QLock;
535d9de2d3SDavid du Colombier };
545d9de2d3SDavid du Colombier 
555d9de2d3SDavid du Colombier struct Udev {
565d9de2d3SDavid du Colombier 	char	*name;
575d9de2d3SDavid du Colombier 	Unpackfn *unpack;
585d9de2d3SDavid du Colombier 	Transmitfn *transmit;
595d9de2d3SDavid du Colombier };
605d9de2d3SDavid du Colombier 
615d9de2d3SDavid du Colombier static Cmdtab cmds[] = {
625d9de2d3SDavid du Colombier 	{ Bind,		"bind",		7, },
635d9de2d3SDavid du Colombier 	{ Unbind,	"unbind",	0, },
645d9de2d3SDavid du Colombier };
655d9de2d3SDavid du Colombier 
665d9de2d3SDavid du Colombier static Unpackfn unpackcdc, unpackasix, unpacksmsc;
675d9de2d3SDavid du Colombier static Transmitfn transmitcdc, transmitasix, transmitsmsc;
685d9de2d3SDavid du Colombier 
695d9de2d3SDavid du Colombier static Udev udevtab[] = {
705d9de2d3SDavid du Colombier 	{ "cdc",	unpackcdc,	transmitcdc, },
715d9de2d3SDavid du Colombier 	{ "asix",	unpackasix,	transmitasix, },
725d9de2d3SDavid du Colombier 	{ "smsc",	unpacksmsc,	transmitsmsc, },
735d9de2d3SDavid du Colombier 	{ nil },
745d9de2d3SDavid du Colombier };
755d9de2d3SDavid du Colombier 
765d9de2d3SDavid du Colombier static void
dump(int c,Block * b)775d9de2d3SDavid du Colombier dump(int c, Block *b)
785d9de2d3SDavid du Colombier {
795d9de2d3SDavid du Colombier 	int s, i;
805d9de2d3SDavid du Colombier 
815d9de2d3SDavid du Colombier 	s = splhi();
825d9de2d3SDavid du Colombier 	print("%c%ld:", c, BLEN(b));
835d9de2d3SDavid du Colombier 	for(i = 0; i < 32; i++)
845d9de2d3SDavid du Colombier 		print(" %2.2ux", b->rp[i]);
855d9de2d3SDavid du Colombier 	print("\n");
865d9de2d3SDavid du Colombier 	splx(s);
875d9de2d3SDavid du Colombier }
885d9de2d3SDavid du Colombier 
895d9de2d3SDavid du Colombier static int
unpack(Ether * edev,Block * b,int m)905d9de2d3SDavid du Colombier unpack(Ether *edev, Block *b, int m)
915d9de2d3SDavid du Colombier {
925d9de2d3SDavid du Colombier 	Block *nb;
935d9de2d3SDavid du Colombier 	Ctlr *ctlr;
945d9de2d3SDavid du Colombier 
955d9de2d3SDavid du Colombier 	ctlr = edev->ctlr;
965d9de2d3SDavid du Colombier 	ddump('?', b);
975d9de2d3SDavid du Colombier 	if(m == BLEN(b)){
985d9de2d3SDavid du Colombier 		etheriq(edev, b, 1);
995d9de2d3SDavid du Colombier 		ctlr->rxpkt++;
1005d9de2d3SDavid du Colombier 		return 1;
1015d9de2d3SDavid du Colombier 	}
1025d9de2d3SDavid du Colombier 	nb = iallocb(m);
1035d9de2d3SDavid du Colombier 	if(nb != nil){
1045d9de2d3SDavid du Colombier 		memmove(nb->wp, b->rp, m);
1055d9de2d3SDavid du Colombier 		nb->wp += m;
1065d9de2d3SDavid du Colombier 		etheriq(edev, nb, 1);
1075d9de2d3SDavid du Colombier 		ctlr->rxpkt++;
1085d9de2d3SDavid du Colombier 	}else
1095d9de2d3SDavid du Colombier 		edev->soverflows++;
1105d9de2d3SDavid du Colombier 	b->rp += m;
1115d9de2d3SDavid du Colombier 	return 0;
1125d9de2d3SDavid du Colombier }
1135d9de2d3SDavid du Colombier 
1145d9de2d3SDavid du Colombier static int
unpackcdc(Ether * edev,Block * b)1155d9de2d3SDavid du Colombier unpackcdc(Ether *edev, Block *b)
1165d9de2d3SDavid du Colombier {
1175d9de2d3SDavid du Colombier 	int m;
1185d9de2d3SDavid du Colombier 
1195d9de2d3SDavid du Colombier 	m = BLEN(b);
1205d9de2d3SDavid du Colombier 	if(m < 6)
1215d9de2d3SDavid du Colombier 		return -1;
1225d9de2d3SDavid du Colombier 	return unpack(edev, b, m);
1235d9de2d3SDavid du Colombier }
1245d9de2d3SDavid du Colombier 
1255d9de2d3SDavid du Colombier static int
unpackasix(Ether * edev,Block * b)1265d9de2d3SDavid du Colombier unpackasix(Ether *edev, Block *b)
1275d9de2d3SDavid du Colombier {
1285d9de2d3SDavid du Colombier 	ulong hd;
1295d9de2d3SDavid du Colombier 	int m;
1305d9de2d3SDavid du Colombier 	uchar *wp;
1315d9de2d3SDavid du Colombier 
1325d9de2d3SDavid du Colombier 	if(BLEN(b) < 4)
1335d9de2d3SDavid du Colombier 		return -1;
1345d9de2d3SDavid du Colombier 	hd = GET4(b->rp);
1355d9de2d3SDavid du Colombier 	b->rp += 4;
1365d9de2d3SDavid du Colombier 	m = hd & 0xFFFF;
1375d9de2d3SDavid du Colombier 	hd >>= 16;
1385d9de2d3SDavid du Colombier 	if(m != (~hd & 0xFFFF))
1395d9de2d3SDavid du Colombier 		return -1;
1405d9de2d3SDavid du Colombier 	m = ROUND(m, 2);
1415d9de2d3SDavid du Colombier 	if(m < 6 || m > BLEN(b))
1425d9de2d3SDavid du Colombier 		return -1;
1435d9de2d3SDavid du Colombier 	if((wp = b->rp + m) != b->wp && b->wp - wp < 4)
1445d9de2d3SDavid du Colombier 		b->wp = wp;
1455d9de2d3SDavid du Colombier 	return unpack(edev, b, m);
1465d9de2d3SDavid du Colombier }
1475d9de2d3SDavid du Colombier 
1485d9de2d3SDavid du Colombier static int
unpacksmsc(Ether * edev,Block * b)1495d9de2d3SDavid du Colombier unpacksmsc(Ether *edev, Block *b)
1505d9de2d3SDavid du Colombier {
1515d9de2d3SDavid du Colombier 	ulong hd;
1525d9de2d3SDavid du Colombier 	int m;
1535d9de2d3SDavid du Colombier 
1545d9de2d3SDavid du Colombier 	ddump('@', b);
1555d9de2d3SDavid du Colombier 	if(BLEN(b) < 4)
1565d9de2d3SDavid du Colombier 		return -1;
1575d9de2d3SDavid du Colombier 	hd = GET4(b->rp);
1585d9de2d3SDavid du Colombier 	b->rp += 4;
1595d9de2d3SDavid du Colombier 	m = hd >> 16;
1605d9de2d3SDavid du Colombier 	if(m < 6 || m > BLEN(b))
1615d9de2d3SDavid du Colombier 		return -1;
1625d9de2d3SDavid du Colombier 	if(BLEN(b) - m < 4)
1635d9de2d3SDavid du Colombier 		b->wp = b->rp + m;
1645d9de2d3SDavid du Colombier 	if(hd & SmscRxerror){
1655d9de2d3SDavid du Colombier 		edev->frames++;
1665d9de2d3SDavid du Colombier 		b->rp += m;
1675d9de2d3SDavid du Colombier 		if(BLEN(b) == 0){
1685d9de2d3SDavid du Colombier 			freeb(b);
1695d9de2d3SDavid du Colombier 			return 1;
1705d9de2d3SDavid du Colombier 		}
1715d9de2d3SDavid du Colombier 	}else if(unpack(edev, b, m) == 1)
1725d9de2d3SDavid du Colombier 		return 1;
1735d9de2d3SDavid du Colombier 	if((m &= 3) != 0)
1745d9de2d3SDavid du Colombier 		b->rp += 4 - m;
1755d9de2d3SDavid du Colombier 	return 0;
1765d9de2d3SDavid du Colombier }
1775d9de2d3SDavid du Colombier 
1785d9de2d3SDavid du Colombier static void
transmit(Ctlr * ctlr,Block * b)1795d9de2d3SDavid du Colombier transmit(Ctlr *ctlr, Block *b)
1805d9de2d3SDavid du Colombier {
1815d9de2d3SDavid du Colombier 	Chan *c;
1825d9de2d3SDavid du Colombier 
1835d9de2d3SDavid du Colombier 	ddump('!', b);
1845d9de2d3SDavid du Colombier 	c = ctlr->outchan;
1855d9de2d3SDavid du Colombier 	devtab[c->type]->bwrite(c, b, 0);
1865d9de2d3SDavid du Colombier }
1875d9de2d3SDavid du Colombier 
1885d9de2d3SDavid du Colombier static void
transmitcdc(Ctlr * ctlr,Block * b)1895d9de2d3SDavid du Colombier transmitcdc(Ctlr *ctlr, Block *b)
1905d9de2d3SDavid du Colombier {
1915d9de2d3SDavid du Colombier 	transmit(ctlr, b);
1925d9de2d3SDavid du Colombier }
1935d9de2d3SDavid du Colombier 
1945d9de2d3SDavid du Colombier static void
transmitasix(Ctlr * ctlr,Block * b)1955d9de2d3SDavid du Colombier transmitasix(Ctlr *ctlr, Block *b)
1965d9de2d3SDavid du Colombier {
1975d9de2d3SDavid du Colombier 	int n;
1985d9de2d3SDavid du Colombier 
1995d9de2d3SDavid du Colombier 	n = BLEN(b) & 0xFFFF;
200b4d1cf41SDavid du Colombier 	n |= ~n << 16;
2015d9de2d3SDavid du Colombier 	padblock(b, 4);
2025d9de2d3SDavid du Colombier 	PUT4(b->rp, n);
2035d9de2d3SDavid du Colombier 	if(BLEN(b) % ctlr->maxpkt == 0){
2045d9de2d3SDavid du Colombier 		padblock(b, -4);
2055d9de2d3SDavid du Colombier 		PUT4(b->wp, 0xFFFF0000);
2065d9de2d3SDavid du Colombier 		b->wp += 4;
2075d9de2d3SDavid du Colombier 	}
2085d9de2d3SDavid du Colombier 	transmit(ctlr, b);
2095d9de2d3SDavid du Colombier }
2105d9de2d3SDavid du Colombier 
2115d9de2d3SDavid du Colombier static void
transmitsmsc(Ctlr * ctlr,Block * b)2125d9de2d3SDavid du Colombier transmitsmsc(Ctlr *ctlr, Block *b)
2135d9de2d3SDavid du Colombier {
2145d9de2d3SDavid du Colombier 	int n;
2155d9de2d3SDavid du Colombier 
2165d9de2d3SDavid du Colombier 	n = BLEN(b) & 0x7FF;
2175d9de2d3SDavid du Colombier 	padblock(b, 8);
2185d9de2d3SDavid du Colombier 	PUT4(b->rp, n | SmscTxfirst | SmscTxlast);
2195d9de2d3SDavid du Colombier 	PUT4(b->rp+4, n);
2205d9de2d3SDavid du Colombier 	transmit(ctlr, b);
2215d9de2d3SDavid du Colombier }
2225d9de2d3SDavid du Colombier 
2235d9de2d3SDavid du Colombier static void
etherusbproc(void * a)2245d9de2d3SDavid du Colombier etherusbproc(void *a)
2255d9de2d3SDavid du Colombier {
2265d9de2d3SDavid du Colombier 	Ether *edev;
2275d9de2d3SDavid du Colombier 	Ctlr *ctlr;
2285d9de2d3SDavid du Colombier 	Chan *c;
2295d9de2d3SDavid du Colombier 	Block *b;
2305d9de2d3SDavid du Colombier 
2315d9de2d3SDavid du Colombier 	edev = a;
2325d9de2d3SDavid du Colombier 	ctlr = edev->ctlr;
2335d9de2d3SDavid du Colombier 	c = ctlr->inchan;
2345d9de2d3SDavid du Colombier 	b = nil;
2355d9de2d3SDavid du Colombier 	if(waserror()){
2365d9de2d3SDavid du Colombier 		print("etherusbproc: error exit %s\n", up->errstr);
2375d9de2d3SDavid du Colombier 		pexit(up->errstr, 1);
2385d9de2d3SDavid du Colombier 		return;
2395d9de2d3SDavid du Colombier 	}
2405d9de2d3SDavid du Colombier 	for(;;){
2415d9de2d3SDavid du Colombier 		if(b == nil){
2425d9de2d3SDavid du Colombier 			b = devtab[c->type]->bread(c, ctlr->bufsize, 0);
2435d9de2d3SDavid du Colombier 			ctlr->rxbuf++;
2445d9de2d3SDavid du Colombier 		}
2455d9de2d3SDavid du Colombier 		switch(ctlr->udev->unpack(edev, b)){
2465d9de2d3SDavid du Colombier 		case -1:
2475d9de2d3SDavid du Colombier 			edev->buffs++;
2485d9de2d3SDavid du Colombier 			freeb(b);
2495d9de2d3SDavid du Colombier 			/* fall through */
2505d9de2d3SDavid du Colombier 		case 1:
2515d9de2d3SDavid du Colombier 			b = nil;
2525d9de2d3SDavid du Colombier 			break;
2535d9de2d3SDavid du Colombier 		}
2545d9de2d3SDavid du Colombier 	}
2555d9de2d3SDavid du Colombier }
2565d9de2d3SDavid du Colombier 
2575d9de2d3SDavid du Colombier /*
2585d9de2d3SDavid du Colombier  * bind type indev outdev mac bufsize maxpkt
2595d9de2d3SDavid du Colombier  */
2605d9de2d3SDavid du Colombier static void
bind(Ctlr * ctlr,Udev * udev,Cmdbuf * cb)2615d9de2d3SDavid du Colombier bind(Ctlr *ctlr, Udev *udev, Cmdbuf *cb)
2625d9de2d3SDavid du Colombier {
2635d9de2d3SDavid du Colombier 	Chan *inchan, *outchan;
2645d9de2d3SDavid du Colombier 	char *buf;
2655d9de2d3SDavid du Colombier 	uint bufsize, maxpkt;
2665d9de2d3SDavid du Colombier 
2675d9de2d3SDavid du Colombier 	qlock(ctlr);
2685d9de2d3SDavid du Colombier 	inchan = outchan = nil;
2695d9de2d3SDavid du Colombier 	buf = nil;
2705d9de2d3SDavid du Colombier 	if(waserror()){
2715d9de2d3SDavid du Colombier 		free(buf);
2725d9de2d3SDavid du Colombier 		if(inchan)
2735d9de2d3SDavid du Colombier 			cclose(inchan);
2745d9de2d3SDavid du Colombier 		if(outchan)
2755d9de2d3SDavid du Colombier 			cclose(outchan);
2765d9de2d3SDavid du Colombier 		qunlock(ctlr);
2775d9de2d3SDavid du Colombier 		nexterror();
2785d9de2d3SDavid du Colombier 	}
2795d9de2d3SDavid du Colombier 	if(ctlr->buf != nil)
2805d9de2d3SDavid du Colombier 		cmderror(cb, "already bound to a device");
2815d9de2d3SDavid du Colombier 	maxpkt = strtol(cb->f[6], 0, 0);
2825d9de2d3SDavid du Colombier 	if(maxpkt < 8 || maxpkt > 512)
2835d9de2d3SDavid du Colombier 		cmderror(cb, "bad maxpkt");
2845d9de2d3SDavid du Colombier 	bufsize = strtol(cb->f[5], 0, 0);
2855d9de2d3SDavid du Colombier 	if(bufsize < maxpkt || bufsize > 32*1024)
2865d9de2d3SDavid du Colombier 		cmderror(cb, "bad bufsize");
2875d9de2d3SDavid du Colombier 	buf = smalloc(bufsize);
2885d9de2d3SDavid du Colombier 	inchan = namec(cb->f[2], Aopen, OREAD, 0);
2895d9de2d3SDavid du Colombier 	outchan = namec(cb->f[3], Aopen, OWRITE, 0);
2905d9de2d3SDavid du Colombier 	assert(inchan != nil && outchan != nil);
2915d9de2d3SDavid du Colombier 	if(parsemac(ctlr->edev->ea, cb->f[4], Eaddrlen) != Eaddrlen)
2925d9de2d3SDavid du Colombier 		cmderror(cb, "bad etheraddr");
2935d9de2d3SDavid du Colombier 	memmove(ctlr->edev->addr, ctlr->edev->ea, Eaddrlen);
2945d9de2d3SDavid du Colombier 	print("\netherusb %s: %E\n", udev->name, ctlr->edev->addr);
2955d9de2d3SDavid du Colombier 	ctlr->buf = buf;
2965d9de2d3SDavid du Colombier 	ctlr->inchan = inchan;
2975d9de2d3SDavid du Colombier 	ctlr->outchan = outchan;
2985d9de2d3SDavid du Colombier 	ctlr->bufsize = bufsize;
2995d9de2d3SDavid du Colombier 	ctlr->maxpkt = maxpkt;
3005d9de2d3SDavid du Colombier 	ctlr->udev = udev;
3015d9de2d3SDavid du Colombier 	kproc("etherusb", etherusbproc, ctlr->edev);
3025d9de2d3SDavid du Colombier 	poperror();
3035d9de2d3SDavid du Colombier 	qunlock(ctlr);
3045d9de2d3SDavid du Colombier }
3055d9de2d3SDavid du Colombier 
3065d9de2d3SDavid du Colombier static void
unbind(Ctlr * ctlr)3075d9de2d3SDavid du Colombier unbind(Ctlr *ctlr)
3085d9de2d3SDavid du Colombier {
3095d9de2d3SDavid du Colombier 	qlock(ctlr);
3105d9de2d3SDavid du Colombier 	if(ctlr->buf != nil){
3115d9de2d3SDavid du Colombier 		free(ctlr->buf);
3125d9de2d3SDavid du Colombier 		ctlr->buf = nil;
3135d9de2d3SDavid du Colombier 		if(ctlr->inchan)
3145d9de2d3SDavid du Colombier 			cclose(ctlr->inchan);
3155d9de2d3SDavid du Colombier 		if(ctlr->outchan)
3165d9de2d3SDavid du Colombier 			cclose(ctlr->outchan);
3175d9de2d3SDavid du Colombier 		ctlr->inchan = ctlr->outchan = nil;
3185d9de2d3SDavid du Colombier 	}
3195d9de2d3SDavid du Colombier 	qunlock(ctlr);
3205d9de2d3SDavid du Colombier }
3215d9de2d3SDavid du Colombier 
3225d9de2d3SDavid du Colombier static long
etherusbifstat(Ether * edev,void * a,long n,ulong offset)3235d9de2d3SDavid du Colombier etherusbifstat(Ether* edev, void* a, long n, ulong offset)
3245d9de2d3SDavid du Colombier {
3255d9de2d3SDavid du Colombier 	Ctlr *ctlr;
3265d9de2d3SDavid du Colombier 	char *p;
3275d9de2d3SDavid du Colombier 	int l;
3285d9de2d3SDavid du Colombier 
3295d9de2d3SDavid du Colombier 	ctlr = edev->ctlr;
3305d9de2d3SDavid du Colombier 	p = malloc(READSTR);
3315d9de2d3SDavid du Colombier 	l = 0;
3325d9de2d3SDavid du Colombier 
3335d9de2d3SDavid du Colombier 	l += snprint(p+l, READSTR-l, "rxbuf: %ud\n", ctlr->rxbuf);
3345d9de2d3SDavid du Colombier 	l += snprint(p+l, READSTR-l, "rxpkt: %ud\n", ctlr->rxpkt);
3355d9de2d3SDavid du Colombier 	l += snprint(p+l, READSTR-l, "txbuf: %ud\n", ctlr->txbuf);
3365d9de2d3SDavid du Colombier 	l += snprint(p+l, READSTR-l, "txpkt: %ud\n", ctlr->txpkt);
3375d9de2d3SDavid du Colombier 	USED(l);
3385d9de2d3SDavid du Colombier 
3395d9de2d3SDavid du Colombier 	n = readstr(offset, a, n, p);
3405d9de2d3SDavid du Colombier 	free(p);
3415d9de2d3SDavid du Colombier 	return n;
3425d9de2d3SDavid du Colombier }
3435d9de2d3SDavid du Colombier 
3445d9de2d3SDavid du Colombier static void
etherusbtransmit(Ether * edev)3455d9de2d3SDavid du Colombier etherusbtransmit(Ether *edev)
3465d9de2d3SDavid du Colombier {
3475d9de2d3SDavid du Colombier 	Ctlr *ctlr;
3485d9de2d3SDavid du Colombier 	Block *b;
3495d9de2d3SDavid du Colombier 
3505d9de2d3SDavid du Colombier 	ctlr = edev->ctlr;
3515d9de2d3SDavid du Colombier 	while((b = qget(edev->oq)) != nil){
3525d9de2d3SDavid du Colombier 		ctlr->txpkt++;
3535d9de2d3SDavid du Colombier 		if(ctlr->buf == nil)
3545d9de2d3SDavid du Colombier 			freeb(b);
3555d9de2d3SDavid du Colombier 		else{
3565d9de2d3SDavid du Colombier 			ctlr->udev->transmit(ctlr, b);
3575d9de2d3SDavid du Colombier 			ctlr->txbuf++;
3585d9de2d3SDavid du Colombier 		}
3595d9de2d3SDavid du Colombier 	}
3605d9de2d3SDavid du Colombier }
3615d9de2d3SDavid du Colombier 
3625d9de2d3SDavid du Colombier static long
etherusbctl(Ether * edev,void * buf,long n)3635d9de2d3SDavid du Colombier etherusbctl(Ether* edev, void* buf, long n)
3645d9de2d3SDavid du Colombier {
3655d9de2d3SDavid du Colombier 	Ctlr *ctlr;
3665d9de2d3SDavid du Colombier 	Cmdbuf *cb;
3675d9de2d3SDavid du Colombier 	Cmdtab *ct;
3685d9de2d3SDavid du Colombier 	Udev *udev;
3695d9de2d3SDavid du Colombier 
3705d9de2d3SDavid du Colombier 	if((ctlr = edev->ctlr) == nil)
3715d9de2d3SDavid du Colombier 		error(Enonexist);
3725d9de2d3SDavid du Colombier 
3735d9de2d3SDavid du Colombier 	cb = parsecmd(buf, n);
3745d9de2d3SDavid du Colombier 	if(waserror()){
3755d9de2d3SDavid du Colombier 		free(cb);
3765d9de2d3SDavid du Colombier 		nexterror();
3775d9de2d3SDavid du Colombier 	}
3785d9de2d3SDavid du Colombier 	ct = lookupcmd(cb, cmds, nelem(cmds));
3795d9de2d3SDavid du Colombier 	switch(ct->index){
3805d9de2d3SDavid du Colombier 	case Bind:
3815d9de2d3SDavid du Colombier 		for(udev = udevtab; udev->name; udev++)
3825d9de2d3SDavid du Colombier 			if(strcmp(cb->f[1], udev->name) == 0)
3835d9de2d3SDavid du Colombier 				break;
3845d9de2d3SDavid du Colombier 		if(udev->name == nil)
3855d9de2d3SDavid du Colombier 			cmderror(cb, "unknown etherusb type");
3865d9de2d3SDavid du Colombier 		bind(ctlr, udev, cb);
3875d9de2d3SDavid du Colombier 		break;
3885d9de2d3SDavid du Colombier 	case Unbind:
3895d9de2d3SDavid du Colombier 		unbind(ctlr);
3905d9de2d3SDavid du Colombier 		break;
3915d9de2d3SDavid du Colombier 	default:
3925d9de2d3SDavid du Colombier 		cmderror(cb, "unknown etherusb control message");
3935d9de2d3SDavid du Colombier 	}
3945d9de2d3SDavid du Colombier 	poperror();
3955d9de2d3SDavid du Colombier 	free(cb);
3965d9de2d3SDavid du Colombier 	return n;
3975d9de2d3SDavid du Colombier }
3985d9de2d3SDavid du Colombier 
3995d9de2d3SDavid du Colombier static void
etherusbattach(Ether * edev)4005d9de2d3SDavid du Colombier etherusbattach(Ether* edev)
4015d9de2d3SDavid du Colombier {
4025d9de2d3SDavid du Colombier 	Ctlr *ctlr;
4035d9de2d3SDavid du Colombier 
4045d9de2d3SDavid du Colombier 	ctlr = edev->ctlr;
4055d9de2d3SDavid du Colombier 	ctlr->edev = edev;
4065d9de2d3SDavid du Colombier }
4075d9de2d3SDavid du Colombier 
4085d9de2d3SDavid du Colombier static int
etherusbpnp(Ether * edev)4095d9de2d3SDavid du Colombier etherusbpnp(Ether* edev)
4105d9de2d3SDavid du Colombier {
4115d9de2d3SDavid du Colombier 	Ctlr *ctlr;
4125d9de2d3SDavid du Colombier 
4135d9de2d3SDavid du Colombier 	ctlr = malloc(sizeof(Ctlr));
4145d9de2d3SDavid du Colombier 	edev->ctlr = ctlr;
4155d9de2d3SDavid du Colombier 	edev->irq = -1;
4165d9de2d3SDavid du Colombier 	edev->mbps = 100;	/* TODO: get this from usbether */
417*853458f3SDavid du Colombier 
418*853458f3SDavid du Colombier 	/*
419*853458f3SDavid du Colombier 	 * Linkage to the generic ethernet driver.
420*853458f3SDavid du Colombier 	 */
4215d9de2d3SDavid du Colombier 	edev->attach = etherusbattach;
4225d9de2d3SDavid du Colombier 	edev->transmit = etherusbtransmit;
423*853458f3SDavid du Colombier 	edev->interrupt = nil;
4245d9de2d3SDavid du Colombier 	edev->ifstat = etherusbifstat;
4255d9de2d3SDavid du Colombier 	edev->ctl = etherusbctl;
426*853458f3SDavid du Colombier 
427*853458f3SDavid du Colombier 	edev->arg = edev;
428*853458f3SDavid du Colombier 	/* TODO: promiscuous, multicast (for ipv6), shutdown (for reboot) */
429*853458f3SDavid du Colombier //	edev->promiscuous = etherusbpromiscuous;
430*853458f3SDavid du Colombier //	edev->shutdown = etherusbshutdown;
431*853458f3SDavid du Colombier //	edev->multicast = etherusbmulticast;
432*853458f3SDavid du Colombier 
4335d9de2d3SDavid du Colombier 	return 0;
4345d9de2d3SDavid du Colombier }
4355d9de2d3SDavid du Colombier 
4365d9de2d3SDavid du Colombier void
etherusblink(void)4375d9de2d3SDavid du Colombier etherusblink(void)
4385d9de2d3SDavid du Colombier {
4395d9de2d3SDavid du Colombier 	addethercard("usb", etherusbpnp);
4405d9de2d3SDavid du Colombier }
441