1906943f9SDavid du Colombier #include <u.h> 2906943f9SDavid du Colombier #include <libc.h> 3906943f9SDavid du Colombier #include <thread.h> 4906943f9SDavid du Colombier #include "usb.h" 5906943f9SDavid du Colombier 6906943f9SDavid du Colombier /* 7906943f9SDavid du Colombier * epN.M -> N 8906943f9SDavid du Colombier */ 9906943f9SDavid du Colombier static int 10906943f9SDavid du Colombier nameid(char *s) 11906943f9SDavid du Colombier { 12906943f9SDavid du Colombier char *r; 13906943f9SDavid du Colombier char nm[20]; 14906943f9SDavid du Colombier 15906943f9SDavid du Colombier r = strrchr(s, 'p'); 16906943f9SDavid du Colombier if(r == nil) 17906943f9SDavid du Colombier return -1; 18906943f9SDavid du Colombier strecpy(nm, nm+sizeof(nm), r+1); 19906943f9SDavid du Colombier r = strchr(nm, '.'); 20906943f9SDavid du Colombier if(r == nil) 21906943f9SDavid du Colombier return -1; 22906943f9SDavid du Colombier *r = 0; 23906943f9SDavid du Colombier return atoi(nm); 24906943f9SDavid du Colombier } 25906943f9SDavid du Colombier 26906943f9SDavid du Colombier Dev* 27906943f9SDavid du Colombier openep(Dev *d, int id) 28906943f9SDavid du Colombier { 29906943f9SDavid du Colombier char *mode; /* How many modes? */ 30906943f9SDavid du Colombier Ep *ep; 31906943f9SDavid du Colombier Altc *ac; 32906943f9SDavid du Colombier Dev *epd; 33906943f9SDavid du Colombier Usbdev *ud; 34906943f9SDavid du Colombier char name[40]; 35906943f9SDavid du Colombier 36*16146bc9SDavid du Colombier if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0) 37*16146bc9SDavid du Colombier return nil; 38906943f9SDavid du Colombier if(d->cfd < 0 || d->usb == nil){ 39906943f9SDavid du Colombier werrstr("device not configured"); 40906943f9SDavid du Colombier return nil; 41906943f9SDavid du Colombier } 42906943f9SDavid du Colombier ud = d->usb; 43906943f9SDavid du Colombier if(id < 0 || id >= nelem(ud->ep) || ud->ep[id] == nil){ 44906943f9SDavid du Colombier werrstr("bad enpoint number"); 45906943f9SDavid du Colombier return nil; 46906943f9SDavid du Colombier } 47906943f9SDavid du Colombier ep = ud->ep[id]; 48906943f9SDavid du Colombier mode = "rw"; 49906943f9SDavid du Colombier if(ep->dir == Ein) 50906943f9SDavid du Colombier mode = "r"; 51906943f9SDavid du Colombier if(ep->dir == Eout) 52906943f9SDavid du Colombier mode = "w"; 53906943f9SDavid du Colombier snprint(name, sizeof(name), "/dev/usb/ep%d.%d", d->id, id); 54906943f9SDavid du Colombier if(access(name, AEXIST) == 0){ 55906943f9SDavid du Colombier dprint(2, "%s: %s already exists; trying to open\n", argv0, name); 56906943f9SDavid du Colombier return opendev(name); 57906943f9SDavid du Colombier } 58906943f9SDavid du Colombier if(devctl(d, "new %d %d %s", id, ep->type, mode) < 0){ 59906943f9SDavid du Colombier dprint(2, "%s: %s: new: %r\n", argv0, d->dir); 60906943f9SDavid du Colombier return nil; 61906943f9SDavid du Colombier } 62906943f9SDavid du Colombier epd = opendev(name); 63906943f9SDavid du Colombier if(epd == nil) 64906943f9SDavid du Colombier return nil; 65906943f9SDavid du Colombier epd->id = id; 66906943f9SDavid du Colombier if(devctl(epd, "maxpkt %d", ep->maxpkt) < 0) 67906943f9SDavid du Colombier fprint(2, "%s: %s: openep: maxpkt: %r\n", argv0, epd->dir); 68906943f9SDavid du Colombier else 69906943f9SDavid du Colombier dprint(2, "%s: %s: maxpkt %d\n", argv0, epd->dir, ep->maxpkt); 70906943f9SDavid du Colombier epd->maxpkt = ep->maxpkt; 71906943f9SDavid du Colombier ac = ep->iface->altc[0]; 72906943f9SDavid du Colombier if(ep->ntds > 1 && devctl(epd, "ntds %d", ep->ntds) < 0) 73906943f9SDavid du Colombier fprint(2, "%s: %s: openep: ntds: %r\n", argv0, epd->dir); 74906943f9SDavid du Colombier else 75906943f9SDavid du Colombier dprint(2, "%s: %s: ntds %d\n", argv0, epd->dir, ep->ntds); 76906943f9SDavid du Colombier 77906943f9SDavid du Colombier /* 78906943f9SDavid du Colombier * For iso endpoints and high speed interrupt endpoints the pollival is 79906943f9SDavid du Colombier * actually 2ⁿ and not n. 80906943f9SDavid du Colombier * The kernel usb driver must take that into account. 81906943f9SDavid du Colombier * It's simpler this way. 82906943f9SDavid du Colombier */ 83906943f9SDavid du Colombier 84906943f9SDavid du Colombier if(ac != nil && (ep->type == Eintr || ep->type == Eiso) && ac->interval != 0) 85906943f9SDavid du Colombier if(devctl(epd, "pollival %d", ac->interval) < 0) 86906943f9SDavid du Colombier fprint(2, "%s: %s: openep: pollival: %r\n", argv0, epd->dir); 87906943f9SDavid du Colombier return epd; 88906943f9SDavid du Colombier } 89906943f9SDavid du Colombier 90906943f9SDavid du Colombier Dev* 91906943f9SDavid du Colombier opendev(char *fn) 92906943f9SDavid du Colombier { 93906943f9SDavid du Colombier Dev *d; 94906943f9SDavid du Colombier int l; 95906943f9SDavid du Colombier 96*16146bc9SDavid du Colombier if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0) 97*16146bc9SDavid du Colombier return nil; 98906943f9SDavid du Colombier d = emallocz(sizeof(Dev), 1); 99906943f9SDavid du Colombier incref(d); 100906943f9SDavid du Colombier 101906943f9SDavid du Colombier l = strlen(fn); 102906943f9SDavid du Colombier d->dfd = -1; 103906943f9SDavid du Colombier d->dir = emallocz(l + strlen("/data") + 1, 0); 104906943f9SDavid du Colombier strcpy(d->dir, fn); 105906943f9SDavid du Colombier strcpy(d->dir+l, "/ctl"); 106906943f9SDavid du Colombier d->cfd = open(d->dir, ORDWR|OCEXEC); 107906943f9SDavid du Colombier d->dir[l] = 0; 108906943f9SDavid du Colombier d->id = nameid(fn); 109906943f9SDavid du Colombier if(d->cfd < 0){ 110906943f9SDavid du Colombier werrstr("can't open endpoint %s: %r", d->dir); 111906943f9SDavid du Colombier free(d->dir); 112906943f9SDavid du Colombier free(d); 113906943f9SDavid du Colombier return nil; 114906943f9SDavid du Colombier } 115906943f9SDavid du Colombier dprint(2, "%s: opendev %#p %s\n", argv0, d, fn); 116906943f9SDavid du Colombier return d; 117906943f9SDavid du Colombier } 118906943f9SDavid du Colombier 119906943f9SDavid du Colombier int 120906943f9SDavid du Colombier opendevdata(Dev *d, int mode) 121906943f9SDavid du Colombier { 122906943f9SDavid du Colombier int l; 123906943f9SDavid du Colombier 124906943f9SDavid du Colombier l = strlen(d->dir); 125906943f9SDavid du Colombier strcpy(d->dir+l, "/data"); 126906943f9SDavid du Colombier d->dfd = open(d->dir, mode|OCEXEC); 127906943f9SDavid du Colombier d->dir[l] = 0; 128906943f9SDavid du Colombier return d->dfd; 129906943f9SDavid du Colombier } 130906943f9SDavid du Colombier 131906943f9SDavid du Colombier int 132906943f9SDavid du Colombier loaddevconf(Dev *d, int n) 133906943f9SDavid du Colombier { 134906943f9SDavid du Colombier uchar buf[1024]; /* enough room for extra descriptors */ 135906943f9SDavid du Colombier int nr; 136906943f9SDavid du Colombier int type; 137906943f9SDavid du Colombier 138906943f9SDavid du Colombier if(n >= nelem(d->usb->conf)){ 139906943f9SDavid du Colombier werrstr("loaddevconf: bug: out of configurations in device"); 140906943f9SDavid du Colombier fprint(2, "%s: %r\n", argv0); 141906943f9SDavid du Colombier return -1; 142906943f9SDavid du Colombier } 143906943f9SDavid du Colombier type = Rd2h|Rstd|Rdev; 144906943f9SDavid du Colombier nr = usbcmd(d, type, Rgetdesc, Dconf<<8|n, 0, buf, 1024); 145906943f9SDavid du Colombier if(nr < Dconflen) 146906943f9SDavid du Colombier return -1; 147906943f9SDavid du Colombier if(d->usb->conf[n] == nil) 148906943f9SDavid du Colombier d->usb->conf[n] = emallocz(sizeof(Conf), 1); 149906943f9SDavid du Colombier return parseconf(d->usb, d->usb->conf[n], buf, nr); 150906943f9SDavid du Colombier } 151906943f9SDavid du Colombier 152906943f9SDavid du Colombier Ep* 153906943f9SDavid du Colombier mkep(Usbdev *d, int id) 154906943f9SDavid du Colombier { 155906943f9SDavid du Colombier Ep *ep; 156906943f9SDavid du Colombier 157906943f9SDavid du Colombier d->ep[id] = ep = emallocz(sizeof(Ep), 1); 158906943f9SDavid du Colombier ep->id = id; 159906943f9SDavid du Colombier return ep; 160906943f9SDavid du Colombier } 161906943f9SDavid du Colombier 162906943f9SDavid du Colombier static char* 163906943f9SDavid du Colombier mkstr(uchar *b, int n) 164906943f9SDavid du Colombier { 165906943f9SDavid du Colombier Rune r; 166906943f9SDavid du Colombier char *us; 167906943f9SDavid du Colombier char *s; 168906943f9SDavid du Colombier char *e; 169906943f9SDavid du Colombier 170906943f9SDavid du Colombier if(n <= 2 || (n & 1) != 0) 171906943f9SDavid du Colombier return strdup("none"); 172906943f9SDavid du Colombier n = (n - 2)/2; 173906943f9SDavid du Colombier b += 2; 174906943f9SDavid du Colombier us = s = emallocz(n*UTFmax+1, 0); 175906943f9SDavid du Colombier e = s + n*UTFmax+1; 176906943f9SDavid du Colombier for(; --n >= 0; b += 2){ 177906943f9SDavid du Colombier r = GET2(b); 178906943f9SDavid du Colombier s = seprint(s, e, "%C", r); 179906943f9SDavid du Colombier } 180906943f9SDavid du Colombier return us; 181906943f9SDavid du Colombier } 182906943f9SDavid du Colombier 183906943f9SDavid du Colombier char* 184906943f9SDavid du Colombier loaddevstr(Dev *d, int sid) 185906943f9SDavid du Colombier { 186906943f9SDavid du Colombier uchar buf[128]; 187906943f9SDavid du Colombier int type; 188906943f9SDavid du Colombier int nr; 189906943f9SDavid du Colombier 190906943f9SDavid du Colombier if(sid == 0) 191906943f9SDavid du Colombier return estrdup("none"); 192906943f9SDavid du Colombier type = Rd2h|Rstd|Rdev; 193906943f9SDavid du Colombier nr=usbcmd(d, type, Rgetdesc, Dstr<<8|sid, 0, buf, sizeof(buf)); 194906943f9SDavid du Colombier return mkstr(buf, nr); 195906943f9SDavid du Colombier } 196906943f9SDavid du Colombier 197906943f9SDavid du Colombier int 198906943f9SDavid du Colombier loaddevdesc(Dev *d) 199906943f9SDavid du Colombier { 200906943f9SDavid du Colombier uchar buf[Ddevlen+255]; 201906943f9SDavid du Colombier int nr; 202906943f9SDavid du Colombier int type; 203906943f9SDavid du Colombier Ep *ep0; 204906943f9SDavid du Colombier 205906943f9SDavid du Colombier type = Rd2h|Rstd|Rdev; 206906943f9SDavid du Colombier nr = sizeof(buf); 207906943f9SDavid du Colombier memset(buf, 0, Ddevlen); 208906943f9SDavid du Colombier if((nr=usbcmd(d, type, Rgetdesc, Ddev<<8|0, 0, buf, nr)) < 0) 209906943f9SDavid du Colombier return -1; 210906943f9SDavid du Colombier /* 211906943f9SDavid du Colombier * Several hubs are returning descriptors of 17 bytes, not 18. 212906943f9SDavid du Colombier * We accept them and leave number of configurations as zero. 213906943f9SDavid du Colombier * (a get configuration descriptor also fails for them!) 214906943f9SDavid du Colombier */ 215906943f9SDavid du Colombier if(nr < Ddevlen){ 216906943f9SDavid du Colombier print("%s: %s: warning: device with short descriptor\n", 217906943f9SDavid du Colombier argv0, d->dir); 218906943f9SDavid du Colombier if(nr < Ddevlen-1){ 219906943f9SDavid du Colombier werrstr("short device descriptor (%d bytes)", nr); 220906943f9SDavid du Colombier return -1; 221906943f9SDavid du Colombier } 222906943f9SDavid du Colombier } 223906943f9SDavid du Colombier d->usb = emallocz(sizeof(Usbdev), 1); 224906943f9SDavid du Colombier ep0 = mkep(d->usb, 0); 225906943f9SDavid du Colombier ep0->dir = Eboth; 226906943f9SDavid du Colombier ep0->type = Econtrol; 227906943f9SDavid du Colombier ep0->maxpkt = d->maxpkt = 8; /* a default */ 228906943f9SDavid du Colombier nr = parsedev(d, buf, nr); 229906943f9SDavid du Colombier if(nr >= 0){ 230906943f9SDavid du Colombier d->usb->vendor = loaddevstr(d, d->usb->vsid); 231906943f9SDavid du Colombier if(strcmp(d->usb->vendor, "none") != 0){ 232906943f9SDavid du Colombier d->usb->product = loaddevstr(d, d->usb->psid); 233906943f9SDavid du Colombier d->usb->serial = loaddevstr(d, d->usb->ssid); 234906943f9SDavid du Colombier } 235906943f9SDavid du Colombier } 236906943f9SDavid du Colombier return nr; 237906943f9SDavid du Colombier } 238906943f9SDavid du Colombier 239906943f9SDavid du Colombier int 240906943f9SDavid du Colombier configdev(Dev *d) 241906943f9SDavid du Colombier { 242906943f9SDavid du Colombier int i; 243906943f9SDavid du Colombier 244906943f9SDavid du Colombier if(d->dfd < 0) 245906943f9SDavid du Colombier opendevdata(d, ORDWR); 246906943f9SDavid du Colombier if(loaddevdesc(d) < 0) 247906943f9SDavid du Colombier return -1; 248906943f9SDavid du Colombier for(i = 0; i < d->usb->nconf; i++) 249906943f9SDavid du Colombier if(loaddevconf(d, i) < 0) 250906943f9SDavid du Colombier return -1; 251906943f9SDavid du Colombier return 0; 252906943f9SDavid du Colombier } 253906943f9SDavid du Colombier 254906943f9SDavid du Colombier static void 255906943f9SDavid du Colombier closeconf(Conf *c) 256906943f9SDavid du Colombier { 257906943f9SDavid du Colombier int i; 258906943f9SDavid du Colombier int a; 259906943f9SDavid du Colombier 260906943f9SDavid du Colombier if(c == nil) 261906943f9SDavid du Colombier return; 262906943f9SDavid du Colombier for(i = 0; i < nelem(c->iface); i++) 263906943f9SDavid du Colombier if(c->iface[i] != nil){ 264906943f9SDavid du Colombier for(a = 0; a < nelem(c->iface[i]->altc); a++) 265906943f9SDavid du Colombier free(c->iface[i]->altc[a]); 266906943f9SDavid du Colombier free(c->iface[i]); 267906943f9SDavid du Colombier } 268906943f9SDavid du Colombier free(c); 269906943f9SDavid du Colombier } 270906943f9SDavid du Colombier 271906943f9SDavid du Colombier void 272906943f9SDavid du Colombier closedev(Dev *d) 273906943f9SDavid du Colombier { 274906943f9SDavid du Colombier int i; 275906943f9SDavid du Colombier Usbdev *ud; 276906943f9SDavid du Colombier 277906943f9SDavid du Colombier if(d==nil || decref(d) != 0) 278906943f9SDavid du Colombier return; 279906943f9SDavid du Colombier dprint(2, "%s: closedev %#p %s\n", argv0, d, d->dir); 280906943f9SDavid du Colombier if(d->free != nil) 281906943f9SDavid du Colombier d->free(d->aux); 282906943f9SDavid du Colombier if(d->cfd >= 0) 283906943f9SDavid du Colombier close(d->cfd); 284906943f9SDavid du Colombier if(d->dfd >= 0) 285906943f9SDavid du Colombier close(d->dfd); 286906943f9SDavid du Colombier d->cfd = d->dfd = -1; 287906943f9SDavid du Colombier free(d->dir); 288906943f9SDavid du Colombier d->dir = nil; 289906943f9SDavid du Colombier ud = d->usb; 290906943f9SDavid du Colombier d->usb = nil; 291906943f9SDavid du Colombier if(ud != nil){ 292906943f9SDavid du Colombier free(ud->vendor); 293906943f9SDavid du Colombier free(ud->product); 294906943f9SDavid du Colombier free(ud->serial); 295906943f9SDavid du Colombier for(i = 0; i < nelem(ud->ep); i++) 296906943f9SDavid du Colombier free(ud->ep[i]); 297906943f9SDavid du Colombier for(i = 0; i < nelem(ud->ddesc); i++) 298906943f9SDavid du Colombier free(ud->ddesc[i]); 299906943f9SDavid du Colombier 300906943f9SDavid du Colombier for(i = 0; i < nelem(ud->conf); i++) 301906943f9SDavid du Colombier closeconf(ud->conf[i]); 302906943f9SDavid du Colombier free(ud); 303906943f9SDavid du Colombier } 304906943f9SDavid du Colombier free(d); 305906943f9SDavid du Colombier } 306906943f9SDavid du Colombier 307906943f9SDavid du Colombier static char* 308906943f9SDavid du Colombier reqstr(int type, int req) 309906943f9SDavid du Colombier { 310906943f9SDavid du Colombier char *s; 311906943f9SDavid du Colombier static char* ds[] = { "dev", "if", "ep", "oth" }; 312906943f9SDavid du Colombier static char buf[40]; 313906943f9SDavid du Colombier 314906943f9SDavid du Colombier if(type&Rd2h) 315906943f9SDavid du Colombier s = seprint(buf, buf+sizeof(buf), "d2h"); 316906943f9SDavid du Colombier else 317906943f9SDavid du Colombier s = seprint(buf, buf+sizeof(buf), "h2d"); 318906943f9SDavid du Colombier if(type&Rclass) 319906943f9SDavid du Colombier s = seprint(s, buf+sizeof(buf), "|cls"); 320906943f9SDavid du Colombier else if(type&Rvendor) 321906943f9SDavid du Colombier s = seprint(s, buf+sizeof(buf), "|vnd"); 322906943f9SDavid du Colombier else 323906943f9SDavid du Colombier s = seprint(s, buf+sizeof(buf), "|std"); 324906943f9SDavid du Colombier s = seprint(s, buf+sizeof(buf), "|%s", ds[type&3]); 325906943f9SDavid du Colombier 326906943f9SDavid du Colombier switch(req){ 327906943f9SDavid du Colombier case Rgetstatus: s = seprint(s, buf+sizeof(buf), " getsts"); break; 328906943f9SDavid du Colombier case Rclearfeature: s = seprint(s, buf+sizeof(buf), " clrfeat"); break; 329906943f9SDavid du Colombier case Rsetfeature: s = seprint(s, buf+sizeof(buf), " setfeat"); break; 330906943f9SDavid du Colombier case Rsetaddress: s = seprint(s, buf+sizeof(buf), " setaddr"); break; 331906943f9SDavid du Colombier case Rgetdesc: s = seprint(s, buf+sizeof(buf), " getdesc"); break; 332906943f9SDavid du Colombier case Rsetdesc: s = seprint(s, buf+sizeof(buf), " setdesc"); break; 333906943f9SDavid du Colombier case Rgetconf: s = seprint(s, buf+sizeof(buf), " getcnf"); break; 334906943f9SDavid du Colombier case Rsetconf: s = seprint(s, buf+sizeof(buf), " setcnf"); break; 335906943f9SDavid du Colombier case Rgetiface: s = seprint(s, buf+sizeof(buf), " getif"); break; 336906943f9SDavid du Colombier case Rsetiface: s = seprint(s, buf+sizeof(buf), " setif"); break; 337906943f9SDavid du Colombier } 338906943f9SDavid du Colombier USED(s); 339906943f9SDavid du Colombier return buf; 340906943f9SDavid du Colombier } 341906943f9SDavid du Colombier 342906943f9SDavid du Colombier static int 343906943f9SDavid du Colombier cmdreq(Dev *d, int type, int req, int value, int index, uchar *data, int count) 344906943f9SDavid du Colombier { 345906943f9SDavid du Colombier int ndata, n; 346906943f9SDavid du Colombier uchar *wp; 347906943f9SDavid du Colombier uchar buf[8]; 348906943f9SDavid du Colombier char *hd, *rs; 349906943f9SDavid du Colombier 350906943f9SDavid du Colombier assert(d != nil); 351906943f9SDavid du Colombier if(data == nil){ 352906943f9SDavid du Colombier wp = buf; 353906943f9SDavid du Colombier ndata = 0; 354906943f9SDavid du Colombier }else{ 355906943f9SDavid du Colombier ndata = count; 356906943f9SDavid du Colombier wp = emallocz(8+ndata, 0); 357906943f9SDavid du Colombier } 358906943f9SDavid du Colombier wp[0] = type; 359906943f9SDavid du Colombier wp[1] = req; 360906943f9SDavid du Colombier PUT2(wp+2, value); 361906943f9SDavid du Colombier PUT2(wp+4, index); 362906943f9SDavid du Colombier PUT2(wp+6, count); 363906943f9SDavid du Colombier if(data != nil) 364906943f9SDavid du Colombier memmove(wp+8, data, ndata); 365906943f9SDavid du Colombier if(usbdebug>2){ 366906943f9SDavid du Colombier hd = hexstr(wp, ndata+8); 367906943f9SDavid du Colombier rs = reqstr(type, req); 368906943f9SDavid du Colombier fprint(2, "%s: %s val %d|%d idx %d cnt %d out[%d] %s\n", 369906943f9SDavid du Colombier d->dir, rs, value>>8, value&0xFF, 370906943f9SDavid du Colombier index, count, ndata+8, hd); 371906943f9SDavid du Colombier free(hd); 372906943f9SDavid du Colombier } 373906943f9SDavid du Colombier n = write(d->dfd, wp, 8+ndata); 374906943f9SDavid du Colombier if(wp != buf) 375906943f9SDavid du Colombier free(wp); 376906943f9SDavid du Colombier if(n < 0) 377906943f9SDavid du Colombier return -1; 378906943f9SDavid du Colombier if(n != 8+ndata){ 379906943f9SDavid du Colombier dprint(2, "%s: cmd: short write: %d\n", argv0, n); 380906943f9SDavid du Colombier return -1; 381906943f9SDavid du Colombier } 382906943f9SDavid du Colombier return n; 383906943f9SDavid du Colombier } 384906943f9SDavid du Colombier 385906943f9SDavid du Colombier static int 386906943f9SDavid du Colombier cmdrep(Dev *d, void *buf, int nb) 387906943f9SDavid du Colombier { 388906943f9SDavid du Colombier char *hd; 389906943f9SDavid du Colombier 390906943f9SDavid du Colombier nb = read(d->dfd, buf, nb); 391906943f9SDavid du Colombier if(nb >0 && usbdebug > 2){ 392906943f9SDavid du Colombier hd = hexstr(buf, nb); 393906943f9SDavid du Colombier fprint(2, "%s: in[%d] %s\n", d->dir, nb, hd); 394906943f9SDavid du Colombier free(hd); 395906943f9SDavid du Colombier } 396906943f9SDavid du Colombier return nb; 397906943f9SDavid du Colombier } 398906943f9SDavid du Colombier 399906943f9SDavid du Colombier int 400906943f9SDavid du Colombier usbcmd(Dev *d, int type, int req, int value, int index, uchar *data, int count) 401906943f9SDavid du Colombier { 402906943f9SDavid du Colombier int r; 403906943f9SDavid du Colombier int i; 404906943f9SDavid du Colombier int nerr; 405906943f9SDavid du Colombier char err[64]; 406906943f9SDavid du Colombier 407906943f9SDavid du Colombier /* 408906943f9SDavid du Colombier * Some devices do not respond to commands some times. 409906943f9SDavid du Colombier * Others even report errors but later work just fine. Retry. 410906943f9SDavid du Colombier */ 411906943f9SDavid du Colombier r = -1; 412906943f9SDavid du Colombier *err = 0; 413906943f9SDavid du Colombier for(i = nerr = 0; i < Uctries; i++){ 414906943f9SDavid du Colombier if(type&Rd2h) 415906943f9SDavid du Colombier r = cmdreq(d, type, req, value, index, nil, count); 416906943f9SDavid du Colombier else 417906943f9SDavid du Colombier r = cmdreq(d, type, req, value, index, data, count); 418906943f9SDavid du Colombier if(r > 0){ 419906943f9SDavid du Colombier if((type&Rd2h) == 0) 420906943f9SDavid du Colombier break; 421906943f9SDavid du Colombier r = cmdrep(d, data, count); 422906943f9SDavid du Colombier if(r > 0) 423906943f9SDavid du Colombier break; 424906943f9SDavid du Colombier if(r == 0) 425906943f9SDavid du Colombier werrstr("no data from device"); 426906943f9SDavid du Colombier } 427906943f9SDavid du Colombier nerr++; 428906943f9SDavid du Colombier if(*err == 0) 429906943f9SDavid du Colombier rerrstr(err, sizeof(err)); 430906943f9SDavid du Colombier sleep(Ucdelay); 431906943f9SDavid du Colombier } 432906943f9SDavid du Colombier if(r > 0 && i >= 2){ 433906943f9SDavid du Colombier /* let the user know the device is not in good shape */ 434906943f9SDavid du Colombier fprint(2, "%s: usbcmd: %s: required %d attempts (%s)\n", 435906943f9SDavid du Colombier argv0, d->dir, i, err); 436906943f9SDavid du Colombier } 437906943f9SDavid du Colombier return r; 438906943f9SDavid du Colombier } 439906943f9SDavid du Colombier 440906943f9SDavid du Colombier int 441906943f9SDavid du Colombier unstall(Dev *dev, Dev *ep, int dir) 442906943f9SDavid du Colombier { 443906943f9SDavid du Colombier int r; 444906943f9SDavid du Colombier 445906943f9SDavid du Colombier if(dir == Ein) 446906943f9SDavid du Colombier dir = 0x80; 447906943f9SDavid du Colombier else 448906943f9SDavid du Colombier dir = 0; 449906943f9SDavid du Colombier r = Rh2d|Rstd|Rep; 450906943f9SDavid du Colombier if(usbcmd(dev, r, Rclearfeature, Fhalt, ep->id|dir, nil, 0)<0){ 451906943f9SDavid du Colombier werrstr("unstall: %s: %r", ep->dir); 452906943f9SDavid du Colombier return -1; 453906943f9SDavid du Colombier } 454906943f9SDavid du Colombier if(devctl(ep, "clrhalt") < 0){ 455906943f9SDavid du Colombier werrstr("clrhalt: %s: %r", ep->dir); 456906943f9SDavid du Colombier return -1; 457906943f9SDavid du Colombier } 458906943f9SDavid du Colombier return 0; 459906943f9SDavid du Colombier } 460906943f9SDavid du Colombier 461906943f9SDavid du Colombier /* 462906943f9SDavid du Colombier * To be sure it uses a single write. 463906943f9SDavid du Colombier */ 464906943f9SDavid du Colombier int 465906943f9SDavid du Colombier devctl(Dev *dev, char *fmt, ...) 466906943f9SDavid du Colombier { 467906943f9SDavid du Colombier char buf[128]; 468906943f9SDavid du Colombier va_list arg; 469906943f9SDavid du Colombier char *e; 470906943f9SDavid du Colombier 471906943f9SDavid du Colombier va_start(arg, fmt); 472906943f9SDavid du Colombier e = vseprint(buf, buf+sizeof(buf), fmt, arg); 473906943f9SDavid du Colombier va_end(arg); 474906943f9SDavid du Colombier return write(dev->cfd, buf, e-buf); 475906943f9SDavid du Colombier } 476