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