nusb/ether: add RNDIS support (tested on Nexus 5)
This commit is contained in:
parent
0f98415f99
commit
99c0abc76d
5 changed files with 191 additions and 8 deletions
|
@ -162,14 +162,15 @@ Without specifying the
|
|||
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
|
||||
|
|
|
@ -20,6 +20,8 @@ if(! nusb/usbd)
|
|||
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
|
||||
|
|
|
@ -763,6 +763,7 @@ extern int a88772init(Dev *);
|
|||
extern int smscinit(Dev *);
|
||||
extern int cdcinit(Dev *);
|
||||
extern int urlinit(Dev *);
|
||||
extern int rndisinit(Dev *);
|
||||
|
||||
static struct {
|
||||
char *name;
|
||||
|
@ -772,8 +773,9 @@ static struct {
|
|||
"smsc", smscinit,
|
||||
"a88178", a88178init,
|
||||
"a88772", a88772init,
|
||||
"aue", aueinit,
|
||||
"url", urlinit,
|
||||
"aue", aueinit,
|
||||
"url", urlinit,
|
||||
"rndis", rndisinit,
|
||||
};
|
||||
|
||||
void
|
||||
|
|
|
@ -5,7 +5,7 @@ LIB=../lib/usb.a$O
|
|||
|
||||
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
|
||||
|
||||
|
|
178
sys/src/cmd/nusb/ether/rndis.c
Normal file
178
sys/src/cmd/nusb/ether/rndis.c
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue