/* * 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. Load more
/* * 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 int qtype(vlong q) { return q&0xFF; } static int qnum(vlong q) { return (q >> 8) & 0xFFFFFF; } static uvlong mkqid(int n, int t) { uvlong q; q = (n&0xFFFFFF) << 8 | t&0xFF; return q; } static void freebuf(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; } void dumpframe(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 void etherdump(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 void filldir(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 int rootdirgen(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 int conndirgen(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 int fswalk(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 int fsstat(Usbfs *fs, Qid qid, Dir *d) { filldir(fs, d, qdirtab(qid.path), qnum(qid.path)); return 0; } static int fsopen(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; }