shithub: riscv

Download patch

ref: 99c0abc76db4e534a6e2dd76ef57c37f297bf735
parent: 0f98415f99948880cd25190a2aee8f5bbefcdfcb
author: ftrvxmtrx <devnull@localhost>
date: Tue Apr 22 19:34:52 EDT 2014

nusb/ether: add RNDIS support (tested on Nexus 5)

--- a/sys/man/4/nusb
+++ b/sys/man/4/nusb
@@ -162,14 +162,15 @@
 option, the device is assumed to be a CDC compliant ethernet
 communication device. Other devices might require setting an
 explicit
-.IR ethertype
+.IR ethertype ,
 such as
+.BR rndis ,
 .BR smsc ,
-.B a88772
+.BR a88772
 or
-.B a88178
-see
-.IR nusbrc (8).
+.BR a88178
+(see
+.IR nusbrc (8)).
 On devices that support it, the mac address can be set using
 the
 .B -a
--- a/sys/src/9/boot/nusbrc
+++ b/sys/src/9/boot/nusbrc
@@ -20,6 +20,8 @@
 			nusb/ether -t aue $etherargs $1 &
 		case 0bda8150
 			nusb/ether -t url $etherargs $1 &
+		case 18d14ee3
+			nusb/ether -t rndis $etherargs $1 &
 		case *
 			switch($4){
 			case *03
--- a/sys/src/cmd/nusb/ether/ether.c
+++ b/sys/src/cmd/nusb/ether/ether.c
@@ -763,6 +763,7 @@
 extern int smscinit(Dev *);
 extern int cdcinit(Dev *);
 extern int urlinit(Dev *);
+extern int rndisinit(Dev *);
 
 static struct {
 	char *name;
@@ -772,8 +773,9 @@
 	"smsc",		smscinit,
 	"a88178",	a88178init,
 	"a88772",	a88772init,
-	"aue", aueinit,
-	"url", urlinit,
+	"aue",		aueinit,
+	"url",		urlinit,
+	"rndis",	rndisinit,
 };
 
 void
--- a/sys/src/cmd/nusb/ether/mkfile
+++ b/sys/src/cmd/nusb/ether/mkfile
@@ -5,7 +5,7 @@
 
 TARG=ether
 HFILES=
-OFILES=ether.$O cdc.$O smsc.$O asix.$O aue.$O url.$O
+OFILES=ether.$O cdc.$O smsc.$O asix.$O aue.$O url.$O rndis.$O
 
 </sys/src/cmd/mkone
 
--- /dev/null
+++ b/sys/src/cmd/nusb/ether/rndis.c
@@ -1,0 +1,178 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+
+#include "usb.h"
+#include "dat.h"
+
+static uchar minit[24] = {
+	2,  0, 0, 0, /* type = 2 (init) */
+	24, 0, 0, 0, /* len = 24 */
+	0,  0, 0, 0, /* rid = 1 */
+	1,  0, 0, 0, /* vmajor = 1 */
+	1,  0, 0, 0, /* vminor = 1 */
+	0,  0, 0, 0, /* max xfer */
+};
+static uchar mgetmac[28] = {
+	4,  0, 0, 0, /* type = 4 (query) */
+	28, 0, 0, 0, /* len = 28 */
+	0,  0, 0, 0, /* rid = 2 */
+	1,  1, 1, 1, /* oid = get "permanent address" */
+	0,  0, 0, 0, /* buflen = 0 */
+	0,  0, 0, 0, /* bufoff = 0 */
+	0,  0, 0, 0, /* reserved = 0 */
+};
+static uchar mfilter[32] = {
+	5,  0, 0, 0, /* type = 5 (set) */
+	32, 0, 0, 0, /* len = 32 */
+	0,  0, 0, 0, /* rid = 3 */
+	14, 1, 1, 0, /* oid = "current filter" */
+	4,  0, 0, 0, /* buflen = 4 */
+	20, 0, 0, 0, /* bufoff = 20 (8+20=28) */
+	0,  0, 0, 0, /* reserved = 0 */
+	12, 0, 0, 0, /* filter = all multicast + broadcast */
+};
+
+static int
+rndisout(Dev *d, int id, uchar *msg, int sz)
+{
+	return usbcmd(d, Rh2d|Rclass|Riface, Rgetstatus, 0, id, msg, sz);
+}
+
+static int
+rndisin(Dev *d, int id, uchar *buf, int sz)
+{
+	int r, status;
+	for(;;){
+		if((r = usbcmd(d, Rd2h|Rclass|Riface, Rclearfeature, 0, id, buf, sz)) >= 16){
+			if((status = GET4(buf+12)) != 0){
+				werrstr("status 0x%02x", status);
+				r = -1;
+			}else if(GET4(buf) == 7) /* ignore status messages */
+				continue;
+		}else if(r > 0){
+			werrstr("short recv: %d", r);
+			r = -1;
+		}
+		break;
+	}
+	return r;
+}
+
+static int
+rndisreceive(Dev *ep)
+{
+	Block *b;
+	int n, len;
+	int doff, dlen;
+
+	b = allocb(Maxpkt);
+	if((n = read(ep->dfd, b->rp, b->lim - b->base)) >= 0){
+		if(n < 44)
+			werrstr("short packet: %d bytes", n);
+		else if(GET4(b->rp) != 1)
+			werrstr("not a network packet: type 0x%08ux", GET4(b->wp));
+		else{
+			doff = GET4(b->rp+8);
+			dlen = GET4(b->rp+12);
+			if((len = GET4(b->rp+4)) != n || 8+doff+dlen > len || dlen < Ehdrsz)
+				werrstr("bad packet: doff %d, dlen %d, len %d", doff, dlen, len);
+			else{
+				b->rp += 8 + doff;
+				b->wp = b->rp + dlen;
+
+				etheriq(b, 1);
+				return 0;
+			}
+		}
+	}
+
+	freeb(b);
+	return -1;
+}
+
+static void
+rndistransmit(Dev *ep, Block *b)
+{
+	int n;
+	uchar *req;
+
+	n = BLEN(b);
+	if((req = malloc(44 + n)) != nil){
+		PUT4(req, 1);      /* type = 1 (packet) */
+		PUT4(req+4, 44+n); /* len */
+		PUT4(req+8, 44-8); /* data offset */
+		PUT4(req+12, n);   /* data length */
+		memset(req+16, 0, 7*4);
+		memcpy(req+44, b->rp, n);
+		write(ep->dfd, req, 44+n);
+		free(req);
+	}
+	freeb(b);
+}
+
+int
+rndisinit(Dev *d)
+{
+	uchar res[128];
+	int r, i, off, sz;
+	Ep *ep;
+
+	r = 0;
+	for(i = 0; i < nelem(d->usb->ep); i++){
+		if((ep = d->usb->ep[i]) == nil)
+			continue;
+		if(ep->iface->csp == 0x000301e0)
+			r = 1;
+	}
+	if(!r){
+		werrstr("no rndis found");
+		return -1;
+	}
+
+	/* initialize */
+	PUT4(minit+20, 1580); /* max xfer = 1580 */
+	if(rndisout(d, 0, minit, sizeof(minit)) < 0)
+		werrstr("init: %r");
+	else if((r = rndisin(d, 0, res, sizeof(res))) < 0)
+		werrstr("init: %r");
+	else if(GET4(res) != 0x80000002 || r < 52)
+		werrstr("not an init response: type 0x%08ux, len %d", GET4(res), r);
+	/* check the type */
+	else if((r = GET4(res+24)) != 1)
+		werrstr("not a connectionless device: %d", r);
+	else if((r = GET4(res+28)) != 0)
+		werrstr("not a 802.3 device: %d", r);
+	else{
+		/* get mac address */
+		if(rndisout(d, 0, mgetmac, sizeof(mgetmac)) < 0)
+			werrstr("send getmac: %r");
+		else if((r = rndisin(d, 0, res, sizeof(res))) < 0)
+			werrstr("recv getmac: %r");
+		else if(GET4(res) != 0x80000004 || r < 24)
+			werrstr("not a query response: type 0x%08ux, len %d", GET4(res), r);
+		else {
+			sz = GET4(res+16);
+			off = GET4(res+20);
+			if(8+off+sz > r || sz != 6)
+				werrstr("invalid mac: off %d, sz %d, len %d", off, sz, r);
+			else{
+				memcpy(macaddr, res+8+off, 6);
+				/* set the filter */
+				if(rndisout(d, 0, mfilter, sizeof(mfilter)) < 0)
+					werrstr("send filter: %r");
+				else if(rndisin(d, 0, res, sizeof(res)) < 0)
+					werrstr("recv filter: %r");
+				else if(GET4(res) != 0x80000005)
+					werrstr("not a filter response: type 0x%08ux", GET4(res));
+				else{
+					epreceive = rndisreceive;
+					eptransmit = rndistransmit;
+					return 0;
+				}
+			}
+		}
+	}
+
+	return -1;
+}