123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 |
- /*
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
- #include <u.h>
- #include <libc.h>
- #include <thread.h>
- #include <usb/usb.h>
- /*
- * epN.M -> N
- */
- static int
- nameid(char *s)
- {
- char *r;
- char nm[20];
- r = strrchr(s, 'p');
- if(r == nil)
- return -1;
- strecpy(nm, nm+sizeof(nm), r+1);
- r = strchr(nm, '.');
- if(r == nil)
- return -1;
- *r = 0;
- return atoi(nm);
- }
- Dev*
- openep(Dev *d, int id)
- {
- char *mode; /* How many modes? */
- Ep *ep;
- Altc *ac;
- Dev *epd;
- Usbdev *ud;
- char name[40];
- if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0)
- return nil;
- if(d->cfd < 0 || d->usb == nil){
- werrstr("device not configured");
- return nil;
- }
- ud = d->usb;
- if(id < 0 || id >= nelem(ud->ep) || ud->ep[id] == nil){
- werrstr("bad enpoint number");
- return nil;
- }
- ep = ud->ep[id];
- mode = "rw";
- if(ep->dir == Ein)
- mode = "r";
- if(ep->dir == Eout)
- mode = "w";
- snprint(name, sizeof(name), "/dev/usb/ep%d.%d", d->id, id);
- if(access(name, AEXIST) == 0){
- dprint(2, "%s: %s already exists; trying to open\n", argv0, name);
- epd = opendev(name);
- if(epd != nil)
- epd->maxpkt = ep->maxpkt; /* guess */
- return epd;
- }
- if(devctl(d, "new %d %d %s", id, ep->type, mode) < 0){
- dprint(2, "%s: %s: new: %r\n", argv0, d->dir);
- return nil;
- }
- epd = opendev(name);
- if(epd == nil)
- return nil;
- epd->id = id;
- if(devctl(epd, "maxpkt %d", ep->maxpkt) < 0)
- fprint(2, "%s: %s: openep: maxpkt: %r\n", argv0, epd->dir);
- else
- dprint(2, "%s: %s: maxpkt %d\n", argv0, epd->dir, ep->maxpkt);
- epd->maxpkt = ep->maxpkt;
- ac = ep->iface->altc[0];
- if(ep->ntds > 1 && devctl(epd, "ntds %d", ep->ntds) < 0)
- fprint(2, "%s: %s: openep: ntds: %r\n", argv0, epd->dir);
- else
- dprint(2, "%s: %s: ntds %d\n", argv0, epd->dir, ep->ntds);
- /*
- * For iso endpoints and high speed interrupt endpoints the pollival is
- * actually 2ⁿ and not n.
- * The kernel usb driver must take that into account.
- * It's simpler this way.
- */
- if(ac != nil && (ep->type == Eintr || ep->type == Eiso) && ac->interval != 0)
- if(devctl(epd, "pollival %d", ac->interval) < 0)
- fprint(2, "%s: %s: openep: pollival: %r\n", argv0, epd->dir);
- return epd;
- }
- Dev*
- opendev(char *fn)
- {
- Dev *d;
- int l;
- if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0)
- return nil;
- d = emallocz(sizeof(Dev), 1);
- incref(&d->Ref);
- l = strlen(fn);
- d->dfd = -1;
- /*
- * +30 to allocate extra size to concat "/<epfilename>"
- * we should probably remove that feature from the manual
- * and from the code after checking out that nobody relies on
- * that.
- */
- d->dir = emallocz(l + 30, 0);
- strcpy(d->dir, fn);
- strcpy(d->dir+l, "/ctl");
- d->cfd = open(d->dir, ORDWR|OCEXEC);
- d->dir[l] = 0;
- d->id = nameid(fn);
- if(d->cfd < 0){
- werrstr("can't open endpoint %s: %r", d->dir);
- free(d->dir);
- free(d);
- return nil;
- }
- dprint(2, "%s: opendev %#p %s\n", argv0, d, fn);
- return d;
- }
- int
- opendevdata(Dev *d, int mode)
- {
- char buf[80]; /* more than enough for a usb path */
- seprint(buf, buf+sizeof(buf), "%s/data", d->dir);
- d->dfd = open(buf, mode|OCEXEC);
- return d->dfd;
- }
- enum
- {
- /*
- * Max device conf is also limited by max control request size as
- * limited by Maxctllen in the kernel usb.h (both limits are arbitrary).
- */
- Maxdevconf = 4 * 1024, /* asking for 16K kills Newsham's disk */
- };
- int
- loaddevconf(Dev *d, int n)
- {
- uint8_t *buf;
- int nr;
- int type;
- if(n >= nelem(d->usb->conf)){
- werrstr("loaddevconf: bug: out of configurations in device");
- fprint(2, "%s: %r\n", argv0);
- return -1;
- }
- buf = emallocz(Maxdevconf, 0);
- type = Rd2h|Rstd|Rdev;
- nr = usbcmd(d, type, Rgetdesc, Dconf<<8|n, 0, buf, Maxdevconf);
- if(nr < Dconflen){
- 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);
- free(buf);
- return nr;
- }
- Ep*
- mkep(Usbdev *d, int id)
- {
- Ep *ep;
- d->ep[id] = ep = emallocz(sizeof(Ep), 1);
- ep->id = id;
- return ep;
- }
- static char*
- mkstr(uint8_t *b, int n)
- {
- Rune r;
- char *us;
- char *s;
- char *e;
- if(n <= 2 || (n & 1) != 0)
- return strdup("none");
- n = (n - 2)/2;
- b += 2;
- us = s = emallocz(n*UTFmax+1, 0);
- e = s + n*UTFmax+1;
- for(; --n >= 0; b += 2){
- r = GET2(b);
- s = seprint(s, e, "%C", r);
- }
- return us;
- }
- char*
- loaddevstr(Dev *d, int sid)
- {
- uint8_t buf[128];
- int type;
- int nr;
- if(sid == 0)
- return estrdup("none");
- type = Rd2h|Rstd|Rdev;
- nr=usbcmd(d, type, Rgetdesc, Dstr<<8|sid, 0, buf, sizeof(buf));
- return mkstr(buf, nr);
- }
- int
- loaddevdesc(Dev *d)
- {
- uint8_t buf[Ddevlen+255];
- 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)
- 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;
- }
- }
- d->usb = emallocz(sizeof(Usbdev), 1);
- ep0 = mkep(d->usb, 0);
- ep0->dir = Eboth;
- ep0->type = Econtrol;
- ep0->maxpkt = d->maxpkt = 8; /* a default */
- nr = parsedev(d, buf, nr);
- if(nr >= 0){
- d->usb->vendor = loaddevstr(d, d->usb->vsid);
- if(strcmp(d->usb->vendor, "none") != 0){
- d->usb->product = loaddevstr(d, d->usb->psid);
- d->usb->serial = loaddevstr(d, d->usb->ssid);
- }
- }
- return nr;
- }
- int
- configdev(Dev *d)
- {
- int i;
- if(d->dfd < 0)
- opendevdata(d, ORDWR);
- if(loaddevdesc(d) < 0)
- return -1;
- for(i = 0; i < d->usb->nconf; i++)
- if(loaddevconf(d, i) < 0)
- return -1;
- return 0;
- }
- static void
- closeconf(Conf *c)
- {
- int i;
- int a;
- if(c == nil)
- return;
- for(i = 0; i < nelem(c->iface); i++)
- if(c->iface[i] != nil){
- for(a = 0; a < nelem(c->iface[i]->altc); a++)
- free(c->iface[i]->altc[a]);
- free(c->iface[i]);
- }
- free(c);
- }
- void
- closedev(Dev *d)
- {
- int i;
- Usbdev *ud;
- if(d==nil || decref(&d->Ref) != 0)
- return;
- dprint(2, "%s: closedev %#p %s\n", argv0, d, d->dir);
- if(d->free != nil)
- d->free(d->aux);
- if(d->cfd >= 0)
- close(d->cfd);
- if(d->dfd >= 0)
- close(d->dfd);
- d->cfd = d->dfd = -1;
- free(d->dir);
- d->dir = nil;
- ud = d->usb;
- d->usb = nil;
- if(ud != nil){
- free(ud->vendor);
- free(ud->product);
- free(ud->serial);
- for(i = 0; i < nelem(ud->ep); i++)
- free(ud->ep[i]);
- for(i = 0; i < nelem(ud->ddesc); i++)
- free(ud->ddesc[i]);
- for(i = 0; i < nelem(ud->conf); i++)
- closeconf(ud->conf[i]);
- free(ud);
- }
- free(d);
- }
- static char*
- reqstr(int type, int req)
- {
- char *s;
- static char* ds[] = { "dev", "if", "ep", "oth" };
- static char buf[40];
- if(type&Rd2h)
- s = seprint(buf, buf+sizeof(buf), "d2h");
- else
- s = seprint(buf, buf+sizeof(buf), "h2d");
- if(type&Rclass)
- s = seprint(s, buf+sizeof(buf), "|cls");
- else if(type&Rvendor)
- s = seprint(s, buf+sizeof(buf), "|vnd");
- else
- s = seprint(s, buf+sizeof(buf), "|std");
- s = seprint(s, buf+sizeof(buf), "|%s", ds[type&3]);
- switch(req){
- case Rgetstatus: s = seprint(s, buf+sizeof(buf), " getsts"); break;
- case Rclearfeature: s = seprint(s, buf+sizeof(buf), " clrfeat"); break;
- case Rsetfeature: s = seprint(s, buf+sizeof(buf), " setfeat"); break;
- case Rsetaddress: s = seprint(s, buf+sizeof(buf), " setaddr"); break;
- case Rgetdesc: s = seprint(s, buf+sizeof(buf), " getdesc"); break;
- case Rsetdesc: s = seprint(s, buf+sizeof(buf), " setdesc"); break;
- case Rgetconf: s = seprint(s, buf+sizeof(buf), " getcnf"); break;
- case Rsetconf: s = seprint(s, buf+sizeof(buf), " setcnf"); break;
- case Rgetiface: s = seprint(s, buf+sizeof(buf), " getif"); break;
- case Rsetiface: s = seprint(s, buf+sizeof(buf), " setif"); break;
- }
- USED(s);
- return buf;
- }
- static int
- cmdreq(Dev *d, int type, int req, int value, int index, uint8_t *data,
- int count)
- {
- int ndata, n;
- uint8_t *wp;
- uint8_t buf[8];
- char *hd, *rs;
- assert(d != nil);
- if(data == nil){
- wp = buf;
- ndata = 0;
- }else{
- ndata = count;
- wp = emallocz(8+ndata, 0);
- }
- wp[0] = type;
- wp[1] = req;
- PUT2(wp+2, value);
- PUT2(wp+4, index);
- PUT2(wp+6, count);
- if(data != nil)
- memmove(wp+8, data, ndata);
- if(usbdebug>2){
- hd = hexstr(wp, ndata+8);
- rs = reqstr(type, req);
- fprint(2, "%s: %s val %d|%d idx %d cnt %d out[%d] %s\n",
- d->dir, rs, value>>8, value&0xFF,
- index, count, ndata+8, hd);
- free(hd);
- }
- n = write(d->dfd, wp, 8+ndata);
- if(wp != buf)
- free(wp);
- if(n < 0)
- return -1;
- if(n != 8+ndata){
- dprint(2, "%s: cmd: short write: %d\n", argv0, n);
- return -1;
- }
- return n;
- }
- static int
- cmdrep(Dev *d, void *buf, int nb)
- {
- char *hd;
- nb = read(d->dfd, buf, nb);
- if(nb >0 && usbdebug > 2){
- hd = hexstr(buf, nb);
- fprint(2, "%s: in[%d] %s\n", d->dir, nb, hd);
- free(hd);
- }
- return nb;
- }
- int
- usbcmd(Dev *d, int type, int req, int value, int index, uint8_t *data,
- int count)
- {
- int i, r, nerr;
- char err[64];
- /*
- * Some devices do not respond to commands some times.
- * Others even report errors but later work just fine. Retry.
- */
- r = -1;
- *err = 0;
- for(i = nerr = 0; i < Uctries; i++){
- if(type & Rd2h)
- r = cmdreq(d, type, req, value, index, nil, count);
- else
- r = cmdreq(d, type, req, value, index, data, count);
- if(r > 0){
- if((type & Rd2h) == 0)
- break;
- r = cmdrep(d, data, count);
- if(r > 0)
- break;
- if(r == 0)
- werrstr("no data from device");
- }
- nerr++;
- if(*err == 0)
- rerrstr(err, sizeof(err));
- sleep(Ucdelay);
- }
- if(r > 0 && i >= 2)
- /* let the user know the device is not in good shape */
- fprint(2, "%s: usbcmd: %s: required %d attempts (%s)\n",
- argv0, d->dir, i, err);
- return r;
- }
- int
- unstall(Dev *dev, Dev *ep, int dir)
- {
- int r;
- if(dir == Ein)
- dir = 0x80;
- else
- dir = 0;
- r = Rh2d|Rstd|Rep;
- if(usbcmd(dev, r, Rclearfeature, Fhalt, ep->id|dir, nil, 0)<0){
- werrstr("unstall: %s: %r", ep->dir);
- return -1;
- }
- if(devctl(ep, "clrhalt") < 0){
- werrstr("clrhalt: %s: %r", ep->dir);
- return -1;
- }
- return 0;
- }
- /*
- * To be sure it uses a single write.
- */
- int
- devctl(Dev *dev, char *fmt, ...)
- {
- char buf[128];
- va_list arg;
- char *e;
- va_start(arg, fmt);
- e = vseprint(buf, buf+sizeof(buf), fmt, arg);
- va_end(arg);
- return write(dev->cfd, buf, e-buf);
- }
|