diff options
author | cinap_lenrek <cinap_lenrek@gmx.de> | 2012-12-06 23:05:24 +0100 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@gmx.de> | 2012-12-06 23:05:24 +0100 |
commit | 23239adf54d11628c1263df067b892130d67650b (patch) | |
tree | 1f2357ff3ae68cd0611f1ed78264a98006375971 | |
parent | 49c8aed2db34c876ff81da10043fd278256e26db (diff) |
nusb/ether: port drivers for asix and smsc ethernet
-rwxr-xr-x | sys/src/9/boot/nusbrc | 52 | ||||
-rw-r--r-- | sys/src/cmd/nusb/ether/asix.c | 377 | ||||
-rw-r--r-- | sys/src/cmd/nusb/ether/cdc.c | 98 | ||||
-rw-r--r-- | sys/src/cmd/nusb/ether/dat.h | 19 | ||||
-rw-r--r-- | sys/src/cmd/nusb/ether/ether.c | 166 | ||||
-rw-r--r-- | sys/src/cmd/nusb/ether/mkfile | 2 | ||||
-rw-r--r-- | sys/src/cmd/nusb/ether/smsc.c | 297 |
7 files changed, 886 insertions, 125 deletions
diff --git a/sys/src/9/boot/nusbrc b/sys/src/9/boot/nusbrc index 955520f66..9a9f70754 100755 --- a/sys/src/9/boot/nusbrc +++ b/sys/src/9/boot/nusbrc @@ -11,30 +11,36 @@ if(! nusb/usbd) @{ rfork ne fn attach { - switch($4){ - case *03 - nusb/kb $1 & - case *02 - nusb/ether $1 & - case *08 - @{ - rfork ne - nusb/disk $1 - cd '#σ/usb' - for(dev in sdU^$1.*) if(test -d $dev) { - diskparts $dev - for(part in $dev/dos* $dev/9fat) if(test -r $part) { - mkdir -m 0700 '#σc/'^$dev || exit - {dossrv -s -f $part &} <[0=1] | - echo 0 >'#σc/'^$dev/dos - exit - } - } - } & + switch($2$3){ + case 0b957720 0b95772a 0db0a877 13b10018 15577720 20013c05 07d13c05 05ac1402 + nusb/ether -t a88772 $etherargs $1 & + case 0b951780 14eaab11 17370039 0411006e 050d5055 + nusb/ether -t a88178 $etherargs $1 & case * - if(~ $2 0424 && ~ $3 ec00){ - # raspberry pi ethernet - nusb/ether $1 & + switch($4){ + case *03 + nusb/kb $1 & + case *02 + # CDC ethernet + nusb/ether $etherargs $1 & + case *08 + @{ + rfork ne + nusb/disk $1 + cd '#σ/usb' + for(dev in sdU^$1.*) if(test -d $dev) { + diskparts $dev + for(part in $dev/dos* $dev/9fat) if(test -r $part) { + mkdir -m 0700 '#σc/'^$dev || exit + {dossrv -s -f $part &} <[0=1] | + echo 0 >'#σc/'^$dev/dos + exit + } + } + } & + case * + if(~ $2 0424) + nusb/ether -t smsc $etherargs $1 & } } } diff --git a/sys/src/cmd/nusb/ether/asix.c b/sys/src/cmd/nusb/ether/asix.c new file mode 100644 index 000000000..88fcfc4ad --- /dev/null +++ b/sys/src/cmd/nusb/ether/asix.c @@ -0,0 +1,377 @@ +/* + * Asix USB ether adapters + * I got no documentation for it, thus the bits + * come from other systems; it's likely this is + * doing more than needed in some places and + * less than required in others. + */ +#include <u.h> +#include <libc.h> +#include <thread.h> +#include <bio.h> +#include <auth.h> +#include <fcall.h> +#include <9p.h> + +#include "usb.h" +#include "dat.h" + +enum +{ + + /* Asix commands */ + Cswmii = 0x06, /* set sw mii */ + Crmii = 0x07, /* read mii reg */ + Cwmii = 0x08, /* write mii reg */ + Chwmii = 0x0a, /* set hw mii */ + Creeprom = 0x0b, /* read eeprom */ + Cwdis = 0x0e, /* write disable */ + Cwena = 0x0d, /* write enable */ + Crrxctl = 0x0f, /* read rx ctl */ + Cwrxctl = 0x10, /* write rx ctl */ + Cwipg = 0x12, /* write ipg */ + Crmac = 0x13, /* read mac addr */ + Crphy = 0x19, /* read phy id */ + Cwmedium = 0x1b, /* write medium mode */ + Crgpio = 0x1e, /* read gpio */ + Cwgpio = 0x1f, /* write gpios */ + Creset = 0x20, /* reset */ + Cwphy = 0x22, /* select phy */ + + /* reset codes */ + Rclear = 0x00, + Rprte = 0x04, + Rprl = 0x08, + Riprl = 0x20, + Rippd = 0x40, + + Gpiogpo1en = 0x04, /* gpio1 enable */, + Gpiogpo1 = 0x08, /* gpio1 value */ + Gpiogpo2en = 0x10, /* gpio2 enable */ + Gpiogpo2 = 0x20, /* gpio2 value */ + Gpiorse = 0x80, /* gpio reload serial eeprom */ + + Pmask = 0x1F, + Pembed = 0x10, /* embedded phy */ + + Mfd = 0x002, /* media */ + Mac = 0x004, + Mrfc = 0x010, + Mtfc = 0x020, + Mjfe = 0x040, + Mre = 0x100, + Mps = 0x200, + Mall772 = Mfd|Mrfc|Mtfc|Mps|Mac|Mre, + Mall178 = Mps|Mfd|Mac|Mrfc|Mtfc|Mjfe|Mre, + + Ipgdflt = 0x15|0x0c|0x12, /* default ipg0, 1, 2 */ + Rxctlso = 0x80, + Rxctlab = 0x08, + Rxctlsep = 0x04, + Rxctlamall = 0x02, /* all multicast */ + Rxctlprom = 0x01, /* promiscuous */ + + /* MII */ + Miibmcr = 0x00, /* basic mode ctrl reg. */ + Bmcrreset = 0x8000, /* reset */ + Bmcranena = 0x1000, /* auto neg. enable */ + Bmcrar = 0x0200, /* announce restart */ + + Miiad = 0x04, /* advertise reg. */ + Adcsma = 0x0001, + Ad1000f = 0x0200, + Ad1000h = 0x0100, + Ad10h = 0x0020, + Ad10f = 0x0040, + Ad100h = 0x0080, + Ad100f = 0x0100, + Adpause = 0x0400, + Adall = Ad10h|Ad10f|Ad100h|Ad100f, + + Miimctl = 0x14, /* marvell ctl */ + Mtxdly = 0x02, + Mrxdly = 0x80, + Mtxrxdly = 0x82, + + Miic1000 = 0x09, + +}; + +static uint asixphy; + +static int +asixset(Dev *d, int c, int v) +{ + int r; + int ec; + + r = Rh2d|Rvendor|Rdev; + ec = usbcmd(d, r, c, v, 0, nil, 0); + if(ec < 0) + fprint(2, "%s: asixset %x %x: %r\n", argv0, c, v); + return ec; +} + +static int +asixget(Dev *d, int c, uchar *buf, int l) +{ + int r; + int ec; + + r = Rd2h|Rvendor|Rdev; + ec = usbcmd(d, r, c, 0, 0, buf, l); + if(ec < 0) + fprint(2, "%s: asixget %x: %r\n", argv0, c); + return ec; +} + +static int +getgpio(Dev *d) +{ + uchar c; + + if(asixget(d, Crgpio, &c, 1) < 0) + return -1; + return c; +} + +static int +getphy(Dev *d) +{ + uchar buf[2]; + + if(asixget(d, Crphy, buf, sizeof(buf)) < 0) + return -1; + return buf[1]; +} + +static int +getrxctl(Dev *d) +{ + uchar buf[2]; + + memset(buf, 0, sizeof(buf)); + if(asixget(d, Crrxctl, buf, sizeof(buf)) < 0) + return -1; + return GET2(buf); +} + +static int +miiread(Dev *d, int phy, int reg) +{ + int r; + uchar v[2]; + + r = Rd2h|Rvendor|Rdev; + if(usbcmd(d, r, Crmii, phy, reg, v, 2) < 0){ + fprint(2, "%s: miiwrite: %r\n", argv0); + return -1; + } + r = GET2(v); + if(r == 0xFFFF) + return -1; + return r; +} + + +static int +miiwrite(Dev *d, int phy, int reg, int val) +{ + int r; + uchar v[2]; + + if(asixset(d, Cswmii, 0) < 0) + return -1; + r = Rh2d|Rvendor|Rdev; + PUT2(v, val); + if(usbcmd(d, r, Cwmii, phy, reg, v, 2) < 0){ + fprint(2, "%s: miiwrite: %#x %#x %r\n", argv0, reg, val); + return -1; + } + if(asixset(d, Chwmii, 0) < 0) + return -1; + return 0; +} + +static int +eepromread(Dev *d, int i) +{ + int r; + int ec; + uchar buf[2]; + + r = Rd2h|Rvendor|Rdev; + ec = usbcmd(d, r, Creeprom, i, 0, buf, sizeof(buf)); + if(ec < 0) + fprint(2, "%s: eepromread %d: %r\n", argv0, i); + ec = GET2(buf); + if(ec == 0xFFFF) + ec = -1; + return ec; +} + +static int +asixread(Dev *ep, uchar *p, int plen) +{ + int n, m; + uint hd; + + if(nbin < 4) + nbin = read(ep->dfd, bin, sizeof(bin)); + if(nbin < 0) + return -1; + if(nbin < 4) + return 0; + hd = GET4(bin); + n = hd & 0xFFFF; + m = n+4; + if((n != ~(hd>>16)) || (n < 6) || (m > nbin)){ + nbin = 0; + return 0; + } + if(n > plen) + n = plen; + if(n > 0) + memmove(p, bin+4, n); + if(m < nbin) + memmove(bin, bin+m, nbin - m); + nbin -= m; + return n; +} + +static void +asixwrite(Dev *ep, uchar *p, int n) +{ + if(n > sizeof(bout)-8) + n = sizeof(bout)-8; + PUT4(bout, n | ~(n<<16)); + memmove(bout+4, p, n); + n += 4; + if((n % ep->maxpkt) == 0){ + PUT4(bout+n, 0xFFFF0000); + n += 4; + } + write(ep->dfd, bout, n); +} + +static int +asixpromiscuous(Dev *d, int on) +{ + int rxctl; + + rxctl = getrxctl(d); + if(on != 0) + rxctl |= Rxctlprom; + else + rxctl &= ~Rxctlprom; + return asixset(d, Cwrxctl, rxctl); +} + +int +a88178init(Dev *d) +{ + int bmcr; + int gpio; + int ee17; + + fprint(2, "%s: setting up A88178\n", argv0); + gpio = getgpio(d); + if(gpio < 0) + return -1; + fprint(2, "%s: gpio sts %#x\n", argv0, gpio); + asixset(d, Cwena, 0); + ee17 = eepromread(d, 0x0017); + asixset(d, Cwdis, 0); + asixset(d, Cwgpio, Gpiorse|Gpiogpo1|Gpiogpo1en); + if((ee17 >> 8) != 1){ + asixset(d, Cwgpio, 0x003c); + asixset(d, Cwgpio, 0x001c); + asixset(d, Cwgpio, 0x003c); + }else{ + asixset(d, Cwgpio, Gpiogpo1en); + asixset(d, Cwgpio, Gpiogpo1|Gpiogpo1en); + } + asixset(d, Creset, Rclear); + sleep(150); + asixset(d, Creset, Rippd|Rprl); + sleep(150); + asixset(d, Cwrxctl, 0); + if(asixget(d, Crmac, macaddr, 6) < 0) + return -1; + asixphy = getphy(d); + if(ee17 < 0 || (ee17 & 0x7) == 0){ + miiwrite(d, asixphy, Miimctl, Mtxrxdly); + sleep(60); + } + miiwrite(d, asixphy, Miibmcr, Bmcrreset|Bmcranena); + miiwrite(d, asixphy, Miiad, Adall|Adcsma|Adpause); + miiwrite(d, asixphy, Miic1000, Ad1000f); + bmcr = miiread(d, asixphy, Miibmcr); + if((bmcr & Bmcranena) != 0){ + bmcr |= Bmcrar; + miiwrite(d, asixphy, Miibmcr, bmcr); + } + asixset(d, Cwmedium, Mall178); + asixset(d, Cwrxctl, Rxctlso|Rxctlab); + + epread = asixread; + epwrite = asixwrite; + return 0; +} + +int +a88772init(Dev *d) +{ + int bmcr; + int rc; + + if(asixset(d, Cwgpio, Gpiorse|Gpiogpo2|Gpiogpo2en) < 0) + return -1; + asixphy = getphy(d); + if((asixphy & Pmask) == Pembed){ + /* embedded 10/100 ethernet */ + rc = asixset(d, Cwphy, 1); + }else + rc = asixset(d, Cwphy, 0); + if(rc < 0) + return -1; + if(asixset(d, Creset, Rippd|Rprl) < 0) + return -1; + sleep(150); + if((asixphy & Pmask) == Pembed) + rc = asixset(d, Creset, Riprl); + else + rc = asixset(d, Creset, Rprte); + if(rc < 0) + return -1; + sleep(150); + getrxctl(d); + if(asixset(d, Cwrxctl, 0) < 0) + return -1; + if(asixget(d, Crmac, macaddr, 6) < 0) + return -1; + if(asixset(d, Creset, Rprl) < 0) + return -1; + sleep(150); + if(asixset(d, Creset, Riprl|Rprl) < 0) + return -1; + sleep(150); + + miiwrite(d, asixphy, Miibmcr, Bmcrreset); + miiwrite(d, asixphy, Miiad, Adall|Adcsma); + bmcr = miiread(d, asixphy, Miibmcr); + if((bmcr & Bmcranena) != 0){ + bmcr |= Bmcrar; + miiwrite(d, asixphy, Miibmcr, bmcr); + } + if(asixset(d, Cwmedium, Mall772) < 0) + return -1; + if(asixset(d, Cwipg, Ipgdflt) < 0) + return -1; + if(asixset(d, Cwrxctl, Rxctlso|Rxctlab) < 0) + return -1; + + epread = asixread; + epwrite = asixwrite; + return 0; +} diff --git a/sys/src/cmd/nusb/ether/cdc.c b/sys/src/cmd/nusb/ether/cdc.c new file mode 100644 index 000000000..a36d79fb0 --- /dev/null +++ b/sys/src/cmd/nusb/ether/cdc.c @@ -0,0 +1,98 @@ +/* + * generic CDC + */ + +#include <u.h> +#include <libc.h> +#include <thread.h> +#include <bio.h> +#include <auth.h> +#include <fcall.h> +#include <9p.h> + +#include "usb.h" +#include "dat.h" + +static int +str2mac(uchar *m, char *s) +{ + int i; + + if(strlen(s) != 12) + return -1; + + for(i=0; i<12; i++){ + uchar v; + + if(s[i] >= 'A' && s[i] <= 'F'){ + v = 10 + s[i] - 'A'; + } else if(s[i] >= 'a' && s[i] <= 'f'){ + v = 10 + s[i] - 'a'; + } else if(s[i] >= '0' && s[i] <= '9'){ + v = s[i] - '0'; + } else { + v = 0; + } + if(i&1){ + m[i/2] |= v; + } else { + m[i/2] = v<<4; + } + } + return 0; +} + +static int +cdcread(Dev *ep, uchar *p, int n) +{ + return read(ep->dfd, p, n); +} + +static void +cdcwrite(Dev *ep, uchar *p, int n) +{ + if(write(ep->dfd, p, n) < 0){ + fprint(2, "cdcwrite: %r\n"); + } else { + /* + * this may not work with all CDC devices. the + * linux driver sends one more random byte + * instead of a zero byte transaction. maybe we + * should do the same? + */ + if(n % ep->maxpkt == 0) + write(ep->dfd, "", 0); + } +} +int +cdcinit(Dev *d) +{ + int i; + Usbdev *ud; + uchar *b; + Desc *dd; + char *mac; + + ud = d->usb; + for(i = 0; i < nelem(ud->ddesc); i++) + if((dd = ud->ddesc[i]) != nil){ + b = (uchar*)&dd->data; + if(b[1] == Dfunction && b[2] == Fnether){ + mac = loaddevstr(d, b[3]); + if(mac != nil && strlen(mac) != 12){ + free(mac); + mac = nil; + } + if(mac != nil){ + str2mac(macaddr, mac); + free(mac); + + epread = cdcread; + epwrite = cdcwrite; + + return 0; + } + } + } + return -1; +} diff --git a/sys/src/cmd/nusb/ether/dat.h b/sys/src/cmd/nusb/ether/dat.h new file mode 100644 index 000000000..0e5a962ce --- /dev/null +++ b/sys/src/cmd/nusb/ether/dat.h @@ -0,0 +1,19 @@ +enum +{ + Cdcunion = 6, + Scether = 6, + Fnether = 15, +}; + +int debug; +int setmac; + +/* to be filled in by *init() */ +uchar macaddr[6]; +int (*epread)(Dev *, uchar *, int); +void (*epwrite)(Dev *, uchar *, int); + +/* temporary buffers */ +uchar bout[4*1024]; +uchar bin[4*1024]; +int nbin; diff --git a/sys/src/cmd/nusb/ether/ether.c b/sys/src/cmd/nusb/ether/ether.c index 055668351..952f1a851 100644 --- a/sys/src/cmd/nusb/ether/ether.c +++ b/sys/src/cmd/nusb/ether/ether.c @@ -7,6 +7,9 @@ #include <9p.h> #include "usb.h" +#include "dat.h" + +#include <ip.h> typedef struct Tab Tab; typedef struct Qbuf Qbuf; @@ -17,13 +20,6 @@ typedef struct Stats Stats; enum { - Cdcunion = 6, - Scether = 6, - Fnether = 15, -}; - -enum -{ Qroot, Qiface, Qclone, @@ -96,20 +92,15 @@ struct Stats int out; }; +Stats stats; Conn conn[32]; int nconn = 0; -int debug; -ulong time0; Dev *epin; Dev *epout; -Stats stats; - -uchar macaddr[6]; -int maxpacket = 64; - -char *uname; +ulong time0; +static char *uname; #define PATH(type, n) ((type)|((n)<<8)) #define TYPE(path) (((uint)(path) & 0x000000FF)>>0) @@ -303,25 +294,11 @@ writeconndata(Req *r) d = r->fid->aux; p = r->ifcall.data; n = r->ifcall.count; - if((n == 11) && memcmp(p, "nonblocking", n)==0){ d->nb = 1; goto out; } - - if(write(epout->dfd, p, n) < 0){ - fprint(2, "write: %r\n"); - } else { - /* - * this may not work with all CDC devices. the - * linux driver sends one more random byte - * instead of a zero byte transaction. maybe we - * should do the same? - */ - if(n % epout->maxpkt == 0) - write(epout->dfd, "", 0); - } - + epwrite(epout, p, n); if(receivepacket(p, n) == 0) stats.out++; out: @@ -343,35 +320,6 @@ mac2str(uchar *m) return buf; } -static int -str2mac(uchar *m, char *s) -{ - int i; - - if(strlen(s) != 12) - return -1; - - for(i=0; i<12; i++){ - uchar v; - - if(s[i] >= 'A' && s[i] <= 'F'){ - v = 10 + s[i] - 'A'; - } else if(s[i] >= 'a' && s[i] <= 'f'){ - v = 10 + s[i] - 'a'; - } else if(s[i] >= '0' && s[i] <= '9'){ - v = s[i] - '0'; - } else { - v = 0; - } - if(i&1){ - m[i/2] |= v; - } else { - m[i/2] = v<<4; - } - } - return 0; -} - static void fsread(Req *r) { @@ -402,14 +350,15 @@ fsread(Req *r) "in: %d\n" "out: %d\n" "mbps: %d\n" - "addr: %s\n", - stats.in, stats.out, 10, mac2str(macaddr)); + "addr: %E\n", + stats.in, stats.out, 10, macaddr); readstr(r, buf); respond(r, nil); break; case Qaddr: - readstr(r, mac2str(macaddr)); + snprint(buf, sizeof(buf), "%E", macaddr); + readstr(r, buf); respond(r, nil); break; @@ -695,36 +644,6 @@ findendpoints(Dev *d, int *ei, int *eo) } static int -getmac(Dev *d) -{ - int i; - Usbdev *ud; - uchar *b; - Desc *dd; - char *mac; - - ud = d->usb; - - for(i = 0; i < nelem(ud->ddesc); i++) - if((dd = ud->ddesc[i]) != nil){ - b = (uchar*)&dd->data; - if(b[1] == Dfunction && b[2] == Fnether){ - mac = loaddevstr(d, b[3]); - if(mac != nil && strlen(mac) != 12){ - free(mac); - mac = nil; - } - if(mac != nil){ - str2mac(macaddr, mac); - free(mac); - return 0; - } - } - } - return -1; -} - -static int inote(void *, char *msg) { if(strstr(msg, "interrupt")) @@ -758,7 +677,7 @@ receivepacket(void *buf, int len) if(c->type != t) goto next; if(!c->prom && !(h->d[0]&1)) - if(memcmp(h->d, macaddr, 6)) + if(memcmp(h->d, macaddr, sizeof(macaddr))) goto next; for(d=c->dq; d; d=d->next){ int n; @@ -790,21 +709,25 @@ usbreadproc(void *) { char err[ERRMAX]; uchar buf[4*1024]; + int n, nerr; + atnotify(inote, 1); threadsetname("usbreadproc"); + nerr = 0; for(;;){ - int n; - - n = read(epin->dfd, buf, sizeof(buf)); + n = epread(epin, buf, sizeof(buf)); if(n < 0){ rerrstr(err, sizeof(err)); if(strstr(err, "interrupted") || strstr(err, "timed out")) continue; fprint(2, "usbreadproc: %s\n", err); + if(++nerr < 3) + continue; threadexitsall(err); } + nerr = 0; if(n == 0) continue; if(receivepacket(buf, n) == 0) @@ -827,17 +750,36 @@ Srv fs = static void usage(void) { - fprint(2, "usage: %s [-dD] devid\n", argv0); + fprint(2, "usage: %s [-dD] [-t type] [-a addr] devid\n", argv0); exits("usage"); } + +extern int a88178init(Dev *); +extern int a88772init(Dev *); +extern int smscinit(Dev *); +extern int cdcinit(Dev *); + +static struct { + char *name; + int (*init)(Dev*); +} ethertype[] = { + "cdc", cdcinit, + "smsc", smscinit, + "a88178", a88178init, + "a88772", a88772init, +}; + void threadmain(int argc, char **argv) { - char s[64]; - int ei, eo; + char s[64], *t; + int et, ei, eo; Dev *d; + fmtinstall('E', eipfmt); + + et = 0; /* CDC */ ARGBEGIN { case 'd': debug = 1; @@ -845,6 +787,23 @@ threadmain(int argc, char **argv) case 'D': chatty9p++; break; + case 'a': + setmac = 1; + parseether(macaddr, EARGF(usage())); + break; + case 't': + t = EARGF(usage()); + for(et=0; et<nelem(ethertype); et++) + if(strcmp(t, ethertype[et].name) == 0) + break; + if(et == nelem(ethertype)){ + fprint(2, "unsupported ethertype -t %s, supported types are: ", t); + for(et=0; et<nelem(ethertype); et++) + fprint(2, "%s ", ethertype[et].name); + fprint(2, "\n"); + exits("usage"); + } + break; default: usage(); } ARGEND; @@ -855,8 +814,13 @@ threadmain(int argc, char **argv) d = getdev(atoi(*argv)); if(findendpoints(d, &ei, &eo) < 0) sysfatal("no endpoints found"); - if(getmac(d) < 0) - sysfatal("can't get mac address"); + + werrstr(""); + if((*ethertype[et].init)(d) < 0) + sysfatal("%s init failed: %r", ethertype[et].name); + if(epread == nil || epwrite == nil) + sysfatal("bug in init"); + if((epin = openep(d, ei)) == nil) sysfatal("openep: %r"); if(ei == eo){ diff --git a/sys/src/cmd/nusb/ether/mkfile b/sys/src/cmd/nusb/ether/mkfile index fab85ba6a..369c3aabf 100644 --- a/sys/src/cmd/nusb/ether/mkfile +++ b/sys/src/cmd/nusb/ether/mkfile @@ -5,7 +5,7 @@ LIB=../lib/usb.a$O TARG=ether HFILES= -OFILES=ether.$O +OFILES=ether.$O cdc.$O smsc.$O asix.$O </sys/src/cmd/mkone diff --git a/sys/src/cmd/nusb/ether/smsc.c b/sys/src/cmd/nusb/ether/smsc.c new file mode 100644 index 000000000..23bfc2af3 --- /dev/null +++ b/sys/src/cmd/nusb/ether/smsc.c @@ -0,0 +1,297 @@ +/* + * SMSC LAN95XX + */ + +#include <u.h> +#include <libc.h> +#include <thread.h> +#include <bio.h> +#include <auth.h> +#include <fcall.h> +#include <9p.h> + +#include "usb.h" +#include "dat.h" + +enum { + Doburst = 1, + Resettime = 1000, + E2pbusytime = 1000, + Afcdefault = 0xF830A1, +// Hsburst = 37, /* from original linux driver */ + Hsburst = 8, + Fsburst = 129, + Defbulkdly = 0x2000, + + Ethp8021q = 0x8100, + MACoffset = 1, + PHYinternal = 1, + Rxerror = 0x8000, + Txfirst = 0x2000, + Txlast = 0x1000, + + /* USB vendor requests */ + Writereg = 0xA0, + Readreg = 0xA1, + + /* device registers */ + Intsts = 0x08, + Txcfg = 0x10, + Txon = 1<<2, + Hwcfg = 0x14, + Bir = 1<<12, + Rxdoff = 3<<9, + Mef = 1<<5, + Lrst = 1<<3, + Bce = 1<<1, + Pmctrl = 0x20, + Phyrst = 1<<4, + Ledgpio = 0x24, + Ledspd = 1<<24, + Ledlnk = 1<<20, + Ledfdx = 1<<16, + Afccfg = 0x2C, + E2pcmd = 0x30, + Busy = 1<<31, + Timeout = 1<<10, + Read = 0, + E2pdata = 0x34, + Burstcap = 0x38, + Intepctl = 0x68, + Phyint = 1<<15, + Bulkdelay = 0x6C, + Maccr = 0x100, + Mcpas = 1<<19, + Prms = 1<<18, + Hpfilt = 1<<13, + Txen = 1<<3, + Rxen = 1<<2, + Addrh = 0x104, + Addrl = 0x108, + Hashh = 0x10C, + Hashl = 0x110, + MIIaddr = 0x114, + MIIwrite= 1<<1, + MIIread = 0<<1, + MIIbusy = 1<<0, + MIIdata = 0x118, + Flow = 0x11C, + Vlan1 = 0x120, + Coecr = 0x130, + Txcoe = 1<<16, + Rxcoemd = 1<<1, + Rxcoe = 1<<0, + + /* MII registers */ + Bmcr = 0, + Bmcrreset= 1<<15, + Speed100= 1<<13, + Anenable= 1<<12, + Anrestart= 1<<9, + Fulldpx = 1<<8, + Bmsr = 1, + Advertise = 4, + Adcsma = 0x0001, + Ad10h = 0x0020, + Ad10f = 0x0040, + Ad100h = 0x0080, + Ad100f = 0x0100, + Adpause = 0x0400, + Adpauseasym= 0x0800, + Adall = Ad10h|Ad10f|Ad100h|Ad100f, + Phyintsrc = 29, + Phyintmask = 30, + Anegcomp= 1<<6, + Linkdown= 1<<4, +}; + +static int +wr(Dev *d, int reg, int val) +{ + int ret; + + ret = usbcmd(d, Rh2d|Rvendor|Rdev, Writereg, 0, reg, + (uchar*)&val, sizeof(val)); + if(ret < 0) + fprint(2, "%s: wr(%x, %x): %r", argv0, reg, val); + return ret; +} + +static int +rr(Dev *d, int reg) +{ + int ret, rval; + + ret = usbcmd(d, Rd2h|Rvendor|Rdev, Readreg, 0, reg, + (uchar*)&rval, sizeof(rval)); + if(ret < 0){ + fprint(2, "%s: rr(%x): %r", argv0, reg); + return 0; + } + return rval; +} + +static int +miird(Dev *d, int idx) +{ + while(rr(d, MIIaddr) & MIIbusy) + ; + wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIread); + while(rr(d, MIIaddr) & MIIbusy) + ; + return rr(d, MIIdata); +} + +static void +miiwr(Dev *d, int idx, int val) +{ + while(rr(d, MIIaddr) & MIIbusy) + ; + wr(d, MIIdata, val); + wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIwrite); + while(rr(d, MIIaddr) & MIIbusy) + ; +} + +static int +eepromr(Dev *d, int off, uchar *buf, int len) +{ + int i, v; + + for(i = 0; i < E2pbusytime; i++) + if((rr(d, E2pcmd) & Busy) == 0) + break; + if(i == E2pbusytime) + return -1; + for(i = 0; i < len; i++){ + wr(d, E2pcmd, Busy|Read|(i+off)); + while((v = rr(d, E2pcmd) & (Busy|Timeout)) == Busy) + ; + if(v & Timeout) + return -1; + buf[i] = rr(d, E2pdata); + } + return 0; +} + +static void +phyinit(Dev *d) +{ + int i; + + miiwr(d, Bmcr, Bmcrreset|Anenable); + for(i = 0; i < Resettime/10; i++){ + if((miird(d, Bmcr) & Bmcrreset) == 0) + break; + sleep(10); + } + miiwr(d, Advertise, Adcsma|Adall|Adpause|Adpauseasym); +// miiwr(d, Advertise, Adcsma|Ad10f|Ad10h|Adpause|Adpauseasym); + miird(d, Phyintsrc); + miiwr(d, Phyintmask, Anegcomp|Linkdown); + miiwr(d, Bmcr, miird(d, Bmcr)|Anenable|Anrestart); +} + + +static int +doreset(Dev *d, int reg, int bit) +{ + int i; + + if(wr(d, reg, bit) < 0) + return -1; + for(i = 0; i < Resettime/10; i++){ + if((rr(d, reg) & bit) == 0) + return 1; + sleep(10); + } + return 0; +} + +static int +smscread(Dev *ep, uchar *p, int plen) +{ + int n, m; + uint hd; + + if(nbin < 4) + nbin = read(ep->dfd, bin, Doburst ? Hsburst*512: sizeof(bin)); + if(nbin < 0) + return -1; + if(nbin < 4) + return 0; + hd = GET4(bin); + n = hd >> 16; + m = (n + 4 + 3) & ~3; + if(n < 6 || m > nbin){ + werrstr("frame length"); + nbin = 0; + return 0; + } + if(hd & Rxerror){ + fprint(2, "smsc rx error %8.8ux\n", hd); + n = 0; + }else{ + if(n > plen) + n = plen; + if(n > 0) + memmove(p, bin+4, n); + } + if(m < nbin) + memmove(bin, bin+m, nbin - m); + nbin -= m; + return n; +} + +static void +smscwrite(Dev *ep, uchar *p, int n) +{ + if(n > sizeof(bout)-8) + n = sizeof(bout)-8; + PUT4(bout, n | Txfirst | Txlast); + PUT4(bout+4, n); + memmove(bout+8, p, n); + write(ep->dfd, bout, n+8); +} + +int +smscinit(Dev *d) +{ + if(d->usb->vid != 0x0424) + return -1; + if(!doreset(d, Hwcfg, Lrst) || !doreset(d, Pmctrl, Phyrst)) + return -1; + if(!setmac) + if(eepromr(d, MACoffset, macaddr, 6) < 0) + return -1; + wr(d, Addrl, GET4(macaddr)); + wr(d, Addrh, GET2(macaddr+4)); + if(Doburst){ + wr(d, Hwcfg, (rr(d,Hwcfg)&~Rxdoff)|Bir|Mef|Bce); + wr(d, Burstcap, Hsburst); + }else{ + wr(d, Hwcfg, (rr(d,Hwcfg)&~(Rxdoff|Mef|Bce))|Bir); + wr(d, Burstcap, 0); + } + wr(d, Bulkdelay, Defbulkdly); + wr(d, Intsts, ~0); + wr(d, Ledgpio, Ledspd|Ledlnk|Ledfdx); + wr(d, Flow, 0); + wr(d, Afccfg, Afcdefault); + wr(d, Vlan1, Ethp8021q); + wr(d, Coecr, rr(d,Coecr)&~(Txcoe|Rxcoe)); /* TODO could offload checksums? */ + + wr(d, Hashh, 0); + wr(d, Hashl, 0); + wr(d, Maccr, rr(d,Maccr)&~(Prms|Mcpas|Hpfilt)); + + phyinit(d); + + wr(d, Intepctl, rr(d, Intepctl)|Phyint); + wr(d, Maccr, rr(d, Maccr)|Txen|Rxen); + wr(d, Txcfg, Txon); + + epwrite = smscwrite; + epread = smscread; + return 0; +} |