ref: 23239adf54d11628c1263df067b892130d67650b
parent: 49c8aed2db34c876ff81da10043fd278256e26db
author: cinap_lenrek <[email protected]>
date: Thu Dec 6 18:05:24 EST 2012
nusb/ether: port drivers for asix and smsc ethernet
--- a/sys/src/9/boot/nusbrc
+++ b/sys/src/9/boot/nusbrc
@@ -11,30 +11,36 @@
@{
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 &
}
}
}
--- /dev/null
+++ b/sys/src/cmd/nusb/ether/asix.c
@@ -1,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;
+}
--- /dev/null
+++ b/sys/src/cmd/nusb/ether/cdc.c
@@ -1,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;
+}
--- /dev/null
+++ b/sys/src/cmd/nusb/ether/dat.h
@@ -1,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;
--- a/sys/src/cmd/nusb/ether/ether.c
+++ b/sys/src/cmd/nusb/ether/ether.c
@@ -7,7 +7,10 @@
#include <9p.h>
#include "usb.h"
+#include "dat.h"
+#include <ip.h>
+
typedef struct Tab Tab;
typedef struct Qbuf Qbuf;
typedef struct Dq Dq;
@@ -17,13 +20,6 @@
enum
{
- Cdcunion = 6,
- Scether = 6,
- Fnether = 15,
-};
-
-enum
-{
Qroot,
Qiface,
Qclone,
@@ -96,21 +92,16 @@
int out;
};
+Stats stats;
Conn conn[32];
int nconn = 0;
-int debug;
-ulong time0;
Dev *epin;
Dev *epout;
-Stats stats;
+ulong time0;
+static char *uname;
-uchar macaddr[6];
-int maxpacket = 64;
-
-char *uname;
-
#define PATH(type, n) ((type)|((n)<<8))
#define TYPE(path) (((uint)(path) & 0x000000FF)>>0)
#define NUM(path) (((uint)(path) & 0xFFFFFF00)>>8)
@@ -303,25 +294,11 @@
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 @@
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 @@
"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 @@
}
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 @@
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 @@
{
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 @@
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 @@
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 @@
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){
--- a/sys/src/cmd/nusb/ether/mkfile
+++ b/sys/src/cmd/nusb/ether/mkfile
@@ -5,7 +5,7 @@
TARG=ether
HFILES=
-OFILES=ether.$O
+OFILES=ether.$O cdc.$O smsc.$O asix.$O
</sys/src/cmd/mkone
--- /dev/null
+++ b/sys/src/cmd/nusb/ether/smsc.c
@@ -1,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;
+}