]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/nusb/lib/dev.c
nusb/lib: fix wrong endpoint id when openep() finds already existing endpoint file
[plan9front.git] / sys / src / cmd / nusb / lib / dev.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include "usb.h"
5
6 /*
7  * epN.M -> N
8  */
9 static int
10 nameid(char *s)
11 {
12         char *r;
13         char nm[20];
14
15         r = strrchr(s, 'p');
16         if(r == nil)
17                 return -1;
18         strecpy(nm, nm+sizeof(nm), r+1);
19         r = strchr(nm, '.');
20         if(r == nil)
21                 return -1;
22         *r = 0;
23         return atoi(nm);
24 }
25
26 Dev*
27 openep(Dev *d, int id)
28 {
29         char *mode;     /* How many modes? */
30         Ep *ep;
31         Altc *ac;
32         Dev *epd;
33         Usbdev *ud;
34         char name[40];
35
36         if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0)
37                 return nil;
38         if(d->cfd < 0 || d->usb == nil){
39                 werrstr("device not configured");
40                 return nil;
41         }
42         ud = d->usb;
43         if(id < 0 || id >= nelem(ud->ep) || ud->ep[id] == nil){
44                 werrstr("bad enpoint number");
45                 return nil;
46         }
47         ep = ud->ep[id];
48         mode = "rw";
49         if(ep->dir == Ein)
50                 mode = "r";
51         if(ep->dir == Eout)
52                 mode = "w";
53         snprint(name, sizeof(name), "/dev/usb/ep%d.%d", d->id, id);
54         if(access(name, AEXIST) == 0){
55                 dprint(2, "%s: %s already exists; trying to open\n", argv0, name);
56                 epd = opendev(name);
57                 if(epd != nil){
58                         epd->id = id;
59                         epd->maxpkt = ep->maxpkt;       /* guess */
60                 }
61                 return epd;
62         }
63         if(devctl(d, "new %d %d %s", id, ep->type, mode) < 0){
64                 dprint(2, "%s: %s: new: %r\n", argv0, d->dir);
65                 return nil;
66         }
67         epd = opendev(name);
68         if(epd == nil)
69                 return nil;
70         epd->id = id;
71         if(devctl(epd, "maxpkt %d", ep->maxpkt) < 0)
72                 fprint(2, "%s: %s: openep: maxpkt: %r\n", argv0, epd->dir);
73         else
74                 dprint(2, "%s: %s: maxpkt %d\n", argv0, epd->dir, ep->maxpkt);
75         epd->maxpkt = ep->maxpkt;
76         ac = ep->iface->altc[0];
77         if(ep->ntds > 1 && devctl(epd, "ntds %d", ep->ntds) < 0)
78                 fprint(2, "%s: %s: openep: ntds: %r\n", argv0, epd->dir);
79         else
80                 dprint(2, "%s: %s: ntds %d\n", argv0, epd->dir, ep->ntds);
81
82         /*
83          * For iso endpoints and high speed interrupt endpoints the pollival is
84          * actually 2ⁿ and not n.
85          * The kernel usb driver must take that into account.
86          * It's simpler this way.
87          */
88
89         if(ac != nil && (ep->type == Eintr || ep->type == Eiso) && ac->interval != 0)
90                 if(devctl(epd, "pollival %d", ac->interval) < 0)
91                         fprint(2, "%s: %s: openep: pollival: %r\n", argv0, epd->dir);
92         return epd;
93 }
94
95 Dev*
96 opendev(char *fn)
97 {
98         Dev *d;
99         int l;
100
101         if(access("/dev/usb", AEXIST) < 0 && bind("#u", "/dev", MBEFORE) < 0)
102                 return nil;
103         d = emallocz(sizeof(Dev), 1);
104         incref(d);
105
106         l = strlen(fn);
107         d->dfd = -1;
108         /*
109          * +30 to allocate extra size to concat "/<epfilename>"
110          * we should probably remove that feature from the manual
111          * and from the code after checking out that nobody relies on
112          * that.
113          */
114         d->dir = emallocz(l + 30, 0);
115         strcpy(d->dir, fn);
116         strcpy(d->dir+l, "/ctl");
117         d->cfd = open(d->dir, ORDWR|OCEXEC);
118         d->dir[l] = 0;
119         d->id = nameid(fn);
120         if(d->cfd < 0){
121                 werrstr("can't open endpoint %s: %r", d->dir);
122                 free(d->dir);
123                 free(d);
124                 return nil;
125         }
126         d->hname = nil;
127         dprint(2, "%s: opendev %#p %s\n", argv0, d, fn);
128         return d;
129 }
130
131 int
132 opendevdata(Dev *d, int mode)
133 {
134         char buf[80]; /* more than enough for a usb path */
135
136         seprint(buf, buf+sizeof(buf), "%s/data", d->dir);
137         d->dfd = open(buf, mode|OCEXEC);
138         return d->dfd;
139 }
140
141 enum
142 {
143         /*
144          * Max device conf is also limited by max control request size as
145          * limited by Maxctllen in the kernel usb.h (both limits are arbitrary).
146          */
147         Maxdevconf = 4 * 1024,  /* asking for 16K kills Newsham's disk */
148 };
149
150 int
151 loaddevconf(Dev *d, int n)
152 {
153         uchar *buf;
154         int nr;
155         int type;
156
157         if(n >= nelem(d->usb->conf)){
158                 werrstr("loaddevconf: bug: out of configurations in device");
159                 fprint(2, "%s: %r\n", argv0);
160                 return -1;
161         }
162         buf = emallocz(Maxdevconf, 0);
163         type = Rd2h|Rstd|Rdev;
164         nr = usbcmd(d, type, Rgetdesc, Dconf<<8|n, 0, buf, Maxdevconf);
165         if(nr < Dconflen){
166                 free(buf);
167                 return -1;
168         }
169         if(d->usb->conf[n] == nil)
170                 d->usb->conf[n] = emallocz(sizeof(Conf), 1);
171         nr = parseconf(d->usb, d->usb->conf[n], buf, nr);
172         free(buf);
173         return nr;
174 }
175
176 Ep*
177 mkep(Usbdev *d, int id)
178 {
179         Ep *ep;
180
181         d->ep[id] = ep = emallocz(sizeof(Ep), 1);
182         ep->id = id;
183         return ep;
184 }
185
186 static char*
187 mkstr(uchar *b, int n)
188 {
189         Rune r;
190         char *us;
191         char *s;
192
193         if(n > 0 && n > b[0])
194                 n = b[0];
195         if(n <= 2 || (n & 1) != 0)
196                 return strdup("none");
197         n = (n - 2)/2;
198         b += 2;
199         us = s = emallocz(n*UTFmax+1, 0);
200         for(; --n >= 0; b += 2){
201                 r = GET2(b);
202                 s += runetochar(s, &r);
203         }
204         *s = 0;
205         return us;
206 }
207
208 char*
209 loaddevstr(Dev *d, int sid)
210 {
211         uchar buf[256];
212         int langid;
213         int type;
214         int nr;
215
216         if(sid == 0)
217                 return estrdup("none");
218         type = Rd2h|Rstd|Rdev;
219
220         /*
221          * there are devices which do not return a string if used
222          * with invalid language id, so at least try to use the first
223          * one and choose english if failed
224          */
225         nr=usbcmd(d, type, Rgetdesc, Dstr<<8, 0, buf, sizeof(buf));
226         if(nr < 4)
227                 langid = 0x0409;        // english
228         else
229                 langid = buf[3]<<8|buf[2];
230
231         nr=usbcmd(d, type, Rgetdesc, Dstr<<8|sid, langid, buf, sizeof(buf));
232         return mkstr(buf, nr);
233 }
234
235 int
236 loaddevdesc(Dev *d)
237 {
238         uchar buf[Ddevlen];
239         int nr;
240         int type;
241         Ep *ep0;
242
243         type = Rd2h|Rstd|Rdev;
244         nr = sizeof(buf);
245         memset(buf, 0, nr);
246         if((nr=usbcmd(d, type, Rgetdesc, Ddev<<8|0, 0, buf, nr)) < 0)
247                 return -1;
248         /*
249          * Several hubs are returning descriptors of 17 bytes, not 18.
250          * We accept them and leave number of configurations as zero.
251          * (a get configuration descriptor also fails for them!)
252          */
253         if(nr < Ddevlen){
254                 print("%s: %s: warning: device with short descriptor\n",
255                         argv0, d->dir);
256                 if(nr < Ddevlen-1){
257                         werrstr("short device descriptor (%d bytes)", nr);
258                         return -1;
259                 }
260         }
261         d->usb = emallocz(sizeof(Usbdev), 1);
262         ep0 = mkep(d->usb, 0);
263         ep0->dir = Eboth;
264         ep0->type = Econtrol;
265         ep0->maxpkt = d->maxpkt = 8;            /* a default */
266         nr = parsedev(d, buf, nr);
267         if(nr >= 0){
268                 d->usb->vendor = loaddevstr(d, d->usb->vsid);
269                 if(strcmp(d->usb->vendor, "none") != 0){
270                         d->usb->product = loaddevstr(d, d->usb->psid);
271                         d->usb->serial = loaddevstr(d, d->usb->ssid);
272                 }
273         }
274         return nr;
275 }
276
277 int
278 configdev(Dev *d)
279 {
280         int i;
281
282         if(d->dfd < 0)
283                 opendevdata(d, ORDWR);
284         if(d->dfd < 0)
285                 return -1;
286         if(loaddevdesc(d) < 0)
287                 return -1;
288         for(i = 0; i < d->usb->nconf; i++)
289                 if(loaddevconf(d, i) < 0)
290                         return -1;
291         return 0;
292 }
293
294 static void
295 closeconf(Conf *c)
296 {
297         int i;
298         int a;
299
300         if(c == nil)
301                 return;
302         for(i = 0; i < nelem(c->iface); i++)
303                 if(c->iface[i] != nil){
304                         for(a = 0; a < nelem(c->iface[i]->altc); a++)
305                                 free(c->iface[i]->altc[a]);
306                         free(c->iface[i]);
307                 }
308         free(c);
309 }
310
311 void
312 closedev(Dev *d)
313 {
314         int i;
315         Usbdev *ud;
316
317         if(d==nil || decref(d) != 0)
318                 return;
319         dprint(2, "%s: closedev %#p %s\n", argv0, d, d->dir);
320         if(d->free != nil)
321                 d->free(d->aux);
322         if(d->cfd >= 0)
323                 close(d->cfd);
324         if(d->dfd >= 0)
325                 close(d->dfd);
326         d->cfd = d->dfd = -1;
327         free(d->dir);
328         d->dir = nil;
329         free(d->hname);
330         d->hname = nil;
331         ud = d->usb;
332         d->usb = nil;
333         if(ud != nil){
334                 free(ud->vendor);
335                 free(ud->product);
336                 free(ud->serial);
337                 for(i = 0; i < nelem(ud->ep); i++)
338                         free(ud->ep[i]);
339                 for(i = 0; i < nelem(ud->ddesc); i++)
340                         free(ud->ddesc[i]);
341
342                 for(i = 0; i < nelem(ud->conf); i++)
343                         closeconf(ud->conf[i]);
344                 free(ud);
345         }
346         free(d);
347 }
348
349 static char*
350 reqstr(int type, int req)
351 {
352         char *s;
353         static char* ds[] = { "dev", "if", "ep", "oth" };
354         static char buf[40];
355
356         if(type&Rd2h)
357                 s = seprint(buf, buf+sizeof(buf), "d2h");
358         else
359                 s = seprint(buf, buf+sizeof(buf), "h2d");
360         if(type&Rclass)
361                 s = seprint(s, buf+sizeof(buf), "|cls");
362         else if(type&Rvendor)
363                 s = seprint(s, buf+sizeof(buf), "|vnd");
364         else
365                 s = seprint(s, buf+sizeof(buf), "|std");
366         s = seprint(s, buf+sizeof(buf), "|%s", ds[type&3]);
367
368         switch(req){
369         case Rgetstatus: s = seprint(s, buf+sizeof(buf), " getsts"); break;
370         case Rclearfeature: s = seprint(s, buf+sizeof(buf), " clrfeat"); break;
371         case Rsetfeature: s = seprint(s, buf+sizeof(buf), " setfeat"); break;
372         case Rsetaddress: s = seprint(s, buf+sizeof(buf), " setaddr"); break;
373         case Rgetdesc: s = seprint(s, buf+sizeof(buf), " getdesc"); break;
374         case Rsetdesc: s = seprint(s, buf+sizeof(buf), " setdesc"); break;
375         case Rgetconf: s = seprint(s, buf+sizeof(buf), " getcnf"); break;
376         case Rsetconf: s = seprint(s, buf+sizeof(buf), " setcnf"); break;
377         case Rgetiface: s = seprint(s, buf+sizeof(buf), " getif"); break;
378         case Rsetiface: s = seprint(s, buf+sizeof(buf), " setif"); break;
379         }
380         USED(s);
381         return buf;
382 }
383
384 static int
385 cmdreq(Dev *d, int type, int req, int value, int index, uchar *data, int count)
386 {
387         int ndata, n;
388         uchar *wp;
389         uchar buf[8];
390         char *hd, *rs;
391
392         assert(d != nil);
393         if(data == nil){
394                 wp = buf;
395                 ndata = 0;
396         }else{
397                 ndata = count;
398                 wp = emallocz(8+ndata, 0);
399         }
400         wp[0] = type;
401         wp[1] = req;
402         PUT2(wp+2, value);
403         PUT2(wp+4, index);
404         PUT2(wp+6, count);
405         if(data != nil)
406                 memmove(wp+8, data, ndata);
407         if(usbdebug>2){
408                 hd = hexstr(wp, ndata+8);
409                 rs = reqstr(type, req);
410                 fprint(2, "%s: %s val %d|%d idx %d cnt %d out[%d] %s\n",
411                         d->dir, rs, value>>8, value&0xFF,
412                         index, count, ndata+8, hd);
413                 free(hd);
414         }
415         n = write(d->dfd, wp, 8+ndata);
416         if(wp != buf)
417                 free(wp);
418         if(n < 0)
419                 return -1;
420         if(n != 8+ndata){
421                 dprint(2, "%s: cmd: short write: %d\n", argv0, n);
422                 return -1;
423         }
424         return n;
425 }
426
427 static int
428 cmdrep(Dev *d, void *buf, int nb)
429 {
430         char *hd;
431
432         nb = read(d->dfd, buf, nb);
433         if(nb >0 && usbdebug > 2){
434                 hd = hexstr(buf, nb);
435                 fprint(2, "%s: in[%d] %s\n", d->dir, nb, hd);
436                 free(hd);
437         }
438         return nb;
439 }
440
441 int
442 usbcmd(Dev *d, int type, int req, int value, int index, uchar *data, int count)
443 {
444         int i, r, nerr;
445         char err[64];
446
447         /*
448          * Some devices do not respond to commands some times.
449          * Others even report errors but later work just fine. Retry.
450          */
451         r = -1;
452         *err = 0;
453         for(i = nerr = 0; i < Uctries; i++){
454                 if(type & Rd2h)
455                         r = cmdreq(d, type, req, value, index, nil, count);
456                 else
457                         r = cmdreq(d, type, req, value, index, data, count);
458                 if(r > 0){
459                         if((type & Rd2h) == 0)
460                                 break;
461                         r = cmdrep(d, data, count);
462                         if(r > 0)
463                                 break;
464                         if(r == 0)
465                                 werrstr("no data from device");
466                 }
467                 nerr++;
468                 if(*err == 0)
469                         rerrstr(err, sizeof(err));
470                 sleep(Ucdelay);
471         }
472         if(r > 0 && i >= 2)
473                 /* let the user know the device is not in good shape */
474                 fprint(2, "%s: usbcmd: %s: required %d attempts (%s)\n",
475                         argv0, d->dir, i, err);
476         return r;
477 }
478
479 int
480 unstall(Dev *dev, Dev *ep, int dir)
481 {
482         int r;
483
484         if(dir == Ein)
485                 dir = 0x80;
486         else
487                 dir = 0;
488         r = Rh2d|Rstd|Rep;
489         if(usbcmd(dev, r, Rclearfeature, Fhalt, (ep->id&0xF)|dir, nil, 0)<0){
490                 werrstr("unstall: %s: %r", ep->dir);
491                 return -1;
492         }
493         if(devctl(ep, "clrhalt") < 0){
494                 werrstr("clrhalt: %s: %r", ep->dir);
495                 return -1;
496         }
497         return 0;
498 }
499
500 /*
501  * To be sure it uses a single write.
502  */
503 int
504 devctl(Dev *dev, char *fmt, ...)
505 {
506         char buf[128];
507         va_list arg;
508         char *e;
509
510         va_start(arg, fmt);
511         e = vseprint(buf, buf+sizeof(buf), fmt, arg);
512         va_end(arg);
513         return write(dev->cfd, buf, e-buf);
514 }
515
516 Dev *
517 getdev(char *devid)
518 {
519         char buf[40], *p;
520         Dev *d;
521
522         if(devid[0] == '/' || devid[0] == '#'){
523                 snprint(buf, sizeof buf, "%s", devid);
524                 p = strrchr(buf, '/');
525                 if(p != nil){
526                         p = strrchr(buf, ':');
527                         if(p != nil)
528                                 *p = 0;
529                 }
530         } else {
531                 p = nil;
532                 snprint(buf, sizeof buf, "/dev/usb/ep%ld.0", strtol(devid, &p, 10));
533                 if(*p != ':')
534                         p = nil;
535         }
536
537         d = opendev(buf);
538         if(d == nil)
539                 return nil;
540         if(configdev(d) < 0){
541                 closedev(d);
542                 return nil;
543         }
544
545         if(p == nil){
546                 snprint(buf, sizeof buf, ":%d", d->id);
547                 p = buf;
548         }
549         d->hname = strdup(p+1);
550
551         return d;
552 }