449 lines · 7.7 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * Kernel proxy for usb ethernet device */ #include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "io.h"#include "../port/error.h"#include "../port/netif.h" #include "etherif.h"#include "../ip/ip.h" #define	GET4(p)		((p)[3]<<24 | (p)[2]<<16 | (p)[1]<<8  | (p)[0])#define	PUT4(p, v)	((p)[0] = (v), (p)[1] = (v)>>8, \			 (p)[2] = (v)>>16, (p)[3] = (v)>>24)#define	dprint	if(debug) print#define ddump	if(0) dump static int debug = 0; enum {	Bind	= 0,	Unbind, 	SmscRxerror	= 0x8000,	SmscTxfirst	= 0x2000,	SmscTxlast	= 0x1000,}; typedef struct Ctlr Ctlr;typedef struct Udev Udev; typedef int (Unpackfn)(Ether*, Block*);typedef void (Transmitfn)(Ctlr*, Block*); struct Ctlr {	Ether*	edev;	Udev*	udev;	Chan*	inchan;	Chan*	outchan;	char*	buf;	int	bufsize;	int	maxpkt;	uint	rxbuf;	uint	rxpkt;	uint	txbuf;	uint	txpkt;	QLock;}; struct Udev {	char	*name;	Unpackfn *unpack;	Transmitfn *transmit;};	static Cmdtab cmds[] = {	{ Bind,		"bind",		7, },	{ Unbind,	"unbind",	0, },}; static Unpackfn unpackcdc, unpackasix, unpacksmsc;static Transmitfn transmitcdc, transmitasix, transmitsmsc; static Udev udevtab[] = {	{ "cdc",	unpackcdc,	transmitcdc, },	{ "asix",	unpackasix,	transmitasix, },	{ "smsc",	unpacksmsc,	transmitsmsc, },	{ nil },}; static voiddump(int c, Block *b){	int s, i; 	s = splhi();	print("%c%ld:", c, BLEN(b));	for(i = 0; i < 32; i++)		print(" %2.2ux", b->rp[i]);	print("\n");	splx(s);} static intunpack(Ether *edev, Block *b, int m){	Block *nb;	Ctlr *ctlr; 	ctlr = edev->ctlr;	ddump('?', b);	if(m == BLEN(b)){		etheriq(edev, b, 1);		ctlr->rxpkt++;		return 1;	}	nb = iallocb(m);	if(nb != nil){		memmove(nb->wp, b->rp, m);		nb->wp += m;		etheriq(edev, nb, 1);		ctlr->rxpkt++;	}else		edev->soverflows++;	b->rp += m;	return 0;} static intunpackcdc(Ether *edev, Block *b){	int m; 	m = BLEN(b);	if(m < 6)		return -1;	return unpack(edev, b, m);} static intunpackasix(Ether *edev, Block *b){	ulong hd;	int m;	uchar *wp; 	if(BLEN(b) < 4)		return -1;	hd = GET4(b->rp);	b->rp += 4;	m = hd & 0xFFFF;	hd >>= 16;	if(m != (~hd & 0xFFFF))		return -1;	m = ROUND(m, 2);	if(m < 6 || m > BLEN(b))		return -1;	if((wp = b->rp + m) != b->wp && b->wp - wp < 4)		b->wp = wp;	return unpack(edev, b, m);} static intunpacksmsc(Ether *edev, Block *b){	ulong hd;	int m;		ddump('@', b);	if(BLEN(b) < 4)		return -1;	hd = GET4(b->rp);	b->rp += 4;	m = hd >> 16;	if(m < 6 || m > BLEN(b))		return -1;	if(BLEN(b) - m < 4)		b->wp = b->rp + m;	if(hd & SmscRxerror){		edev->frames++;		b->rp += m;		if(BLEN(b) == 0){			freeb(b);			return 1;		}	}else if(unpack(edev, b, m) == 1)		return 1;	if((m &= 3) != 0)		b->rp += 4 - m;	return 0;} static voidtransmit(Ctlr *ctlr, Block *b){	Chan *c; 	ddump('!', b);	c = ctlr->outchan;	devtab[c->type]->bwrite(c, b, 0);} static voidtransmitcdc(Ctlr *ctlr, Block *b){	transmit(ctlr, b);} static voidtransmitasix(Ctlr *ctlr, Block *b){	int n; 	n = BLEN(b) & 0xFFFF;	n |= ~n << 16;	padblock(b, 4);	PUT4(b->rp, n);	if(BLEN(b) % ctlr->maxpkt == 0){		padblock(b, -4);		PUT4(b->wp, 0xFFFF0000);		b->wp += 4;	}	transmit(ctlr, b);} static voidtransmitsmsc(Ctlr *ctlr, Block *b){	int n; 	n = BLEN(b) & 0x7FF;	padblock(b, 8);	PUT4(b->rp, n | SmscTxfirst | SmscTxlast);	PUT4(b->rp+4, n);	transmit(ctlr, b);} static voidetherusbproc(void *a){	Ether *edev;	Ctlr *ctlr;	Chan *c;	Block *b; 	edev = a;	ctlr = edev->ctlr;	c = ctlr->inchan;	b = nil;	if(waserror()){		print("etherusbproc: error exit %s\n", up->errstr);		pexit(up->errstr, 1);		return;	}	for(;;){		if(b == nil){			b = devtab[c->type]->bread(c, ctlr->bufsize, 0);			ctlr->rxbuf++;		}		switch(ctlr->udev->unpack(edev, b)){		case -1:			edev->buffs++;			freeb(b);			/* fall through */		case 1:			b = nil;			break;		}	}} /* * bind type indev outdev mac bufsize maxpkt */static voidbind(Ctlr *ctlr, Udev *udev, Cmdbuf *cb){	Chan *inchan, *outchan;	char *buf;	uint bufsize, maxpkt; 	qlock(ctlr);	inchan = outchan = nil;	buf = nil;	if(waserror()){		free(buf);		if(inchan)			cclose(inchan);		if(outchan)			cclose(outchan);		qunlock(ctlr);		nexterror();	}	if(ctlr->buf != nil)		cmderror(cb, "already bound to a device");	maxpkt = strtol(cb->f[6], 0, 0);	if(maxpkt < 8 || maxpkt > 512)		cmderror(cb, "bad maxpkt");	bufsize = strtol(cb->f[5], 0, 0);	if(bufsize < maxpkt || bufsize > 32*1024)		cmderror(cb, "bad bufsize");	buf = smalloc(bufsize);	inchan = namec(cb->f[2], Aopen, OREAD, 0);	outchan = namec(cb->f[3], Aopen, OWRITE, 0);	assert(inchan != nil && outchan != nil);	if(parsemac(ctlr->edev->ea, cb->f[4], Eaddrlen) != Eaddrlen)		cmderror(cb, "bad etheraddr");	memmove(ctlr->edev->addr, ctlr->edev->ea, Eaddrlen);	print("\netherusb %s: %E\n", udev->name, ctlr->edev->addr);	ctlr->buf = buf;	ctlr->inchan = inchan;	ctlr->outchan = outchan;	ctlr->bufsize = bufsize;	ctlr->maxpkt = maxpkt;	ctlr->udev = udev;	kproc("etherusb", etherusbproc, ctlr->edev);	poperror();	qunlock(ctlr);} static voidunbind(Ctlr *ctlr){	qlock(ctlr);	if(ctlr->buf != nil){		free(ctlr->buf);		ctlr->buf = nil;		if(ctlr->inchan)			cclose(ctlr->inchan);		if(ctlr->outchan)			cclose(ctlr->outchan);		ctlr->inchan = ctlr->outchan = nil;	}	qunlock(ctlr);} static longetherusbifstat(Ether* edev, void* a, long n, ulong offset){	Ctlr *ctlr;	char *p;	int l; 	ctlr = edev->ctlr;	p = malloc(READSTR);	l = 0; 	l += snprint(p+l, READSTR-l, "rxbuf: %ud\n", ctlr->rxbuf);	l += snprint(p+l, READSTR-l, "rxpkt: %ud\n", ctlr->rxpkt);	l += snprint(p+l, READSTR-l, "txbuf: %ud\n", ctlr->txbuf);	l += snprint(p+l, READSTR-l, "txpkt: %ud\n", ctlr->txpkt);	USED(l); 	n = readstr(offset, a, n, p);	free(p);	return n;} static voidetherusbtransmit(Ether *edev){	Ctlr *ctlr;	Block *b;		ctlr = edev->ctlr;	while((b = qget(edev->oq)) != nil){		ctlr->txpkt++;		if(ctlr->buf == nil)			freeb(b);		else{			ctlr->udev->transmit(ctlr, b);			ctlr->txbuf++;		}	}} static longetherusbctl(Ether* edev, void* buf, long n){	Ctlr *ctlr;	Cmdbuf *cb;	Cmdtab *ct;	Udev *udev; 	if((ctlr = edev->ctlr) == nil)		error(Enonexist); 	cb = parsecmd(buf, n);	if(waserror()){		free(cb);		nexterror();	}	ct = lookupcmd(cb, cmds, nelem(cmds));	switch(ct->index){	case Bind:		for(udev = udevtab; udev->name; udev++)			if(strcmp(cb->f[1], udev->name) == 0)				break;		if(udev->name == nil)			cmderror(cb, "unknown etherusb type");		bind(ctlr, udev, cb);		break;	case Unbind:		unbind(ctlr);		break;	default:		cmderror(cb, "unknown etherusb control message");	}	poperror();	free(cb);	return n;} static voidetherusbattach(Ether* edev){	Ctlr *ctlr; 	ctlr = edev->ctlr;	ctlr->edev = edev;} static intetherusbpnp(Ether* edev){	Ctlr *ctlr; 	ctlr = malloc(sizeof(Ctlr));	edev->ctlr = ctlr;	edev->irq = -1;	edev->mbps = 100;	/* TODO: get this from usbether */ 	/*	 * Linkage to the generic ethernet driver.	 */	edev->attach = etherusbattach;	edev->transmit = etherusbtransmit;	edev->interrupt = nil;	edev->ifstat = etherusbifstat;	edev->ctl = etherusbctl; 	edev->arg = edev;	/* TODO: promiscuous, multicast (for ipv6), shutdown (for reboot) *///	edev->promiscuous = etherusbpromiscuous;//	edev->shutdown = etherusbshutdown;//	edev->multicast = etherusbmulticast; 	return 0;} voidetherusblink(void){	addethercard("usb", etherusbpnp);}