shithub: riscv

Download patch

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;
+}