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

This commit is contained in:
ftrvxmtrx 2014-04-22 23:34:52 +02:00
parent 0f98415f99
commit 99c0abc76d
5 changed files with 191 additions and 8 deletions

View file

@ -162,14 +162,15 @@ Without specifying the
option, the device is assumed to be a CDC compliant ethernet option, the device is assumed to be a CDC compliant ethernet
communication device. Other devices might require setting an communication device. Other devices might require setting an
explicit explicit
.IR ethertype .IR ethertype ,
such as such as
.BR rndis ,
.BR smsc , .BR smsc ,
.B a88772 .BR a88772
or or
.B a88178 .BR a88178
see (see
.IR nusbrc (8). .IR nusbrc (8)).
On devices that support it, the mac address can be set using On devices that support it, the mac address can be set using
the the
.B -a .B -a

View file

@ -20,6 +20,8 @@ if(! nusb/usbd)
nusb/ether -t aue $etherargs $1 & nusb/ether -t aue $etherargs $1 &
case 0bda8150 case 0bda8150
nusb/ether -t url $etherargs $1 & nusb/ether -t url $etherargs $1 &
case 18d14ee3
nusb/ether -t rndis $etherargs $1 &
case * case *
switch($4){ switch($4){
case *03 case *03

View file

@ -763,6 +763,7 @@ extern int a88772init(Dev *);
extern int smscinit(Dev *); extern int smscinit(Dev *);
extern int cdcinit(Dev *); extern int cdcinit(Dev *);
extern int urlinit(Dev *); extern int urlinit(Dev *);
extern int rndisinit(Dev *);
static struct { static struct {
char *name; char *name;
@ -772,8 +773,9 @@ static struct {
"smsc", smscinit, "smsc", smscinit,
"a88178", a88178init, "a88178", a88178init,
"a88772", a88772init, "a88772", a88772init,
"aue", aueinit, "aue", aueinit,
"url", urlinit, "url", urlinit,
"rndis", rndisinit,
}; };
void void

View file

@ -5,7 +5,7 @@ LIB=../lib/usb.a$O
TARG=ether TARG=ether
HFILES= 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 </sys/src/cmd/mkone

View file

@ -0,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;
}