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