Add support for Microchop (ex SMSC) LAN78xx usb ethernet, as used in LAN7515 chip on Raspberry Pi 3B+. Also tweak performance a bit for SMSC ethernet as used in earlier Raspberry Pi models. Note: this patch incorporates all changes from usbether-rpi and usb-ether-cdc and therefore supersedes those two patches. It may be necessary to revert them with patch/undo before applying this patch. Reference: /n/sources/patch/usb-ether-lan78xx Date: Thu Apr 5 11:56:16 CES 2018 Signed-off-by: miller@hamnavoe.com --- /sys/src/cmd/usb/ether/ether.h Thu Apr 5 11:52:59 2018 +++ /sys/src/cmd/usb/ether/ether.h Thu Apr 5 11:52:57 2018 @@ -14,6 +14,7 @@ A88179, A88772, S95xx, /* SMSC */ + S78xx, Eaddrlen = 6, Epktlen = 1514, @@ -114,6 +115,7 @@ int ethermain(Dev *dev, int argc, char **argv); int asixreset(Ether*); int smscreset(Ether*); +int lan78xxreset(Ether*); int cdcreset(Ether*); int parseaddr(uchar *m, char *s); void dumpframe(char *tag, void *p, int n); --- /sys/src/cmd/usb/ether/ether.c Thu Apr 5 11:53:05 2018 +++ /sys/src/cmd/usb/ether/ether.c Thu Apr 5 11:53:01 2018 @@ -82,6 +82,9 @@ {0x0424, 0x9505, S95xx}, {0x0424, 0x9E00, S95xx}, {0x0424, 0x9E01, S95xx}, + /* LAN78xx family - gigabit ethernet + */ + {0x0424, 0x7800, S78xx}, /* raspberry pi 3 B+ */ {0, 0, 0}, }; @@ -121,6 +124,7 @@ { asixreset, smscreset, + lan78xxreset, cdcreset, /* keep last */ }; @@ -1145,29 +1149,34 @@ static int kernelproxy(Ether *e) { - int ctlfd, n; + int ctlfd, n, i; + char ename[20]; char eaddr[13]; - ctlfd = open("#l0/ether0/clone", ORDWR); - if(ctlfd < 0){ - deprint(2, "%s: etherusb bind #l0: %r\n", argv0); - return -1; - } close(e->epin->dfd); close(e->epout->dfd); - seprintaddr(eaddr, eaddr+sizeof(eaddr), e->addr); - n = fprint(ctlfd, "bind %s #u/usb/ep%d.%d/data #u/usb/ep%d.%d/data %s %d %d", - e->name, e->dev->id, e->epin->id, e->dev->id, e->epout->id, - eaddr, e->bufsize, e->epout->maxpkt); - if(n < 0){ - deprint(2, "%s: etherusb bind #l0: %r\n", argv0); - opendevdata(e->epin, OREAD); - opendevdata(e->epout, OWRITE); + for(i = 0; i < 10; i++){ + sprint(ename, "#l%d/ether%d/clone", i, i); + ctlfd = open(ename, ORDWR); + if(ctlfd < 0){ + deprint(2, "%s: etherusb bind %.3s: %r\n", argv0, ename); + break; + } + seprintaddr(eaddr, eaddr+sizeof(eaddr), e->addr); + n = fprint(ctlfd, "bind %s #u/usb/ep%d.%d/data #u/usb/ep%d.%d/data %s %d %d", + e->name, e->dev->id, e->epin->id, e->dev->id, e->epout->id, + eaddr, e->bufsize, e->epout->maxpkt); + if(n < 0){ + deprint(2, "%s: etherusb bind %.3s: %r\n", argv0, ename); + close(ctlfd); + continue; + } close(ctlfd); - return -1; + return 0; } - close(ctlfd); - return 0; + opendevdata(e->epin, OREAD); + opendevdata(e->epout, OWRITE); + return -1; } int @@ -1222,8 +1231,18 @@ if(openeps(e, epin, epout) < 0) return -1; - if(kernelproxy(e) == 0) + if(kernelproxy(e) == 0){ + if(e->multicast != nil){ + /* + * Until there is an interface for the kernel etherusb driver + * to write to usb/ether ctl file, multicast needs to be on + * by default so ipv6 will work. + */ + e->nmcasts++; + e->multicast(e, nil, 1); + } return 0; + } e->fs = etherfs; snprint(e->fs.name, sizeof(e->fs.name), "etherU%d", devid); e->fs.dev = dev; --- /sys/src/cmd/usb/ether/mkfile Thu Apr 5 11:53:08 2018 +++ /sys/src/cmd/usb/ether/mkfile Thu Apr 5 11:53:06 2018 @@ -8,6 +8,7 @@ ether.$O\ asix.$O\ smsc.$O\ + lan78xx.$O\ cdc.$O\ HFILES=\ --- /sys/src/cmd/usb/ether/lan78xx.c Thu Jan 1 00:00:00 1970 +++ /sys/src/cmd/usb/ether/lan78xx.c Thu Apr 5 11:53:10 2018 @@ -0,0 +1,452 @@ +/* + * Microchip (ex SMSC) LAN78XX + * Also used as ethernet core in LAN7515 usb hub + ethernet + */ + +#include +#include +#include +#include +#include "usb.h" +#include "usbfs.h" +#include "ether.h" + +enum { + Doburst = 1, + Resettime = 1000, + E2pbusytime = 1000, + Hsburst = 32, + Defbulkdly = 1000, + Rxfifosize = (12*1024), + Txfifosize = (12*1024), + + MACoffset = 1, + PHYinternal = 1, + Rxerror = 0x00400000, + Txfcs = 1<<22, + + /* USB vendor requests */ + Writereg = 0xA0, + Readreg = 0xA1, + + /* device registers */ + Idrev = 0x00, + Intsts = 0x0C, + Hwcfg = 0x10, + Led0en = 1<<20, + Led1en = 1<<21, + Mef = 1<<4, + Lrst = 1<<1, + Pmctrl = 0x14, + Ready = 1<<7, + Phyrst = 1<<4, + Gpiocfg0 = 0x18, + Gpiocfg1 = 0x1C, + E2pcmd = 0x40, + Busy = 1<<31, + Timeout = 1<<10, + Loaded = 1<<9, + Read = 0, + E2pdata = 0x44, + Burstcap = 0x90, + Intepctl = 0x98, + Phyint = 1<<17, + Bulkdelay = 0x94, + Rfectl = 0xB0, + Rxcoe = 0xF<<11, + Ab = 1<<10, + Am = 1<<9, + Au = 1<<8, + Dpf = 1<<1, + Usbcfg0 = 0x80, + Bir = 1<<6, + Bce = 1<<5, + Usbcfg1 = 0x84, + Rxfifoctl = 0xC0, + Rxen = 1<<31, + Txfifoctl = 0xC4, + Txen = 1<<31, + Rxfifo = 0xC8, + Txfifo = 0xCc, + Fctflow = 0xD0, + Maccr = 0x100, + Add = 1<<12, + Asd = 1<<11, + Macrx = 0x104, + Macfcs = 1<<4, + Macrxen = 1<<0, + Mactx = 0x108, + Mactxen = 1<<0, + Addrh = 0x118, + Addrl = 0x11C, + MIIaddr = 0x120, + MIIwrite= 1<<1, + MIIread = 0<<1, + MIIbusy = 1<<0, + MIIdata = 0x124, + Flow = 0x10C, + Addrfilth = 0x400, + Afvalid = 1<<31, + Addrfiltl = 0x404, + + /* MII registers */ + Bmcr = 0, + Bmcrreset= 1<<15, + Speed100= 1<<13, + Anenable= 1<<12, + Anrestart= 1<<9, + Fulldpx = 1<<8, + Speed1000= 1<<6, + Bmsr = 1, + Advertise = 4, + Adcsma = 0x0001, + Ad10h = 0x0020, + Ad10f = 0x0040, + Ad100h = 0x0080, + Ad100f = 0x0100, + Adpause = 0x0400, + Adpauseasym= 0x0800, + Adall = Ad10h|Ad10f|Ad100h|Ad100f, + Lpa = 5, + Ctrl1000 = 9, + Ad1000h = 0x0400, + Ad1000f = 0x0200, + Ledmodes = 29, + Led0shift = 0, + Led1shift = 4, + Linkact = 0x0, + Link1000 = 0x1, + Phyintmask = 25, + Anegcomp= 1<<10, + Linkchg = 1<<13, +}; + +static int burstcap = Hsburst, bulkdelay = Defbulkdly; + +static int +wr(Dev *d, int reg, int val) +{ + int ret; + + ret = usbcmd(d, Rh2d|Rvendor|Rdev, Writereg, 0, reg, + (uchar*)&val, sizeof(val)); + if(ret < 0) + deprint(2, "%s: wr(%x, %x): %r", argv0, reg, val); + return ret; +} + +static int +rr(Dev *d, int reg) +{ + int ret, rval; + + ret = usbcmd(d, Rd2h|Rvendor|Rdev, Readreg, 0, reg, + (uchar*)&rval, sizeof(rval)); + if(ret < 0){ + fprint(2, "%s: rr(%x): %r", argv0, reg); + return 0; + } + return rval; +} + +static int +miird(Dev *d, int idx) +{ + while(rr(d, MIIaddr) & MIIbusy) + ; + wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIread | MIIbusy); + while(rr(d, MIIaddr) & MIIbusy) + ; + return rr(d, MIIdata); +} + +static void +miiwr(Dev *d, int idx, int val) +{ + while(rr(d, MIIaddr) & MIIbusy) + ; + wr(d, MIIdata, val); + wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIwrite | MIIbusy); + while(rr(d, MIIaddr) & MIIbusy) + ; +} + +static int +eepromr(Dev *d, int off, uchar *buf, int len) +{ + int i, v; + + for(i = 0; i < E2pbusytime; i++) + if((rr(d, E2pcmd) & Busy) == 0) + break; + if(i == E2pbusytime) + return -1; + for(i = 0; i < len; i++){ + wr(d, E2pcmd, Busy|Read|(i+off)); + while((v = rr(d, E2pcmd) & (Busy|Timeout)) == Busy) + ; + if(v & Timeout) + return -1; + buf[i] = rr(d, E2pdata); + } + return 0; +} + +static void +phyinit(Dev *d) +{ + int i; + + miiwr(d, Bmcr, Bmcrreset|Anenable); + for(i = 0; i < Resettime/10; i++){ + if((miird(d, Bmcr) & Bmcrreset) == 0) + break; + sleep(10); + } + miiwr(d, Advertise, Adcsma|Adall|Adpause|Adpauseasym); + miiwr(d, Ctrl1000, Ad1000f); + miiwr(d, Phyintmask, 0); + miiwr(d, Ledmodes, (Linkact<cid != S78xx) + return -1; + d = ether->dev; + deprint(2, "%s: setting up LAN78XX\n", argv0); + deprint(2, "chip id/rev = %8.8ux\n", rr(d, Idrev)); + if(!doreset(d, Hwcfg, Lrst) || !doreset(d, Pmctrl, Phyrst)) + return -1; + for(i = 0; i < Resettime/10; i++){ + if(rr(d, Pmctrl) & Ready) + break; + sleep(10); + } + if((rr(d, Pmctrl) & Ready) == 0){ + deprint(2, "%s: device not ready after reset\n", argv0); + return -1; + } + if(getmac(d, ether->addr) < 0) + deprint(2, "%s: can't read etheraddr from EEPROM\n", argv0); + a = GET4(ether->addr); + wr(d, Addrl, a); + wr(d, Addrfiltl, a); + a = GET2(ether->addr+4); + wr(d, Addrh, a); + wr(d, Addrfilth, a|Afvalid); + deprint(2, "Address filter %8.8ux %8.8ux\n", rr(d, Addrfilth), rr(d, Addrfiltl)); + + wr(d, Usbcfg0, rr(d, Usbcfg0) | Bir); + if(Doburst){ + wr(d, Hwcfg, rr(d, Hwcfg)|Mef); + wr(d, Usbcfg0, rr(d, Usbcfg0)|Bce); + wr(d, Burstcap, burstcap); + wr(d, Bulkdelay, bulkdelay); + }else{ + wr(d, Hwcfg, rr(d, Hwcfg)&~Mef); + wr(d, Usbcfg0, rr(d, Usbcfg0)&~Bce); + wr(d, Burstcap, 0); + wr(d, Bulkdelay, 0); + } + wr(d, Rxfifo, (Rxfifosize-512)/512); + wr(d, Txfifo, (Txfifosize-512)/512); + wr(d, Intsts, ~0); + wr(d, Hwcfg, rr(d, Hwcfg) | Led0en|Led1en); + wr(d, Flow, 0); + wr(d, Fctflow, 0); + wr(d, Rfectl, (rr(d, Rfectl) & ~Rxcoe) | Ab|Dpf); /* TODO could offload checksums? */ + + phyinit(d); + + wr(d, Maccr, rr(d,Maccr)|Add|Asd); + + wr(d, Intepctl, rr(d, Intepctl)|Phyint); + wr(d, Mactx, Mactxen); + wr(d, Macrx, rr(d, Macrx) | Macfcs|Macrxen); + wr(d, Txfifoctl, Txen); + wr(d, Rxfifoctl, Rxen); + + return 0; +} + +static long +lan78xxbread(Ether *e, Buf *bp) +{ + uint hd; + int n, m; + Buf *rbp; + + rbp = e->aux; + if(rbp->ndata < 10){ + rbp->rp = rbp->data; + rbp->ndata = read(e->epin->dfd, rbp->rp, Doburst? burstcap*512: + Maxpkt); + if(rbp->ndata < 0) + return -1; + } + if(rbp->ndata < 10){ + werrstr("short frame"); + fprint(2, "lan78xx short frame %d bytes\n", rbp->ndata); + sleep(1000); + return 0; + } + hd = GET4(rbp->rp); + n = hd & 0x3FFF; + rbp->rp += 10; + rbp->ndata -= 10; + if(n < 6 || n > rbp->ndata){ + werrstr("frame length"); + fprint(2, "lan78xx length error packet %d buf %d\n", n, rbp->ndata); + rbp->ndata = 0; + return 0; + } + if(hd & Rxerror){ + fprint(2, "lan78xx rx error %8.8ux\n", hd); + n = 0; + }else{ + bp->rp = bp->data + Hdrsize; + memmove(bp->rp, rbp->rp, n); + } + bp->ndata = n; + rbp->rp += n; + rbp->ndata -= n; + if(rbp->ndata > 0){ + m = rbp->rp - rbp->data; + if(m&3){ + m = 4 - (m&3); + rbp->rp += m; + rbp->ndata -= m; + } + } + return n; +} + +static long +lan78xxbwrite(Ether *e, Buf *bp) +{ + int n; + + n = bp->ndata & 0xFFFFF; + bp->rp -= 8; + bp->ndata += 8; + PUT4(bp->rp, n | Txfcs); + PUT4(bp->rp+4, 0); + n = write(e->epout->dfd, bp->rp, bp->ndata); + if(n != bp->ndata) + deprint(2, "bwrite %d: %r\n", n); + return n; +} + +static int +lan78xxpromiscuous(Ether *e, int on) +{ + Dev *d; + int rxctl; + + d = e->dev; + rxctl = rr(d, Rfectl); + if(on) + rxctl |= Am|Au; + else + rxctl &= ~(Am|Au); + return wr(d, Rfectl, rxctl); +} + +static int +lan78xxmulticast(Ether *e, uchar *addr, int on) +{ + int rxctl; + Dev *d; + + USED(addr, on); + /* BUG: should write multicast filter */ + d = e->dev; + rxctl = rr(d, Rfectl); + if(e->nmcasts != 0) + rxctl |= Am; + else + rxctl &= ~Am; + deprint(2, "%s: lan78xxmulticast %d\n", argv0, e->nmcasts); + return wr(d, Rfectl, rxctl); +} + +static void +lan78xxfree(Ether *ether) +{ + free(ether->aux); + ether->aux = nil; +} + +int +lan78xxreset(Ether *ether) +{ + Cinfo *ip; + Dev *dev; + + dev = ether->dev; + for(ip = cinfo; ip->vid != 0; ip++) + if(ip->vid == dev->usb->vid && ip->did == dev->usb->did){ + ether->cid = ip->cid; + if(lan78xxinit(ether) < 0){ + deprint(2, "%s: lan78xx init failed: %r\n", argv0); + return -1; + } + deprint(2, "%s: lan78xx reset done\n", argv0); + ether->name = "lan78xx"; + if(Doburst){ + ether->bufsize = burstcap*512; + ether->aux = emallocz(sizeof(Buf) + + ether->bufsize - Maxpkt, 1); + }else{ + ether->bufsize = Maxpkt; + ether->aux = emallocz(sizeof(Buf), 1); + } + ether->free = lan78xxfree; + ether->bread = lan78xxbread; + ether->bwrite = lan78xxbwrite; + ether->promiscuous = lan78xxpromiscuous; + ether->multicast = lan78xxmulticast; + ether->mbps = 100; /* BUG */ + return 0; + } + return -1; +} --- /sys/src/cmd/usb/ether/smsc.c Thu Apr 5 11:53:13 2018 +++ /sys/src/cmd/usb/ether/smsc.c Thu Apr 5 11:53:11 2018 @@ -15,10 +15,9 @@ Resettime = 1000, E2pbusytime = 1000, Afcdefault = 0xF830A1, -// Hsburst = 37, /* from original linux driver */ - Hsburst = 8, + Hsburst = 24, Fsburst = 129, - Defbulkdly = 0x2000, + Defbulkdly = 1000, Ethp8021q = 0x8100, MACoffset = 1, @@ -102,6 +101,8 @@ Linkdown= 1<<4, }; +static int burstcap = Hsburst, bulkdelay = Defbulkdly; + static int wr(Dev *d, int reg, int val) { @@ -238,12 +239,13 @@ wr(d, Addrh, GET2(ether->addr+4)); if(Doburst){ wr(d, Hwcfg, (rr(d,Hwcfg)&~Rxdoff)|Bir|Mef|Bce); - wr(d, Burstcap, Hsburst); + wr(d, Burstcap, burstcap); + wr(d, Bulkdelay, bulkdelay); }else{ wr(d, Hwcfg, (rr(d,Hwcfg)&~(Rxdoff|Mef|Bce))|Bir); wr(d, Burstcap, 0); + wr(d, Bulkdelay, 0); } - wr(d, Bulkdelay, Defbulkdly); wr(d, Intsts, ~0); wr(d, Ledgpio, Ledspd|Ledlnk|Ledfdx); wr(d, Flow, 0); @@ -274,7 +276,7 @@ rbp = e->aux; if(rbp->ndata < 4){ rbp->rp = rbp->data; - rbp->ndata = read(e->epin->dfd, rbp->rp, Doburst? Hsburst*512: + rbp->ndata = read(e->epin->dfd, rbp->rp, Doburst? burstcap*512: Maxpkt); if(rbp->ndata < 0) return -1; @@ -285,20 +287,24 @@ return 0; } hd = GET4(rbp->rp); + rbp->rp += 4; + rbp->ndata -= 4; n = hd >> 16; - m = (n + 4 + 3) & ~3; - if(n < 6 || m > rbp->ndata){ + if(n < 6 || n > rbp->ndata){ werrstr("frame length"); fprint(2, "smsc length error packet %d buf %d\n", n, rbp->ndata); rbp->ndata = 0; return 0; } + m = n; + if(rbp->ndata - m < 4) + m = rbp->ndata; if(hd & Rxerror){ fprint(2, "smsc rx error %8.8ux\n", hd); n = 0; }else{ bp->rp = bp->data + Hdrsize; - memmove(bp->rp, rbp->rp+4, n); + memmove(bp->rp, rbp->rp, n); } bp->ndata = n; rbp->rp += m; @@ -323,38 +329,34 @@ static int smscpromiscuous(Ether *e, int on) { - USED(on, e); -#ifdef TODO /* copied from asix */ + Dev *d; int rxctl; - deprint(2, "%s: smscpromiscuous %d\n", argv0, on); - rxctl = getrxctl(e->dev); - if(on != 0) - rxctl |= Rxctlprom; + d = e->dev; + rxctl = rr(d, Maccr); + if(on) + rxctl |= Prms; else - rxctl &= ~Rxctlprom; - return wr(e->dev, Cwrxctl, rxctl); -#endif - return -1; + rxctl &= ~Prms; + return wr(d, Maccr, rxctl); } static int smscmulticast(Ether *e, uchar *addr, int on) { - USED(addr, on, e); -#ifdef TODO /* needed for ipv6; copied from asix */ int rxctl; + Dev *d; + USED(addr, on); /* BUG: should write multicast filter */ - rxctl = getrxctl(e->dev); + d = e->dev; + rxctl = rr(d, Maccr); if(e->nmcasts != 0) - rxctl |= Rxctlamall; + rxctl |= Mcpas; else - rxctl &= ~Rxctlamall; + rxctl &= ~Mcpas; deprint(2, "%s: smscmulticast %d\n", argv0, e->nmcasts); - return wr(e->dev, Cwrxctl, rxctl); -#endif - return -1; + return wr(d, Maccr, rxctl); } static void @@ -381,7 +383,7 @@ deprint(2, "%s: smsc reset done\n", argv0); ether->name = "smsc"; if(Doburst){ - ether->bufsize = Hsburst*512; + ether->bufsize = burstcap*512; ether->aux = emallocz(sizeof(Buf) + ether->bufsize - Maxpkt, 1); }else{ --- /sys/src/cmd/usb/usbd/usbdb Thu Apr 5 11:53:16 2018 +++ /sys/src/cmd/usb/usbd/usbdb Thu Apr 5 11:53:15 2018 @@ -5,6 +5,8 @@ disk class=storage args= ether class=255 csp=0x00ffff vid=0x0b95 args= ether class=255 csp=0xff00ff vid=0x0424 did=0xec00 args= + ether class=255 csp=0xff00ff vid=0x0424 did=0x7800 args= + ether class=255 csp=0x000602 vid=0x0bda did=0x8152 args= serial class=255 csp=0xffffff vid=0x9e88 did=0x9e8f args= serial class=255 csp=0xffffff vid=0x0403 args= serial class=255 csp=0x0000ff vid=0x10c4 args= --- /sys/src/cmd/usb/ether/cdc.c Thu Apr 5 11:53:20 2018 +++ /sys/src/cmd/usb/ether/cdc.c Thu Apr 5 11:53:18 2018 @@ -48,13 +48,29 @@ int cdcreset(Ether *ether) { + Usbdev *ud; + Ep *ep; + int i; + /* * Assume that all communication devices are going to * be std. ethernet communication devices. Specific controllers * must have been probed first. - * NB: This ignores unions. */ - if(ether->dev->usb->class == Clcomms) + ud = ether->dev->usb; + if(ud->class == Clcomms) return getmac(ether); + if(getmac(ether) == 0){ + for(i = 0; i < Nep; i++){ + ep = ud->ep[i]; + if(ep == nil) + continue; + if(ep->iface == nil || !okclass(ep->iface)) + continue; + if(ep->conf->cval != 1) + usbcmd(ether->dev, Rh2d|Rstd|Rdev, Rsetconf, ep->conf->cval, 0, nil, 0); + return 0; + } + } return -1; } --- /sys/src/cmd/usb/lib/dev.c Thu Apr 5 11:53:24 2018 +++ /sys/src/cmd/usb/lib/dev.c Thu Apr 5 11:53:21 2018 @@ -148,7 +148,7 @@ loaddevconf(Dev *d, int n) { uchar *buf; - int nr; + int l, nr; int type; if(n >= nelem(d->usb->conf)){ @@ -158,11 +158,22 @@ } buf = emallocz(Maxdevconf, 0); type = Rd2h|Rstd|Rdev; - nr = usbcmd(d, type, Rgetdesc, Dconf<<8|n, 0, buf, Maxdevconf); + nr = usbcmd(d, type, Rgetdesc, Dconf<<8|n, 0, buf, Dconflen); if(nr < Dconflen){ free(buf); return -1; } + l = GET2(((DConf*)buf)->wTotalLength); + if(l > Maxdevconf){ + free(buf); + return -1; + } + nr = usbcmd(d, type, Rgetdesc, Dconf<<8|n, 0, buf, l); + if(nr < l){ + free(buf); + return -1; + } + if(d->usb->conf[n] == nil) d->usb->conf[n] = emallocz(sizeof(Conf), 1); nr = parseconf(d->usb, d->usb->conf[n], buf, nr); @@ -188,6 +199,8 @@ char *s; char *e; + if(n > b[0]) + n = b[0]; if(n <= 2 || (n & 1) != 0) return strdup("none"); n = (n - 2)/2; @@ -204,42 +217,37 @@ char* loaddevstr(Dev *d, int sid) { - uchar buf[128]; - int type; - int nr; + uchar buf[256]; + int type, langid, nr; if(sid == 0) return estrdup("none"); type = Rd2h|Rstd|Rdev; - nr=usbcmd(d, type, Rgetdesc, Dstr<<8|sid, 0, buf, sizeof(buf)); + nr = usbcmd(d, type, Rgetdesc, Dstr<<8|sid, 0, buf, sizeof(buf)); + if(nr < 4) + langid = 0x0409; /* english */ + else + langid = buf[3]<<8 | buf[2]; + nr = usbcmd(d, type, Rgetdesc, Dstr<<8|sid, langid, buf, sizeof(buf)); + return mkstr(buf, nr); } int loaddevdesc(Dev *d) { - uchar buf[Ddevlen+255]; + uchar buf[Ddevlen]; int nr; int type; Ep *ep0; type = Rd2h|Rstd|Rdev; - nr = sizeof(buf); memset(buf, 0, Ddevlen); - if((nr=usbcmd(d, type, Rgetdesc, Ddev<<8|0, 0, buf, nr)) < 0) + if((nr = usbcmd(d, type, Rgetdesc, Ddev<<8|0, 0, buf, Ddevlen)) < 0) return -1; - /* - * Several hubs are returning descriptors of 17 bytes, not 18. - * We accept them and leave number of configurations as zero. - * (a get configuration descriptor also fails for them!) - */ if(nr < Ddevlen){ - print("%s: %s: warning: device with short descriptor\n", - argv0, d->dir); - if(nr < Ddevlen-1){ - werrstr("short device descriptor (%d bytes)", nr); - return -1; - } + werrstr("short device descriptor (%d bytes)", nr); + return -1; } d->usb = emallocz(sizeof(Usbdev), 1); ep0 = mkep(d->usb, 0); @@ -254,6 +262,8 @@ d->usb->serial = loaddevstr(d, d->usb->ssid); } } + else + print("usbd: desc error: %r"); return nr; }