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