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