Compiler flags update
[platform/upstream/libusb.git] / libusb / descriptor.c
1 /*
2  * USB descriptor handling functions for libusb
3  * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
4  * Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include "libusbi.h"
25
26 #define DESC_HEADER_LENGTH              2
27 #define DEVICE_DESC_LENGTH              18
28 #define CONFIG_DESC_LENGTH              9
29 #define INTERFACE_DESC_LENGTH           9
30 #define ENDPOINT_DESC_LENGTH            7
31 #define ENDPOINT_AUDIO_DESC_LENGTH      9
32
33 int fpi_parse_descriptor(unsigned char *source, char *descriptor, void *dest)
34 {
35         unsigned char *sp = source, *dp = dest;
36         uint16_t w;
37         uint32_t d;
38         char *cp;
39
40         for (cp = descriptor; *cp; cp++) {
41                 switch (*cp) {
42                         case 'b':       /* 8-bit byte */
43                                 *dp++ = *sp++;
44                                 break;
45                         case 'w':       /* 16-bit word, convert from little endian to CPU */
46                                 w = (sp[1] << 8) | sp[0]; sp += 2;
47                                 dp += ((unsigned long)dp & 1);  /* Align to word boundary */
48                                 *((uint16_t *)dp) = w; dp += 2;
49                                 break;
50                         case 'd':       /* 32-bit dword, convert from little endian to CPU */
51                                 d = (sp[3] << 24) | (sp[2] << 16) | (sp[1] << 8) | sp[0]; sp += 4;
52                                 dp += ((unsigned long)dp & 2);  /* Align to dword boundary */
53                                 *((uint32_t *)dp) = d; dp += 4;
54                                 break;
55                         case 'W':       /* 16-bit word, keep CPU endianess */
56                                 dp += ((unsigned long)dp & 1);  /* Align to word boundary */
57                                 memcpy(dp, sp, 2); sp += 2; dp += 2;
58                                 break;
59                         case 'D':       /* 32-bit dword, keep CPU endianess */
60                                 dp += ((unsigned long)dp & 2);  /* Align to dword boundary */
61                                 memcpy(dp, sp, 4); sp += 4; dp += 4;
62                                 break;
63                 }
64         }
65
66         return sp - source;
67 }
68
69 static int parse_endpoint(struct usb_endpoint_descriptor *endpoint,
70         unsigned char *buffer, int size)
71 {
72         struct usb_descriptor_header header;
73         unsigned char *begin;
74         int parsed = 0;
75         int len;
76
77         fpi_parse_descriptor(buffer, "bb", &header);
78
79         /* Everything should be fine being passed into here, but we sanity */
80         /*  check JIC */
81         if (header.bLength > size) {
82                 fp_err("ran out of descriptors parsing");
83                 return -1;
84         }
85
86         if (header.bDescriptorType != USB_DT_ENDPOINT) {
87                 fp_err("unexpected descriptor %x (expected %x)",
88                         header.bDescriptorType, USB_DT_ENDPOINT);
89                 return parsed;
90         }
91
92         if (header.bLength >= ENDPOINT_AUDIO_DESC_LENGTH)
93                 fpi_parse_descriptor(buffer, "bbbbwbbb", endpoint);
94         else if (header.bLength >= ENDPOINT_DESC_LENGTH)
95                 fpi_parse_descriptor(buffer, "bbbbwb", endpoint);
96
97         buffer += header.bLength;
98         size -= header.bLength;
99         parsed += header.bLength;
100
101         /* Skip over the rest of the Class Specific or Vendor Specific */
102         /*  descriptors */
103         begin = buffer;
104         while (size >= DESC_HEADER_LENGTH) {
105                 fpi_parse_descriptor(buffer, "bb", &header);
106
107                 if (header.bLength < 2) {
108                         fp_err("invalid descriptor length %d", header.bLength);
109                         return -1;
110                 }
111
112                 /* If we find another "proper" descriptor then we're done  */
113                 if ((header.bDescriptorType == USB_DT_ENDPOINT) ||
114                                 (header.bDescriptorType == USB_DT_INTERFACE) ||
115                                 (header.bDescriptorType == USB_DT_CONFIG) ||
116                                 (header.bDescriptorType == USB_DT_DEVICE))
117                         break;
118
119                 fp_dbg("skipping descriptor %x", header.bDescriptorType);
120                 buffer += header.bLength;
121                 size -= header.bLength;
122                 parsed += header.bLength;
123         }
124
125         /* Copy any unknown descriptors into a storage area for drivers */
126         /*  to later parse */
127         len = (int)(buffer - begin);
128         if (!len) {
129                 endpoint->extra = NULL;
130                 endpoint->extralen = 0;
131                 return parsed;
132         }
133
134         endpoint->extra = malloc(len);
135         if (!endpoint->extra) {
136                 endpoint->extralen = 0;
137                 return parsed;
138         }
139
140         memcpy(endpoint->extra, begin, len);
141         endpoint->extralen = len;
142
143         return parsed;
144 }
145
146 static int parse_interface(struct usb_interface *interface,
147         unsigned char *buffer, int size)
148 {
149         int i;
150         int len;
151         int r;
152         int parsed = 0;
153         int tmp;
154         struct usb_descriptor_header header;
155         struct usb_interface_descriptor *ifp;
156         unsigned char *begin;
157
158         interface->num_altsetting = 0;
159
160         while (size >= INTERFACE_DESC_LENGTH) {
161                 interface->altsetting = realloc(interface->altsetting,
162                         sizeof(struct usb_interface_descriptor) *
163                         (interface->num_altsetting + 1));
164                 if (!interface->altsetting)
165                         return -1;
166
167                 ifp = interface->altsetting + interface->num_altsetting;
168                 interface->num_altsetting++;
169                 fpi_parse_descriptor(buffer, "bbbbbbbbb", ifp);
170
171                 /* Skip over the interface */
172                 buffer += ifp->bLength;
173                 parsed += ifp->bLength;
174                 size -= ifp->bLength;
175
176                 begin = buffer;
177
178                 /* Skip over any interface, class or vendor descriptors */
179                 while (size >= DESC_HEADER_LENGTH) {
180                         fpi_parse_descriptor(buffer, "bb", &header);
181                         if (header.bLength < 2) {
182                                 fp_err("invalid descriptor of length %d", header.bLength);
183                                 return -1;
184                         }
185
186                         /* If we find another "proper" descriptor then we're done */
187                         if ((header.bDescriptorType == USB_DT_INTERFACE) ||
188                                         (header.bDescriptorType == USB_DT_ENDPOINT) ||
189                                         (header.bDescriptorType == USB_DT_CONFIG) ||
190                                         (header.bDescriptorType == USB_DT_DEVICE))
191                                 break;
192
193                         buffer += header.bLength;
194                         parsed += header.bLength;
195                         size -= header.bLength;
196                 }
197
198                 /* Copy any unknown descriptors into a storage area for */
199                 /*  drivers to later parse */
200                 len = (int)(buffer - begin);
201                 if (!len) {
202                         ifp->extra = NULL;
203                         ifp->extralen = 0;
204                 } else {
205                         ifp->extra = malloc(len);
206                         if (!ifp->extra) {
207                                 ifp->extralen = 0;
208                                 /* FIXME will leak memory */
209                                 return -1;
210                         }
211                         memcpy(ifp->extra, begin, len);
212                         ifp->extralen = len;
213                 }
214
215                 /* Did we hit an unexpected descriptor? */
216                 fpi_parse_descriptor(buffer, "bb", &header);
217                 if ((size >= DESC_HEADER_LENGTH) &&
218                                 ((header.bDescriptorType == USB_DT_CONFIG) ||
219                                  (header.bDescriptorType == USB_DT_DEVICE)))
220                         return parsed;
221
222                 if (ifp->bNumEndpoints > USB_MAXENDPOINTS) {
223                         fp_err("too many endpoints (%d)", ifp->bNumEndpoints);
224                         /* FIXME will leak memory */
225                         return -1;
226                 }
227
228                 if (ifp->bNumEndpoints > 0) {
229                         tmp = ifp->bNumEndpoints * sizeof(struct usb_endpoint_descriptor);
230                         ifp->endpoint = malloc(tmp);
231                         if (!ifp->endpoint)
232                                 /* FIXME will leak memory? */
233                                 return -1;      
234
235                         memset(ifp->endpoint, 0, tmp);
236                         for (i = 0; i < ifp->bNumEndpoints; i++) {
237                                 fpi_parse_descriptor(buffer, "bb", &header);
238
239                                 if (header.bLength > size) {
240                                         fp_err("ran out of descriptors parsing");
241                                         /* FIXME will leak memory */
242                                         return -1;
243                                 }
244
245                                 r = parse_endpoint(ifp->endpoint + i, buffer, size);
246                                 if (r < 0)
247                                         /* FIXME will leak memory */
248                                         return r;
249
250                                 buffer += r;
251                                 parsed += r;
252                                 size -= r;
253                         }
254                 } else
255                         ifp->endpoint = NULL;
256
257                 /* We check to see if it's an alternate to this one */
258                 ifp = (struct usb_interface_descriptor *) buffer;
259                 if (size < USB_DT_INTERFACE_SIZE ||
260                                 ifp->bDescriptorType != USB_DT_INTERFACE ||
261                                 !ifp->bAlternateSetting)
262                         return parsed;
263         }
264
265         return parsed;
266 }
267
268 int fpi_parse_configuration(struct usb_config_descriptor *config,
269         unsigned char *buffer)
270 {
271         int i;
272         int r;
273         int size;
274         int tmp;
275         struct usb_descriptor_header header;
276
277         fpi_parse_descriptor(buffer, "bbwbbbbb", config);
278         size = config->wTotalLength;
279
280         if (config->bNumInterfaces > USB_MAXINTERFACES) {
281                 fp_err("too many interfaces (%d)", config->bNumInterfaces);
282                 return -1;
283         }
284
285         tmp = config->bNumInterfaces * sizeof(struct usb_interface);
286         config->interface = malloc(tmp);
287         if (!config->interface)
288                 return -1;      
289
290         memset(config->interface, 0, tmp);
291         buffer += config->bLength;
292         size -= config->bLength;
293
294         config->extra = NULL;
295         config->extralen = 0;
296
297         for (i = 0; i < config->bNumInterfaces; i++) {
298                 int len;
299                 unsigned char *begin;
300
301                 /* Skip over the rest of the Class Specific or Vendor */
302                 /*  Specific descriptors */
303                 begin = buffer;
304                 while (size >= DESC_HEADER_LENGTH) {
305                         fpi_parse_descriptor(buffer, "bb", &header);
306
307                         if ((header.bLength > size) ||
308                                         (header.bLength < DESC_HEADER_LENGTH)) {
309                                 fp_err("invalid descriptor length of %d", header.bLength);
310                                 return -1;
311                         }
312
313                         /* If we find another "proper" descriptor then we're done */
314                         if ((header.bDescriptorType == USB_DT_ENDPOINT) ||
315                                         (header.bDescriptorType == USB_DT_INTERFACE) ||
316                                         (header.bDescriptorType == USB_DT_CONFIG) ||
317                                         (header.bDescriptorType == USB_DT_DEVICE))
318                                 break;
319
320                         fp_dbg("skipping descriptor 0x%x\n", header.bDescriptorType);
321                         buffer += header.bLength;
322                         size -= header.bLength;
323                 }
324
325                 /* Copy any unknown descriptors into a storage area for */
326                 /*  drivers to later parse */
327                 len = (int)(buffer - begin);
328                 if (len) {
329                         /* FIXME: We should realloc and append here */
330                         if (!config->extralen) {
331                                 config->extra = malloc(len);
332                                 if (!config->extra) {
333                                         config->extralen = 0;
334                                         /* FIXME will leak memory */
335                                         return -1;
336                                 }
337
338                                 memcpy(config->extra, begin, len);
339                                 config->extralen = len;
340                         }
341                 }
342
343                 r = parse_interface(config->interface + i, buffer, size);
344                 if (r < 0)
345                         return r;
346
347                 buffer += r;
348                 size -= r;
349         }
350
351         return size;
352 }
353