ref: 63a0d519bcdb02b226023b1c07343bc52791a677
dir: /sys/src/cmd/nusb/lib/parse.c/
#include <u.h> #include <libc.h> #include <thread.h> #include "usb.h" int parsedev(Dev *xd, uchar *b, int n) { Usbdev *d; DDev *dd; char *hd; d = xd->usb; assert(d != nil); dd = (DDev*)b; if(usbdebug>1){ hd = hexstr(b, Ddevlen); fprint(2, "%s: parsedev %s: %s\n", argv0, xd->dir, hd); free(hd); } if(dd->bLength < Ddevlen){ werrstr("short dev descr. (%d < %d)", dd->bLength, Ddevlen); return -1; } if(dd->bDescriptorType != Ddev){ werrstr("%d is not a dev descriptor", dd->bDescriptorType); return -1; } d->csp = CSP(dd->bDevClass, dd->bDevSubClass, dd->bDevProtocol); d->ver = GET2(dd->bcdUSB); xd->isusb3 = (d->ver >= 0x0300); if(xd->isusb3) d->ep[0]->maxpkt = xd->maxpkt = 1<<dd->bMaxPacketSize0; else d->ep[0]->maxpkt = xd->maxpkt = dd->bMaxPacketSize0; d->class = dd->bDevClass; d->nconf = dd->bNumConfigurations; if(d->nconf == 0) dprint(2, "%s: %s: no configurations\n", argv0, xd->dir); d->vid = GET2(dd->idVendor); d->did = GET2(dd->idProduct); d->dno = GET2(dd->bcdDev); d->vsid = dd->iManufacturer; d->psid = dd->iProduct; d->ssid = dd->iSerialNumber; if(n > Ddevlen && usbdebug>1) fprint(2, "%s: %s: parsedev: %d bytes left", argv0, xd->dir, n - Ddevlen); return Ddevlen; } static int parseiface(Usbdev *d, Conf *c, uchar *b, int n, Iface **ipp, Altc **app) { int class, subclass, proto; int ifid, altid; DIface *dip; Iface *ip; assert(d != nil && c != nil); if(n < Difacelen){ werrstr("short interface descriptor"); return -1; } dip = (DIface *)b; ifid = dip->bInterfaceNumber; if(ifid < 0 || ifid >= nelem(c->iface)){ werrstr("bad interface number %d", ifid); return -1; } if(c->iface[ifid] == nil) c->iface[ifid] = emallocz(sizeof(Iface), 1); ip = c->iface[ifid]; class = dip->bInterfaceClass; subclass = dip->bInterfaceSubClass; proto = dip->bInterfaceProtocol; ip->csp = CSP(class, subclass, proto); if(ip->csp == 0) ip->csp = d->csp; if(d->csp == 0) /* use csp from 1st iface */ d->csp = ip->csp; /* if device has none */ if(d->class == 0) d->class = class; ip->id = ifid; if(c == d->conf[0] && ifid == 0) /* ep0 was already there */ d->ep[0]->iface = ip; altid = dip->bAlternateSetting; if(altid < 0 || altid >= nelem(ip->altc)){ werrstr("bad alternate conf. number %d", altid); return -1; } if(ip->altc[altid] == nil) ip->altc[altid] = emallocz(sizeof(Altc), 1); *ipp = ip; *app = ip->altc[altid]; return Difacelen; } extern Ep* mkep(Usbdev *, int); static int parseendpt(Usbdev *d, Conf *c, Iface *ip, Altc *altc, uchar *b, int n, Ep **epp) { int i, dir, epid, type, addr; Ep *ep; DEp *dep; assert(d != nil && c != nil && ip != nil && altc != nil); if(n < Deplen){ werrstr("short endpoint descriptor"); return -1; } dep = (DEp *)b; altc->attrib = dep->bmAttributes; /* here? */ altc->interval = dep->bInterval; type = dep->bmAttributes & 0x03; addr = dep->bEndpointAddress; if(addr & 0x80) dir = Ein; else dir = Eout; epid = addr & 0xF; /* default map to 0..15 */ assert(epid < nelem(d->ep)); ep = d->ep[epid]; if(ep == nil){ ep = mkep(d, epid); ep->dir = dir; }else if((ep->addr & 0x80) != (addr & 0x80)){ if(ep->type == type) ep->dir = Eboth; else { /* * resolve conflict when same endpoint number * is used for different input and output types. * map input endpoint to 16..31 and output to 0..15. */ ep->id = ((ep->addr & 0x80) != 0)<<4 | (ep->addr & 0xF); d->ep[ep->id] = ep; epid = ep->id ^ 0x10; ep = mkep(d, epid); ep->dir = dir; } } ep->maxpkt = GET2(dep->wMaxPacketSize); ep->ntds = 1 + ((ep->maxpkt >> 11) & 3); ep->maxpkt &= 0x7FF; altc->maxpkt = ep->maxpkt; altc->ntds = ep->ntds; ep->addr = addr; ep->type = type; ep->isotype = (dep->bmAttributes>>2) & 0x03; ep->conf = c; ep->iface = ip; for(i = 0; i < nelem(ip->ep); i++) if(ip->ep[i] == nil) break; if(i == nelem(ip->ep)){ werrstr("parseendpt: bug: too many end points on interface " "with csp %#lux", ip->csp); fprint(2, "%s: %r\n", argv0); return -1; } *epp = ip->ep[i] = ep; return Dep; } static char* dname(int dtype) { switch(dtype){ case Ddev: return "device"; case Dconf: return "config"; case Dstr: return "string"; case Diface: return "interface"; case Dep: return "endpoint"; case Dreport: return "report"; case Dphysical: return "phys"; default: return "desc"; } } int parsedesc(Usbdev *d, Conf *c, uchar *b, int n) { int len, nd, tot; Iface *ip; Ep *ep; Altc *altc; char *hd; assert(d != nil && c != nil); tot = 0; ip = nil; ep = nil; altc = nil; for(nd = 0; nd < nelem(d->ddesc); nd++) if(d->ddesc[nd] == nil) break; while(n > 2 && b[0] != 0 && b[0] <= n){ len = b[0]; if(usbdebug>1){ hd = hexstr(b, len); fprint(2, "%s:\t\tparsedesc %s %x[%d] %s\n", argv0, dname(b[1]), b[1], b[0], hd); free(hd); } switch(b[1]){ case Ddev: case Dconf: werrstr("unexpected descriptor %d", b[1]); ddprint(2, "%s\tparsedesc: %r", argv0); break; case Diface: if(parseiface(d, c, b, n, &ip, &altc) < 0){ ddprint(2, "%s\tparsedesc: %r\n", argv0); return -1; } break; case Dep: if(ip == nil || altc == nil){ werrstr("unexpected endpoint descriptor"); break; } if(parseendpt(d, c, ip, altc, b, n, &ep) < 0){ ddprint(2, "%s\tparsedesc: %r\n", argv0); return -1; } break; default: if(nd == nelem(d->ddesc)){ fprint(2, "%s: parsedesc: too many " "device-specific descriptors for device" " %s %s\n", argv0, d->vendor, d->product); break; } d->ddesc[nd] = emallocz(sizeof(Desc)+b[0], 0); d->ddesc[nd]->iface = ip; d->ddesc[nd]->ep = ep; d->ddesc[nd]->altc = altc; d->ddesc[nd]->conf = c; memmove(&d->ddesc[nd]->data, b, len); ++nd; } n -= len; b += len; tot += len; } return tot; } int parseconf(Usbdev *d, Conf *c, uchar *b, int n) { DConf* dc; int l; int nr; char *hd; assert(d != nil && c != nil); dc = (DConf*)b; if(usbdebug>1){ hd = hexstr(b, Dconflen); fprint(2, "%s:\tparseconf %s\n", argv0, hd); free(hd); } if(dc->bLength < Dconflen){ werrstr("short configuration descriptor"); return -1; } if(dc->bDescriptorType != Dconf){ werrstr("not a configuration descriptor"); return -1; } c->cval = dc->bConfigurationValue; c->attrib = dc->bmAttributes; c->milliamps = dc->MaxPower*2; l = GET2(dc->wTotalLength); if(n < l){ werrstr("truncated configuration info"); return -1; } n -= Dconflen; b += Dconflen; nr = 0; if(n > 0 && (nr=parsedesc(d, c, b, n)) < 0) return -1; n -= nr; if(n > 0 && usbdebug>1) fprint(2, "%s:\tparseconf: %d bytes left\n", argv0, n); return l; }