]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/nusb/lib/parse.c
nusb: handle 0 csp value on iface, remove unneeded configdev() calls after getdev()
[plan9front.git] / sys / src / cmd / nusb / lib / parse.c
1 #include <u.h>
2 #include <libc.h>
3 #include <thread.h>
4 #include <bio.h>
5 #include "usb.h"
6
7 int
8 parsedev(Dev *xd, uchar *b, int n)
9 {
10         Usbdev *d;
11         DDev *dd;
12         char *hd;
13
14         d = xd->usb;
15         assert(d != nil);
16         dd = (DDev*)b;
17         if(usbdebug>1){
18                 hd = hexstr(b, Ddevlen);
19                 fprint(2, "%s: parsedev %s: %s\n", argv0, xd->dir, hd);
20                 free(hd);
21         }
22         if(dd->bLength < Ddevlen){
23                 werrstr("short dev descr. (%d < %d)", dd->bLength, Ddevlen);
24                 return -1;
25         }
26         if(dd->bDescriptorType != Ddev){
27                 werrstr("%d is not a dev descriptor", dd->bDescriptorType);
28                 return -1;
29         }
30         d->csp = CSP(dd->bDevClass, dd->bDevSubClass, dd->bDevProtocol);
31         d->ep[0]->maxpkt = xd->maxpkt = dd->bMaxPacketSize0;
32         d->class = dd->bDevClass;
33         d->nconf = dd->bNumConfigurations;
34         if(d->nconf == 0)
35                 dprint(2, "%s: %s: no configurations\n", argv0, xd->dir);
36         d->vid = GET2(dd->idVendor);
37         d->did = GET2(dd->idProduct);
38         d->dno = GET2(dd->bcdDev);
39         d->vsid = dd->iManufacturer;
40         d->psid = dd->iProduct;
41         d->ssid = dd->iSerialNumber;
42         if(n > Ddevlen && usbdebug>1)
43                 fprint(2, "%s: %s: parsedev: %d bytes left",
44                         argv0, xd->dir, n - Ddevlen);
45         return Ddevlen;
46 }
47
48 static int
49 parseiface(Usbdev *d, Conf *c, uchar *b, int n, Iface **ipp, Altc **app)
50 {
51         int class, subclass, proto;
52         int ifid, altid;
53         DIface *dip;
54         Iface *ip;
55
56         assert(d != nil && c != nil);
57         if(n < Difacelen){
58                 werrstr("short interface descriptor");
59                 return -1;
60         }
61         dip = (DIface *)b;
62         ifid = dip->bInterfaceNumber;
63         if(ifid < 0 || ifid >= nelem(c->iface)){
64                 werrstr("bad interface number %d", ifid);
65                 return -1;
66         }
67         if(c->iface[ifid] == nil)
68                 c->iface[ifid] = emallocz(sizeof(Iface), 1);
69         ip = c->iface[ifid];
70         class = dip->bInterfaceClass;
71         subclass = dip->bInterfaceSubClass;
72         proto = dip->bInterfaceProtocol;
73         ip->csp = CSP(class, subclass, proto);
74         if(ip->csp == 0)
75                 ip->csp = d->csp;
76         if(d->csp == 0)                         /* use csp from 1st iface */
77                 d->csp = ip->csp;               /* if device has none */
78         if(d->class == 0)
79                 d->class = class;
80         ip->id = ifid;
81         if(c == d->conf[0] && ifid == 0)        /* ep0 was already there */
82                 d->ep[0]->iface = ip;
83         altid = dip->bAlternateSetting;
84         if(altid < 0 || altid >= nelem(ip->altc)){
85                 werrstr("bad alternate conf. number %d", altid);
86                 return -1;
87         }
88         if(ip->altc[altid] == nil)
89                 ip->altc[altid] = emallocz(sizeof(Altc), 1);
90         *ipp = ip;
91         *app = ip->altc[altid];
92         return Difacelen;
93 }
94
95 extern Ep* mkep(Usbdev *, int);
96
97 static int
98 parseendpt(Usbdev *d, Conf *c, Iface *ip, Altc *altc, uchar *b, int n, Ep **epp)
99 {
100         int i, dir, epid;
101         Ep *ep;
102         DEp *dep;
103
104         assert(d != nil && c != nil && ip != nil && altc != nil);
105         if(n < Deplen){
106                 werrstr("short endpoint descriptor");
107                 return -1;
108         }
109         dep = (DEp *)b;
110         altc->attrib = dep->bmAttributes;       /* here? */
111         altc->interval = dep->bInterval;
112
113         epid = dep->bEndpointAddress & 0xF;
114         assert(epid < nelem(d->ep));
115         if(dep->bEndpointAddress & 0x80)
116                 dir = Ein;
117         else
118                 dir = Eout;
119         ep = d->ep[epid];
120         if(ep == nil){
121                 ep = mkep(d, epid);
122                 ep->dir = dir;
123         }else if((ep->addr & 0x80) != (dep->bEndpointAddress & 0x80))
124                 ep->dir = Eboth;
125         ep->maxpkt = GET2(dep->wMaxPacketSize);
126         ep->ntds = 1 + ((ep->maxpkt >> 11) & 3);
127         ep->maxpkt &= 0x7FF;
128         ep->addr = dep->bEndpointAddress;
129         ep->type = dep->bmAttributes & 0x03;
130         ep->isotype = (dep->bmAttributes>>2) & 0x03;
131         ep->conf = c;
132         ep->iface = ip;
133         for(i = 0; i < nelem(ip->ep); i++)
134                 if(ip->ep[i] == nil)
135                         break;
136         if(i == nelem(ip->ep)){
137                 werrstr("parseendpt: bug: too many end points on interface "
138                         "with csp %#lux", ip->csp);
139                 fprint(2, "%s: %r\n", argv0);
140                 return -1;
141         }
142         *epp = ip->ep[i] = ep;
143         return Dep;
144 }
145
146 static char*
147 dname(int dtype)
148 {
149         switch(dtype){
150         case Ddev:      return "device";
151         case Dconf:     return "config";
152         case Dstr:      return "string";
153         case Diface:    return "interface";
154         case Dep:       return "endpoint";
155         case Dreport:   return "report";
156         case Dphysical: return "phys";
157         default:        return "desc";
158         }
159 }
160
161 int
162 parsedesc(Usbdev *d, Conf *c, uchar *b, int n)
163 {
164         int     len, nd, tot;
165         Iface   *ip;
166         Ep      *ep;
167         Altc    *altc;
168         char    *hd;
169
170         assert(d != nil && c != nil);
171         tot = 0;
172         ip = nil;
173         ep = nil;
174         altc = nil;
175         for(nd = 0; nd < nelem(d->ddesc); nd++)
176                 if(d->ddesc[nd] == nil)
177                         break;
178
179         while(n > 2 && b[0] != 0 && b[0] <= n){
180                 len = b[0];
181                 if(usbdebug>1){
182                         hd = hexstr(b, len);
183                         fprint(2, "%s:\t\tparsedesc %s %x[%d] %s\n",
184                                 argv0, dname(b[1]), b[1], b[0], hd);
185                         free(hd);
186                 }
187                 switch(b[1]){
188                 case Ddev:
189                 case Dconf:
190                         werrstr("unexpected descriptor %d", b[1]);
191                         ddprint(2, "%s\tparsedesc: %r", argv0);
192                         break;
193                 case Diface:
194                         if(parseiface(d, c, b, n, &ip, &altc) < 0){
195                                 ddprint(2, "%s\tparsedesc: %r\n", argv0);
196                                 return -1;
197                         }
198                         break;
199                 case Dep:
200                         if(ip == nil || altc == nil){
201                                 werrstr("unexpected endpoint descriptor");
202                                 break;
203                         }
204                         if(parseendpt(d, c, ip, altc, b, n, &ep) < 0){
205                                 ddprint(2, "%s\tparsedesc: %r\n", argv0);
206                                 return -1;
207                         }
208                         break;
209                 default:
210                         if(nd == nelem(d->ddesc)){
211                                 fprint(2, "%s: parsedesc: too many "
212                                         "device-specific descriptors for device"
213                                         " %s %s\n",
214                                         argv0, d->vendor, d->product);
215                                 break;
216                         }
217                         d->ddesc[nd] = emallocz(sizeof(Desc)+b[0], 0);
218                         d->ddesc[nd]->iface = ip;
219                         d->ddesc[nd]->ep = ep;
220                         d->ddesc[nd]->altc = altc;
221                         d->ddesc[nd]->conf = c;
222                         memmove(&d->ddesc[nd]->data, b, len);
223                         ++nd;
224                 }
225                 n -= len;
226                 b += len;
227                 tot += len;
228         }
229         return tot;
230 }
231
232 int
233 parseconf(Usbdev *d, Conf *c, uchar *b, int n)
234 {
235         DConf* dc;
236         int     l;
237         int     nr;
238         char    *hd;
239
240         assert(d != nil && c != nil);
241         dc = (DConf*)b;
242         if(usbdebug>1){
243                 hd = hexstr(b, Dconflen);
244                 fprint(2, "%s:\tparseconf  %s\n", argv0, hd);
245                 free(hd);
246         }
247         if(dc->bLength < Dconflen){
248                 werrstr("short configuration descriptor");
249                 return -1;
250         }
251         if(dc->bDescriptorType != Dconf){
252                 werrstr("not a configuration descriptor");
253                 return -1;
254         }
255         c->cval = dc->bConfigurationValue;
256         c->attrib = dc->bmAttributes;
257         c->milliamps = dc->MaxPower*2;
258         l = GET2(dc->wTotalLength);
259         if(n < l){
260                 werrstr("truncated configuration info");
261                 return -1;
262         }
263         n -= Dconflen;
264         b += Dconflen;
265         nr = 0;
266         if(n > 0 && (nr=parsedesc(d, c, b, n)) < 0)
267                 return -1;
268         n -= nr;
269         if(n > 0 && usbdebug>1)
270                 fprint(2, "%s:\tparseconf: %d bytes left\n", argv0, n);
271         return l;
272 }