/* * 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 void dump(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 int unpack(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 int unpackcdc(Ether *edev, Block *b) { int m; m = BLEN(b); if(m < 6) return -1; return unpack(edev, b, m); } static int unpackasix(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 int unpacksmsc(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 void transmit(Ctlr *ctlr, Block *b) { Chan *c; ddump('!', b); c = ctlr->outchan; devtab[c->type]->bwrite(c, b, 0); } static void transmitcdc(Ctlr *ctlr, Block *b) { transmit(ctlr, b); } static void transmitasix(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 void transmitsmsc(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 void etherusbproc(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 void bind(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 void unbind(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 long etherusbifstat(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 void etherusbtransmit(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 long etherusbctl(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 void etherusbattach(Ether* edev) { Ctlr *ctlr; ctlr = edev->ctlr; ctlr->edev = edev; } static int etherusbpnp(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; } void etherusblink(void) { addethercard("usb", etherusbpnp); }