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>
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.
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.
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
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
33 int fpi_parse_descriptor(unsigned char *source, char *descriptor, void *dest)
35 unsigned char *sp = source, *dp = dest;
40 for (cp = descriptor; *cp; cp++) {
42 case 'b': /* 8-bit byte */
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;
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;
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;
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;
69 static int parse_endpoint(struct usb_endpoint_descriptor *endpoint,
70 unsigned char *buffer, int size)
72 struct usb_descriptor_header header;
77 fpi_parse_descriptor(buffer, "bb", &header);
79 /* Everything should be fine being passed into here, but we sanity */
81 if (header.bLength > size) {
82 fp_err("ran out of descriptors parsing");
86 if (header.bDescriptorType != USB_DT_ENDPOINT) {
87 fp_err("unexpected descriptor %x (expected %x)",
88 header.bDescriptorType, USB_DT_ENDPOINT);
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);
97 buffer += header.bLength;
98 size -= header.bLength;
99 parsed += header.bLength;
101 /* Skip over the rest of the Class Specific or Vendor Specific */
104 while (size >= DESC_HEADER_LENGTH) {
105 fpi_parse_descriptor(buffer, "bb", &header);
107 if (header.bLength < 2) {
108 fp_err("invalid descriptor length %d", header.bLength);
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))
119 fp_dbg("skipping descriptor %x", header.bDescriptorType);
120 buffer += header.bLength;
121 size -= header.bLength;
122 parsed += header.bLength;
125 /* Copy any unknown descriptors into a storage area for drivers */
127 len = (int)(buffer - begin);
129 endpoint->extra = NULL;
130 endpoint->extralen = 0;
134 endpoint->extra = malloc(len);
135 if (!endpoint->extra) {
136 endpoint->extralen = 0;
140 memcpy(endpoint->extra, begin, len);
141 endpoint->extralen = len;
146 static int parse_interface(struct usb_interface *interface,
147 unsigned char *buffer, int size)
154 struct usb_descriptor_header header;
155 struct usb_interface_descriptor *ifp;
156 unsigned char *begin;
158 interface->num_altsetting = 0;
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)
167 ifp = interface->altsetting + interface->num_altsetting;
168 interface->num_altsetting++;
169 fpi_parse_descriptor(buffer, "bbbbbbbbb", ifp);
171 /* Skip over the interface */
172 buffer += ifp->bLength;
173 parsed += ifp->bLength;
174 size -= ifp->bLength;
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);
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))
193 buffer += header.bLength;
194 parsed += header.bLength;
195 size -= header.bLength;
198 /* Copy any unknown descriptors into a storage area for */
199 /* drivers to later parse */
200 len = (int)(buffer - begin);
205 ifp->extra = malloc(len);
208 /* FIXME will leak memory */
211 memcpy(ifp->extra, begin, len);
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)))
222 if (ifp->bNumEndpoints > USB_MAXENDPOINTS) {
223 fp_err("too many endpoints (%d)", ifp->bNumEndpoints);
224 /* FIXME will leak memory */
228 if (ifp->bNumEndpoints > 0) {
229 tmp = ifp->bNumEndpoints * sizeof(struct usb_endpoint_descriptor);
230 ifp->endpoint = malloc(tmp);
232 /* FIXME will leak memory? */
235 memset(ifp->endpoint, 0, tmp);
236 for (i = 0; i < ifp->bNumEndpoints; i++) {
237 fpi_parse_descriptor(buffer, "bb", &header);
239 if (header.bLength > size) {
240 fp_err("ran out of descriptors parsing");
241 /* FIXME will leak memory */
245 r = parse_endpoint(ifp->endpoint + i, buffer, size);
247 /* FIXME will leak memory */
255 ifp->endpoint = NULL;
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)
268 int fpi_parse_configuration(struct usb_config_descriptor *config,
269 unsigned char *buffer)
275 struct usb_descriptor_header header;
277 fpi_parse_descriptor(buffer, "bbwbbbbb", config);
278 size = config->wTotalLength;
280 if (config->bNumInterfaces > USB_MAXINTERFACES) {
281 fp_err("too many interfaces (%d)", config->bNumInterfaces);
285 tmp = config->bNumInterfaces * sizeof(struct usb_interface);
286 config->interface = malloc(tmp);
287 if (!config->interface)
290 memset(config->interface, 0, tmp);
291 buffer += config->bLength;
292 size -= config->bLength;
294 config->extra = NULL;
295 config->extralen = 0;
297 for (i = 0; i < config->bNumInterfaces; i++) {
299 unsigned char *begin;
301 /* Skip over the rest of the Class Specific or Vendor */
302 /* Specific descriptors */
304 while (size >= DESC_HEADER_LENGTH) {
305 fpi_parse_descriptor(buffer, "bb", &header);
307 if ((header.bLength > size) ||
308 (header.bLength < DESC_HEADER_LENGTH)) {
309 fp_err("invalid descriptor length of %d", header.bLength);
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))
320 fp_dbg("skipping descriptor 0x%x\n", header.bDescriptorType);
321 buffer += header.bLength;
322 size -= header.bLength;
325 /* Copy any unknown descriptors into a storage area for */
326 /* drivers to later parse */
327 len = (int)(buffer - begin);
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 */
338 memcpy(config->extra, begin, len);
339 config->extralen = len;
343 r = parse_interface(config->interface + i, buffer, size);