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