1906943f9SDavid du Colombier #include <u.h> 2906943f9SDavid du Colombier #include <libc.h> 3906943f9SDavid du Colombier #include <thread.h> 4906943f9SDavid du Colombier #include <bio.h> 5906943f9SDavid du Colombier #include "usb.h" 6906943f9SDavid du Colombier 7906943f9SDavid du Colombier int 8906943f9SDavid du Colombier parsedev(Dev *xd, uchar *b, int n) 9906943f9SDavid du Colombier { 10906943f9SDavid du Colombier Usbdev *d; 11906943f9SDavid du Colombier DDev *dd; 12906943f9SDavid du Colombier char *hd; 13906943f9SDavid du Colombier 14906943f9SDavid du Colombier d = xd->usb; 15906943f9SDavid du Colombier assert(d != nil); 16906943f9SDavid du Colombier dd = (DDev*)b; 17906943f9SDavid du Colombier if(usbdebug>1){ 18906943f9SDavid du Colombier hd = hexstr(b, Ddevlen); 19906943f9SDavid du Colombier fprint(2, "%s: parsedev %s: %s\n", argv0, xd->dir, hd); 20906943f9SDavid du Colombier free(hd); 21906943f9SDavid du Colombier } 22906943f9SDavid du Colombier if(dd->bLength < Ddevlen){ 23906943f9SDavid du Colombier werrstr("short dev descr. (%d < %d)", dd->bLength, Ddevlen); 24906943f9SDavid du Colombier return -1; 25906943f9SDavid du Colombier } 26906943f9SDavid du Colombier if(dd->bDescriptorType != Ddev){ 27906943f9SDavid du Colombier werrstr("%d is not a dev descriptor", dd->bDescriptorType); 28906943f9SDavid du Colombier return -1; 29906943f9SDavid du Colombier } 30906943f9SDavid du Colombier d->csp = CSP(dd->bDevClass, dd->bDevSubClass, dd->bDevProtocol); 31906943f9SDavid du Colombier d->ep[0]->maxpkt = xd->maxpkt = dd->bMaxPacketSize0; 32906943f9SDavid du Colombier d->class = dd->bDevClass; 33906943f9SDavid du Colombier d->nconf = dd->bNumConfigurations; 34906943f9SDavid du Colombier if(d->nconf == 0) 35906943f9SDavid du Colombier dprint(2, "%s: %s: no configurations\n", argv0, xd->dir); 36906943f9SDavid du Colombier d->vid = GET2(dd->idVendor); 37906943f9SDavid du Colombier d->did = GET2(dd->idProduct); 38906943f9SDavid du Colombier d->vsid = dd->iManufacturer; 39906943f9SDavid du Colombier d->psid = dd->iProduct; 40906943f9SDavid du Colombier d->ssid = dd->iSerialNumber; 41906943f9SDavid du Colombier if(n > Ddevlen && usbdebug>1) 42906943f9SDavid du Colombier fprint(2, "%s: %s: parsedev: %d bytes left", 43906943f9SDavid du Colombier argv0, xd->dir, n - Ddevlen); 44906943f9SDavid du Colombier return Ddevlen; 45906943f9SDavid du Colombier } 46906943f9SDavid du Colombier 47906943f9SDavid du Colombier static int 48906943f9SDavid du Colombier parseiface(Usbdev *d, Conf *c, uchar *b, int n, Iface **ipp, Altc **app) 49906943f9SDavid du Colombier { 50906943f9SDavid du Colombier int class, subclass, proto; 51906943f9SDavid du Colombier int ifid, altid; 52906943f9SDavid du Colombier DIface *dip; 53906943f9SDavid du Colombier Iface *ip; 54906943f9SDavid du Colombier 55906943f9SDavid du Colombier assert(d != nil && c != nil); 56906943f9SDavid du Colombier if(n < Difacelen){ 57906943f9SDavid du Colombier werrstr("short interface descriptor"); 58906943f9SDavid du Colombier return -1; 59906943f9SDavid du Colombier } 60906943f9SDavid du Colombier dip = (DIface *)b; 61906943f9SDavid du Colombier ifid = dip->bInterfaceNumber; 62906943f9SDavid du Colombier if(ifid < 0 || ifid >= nelem(c->iface)){ 63906943f9SDavid du Colombier werrstr("bad interface number %d", ifid); 64906943f9SDavid du Colombier return -1; 65906943f9SDavid du Colombier } 66906943f9SDavid du Colombier if(c->iface[ifid] == nil) 67906943f9SDavid du Colombier c->iface[ifid] = emallocz(sizeof(Iface), 1); 68906943f9SDavid du Colombier ip = c->iface[ifid]; 69906943f9SDavid du Colombier class = dip->bInterfaceClass; 70906943f9SDavid du Colombier subclass = dip->bInterfaceSubClass; 71906943f9SDavid du Colombier proto = dip->bInterfaceProtocol; 72906943f9SDavid du Colombier ip->csp = CSP(class, subclass, proto); 73906943f9SDavid du Colombier if(d->csp == 0) /* use csp from 1st iface */ 74906943f9SDavid du Colombier d->csp = ip->csp; /* if device has none */ 75906943f9SDavid du Colombier if(d->class == 0) 76906943f9SDavid du Colombier d->class = class; 77906943f9SDavid du Colombier ip->id = ifid; 78906943f9SDavid du Colombier if(c == d->conf[0] && ifid == 0) /* ep0 was already there */ 79906943f9SDavid du Colombier d->ep[0]->iface = ip; 80906943f9SDavid du Colombier altid = dip->bAlternateSetting; 81906943f9SDavid du Colombier if(altid < 0 || altid >= nelem(ip->altc)){ 82906943f9SDavid du Colombier werrstr("bad alternate conf. number %d", altid); 83906943f9SDavid du Colombier return -1; 84906943f9SDavid du Colombier } 85906943f9SDavid du Colombier if(ip->altc[altid] == nil) 86906943f9SDavid du Colombier ip->altc[altid] = emallocz(sizeof(Altc), 1); 87906943f9SDavid du Colombier *ipp = ip; 88906943f9SDavid du Colombier *app = ip->altc[altid]; 89906943f9SDavid du Colombier return Difacelen; 90906943f9SDavid du Colombier } 91906943f9SDavid du Colombier 92906943f9SDavid du Colombier extern Ep* mkep(Usbdev *, int); 93906943f9SDavid du Colombier 94906943f9SDavid du Colombier static int 95906943f9SDavid du Colombier parseendpt(Usbdev *d, Conf *c, Iface *ip, Altc *altc, uchar *b, int n, Ep **epp) 96906943f9SDavid du Colombier { 97*16146bc9SDavid du Colombier int i, dir, epid; 98906943f9SDavid du Colombier Ep *ep; 99906943f9SDavid du Colombier DEp *dep; 100906943f9SDavid du Colombier 101906943f9SDavid du Colombier assert(d != nil && c != nil && ip != nil && altc != nil); 102906943f9SDavid du Colombier if(n < Deplen){ 103906943f9SDavid du Colombier werrstr("short endpoint descriptor"); 104906943f9SDavid du Colombier return -1; 105906943f9SDavid du Colombier } 106906943f9SDavid du Colombier dep = (DEp *)b; 107906943f9SDavid du Colombier altc->attrib = dep->bmAttributes; /* here? */ 108906943f9SDavid du Colombier altc->interval = dep->bInterval; 109906943f9SDavid du Colombier 110906943f9SDavid du Colombier epid = dep->bEndpointAddress & 0xF; 111906943f9SDavid du Colombier assert(epid < nelem(d->ep)); 112906943f9SDavid du Colombier if(dep->bEndpointAddress & 0x80) 113906943f9SDavid du Colombier dir = Ein; 114906943f9SDavid du Colombier else 115906943f9SDavid du Colombier dir = Eout; 116906943f9SDavid du Colombier ep = d->ep[epid]; 117906943f9SDavid du Colombier if(ep == nil){ 118906943f9SDavid du Colombier ep = mkep(d, epid); 119906943f9SDavid du Colombier ep->dir = dir; 120906943f9SDavid du Colombier }else if((ep->addr & 0x80) != (dep->bEndpointAddress & 0x80)) 121906943f9SDavid du Colombier ep->dir = Eboth; 122906943f9SDavid du Colombier ep->maxpkt = GET2(dep->wMaxPacketSize); 123906943f9SDavid du Colombier ep->ntds = 1 + ((ep->maxpkt >> 11) & 3); 124906943f9SDavid du Colombier ep->maxpkt &= 0x7FF; 125906943f9SDavid du Colombier ep->addr = dep->bEndpointAddress; 126906943f9SDavid du Colombier ep->type = dep->bmAttributes & 0x03; 127906943f9SDavid du Colombier ep->isotype = (dep->bmAttributes>>2) & 0x03; 128906943f9SDavid du Colombier ep->conf = c; 129906943f9SDavid du Colombier ep->iface = ip; 130906943f9SDavid du Colombier for(i = 0; i < nelem(ip->ep); i++) 131906943f9SDavid du Colombier if(ip->ep[i] == nil) 132906943f9SDavid du Colombier break; 133906943f9SDavid du Colombier if(i == nelem(ip->ep)){ 134*16146bc9SDavid du Colombier werrstr("parseendpt: bug: too many end points on interface " 135*16146bc9SDavid du Colombier "with csp %#lux", ip->csp); 136906943f9SDavid du Colombier fprint(2, "%s: %r\n", argv0); 137906943f9SDavid du Colombier return -1; 138906943f9SDavid du Colombier } 139906943f9SDavid du Colombier *epp = ip->ep[i] = ep; 140906943f9SDavid du Colombier return Dep; 141906943f9SDavid du Colombier } 142906943f9SDavid du Colombier 143906943f9SDavid du Colombier static char* 144906943f9SDavid du Colombier dname(int dtype) 145906943f9SDavid du Colombier { 146906943f9SDavid du Colombier switch(dtype){ 147906943f9SDavid du Colombier case Ddev: return "device"; 148906943f9SDavid du Colombier case Dconf: return "config"; 149906943f9SDavid du Colombier case Dstr: return "string"; 150906943f9SDavid du Colombier case Diface: return "interface"; 151906943f9SDavid du Colombier case Dep: return "endpoint"; 152906943f9SDavid du Colombier case Dreport: return "report"; 153906943f9SDavid du Colombier case Dphysical: return "phys"; 154906943f9SDavid du Colombier default: return "desc"; 155906943f9SDavid du Colombier } 156906943f9SDavid du Colombier } 157906943f9SDavid du Colombier 158906943f9SDavid du Colombier int 159906943f9SDavid du Colombier parsedesc(Usbdev *d, Conf *c, uchar *b, int n) 160906943f9SDavid du Colombier { 161*16146bc9SDavid du Colombier int len, nd, tot; 162906943f9SDavid du Colombier Iface *ip; 163906943f9SDavid du Colombier Ep *ep; 164906943f9SDavid du Colombier Altc *altc; 165906943f9SDavid du Colombier char *hd; 166906943f9SDavid du Colombier 167906943f9SDavid du Colombier assert(d != nil && c != nil); 168906943f9SDavid du Colombier tot = 0; 169906943f9SDavid du Colombier ip = nil; 170906943f9SDavid du Colombier ep = nil; 171906943f9SDavid du Colombier altc = nil; 172906943f9SDavid du Colombier for(nd = 0; nd < nelem(d->ddesc); nd++) 173906943f9SDavid du Colombier if(d->ddesc[nd] == nil) 174906943f9SDavid du Colombier break; 175906943f9SDavid du Colombier 176906943f9SDavid du Colombier while(n > 2 && b[0] != 0 && b[0] <= n){ 177906943f9SDavid du Colombier len = b[0]; 178906943f9SDavid du Colombier if(usbdebug>1){ 179906943f9SDavid du Colombier hd = hexstr(b, len); 180906943f9SDavid du Colombier fprint(2, "%s:\t\tparsedesc %s %x[%d] %s\n", 181906943f9SDavid du Colombier argv0, dname(b[1]), b[1], b[0], hd); 182906943f9SDavid du Colombier free(hd); 183906943f9SDavid du Colombier } 184906943f9SDavid du Colombier switch(b[1]){ 185906943f9SDavid du Colombier case Ddev: 186906943f9SDavid du Colombier case Dconf: 187906943f9SDavid du Colombier werrstr("unexpected descriptor %d", b[1]); 188906943f9SDavid du Colombier ddprint(2, "%s\tparsedesc: %r", argv0); 189906943f9SDavid du Colombier break; 190906943f9SDavid du Colombier case Diface: 191906943f9SDavid du Colombier if(parseiface(d, c, b, n, &ip, &altc) < 0){ 192906943f9SDavid du Colombier ddprint(2, "%s\tparsedesc: %r\n", argv0); 193906943f9SDavid du Colombier return -1; 194906943f9SDavid du Colombier } 195906943f9SDavid du Colombier break; 196906943f9SDavid du Colombier case Dep: 197906943f9SDavid du Colombier if(ip == nil || altc == nil){ 198906943f9SDavid du Colombier werrstr("unexpected endpoint descriptor"); 199906943f9SDavid du Colombier break; 200906943f9SDavid du Colombier } 201906943f9SDavid du Colombier if(parseendpt(d, c, ip, altc, b, n, &ep) < 0){ 202906943f9SDavid du Colombier ddprint(2, "%s\tparsedesc: %r\n", argv0); 203906943f9SDavid du Colombier return -1; 204906943f9SDavid du Colombier } 205906943f9SDavid du Colombier break; 206906943f9SDavid du Colombier default: 207906943f9SDavid du Colombier if(nd == nelem(d->ddesc)){ 208*16146bc9SDavid du Colombier fprint(2, "%s: parsedesc: too many " 209*16146bc9SDavid du Colombier "device-specific descriptors for device" 210*16146bc9SDavid du Colombier " %s %s\n", 211*16146bc9SDavid du Colombier argv0, d->vendor, d->product); 212906943f9SDavid du Colombier break; 213906943f9SDavid du Colombier } 214906943f9SDavid du Colombier d->ddesc[nd] = emallocz(sizeof(Desc)+b[0], 0); 215906943f9SDavid du Colombier d->ddesc[nd]->iface = ip; 216906943f9SDavid du Colombier d->ddesc[nd]->ep = ep; 217906943f9SDavid du Colombier d->ddesc[nd]->altc = altc; 218906943f9SDavid du Colombier d->ddesc[nd]->conf = c; 219906943f9SDavid du Colombier memmove(&d->ddesc[nd]->data, b, len); 220906943f9SDavid du Colombier ++nd; 221906943f9SDavid du Colombier } 222906943f9SDavid du Colombier n -= len; 223906943f9SDavid du Colombier b += len; 224906943f9SDavid du Colombier tot += len; 225906943f9SDavid du Colombier } 226906943f9SDavid du Colombier return tot; 227906943f9SDavid du Colombier } 228906943f9SDavid du Colombier 229906943f9SDavid du Colombier int 230906943f9SDavid du Colombier parseconf(Usbdev *d, Conf *c, uchar *b, int n) 231906943f9SDavid du Colombier { 232906943f9SDavid du Colombier DConf* dc; 233906943f9SDavid du Colombier int l; 234906943f9SDavid du Colombier int nr; 235906943f9SDavid du Colombier char *hd; 236906943f9SDavid du Colombier 237906943f9SDavid du Colombier assert(d != nil && c != nil); 238906943f9SDavid du Colombier dc = (DConf*)b; 239906943f9SDavid du Colombier if(usbdebug>1){ 240906943f9SDavid du Colombier hd = hexstr(b, Dconflen); 241906943f9SDavid du Colombier fprint(2, "%s:\tparseconf %s\n", argv0, hd); 242906943f9SDavid du Colombier free(hd); 243906943f9SDavid du Colombier } 244906943f9SDavid du Colombier if(dc->bLength < Dconflen){ 245906943f9SDavid du Colombier werrstr("short configuration descriptor"); 246906943f9SDavid du Colombier return -1; 247906943f9SDavid du Colombier } 248906943f9SDavid du Colombier if(dc->bDescriptorType != Dconf){ 249906943f9SDavid du Colombier werrstr("not a configuration descriptor"); 250906943f9SDavid du Colombier return -1; 251906943f9SDavid du Colombier } 252906943f9SDavid du Colombier c->cval = dc->bConfigurationValue; 253906943f9SDavid du Colombier c->attrib = dc->bmAttributes; 254906943f9SDavid du Colombier c->milliamps = dc->MaxPower*2; 255906943f9SDavid du Colombier l = GET2(dc->wTotalLength); 256906943f9SDavid du Colombier if(n < l){ 257906943f9SDavid du Colombier werrstr("truncated configuration info"); 258906943f9SDavid du Colombier return -1; 259906943f9SDavid du Colombier } 260906943f9SDavid du Colombier n -= Dconflen; 261906943f9SDavid du Colombier b += Dconflen; 262906943f9SDavid du Colombier nr = 0; 263906943f9SDavid du Colombier if(n > 0 && (nr=parsedesc(d, c, b, n)) < 0) 264906943f9SDavid du Colombier return -1; 265906943f9SDavid du Colombier n -= nr; 266906943f9SDavid du Colombier if(n > 0 && usbdebug>1) 267906943f9SDavid du Colombier fprint(2, "%s:\tparseconf: %d bytes left\n", argv0, n); 268906943f9SDavid du Colombier return l; 269906943f9SDavid du Colombier } 270