Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
846 | giacomo | 1 | #include <linuxcomp.h> |
2 | #include <linux/usb.h> |
||
3 | #include <linux/module.h> |
||
4 | #include <linux/init.h> |
||
5 | #include <linux/slab.h> |
||
6 | #include <asm/byteorder.h> |
||
7 | |||
8 | |||
9 | #define USB_MAXALTSETTING 128 /* Hard limit */ |
||
10 | #define USB_MAXENDPOINTS 30 /* Hard limit */ |
||
11 | |||
12 | /* these maximums are arbitrary */ |
||
13 | #define USB_MAXCONFIG 8 |
||
14 | #define USB_MAXINTERFACES 32 |
||
15 | |||
16 | static int usb_parse_endpoint(struct usb_host_endpoint *endpoint, unsigned char *buffer, int size) |
||
17 | { |
||
18 | unsigned char *buffer0 = buffer; |
||
19 | struct usb_descriptor_header *header; |
||
20 | unsigned char *begin; |
||
21 | int numskipped; |
||
22 | |||
23 | header = (struct usb_descriptor_header *)buffer; |
||
24 | if (header->bDescriptorType != USB_DT_ENDPOINT) { |
||
25 | warn("unexpected descriptor 0x%X, expecting endpoint, 0x%X", |
||
26 | header->bDescriptorType, USB_DT_ENDPOINT); |
||
27 | return -EINVAL; |
||
28 | } |
||
29 | |||
30 | if (header->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE) |
||
31 | memcpy(&endpoint->desc, buffer, USB_DT_ENDPOINT_AUDIO_SIZE); |
||
32 | else if (header->bLength >= USB_DT_ENDPOINT_SIZE) |
||
33 | memcpy(&endpoint->desc, buffer, USB_DT_ENDPOINT_SIZE); |
||
34 | else { |
||
35 | warn("invalid endpoint descriptor"); |
||
36 | return -EINVAL; |
||
37 | } |
||
38 | |||
39 | if ((endpoint->desc.bEndpointAddress & ~USB_ENDPOINT_DIR_MASK) >= 16) { |
||
40 | warn("invalid endpoint address 0x%X", |
||
41 | endpoint->desc.bEndpointAddress); |
||
42 | return -EINVAL; |
||
43 | } |
||
44 | |||
45 | le16_to_cpus(&endpoint->desc.wMaxPacketSize); |
||
46 | |||
47 | buffer += header->bLength; |
||
48 | size -= header->bLength; |
||
49 | |||
50 | /* Skip over any Class Specific or Vendor Specific descriptors */ |
||
51 | begin = buffer; |
||
52 | numskipped = 0; |
||
53 | while (size >= sizeof(struct usb_descriptor_header)) { |
||
54 | header = (struct usb_descriptor_header *)buffer; |
||
55 | |||
56 | /* If we find another "proper" descriptor then we're done */ |
||
57 | if ((header->bDescriptorType == USB_DT_ENDPOINT) || |
||
58 | (header->bDescriptorType == USB_DT_INTERFACE)) |
||
59 | break; |
||
60 | |||
61 | dbg("skipping descriptor 0x%X", header->bDescriptorType); |
||
62 | numskipped++; |
||
63 | |||
64 | buffer += header->bLength; |
||
65 | size -= header->bLength; |
||
66 | } |
||
67 | if (numskipped) { |
||
68 | dbg("skipped %d class/vendor specific endpoint descriptors", numskipped); |
||
69 | endpoint->extra = begin; |
||
70 | endpoint->extralen = buffer - begin; |
||
71 | } |
||
72 | |||
73 | return buffer - buffer0; |
||
74 | } |
||
75 | |||
76 | static void usb_release_intf(struct device *dev) |
||
77 | { |
||
78 | struct usb_interface *intf; |
||
79 | int j; |
||
80 | |||
81 | intf = to_usb_interface(dev); |
||
82 | |||
83 | if (intf->altsetting) { |
||
84 | for (j = 0; j < intf->num_altsetting; j++) { |
||
85 | struct usb_host_interface *as = &intf->altsetting[j]; |
||
86 | |||
87 | kfree(as->endpoint); |
||
88 | } |
||
89 | kfree(intf->altsetting); |
||
90 | } |
||
91 | kfree(intf); |
||
92 | } |
||
93 | |||
94 | static int usb_parse_interface(struct usb_host_config *config, unsigned char *buffer, int size) |
||
95 | { |
||
96 | unsigned char *buffer0 = buffer; |
||
97 | struct usb_interface_descriptor *d; |
||
98 | int inum, asnum; |
||
99 | struct usb_interface *interface; |
||
100 | struct usb_host_interface *ifp; |
||
101 | int len, numskipped; |
||
102 | struct usb_descriptor_header *header; |
||
103 | unsigned char *begin; |
||
104 | int i, retval; |
||
105 | |||
106 | d = (struct usb_interface_descriptor *) buffer; |
||
107 | if (d->bDescriptorType != USB_DT_INTERFACE) { |
||
108 | warn("unexpected descriptor 0x%X, expecting interface, 0x%X", |
||
109 | d->bDescriptorType, USB_DT_INTERFACE); |
||
110 | return -EINVAL; |
||
111 | } |
||
112 | |||
113 | inum = d->bInterfaceNumber; |
||
114 | if (inum >= config->desc.bNumInterfaces) { |
||
115 | |||
116 | /* Skip to the next interface descriptor */ |
||
117 | buffer += d->bLength; |
||
118 | size -= d->bLength; |
||
119 | while (size >= sizeof(struct usb_descriptor_header)) { |
||
120 | header = (struct usb_descriptor_header *) buffer; |
||
121 | |||
122 | if (header->bDescriptorType == USB_DT_INTERFACE) |
||
123 | break; |
||
124 | buffer += header->bLength; |
||
125 | size -= header->bLength; |
||
126 | } |
||
127 | return buffer - buffer0; |
||
128 | } |
||
129 | |||
130 | interface = config->interface[inum]; |
||
131 | asnum = d->bAlternateSetting; |
||
132 | if (asnum >= interface->num_altsetting) { |
||
133 | warn("invalid alternate setting %d for interface %d", |
||
134 | asnum, inum); |
||
135 | return -EINVAL; |
||
136 | } |
||
137 | |||
138 | ifp = &interface->altsetting[asnum]; |
||
139 | if (ifp->desc.bLength) { |
||
140 | warn("duplicate descriptor for interface %d altsetting %d", |
||
141 | inum, asnum); |
||
142 | return -EINVAL; |
||
143 | } |
||
144 | memcpy(&ifp->desc, buffer, USB_DT_INTERFACE_SIZE); |
||
145 | |||
146 | buffer += d->bLength; |
||
147 | size -= d->bLength; |
||
148 | |||
149 | /* Skip over any Class Specific or Vendor Specific descriptors */ |
||
150 | begin = buffer; |
||
151 | numskipped = 0; |
||
152 | while (size >= sizeof(struct usb_descriptor_header)) { |
||
153 | header = (struct usb_descriptor_header *)buffer; |
||
154 | |||
155 | /* If we find another "proper" descriptor then we're done */ |
||
156 | if ((header->bDescriptorType == USB_DT_INTERFACE) || |
||
157 | (header->bDescriptorType == USB_DT_ENDPOINT)) |
||
158 | break; |
||
159 | |||
160 | dbg("skipping descriptor 0x%X", header->bDescriptorType); |
||
161 | numskipped++; |
||
162 | |||
163 | buffer += header->bLength; |
||
164 | size -= header->bLength; |
||
165 | } |
||
166 | if (numskipped) { |
||
167 | dbg("skipped %d class/vendor specific interface descriptors", numskipped); |
||
168 | ifp->extra = begin; |
||
169 | ifp->extralen = buffer - begin; |
||
170 | } |
||
171 | |||
172 | if (ifp->desc.bNumEndpoints > USB_MAXENDPOINTS) { |
||
173 | warn("too many endpoints for interface %d altsetting %d", |
||
174 | inum, asnum); |
||
175 | return -EINVAL; |
||
176 | } |
||
177 | |||
178 | len = ifp->desc.bNumEndpoints * sizeof(struct usb_host_endpoint); |
||
179 | ifp->endpoint = kmalloc(len, GFP_KERNEL); |
||
180 | if (!ifp->endpoint) { |
||
181 | err("out of memory"); |
||
182 | return -ENOMEM; |
||
183 | } |
||
184 | memset(ifp->endpoint, 0, len); |
||
185 | |||
186 | for (i = 0; i < ifp->desc.bNumEndpoints; i++) { |
||
187 | if (size < USB_DT_ENDPOINT_SIZE) { |
||
188 | warn("ran out of descriptors while parsing endpoints"); |
||
189 | return -EINVAL; |
||
190 | } |
||
191 | |||
192 | retval = usb_parse_endpoint(ifp->endpoint + i, buffer, size); |
||
193 | if (retval < 0) |
||
194 | return retval; |
||
195 | |||
196 | buffer += retval; |
||
197 | size -= retval; |
||
198 | } |
||
199 | |||
200 | return buffer - buffer0; |
||
201 | } |
||
202 | |||
203 | int usb_parse_configuration(struct usb_host_config *config, char *buffer, int size) |
||
204 | { |
||
205 | int nintf, nintf_orig; |
||
206 | int i, j; |
||
207 | struct usb_interface *interface; |
||
208 | char *buffer2; |
||
209 | int size2; |
||
210 | struct usb_descriptor_header *header; |
||
211 | int numskipped, len; |
||
212 | char *begin; |
||
213 | int retval; |
||
214 | |||
215 | memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE); |
||
216 | if (config->desc.bDescriptorType != USB_DT_CONFIG || |
||
217 | config->desc.bLength < USB_DT_CONFIG_SIZE) { |
||
218 | warn("invalid configuration descriptor"); |
||
219 | return -EINVAL; |
||
220 | } |
||
221 | config->desc.wTotalLength = size; |
||
222 | |||
223 | nintf = nintf_orig = config->desc.bNumInterfaces; |
||
224 | if (nintf > USB_MAXINTERFACES) { |
||
225 | warn("too many interfaces (%d max %d)", |
||
226 | nintf, USB_MAXINTERFACES); |
||
227 | config->desc.bNumInterfaces = nintf = USB_MAXINTERFACES; |
||
228 | } |
||
229 | |||
230 | for (i = 0; i < nintf; ++i) { |
||
231 | interface = config->interface[i] = |
||
232 | kmalloc(sizeof(struct usb_interface), GFP_KERNEL); |
||
233 | dbg("kmalloc IF %p, numif %i", interface, i); |
||
234 | if (!interface) { |
||
235 | err("out of memory"); |
||
236 | return -ENOMEM; |
||
237 | } |
||
238 | memset(interface, 0, sizeof(struct usb_interface)); |
||
239 | interface->dev.release = usb_release_intf; |
||
240 | device_initialize(&interface->dev); |
||
241 | } |
||
242 | |||
243 | /* Go through the descriptors, checking their length and counting the |
||
244 | * number of altsettings for each interface */ |
||
245 | buffer2 = buffer; |
||
246 | size2 = size; |
||
247 | j = 0; |
||
248 | while (size2 >= sizeof(struct usb_descriptor_header)) { |
||
249 | header = (struct usb_descriptor_header *) buffer2; |
||
250 | if ((header->bLength > size2) || (header->bLength < 2)) { |
||
251 | warn("invalid descriptor of length %d", header->bLength); |
||
252 | return -EINVAL; |
||
253 | } |
||
254 | |||
255 | if (header->bDescriptorType == USB_DT_INTERFACE) { |
||
256 | struct usb_interface_descriptor *d; |
||
257 | |||
258 | if (header->bLength < USB_DT_INTERFACE_SIZE) { |
||
259 | warn("invalid interface descriptor"); |
||
260 | return -EINVAL; |
||
261 | } |
||
262 | d = (struct usb_interface_descriptor *) header; |
||
263 | i = d->bInterfaceNumber; |
||
264 | if (i >= nintf_orig) { |
||
265 | warn("invalid interface number (%d/%d)", |
||
266 | i, nintf_orig); |
||
267 | return -EINVAL; |
||
268 | } |
||
269 | if (i < nintf) |
||
270 | ++config->interface[i]->num_altsetting; |
||
271 | |||
272 | } else if ((header->bDescriptorType == USB_DT_DEVICE || |
||
273 | header->bDescriptorType == USB_DT_CONFIG) && j) { |
||
274 | warn("unexpected descriptor type 0x%X", header->bDescriptorType); |
||
275 | return -EINVAL; |
||
276 | } |
||
277 | |||
278 | j = 1; |
||
279 | buffer2 += header->bLength; |
||
280 | size2 -= header->bLength; |
||
281 | } |
||
282 | |||
283 | /* Allocate the altsetting arrays */ |
||
284 | for (i = 0; i < config->desc.bNumInterfaces; ++i) { |
||
285 | interface = config->interface[i]; |
||
286 | if (interface->num_altsetting > USB_MAXALTSETTING) { |
||
287 | warn("too many alternate settings for interface %d (%d max %d)\n", |
||
288 | i, interface->num_altsetting, USB_MAXALTSETTING); |
||
289 | return -EINVAL; |
||
290 | } |
||
291 | if (interface->num_altsetting == 0) { |
||
292 | warn("no alternate settings for interface %d", i); |
||
293 | return -EINVAL; |
||
294 | } |
||
295 | |||
296 | len = sizeof(*interface->altsetting) * interface->num_altsetting; |
||
297 | interface->altsetting = kmalloc(len, GFP_KERNEL); |
||
298 | if (!interface->altsetting) { |
||
299 | err("couldn't kmalloc interface->altsetting"); |
||
300 | return -ENOMEM; |
||
301 | } |
||
302 | memset(interface->altsetting, 0, len); |
||
303 | } |
||
304 | |||
305 | buffer += config->desc.bLength; |
||
306 | size -= config->desc.bLength; |
||
307 | |||
308 | /* Skip over any Class Specific or Vendor Specific descriptors */ |
||
309 | begin = buffer; |
||
310 | numskipped = 0; |
||
311 | while (size >= sizeof(struct usb_descriptor_header)) { |
||
312 | header = (struct usb_descriptor_header *)buffer; |
||
313 | |||
314 | /* If we find another "proper" descriptor then we're done */ |
||
315 | if ((header->bDescriptorType == USB_DT_ENDPOINT) || |
||
316 | (header->bDescriptorType == USB_DT_INTERFACE)) |
||
317 | break; |
||
318 | |||
319 | dbg("skipping descriptor 0x%X", header->bDescriptorType); |
||
320 | numskipped++; |
||
321 | |||
322 | buffer += header->bLength; |
||
323 | size -= header->bLength; |
||
324 | } |
||
325 | if (numskipped) { |
||
326 | dbg("skipped %d class/vendor specific configuration descriptors", numskipped); |
||
327 | config->extra = begin; |
||
328 | config->extralen = buffer - begin; |
||
329 | } |
||
330 | |||
331 | /* Parse all the interface/altsetting descriptors */ |
||
332 | while (size >= sizeof(struct usb_descriptor_header)) { |
||
333 | retval = usb_parse_interface(config, buffer, size); |
||
334 | if (retval < 0) |
||
335 | return retval; |
||
336 | |||
337 | buffer += retval; |
||
338 | size -= retval; |
||
339 | } |
||
340 | |||
341 | /* Check for missing altsettings */ |
||
342 | for (i = 0; i < nintf; ++i) { |
||
343 | interface = config->interface[i]; |
||
344 | for (j = 0; j < interface->num_altsetting; ++j) { |
||
345 | if (!interface->altsetting[j].desc.bLength) { |
||
346 | warn("missing altsetting %d for interface %d", j, i); |
||
347 | return -EINVAL; |
||
348 | } |
||
349 | } |
||
350 | } |
||
351 | |||
352 | return size; |
||
353 | } |
||
354 | |||
355 | // hub-only!! ... and only exported for reset/reinit path. |
||
356 | // otherwise used internally on disconnect/destroy path |
||
357 | void usb_destroy_configuration(struct usb_device *dev) |
||
358 | { |
||
359 | int c, i; |
||
360 | |||
361 | if (!dev->config) |
||
362 | return; |
||
363 | |||
364 | if (dev->rawdescriptors) { |
||
365 | for (i = 0; i < dev->descriptor.bNumConfigurations; i++) |
||
366 | kfree(dev->rawdescriptors[i]); |
||
367 | |||
368 | kfree(dev->rawdescriptors); |
||
369 | } |
||
370 | |||
371 | for (c = 0; c < dev->descriptor.bNumConfigurations; c++) { |
||
372 | struct usb_host_config *cf = &dev->config[c]; |
||
373 | |||
374 | for (i = 0; i < cf->desc.bNumInterfaces; i++) { |
||
375 | struct usb_interface *ifp = cf->interface[i]; |
||
376 | |||
377 | if (ifp) |
||
378 | put_device(&ifp->dev); |
||
379 | } |
||
380 | } |
||
381 | kfree(dev->config); |
||
382 | } |
||
383 | |||
384 | |||
385 | // hub-only!! ... and only in reset path, or usb_new_device() |
||
386 | // (used by real hubs and virtual root hubs) |
||
387 | int usb_get_configuration(struct usb_device *dev) |
||
388 | { |
||
389 | int ncfg = dev->descriptor.bNumConfigurations; |
||
390 | int result; |
||
391 | unsigned int cfgno, length; |
||
392 | unsigned char *buffer; |
||
393 | unsigned char *bigbuffer; |
||
394 | struct usb_config_descriptor *desc; |
||
395 | |||
396 | if (ncfg > USB_MAXCONFIG) { |
||
397 | warn("too many configurations (%d max %d)", |
||
398 | ncfg, USB_MAXCONFIG); |
||
399 | dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG; |
||
400 | } |
||
401 | |||
402 | if (ncfg < 1) { |
||
403 | warn("no configurations"); |
||
404 | return -EINVAL; |
||
405 | } |
||
406 | |||
407 | length = ncfg * sizeof(struct usb_host_config); |
||
408 | dev->config = kmalloc(length, GFP_KERNEL); |
||
409 | if (!dev->config) { |
||
410 | err("out of memory"); |
||
411 | return -ENOMEM; |
||
412 | } |
||
413 | memset(dev->config, 0, length); |
||
414 | |||
415 | length = ncfg * sizeof(char *); |
||
416 | dev->rawdescriptors = kmalloc(length, GFP_KERNEL); |
||
417 | if (!dev->rawdescriptors) { |
||
418 | err("out of memory"); |
||
419 | return -ENOMEM; |
||
420 | } |
||
421 | memset(dev->rawdescriptors, 0, length); |
||
422 | |||
423 | buffer = kmalloc(8, GFP_KERNEL); |
||
424 | if (!buffer) { |
||
425 | err("unable to allocate memory for configuration descriptors"); |
||
426 | return -ENOMEM; |
||
427 | } |
||
428 | desc = (struct usb_config_descriptor *)buffer; |
||
429 | |||
430 | for (cfgno = 0; cfgno < ncfg; cfgno++) { |
||
431 | /* We grab the first 8 bytes so we know how long the whole */ |
||
432 | /* configuration is */ |
||
433 | result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 8); |
||
434 | if (result < 8) { |
||
435 | if (result < 0) |
||
436 | err("unable to get descriptor"); |
||
437 | else { |
||
438 | warn("config descriptor too short (expected %i, got %i)", 8, result); |
||
439 | result = -EINVAL; |
||
440 | } |
||
441 | goto err; |
||
442 | } |
||
443 | |||
444 | /* Get the full buffer */ |
||
445 | length = max((int) le16_to_cpu(desc->wTotalLength), USB_DT_CONFIG_SIZE); |
||
446 | |||
447 | bigbuffer = kmalloc(length, GFP_KERNEL); |
||
448 | if (!bigbuffer) { |
||
449 | err("unable to allocate memory for configuration descriptors"); |
||
450 | result = -ENOMEM; |
||
451 | goto err; |
||
452 | } |
||
453 | |||
454 | /* Now that we know the length, get the whole thing */ |
||
455 | result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, length); |
||
456 | if (result < 0) { |
||
457 | err("couldn't get all of config descriptors"); |
||
458 | kfree(bigbuffer); |
||
459 | goto err; |
||
460 | } |
||
461 | |||
462 | if (result < length) { |
||
463 | err("config descriptor too short (expected %i, got %i)", length, result); |
||
464 | result = -EINVAL; |
||
465 | kfree(bigbuffer); |
||
466 | goto err; |
||
467 | } |
||
468 | |||
469 | dev->rawdescriptors[cfgno] = bigbuffer; |
||
470 | |||
471 | result = usb_parse_configuration(&dev->config[cfgno], bigbuffer, length); |
||
472 | if (result > 0) |
||
473 | dbg("descriptor data left"); |
||
474 | else if (result < 0) { |
||
475 | ++cfgno; |
||
476 | goto err; |
||
477 | } |
||
478 | } |
||
479 | |||
480 | kfree(buffer); |
||
481 | return 0; |
||
482 | err: |
||
483 | kfree(buffer); |
||
484 | dev->descriptor.bNumConfigurations = cfgno; |
||
485 | return result; |
||
486 | } |
||
487 |