diff options
-rw-r--r-- | sys/man/4/nusb | 11 | ||||
-rwxr-xr-x | sys/src/9/boot/nusbrc | 2 | ||||
-rw-r--r-- | sys/src/cmd/nusb/ether/ether.c | 6 | ||||
-rw-r--r-- | sys/src/cmd/nusb/ether/mkfile | 2 | ||||
-rw-r--r-- | sys/src/cmd/nusb/ether/rndis.c | 178 |
5 files changed, 191 insertions, 8 deletions
diff --git a/sys/man/4/nusb b/sys/man/4/nusb index ca95638e7..a3b19515f 100644 --- a/sys/man/4/nusb +++ b/sys/man/4/nusb @@ -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 diff --git a/sys/src/9/boot/nusbrc b/sys/src/9/boot/nusbrc index 88be1c85f..534f139eb 100755 --- a/sys/src/9/boot/nusbrc +++ b/sys/src/9/boot/nusbrc @@ -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 diff --git a/sys/src/cmd/nusb/ether/ether.c b/sys/src/cmd/nusb/ether/ether.c index 2c9a9121a..4b8b61845 100644 --- a/sys/src/cmd/nusb/ether/ether.c +++ b/sys/src/cmd/nusb/ether/ether.c @@ -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 diff --git a/sys/src/cmd/nusb/ether/mkfile b/sys/src/cmd/nusb/ether/mkfile index e7b887757..c4dc13d42 100644 --- a/sys/src/cmd/nusb/ether/mkfile +++ b/sys/src/cmd/nusb/ether/mkfile @@ -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 diff --git a/sys/src/cmd/nusb/ether/rndis.c b/sys/src/cmd/nusb/ether/rndis.c new file mode 100644 index 000000000..88e91a725 --- /dev/null +++ b/sys/src/cmd/nusb/ether/rndis.c @@ -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; +} |