1251 lines · 23.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. */ /* * usb/ether - usb ethernet adapter. * BUG: This should use /dev/etherfile to * use the kernel ether device code. */#include <u.h>#include <libc.h>#include <fcall.h>#include <thread.h>#include "usb.h"#include "usbfs.h"#include "ether.h" typedef struct Dirtab Dirtab; enum{	/* Qids. Maintain order (relative to dirtabs structs) */	Qroot	= 0,	Qclone,	Qaddr,	Qifstats,	Qstats,	Qndir,	Qndata,	Qnctl,	Qnifstats,	Qnstats,	Qntype,	Qmax,}; struct Dirtab{	char	*name;	int	qid;	int	mode;}; typedef int (*Resetf)(Ether*); /* * Controllers by vid/vid. Used to locate * specific adapters that do not implement cdc ethernet * Keep null terminated. */Cinfo cinfo[] ={	/* Asix controllers.	 * Only A88178 and A88772 are implemented.	 * Others are easy to add by borrowing code	 * from other systems.	 */	{0x0411, 0x003d, A8817x},	/* buffalo */	{0x0411, 0x006e, A88178},	{0x04f1, 0x3008, A8817x},	{0x050d, 0x5055, A88178},	{0x0557, 0x2009, A8817x},	{0x05ac, 0x1402, A88772},	/* Apple */	{0x077b, 0x2226, A8817x},	{0x07aa, 0x0017, A8817x},	{0x07d1, 0x3c05, A88772},	{0x0b95, 0x1720, A8817x},	/* asix */	{0x0b95, 0x1780, A88178},	/* Geoff */	{0x0b95, 0x7720, A88772},	{0x0b95, 0x772a, A88772},	{0x0db0, 0xa877, A88772},	{0x1189, 0x0893, A8817x},	{0x13b1, 0x0018, A88772},	{0x14ea, 0xab11, A88178},	{0x1557, 0x7720, A88772},	{0x1631, 0x6200, A8817x},	{0x1737, 0x0039, A88178},	{0x2001, 0x3c05, A88772},	{0x6189, 0x182d, A8817x}, 	/* SMSC controllers.	 * LAN95xx family	 */	{0x0424, 0xec00, S95xx},	/* LAN9512 (as in raspberry pi) */	{0x0424, 0x9500, S95xx},	{0x0424, 0x9505, S95xx},	{0x0424, 0x9E00, S95xx},	{0x0424, 0x9E01, S95xx},	{0, 0, 0},}; /* * Each etherU%d is the root of our file system, * which is added to the usb root directory. We only * have to concern ourselfs with each /etherU%d subtree. * * NB: Maintain order in dirtabs, relative to the Qids enum. */ static Dirtab rootdirtab[] ={	"/",		Qroot,		DMDIR|0555,	/* etherU%d */	"clone",	Qclone,		0666,	"addr",		Qaddr,		0444,	"ifstats",	Qifstats,	0444,	"stats",	Qstats,		0444,	/* one dir per connection here */	nil, 0, 0,}; static Dirtab conndirtab[] ={	"%d",		Qndir,		DMDIR|0555,	"data",		Qndata,		0666,	"ctl",		Qnctl,		0666,	"ifstats",	Qnifstats,	0444,	"stats",	Qnstats,	0444,	"type",		Qntype,		0444,	nil, 0,}; int etherdebug; Resetf ethers[] ={	asixreset,	smscreset,	cdcreset,	/* keep last */}; static intqtype(vlong q){	return q&0xFF;} static intqnum(vlong q){	return (q >> 8) & 0xFFFFFF;} static uvlongmkqid(int n, int t){	uvlong q; 	q = (n&0xFFFFFF) << 8 | t&0xFF;	return q;} static voidfreebuf(Ether *e, Buf *bp){	if(0)deprint(2, "%s: freebuf %#p\n", argv0, bp);	if(bp != nil){		qlock(e);		e->nbufs--;		qunlock(e);		sendp(e->bc, bp);	}} static Buf*allocbuf(Ether *e){	Buf *bp; 	bp = nbrecvp(e->bc);	if(bp == nil){		qlock(e);		if(e->nabufs < Nbufs){			bp = emallocz(sizeof(Buf), 1);			e->nabufs++;			setmalloctag(bp, getcallerpc(&e));			deprint(2, "%s: %d buffers\n", argv0, e->nabufs);		}		qunlock(e);	}	if(bp == nil){		deprint(2, "%s: blocked waiting for allocbuf\n", argv0);		bp = recvp(e->bc);	}	bp->rp = bp->data + Hdrsize;	bp->ndata = 0;	if(0)deprint(2, "%s: allocbuf %#p\n", argv0, bp);	qlock(e);	e->nbufs++;	qunlock(e);	return bp;} static Conn*newconn(Ether *e){	int i;	Conn *c; 	qlock(e);	for(i = 0; i < nelem(e->conns); i++){		c = e->conns[i];		if(c == nil || c->ref == 0){			if(c == nil){				c = emallocz(sizeof(Conn), 1);				c->rc = chancreate(sizeof(Buf*), 16);				c->nb = i;			}			c->ref = 1;			if(i == e->nconns)				e->nconns++;			e->conns[i] = c;			deprint(2, "%s: newconn %d\n", argv0, i);			qunlock(e);			return c;		}	}	qunlock(e);	return nil;} static char*seprintaddr(char *s, char *se, uchar *addr){	int i; 	for(i = 0; i < Eaddrlen; i++)		s = seprint(s, se, "%02x", addr[i]);	return s;} voiddumpframe(char *tag, void *p, int n){	Etherpkt *ep;	char buf[128];	char *s, *se;	int i; 	ep = p;	if(n < Eaddrlen * 2 + 2){		fprint(2, "short packet (%d bytes)\n", n);		return;	}	se = buf+sizeof(buf);	s = seprint(buf, se, "%s [%d]: ", tag, n);	s = seprintaddr(s, se, ep->s);	s = seprint(s, se, " -> ");	s = seprintaddr(s, se, ep->d);	s = seprint(s, se, " type 0x%02ux%02ux ", ep->type[0], ep->type[1]);	n -= Eaddrlen * 2 + 2;	for(i = 0; i < n && i < 16; i++)		s = seprint(s, se, "%02x", ep->data[i]);	if(n >= 16)		fprint(2, "%s...\n", buf);	else		fprint(2, "%s\n", buf);} static char*seprintstats(char *s, char *se, Ether *e){	qlock(e);	s = seprint(s, se, "in: %ld\n", e->nin);	s = seprint(s, se, "out: %ld\n", e->nout);	s = seprint(s, se, "input errs: %ld\n", e->nierrs);	s = seprint(s, se, "output errs: %ld\n", e->noerrs);	s = seprint(s, se, "mbps: %d\n", e->mbps);	s = seprint(s, se, "prom: %ld\n", e->prom.ref);	s = seprint(s, se, "addr: ");	s = seprintaddr(s, se, e->addr);	s = seprint(s, se, "\n");	qunlock(e);	return s;} static char*seprintifstats(char *s, char *se, Ether *e){	int i;	Conn *c; 	qlock(e);	s = seprint(s, se, "ctlr id: %#x\n", e->cid);	s = seprint(s, se, "phy: %#x\n", e->phy);	s = seprint(s, se, "exiting: %s\n", e->exiting ? "y" : "n");	s = seprint(s, se, "conns: %d\n", e->nconns);	s = seprint(s, se, "allocated bufs: %d\n", e->nabufs);	s = seprint(s, se, "used bufs: %d\n", e->nbufs);	for(i = 0; i < nelem(e->conns); i++){		c = e->conns[i];		if(c == nil)			continue;		if(c->ref == 0)			s = seprint(s, se, "c[%d]: free\n", i);		else{			s = seprint(s, se, "c[%d]: refs %ld t %#x h %d p %d\n",				c->nb, c->ref, c->type, c->headersonly, c->prom);		}	}	qunlock(e);	return s;} static voidetherdump(Ether *e){	char buf[256]; 	if(etherdebug == 0)		return;	seprintifstats(buf, buf+sizeof(buf), e);	fprint(2, "%s: ether %#p:\n%s\n", argv0, e, buf);} static Conn*getconn(Ether *e, int i, int idleok){	Conn *c; 	qlock(e);	if(i < 0 || i >= e->nconns)		c = nil;	else{		c = e->conns[i];		if(idleok == 0 && c != nil && c->ref == 0)			c = nil;	}	qunlock(e);	return c;} static voidfilldir(Usbfs *fs, Dir *d, Dirtab *tab, int cn){	d->qid.path = mkqid(cn, tab->qid);	d->qid.path |= fs->qid;	d->mode = tab->mode;	if((d->mode & DMDIR) != 0)		d->qid.type = QTDIR;	else		d->qid.type = QTFILE;	if(tab->qid == Qndir)		snprint(d->name, Namesz, "%d", cn);	else		d->name = tab->name;} static introotdirgen(Usbfs *fs, Qid, int i, Dir *d, void *){	Ether *e;	Dirtab *tab;	int cn; 	e = fs->aux;	i++;				/* skip root */	cn = 0;	if(i < nelem(rootdirtab) - 1)	/* null terminated */		tab = &rootdirtab[i];	else{		cn = i - nelem(rootdirtab) + 1;		if(cn < e->nconns)			tab = &conndirtab[0];		else			return -1;	}	filldir(fs, d, tab, cn);	return 0;} static intconndirgen(Usbfs *fs, Qid q, int i, Dir *d, void *){	Dirtab *tab; 	i++;				/* skip root */	if(i < nelem(conndirtab) - 1)	/* null terminated */		tab = &conndirtab[i];	else		return -1;	filldir(fs, d, tab, qnum(q.path));	return 0;} static intfswalk(Usbfs *fs, Fid *fid, char *name){	int cn, i;	char *es;	Dirtab *tab;	Ether *e;	Qid qid; 	e = fs->aux;	qid = fid->qid;	qid.path &= ~fs->qid;	if((qid.type & QTDIR) == 0){		werrstr("walk in non-directory");		return -1;	} 	if(strcmp(name, "..") == 0){		/* must be /etherU%d; i.e. our root dir. */		fid->qid.path = mkqid(0, Qroot) | fs->qid;		fid->qid.vers = 0;		fid->qid.type = QTDIR;		return 0;	}	switch(qtype(qid.path)){	case Qroot:		if(name[0] >= '0' && name[0] <= '9'){			es = name;			cn = strtoul(name, &es, 10);			if(cn >= e->nconns || *es != 0){				werrstr(Enotfound);				return -1;			}			fid->qid.path = mkqid(cn, Qndir) | fs->qid;			fid->qid.vers = 0;			return 0;		}		/* fall */	case Qndir:		if(qtype(qid.path) == Qroot)			tab = rootdirtab;		else			tab = conndirtab;		cn = qnum(qid.path);		for(i = 0; tab[i].name != nil; tab++)			if(strcmp(tab[i].name, name) == 0){				fid->qid.path = mkqid(cn, tab[i].qid)|fs->qid;				fid->qid.vers = 0;				if((tab[i].mode & DMDIR) != 0)					fid->qid.type = QTDIR;				else					fid->qid.type = QTFILE;				return 0;			}		break;	default:		sysfatal("usb: ether: fswalk bug");	}	return -1;} static Dirtab*qdirtab(vlong q){	int i, qt;	Dirtab *tab; 	qt = qtype(q);	if(qt < nelem(rootdirtab) - 1){	/* null terminated */		tab = rootdirtab;		i = qt;	}else{		tab = conndirtab;		i = qt - (nelem(rootdirtab) - 1);		assert(i < nelem(conndirtab) - 1);	}	return &tab[i];} static intfsstat(Usbfs *fs, Qid qid, Dir *d){	filldir(fs, d, qdirtab(qid.path), qnum(qid.path));	return 0;} static intfsopen(Usbfs *fs, Fid *fid, int omode){	int qt;	vlong qid;	Conn *c;	Dirtab *tab;	Ether *e; 	qid = fid->qid.path & ~fs->qid;	e = fs->aux;	qt = qtype(qid);	tab = qdirtab(qid);	omode &= 3;	if(omode != OREAD && (tab->mode&0222) == 0){		werrstr(Eperm);		return -1;	}	switch(qt){	case Qclone:		c = newconn(e);		if(c == nil){			werrstr("no more connections");			return -1;		}

Lines 1–500 of 1251.