xref: /dragonfly/lib/libusb/libusb20_desc.c (revision 63e03116)
1 /* $FreeBSD: head/lib/libusb/libusb20_desc.c 248236 2013-03-13 12:23:14Z hselasky $ */
2 /*-
3  * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #ifdef LIBUSB_GLOBAL_INCLUDE_FILE
28 #include LIBUSB_GLOBAL_INCLUDE_FILE
29 #else
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <time.h>
34 #include <sys/param.h>
35 #include <sys/queue.h>
36 #endif
37 
38 #include "libusb20.h"
39 #include "libusb20_desc.h"
40 #include "libusb20_int.h"
41 
42 static const uint32_t libusb20_me_encode_empty[2];	/* dummy */
43 
44 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_DEVICE_DESC);
45 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_ENDPOINT_DESC);
46 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_INTERFACE_DESC);
47 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONFIG_DESC);
48 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONTROL_SETUP);
49 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_ENDPT_COMP_DESC);
50 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_USB_20_DEVCAP_DESC);
51 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_USB_DEVCAP_DESC);
52 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_BOS_DESCRIPTOR);
53 
54 /*------------------------------------------------------------------------*
55  *	libusb20_parse_config_desc
56  *
57  * Return values:
58  * NULL: Out of memory.
59  * Else: A valid config structure pointer which must be passed to "free()"
60  *------------------------------------------------------------------------*/
61 struct libusb20_config *
62 libusb20_parse_config_desc(const void *config_desc)
63 {
64 	struct libusb20_config *lub_config;
65 	struct libusb20_interface *lub_interface;
66 	struct libusb20_interface *lub_alt_interface;
67 	struct libusb20_interface *last_if;
68 	struct libusb20_endpoint *lub_endpoint;
69 	struct libusb20_endpoint *last_ep;
70 
71 	struct libusb20_me_struct pcdesc;
72 	const uint8_t *ptr;
73 	uint32_t size;
74 	uint16_t niface_no_alt;
75 	uint16_t niface;
76 	uint16_t nendpoint;
77 	uint16_t iface_no;
78 
79 	ptr = config_desc;
80 	if (ptr[1] != LIBUSB20_DT_CONFIG) {
81 		return (NULL);		/* not config descriptor */
82 	}
83 	/*
84 	 * The first "bInterfaceNumber" should never have the value 0xff.
85 	 * Then it is corrupt.
86 	 */
87 	niface_no_alt = 0;
88 	nendpoint = 0;
89 	niface = 0;
90 	iface_no = 0xFFFF;
91 	ptr = NULL;
92 
93 	/* get "wTotalLength" and setup "pcdesc" */
94 	pcdesc.ptr = LIBUSB20_ADD_BYTES(config_desc, 0);
95 	pcdesc.len =
96 	    ((const uint8_t *)config_desc)[2] |
97 	    (((const uint8_t *)config_desc)[3] << 8);
98 	pcdesc.type = LIBUSB20_ME_IS_RAW;
99 
100 	/* descriptor pre-scan */
101 	while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) {
102 		if (ptr[1] == LIBUSB20_DT_ENDPOINT) {
103 			nendpoint++;
104 		} else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) {
105 			niface++;
106 			/* check "bInterfaceNumber" */
107 			if (ptr[2] != iface_no) {
108 				iface_no = ptr[2];
109 				niface_no_alt++;
110 			}
111 		}
112 	}
113 
114 	/* sanity checking */
115 	if (niface >= 256) {
116 		return (NULL);		/* corrupt */
117 	}
118 	if (nendpoint >= 256) {
119 		return (NULL);		/* corrupt */
120 	}
121 	size = sizeof(*lub_config) +
122 	    (niface * sizeof(*lub_interface)) +
123 	    (nendpoint * sizeof(*lub_endpoint)) +
124 	    pcdesc.len;
125 
126 	lub_config = malloc(size);
127 	if (lub_config == NULL) {
128 		return (NULL);		/* out of memory */
129 	}
130 	/* make sure memory is initialised */
131 	memset(lub_config, 0, size);
132 
133 	lub_interface = (void *)(lub_config + 1);
134 	lub_alt_interface = (void *)(lub_interface + niface_no_alt);
135 	lub_endpoint = (void *)(lub_interface + niface);
136 
137 	/*
138 	 * Make a copy of the config descriptor, so that the caller can free
139 	 * the inital config descriptor pointer!
140 	 */
141 	ptr = (void *)(lub_endpoint + nendpoint);
142 	memcpy(LIBUSB20_ADD_BYTES(ptr, 0), config_desc, pcdesc.len);
143 	pcdesc.ptr = LIBUSB20_ADD_BYTES(ptr, 0);
144 	config_desc = LIBUSB20_ADD_BYTES(ptr, 0);
145 
146 	/* init config structure */
147 
148 	ptr = config_desc;
149 
150 	LIBUSB20_INIT(LIBUSB20_CONFIG_DESC, &lub_config->desc);
151 
152 	if (libusb20_me_decode(ptr, ptr[0], &lub_config->desc)) {
153 		/* ignore */
154 	}
155 	lub_config->num_interface = 0;
156 	lub_config->interface = lub_interface;
157 	lub_config->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
158 	lub_config->extra.len = -ptr[0];
159 	lub_config->extra.type = LIBUSB20_ME_IS_RAW;
160 
161 	/* reset states */
162 	niface = 0;
163 	iface_no = 0xFFFF;
164 	ptr = NULL;
165 	lub_interface--;
166 	lub_endpoint--;
167 	last_if = NULL;
168 	last_ep = NULL;
169 
170 	/* descriptor pre-scan */
171 	while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) {
172 		if (ptr[1] == LIBUSB20_DT_ENDPOINT) {
173 			if (last_if) {
174 				lub_endpoint++;
175 				last_ep = lub_endpoint;
176 				last_if->num_endpoints++;
177 
178 				LIBUSB20_INIT(LIBUSB20_ENDPOINT_DESC, &last_ep->desc);
179 
180 				if (libusb20_me_decode(ptr, ptr[0], &last_ep->desc)) {
181 					/* ignore */
182 				}
183 				last_ep->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
184 				last_ep->extra.len = 0;
185 				last_ep->extra.type = LIBUSB20_ME_IS_RAW;
186 			} else {
187 				lub_config->extra.len += ptr[0];
188 			}
189 
190 		} else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) {
191 			if (ptr[2] != iface_no) {
192 				/* new interface */
193 				iface_no = ptr[2];
194 				lub_interface++;
195 				lub_config->num_interface++;
196 				last_if = lub_interface;
197 				niface++;
198 			} else {
199 				/* one more alternate setting */
200 				lub_interface->num_altsetting++;
201 				last_if = lub_alt_interface;
202 				lub_alt_interface++;
203 			}
204 
205 			LIBUSB20_INIT(LIBUSB20_INTERFACE_DESC, &last_if->desc);
206 
207 			if (libusb20_me_decode(ptr, ptr[0], &last_if->desc)) {
208 				/* ignore */
209 			}
210 			/*
211 			 * Sometimes USB devices have corrupt interface
212 			 * descriptors and we need to overwrite the provided
213 			 * interface number!
214 			 */
215 			last_if->desc.bInterfaceNumber = niface - 1;
216 			last_if->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
217 			last_if->extra.len = 0;
218 			last_if->extra.type = LIBUSB20_ME_IS_RAW;
219 			last_if->endpoints = lub_endpoint + 1;
220 			last_if->altsetting = lub_alt_interface;
221 			last_if->num_altsetting = 0;
222 			last_if->num_endpoints = 0;
223 			last_ep = NULL;
224 		} else {
225 			/* unknown descriptor */
226 			if (last_if) {
227 				if (last_ep) {
228 					last_ep->extra.len += ptr[0];
229 				} else {
230 					last_if->extra.len += ptr[0];
231 				}
232 			} else {
233 				lub_config->extra.len += ptr[0];
234 			}
235 		}
236 	}
237 	return (lub_config);
238 }
239 
240 /*------------------------------------------------------------------------*
241  *	libusb20_desc_foreach
242  *
243  * Safe traversal of USB descriptors.
244  *
245  * Return values:
246  * NULL: End of descriptors
247  * Else: Pointer to next descriptor
248  *------------------------------------------------------------------------*/
249 const uint8_t *
250 libusb20_desc_foreach(const struct libusb20_me_struct *pdesc,
251     const uint8_t *psubdesc)
252 {
253 	const uint8_t *start;
254 	const uint8_t *end;
255 	const uint8_t *desc_next;
256 
257 	/* be NULL safe */
258 	if (pdesc == NULL)
259 		return (NULL);
260 
261 	start = (const uint8_t *)pdesc->ptr;
262 	end = LIBUSB20_ADD_BYTES(start, pdesc->len);
263 
264 	/* get start of next descriptor */
265 	if (psubdesc == NULL)
266 		psubdesc = start;
267 	else
268 		psubdesc = psubdesc + psubdesc[0];
269 
270 	/* check that the next USB descriptor is within the range */
271 	if ((psubdesc < start) || (psubdesc >= end))
272 		return (NULL);		/* out of range, or EOD */
273 
274 	/* check start of the second next USB descriptor, if any */
275 	desc_next = psubdesc + psubdesc[0];
276 	if ((desc_next < start) || (desc_next > end))
277 		return (NULL);		/* out of range */
278 
279 	/* check minimum descriptor length */
280 	if (psubdesc[0] < 3)
281 		return (NULL);		/* too short descriptor */
282 
283 	return (psubdesc);		/* return start of next descriptor */
284 }
285 
286 /*------------------------------------------------------------------------*
287  *	libusb20_me_get_1 - safety wrapper to read out one byte
288  *------------------------------------------------------------------------*/
289 uint8_t
290 libusb20_me_get_1(const struct libusb20_me_struct *ie, uint16_t offset)
291 {
292 	if (offset < ie->len) {
293 		return (*((uint8_t *)LIBUSB20_ADD_BYTES(ie->ptr, offset)));
294 	}
295 	return (0);
296 }
297 
298 /*------------------------------------------------------------------------*
299  *	libusb20_me_get_2 - safety wrapper to read out one word
300  *------------------------------------------------------------------------*/
301 uint16_t
302 libusb20_me_get_2(const struct libusb20_me_struct *ie, uint16_t offset)
303 {
304 	return (libusb20_me_get_1(ie, offset) |
305 	    (libusb20_me_get_1(ie, offset + 1) << 8));
306 }
307 
308 /*------------------------------------------------------------------------*
309  *	libusb20_me_encode - encode a message structure
310  *
311  * Description of parameters:
312  * "len" - maximum length of output buffer
313  * "ptr" - pointer to output buffer. If NULL, no data will be written
314  * "pd" - source structure
315  *
316  * Return values:
317  * 0..65535 - Number of bytes used, limited by the "len" input parameter.
318  *------------------------------------------------------------------------*/
319 uint16_t
320 libusb20_me_encode(void *ptr, uint16_t len, const void *pd)
321 {
322 	const uint8_t *pf;		/* pointer to format data */
323 	uint8_t *buf;			/* pointer to output buffer */
324 
325 	uint32_t pd_offset;		/* decoded structure offset */
326 	uint16_t len_old;		/* old length */
327 	uint16_t pd_count;		/* decoded element count */
328 	uint8_t me;			/* message element */
329 
330 	/* initialise */
331 
332 	len_old = len;
333 	buf = ptr;
334 	pd_offset = sizeof(void *);
335 	pf = (*((struct libusb20_me_format *const *)pd))->format;
336 
337 	/* scan */
338 
339 	while (1) {
340 
341 		/* get information element */
342 
343 		me = (pf[0]) & LIBUSB20_ME_MASK;
344 		pd_count = pf[1] | (pf[2] << 8);
345 		pf += 3;
346 
347 		/* encode the message element */
348 
349 		switch (me) {
350 		case LIBUSB20_ME_INT8:
351 			while (pd_count--) {
352 				uint8_t temp;
353 
354 				if (len < 1)	/* overflow */
355 					goto done;
356 				if (buf) {
357 					temp = *((const uint8_t *)
358 					    LIBUSB20_ADD_BYTES(pd, pd_offset));
359 					buf[0] = temp;
360 					buf += 1;
361 				}
362 				pd_offset += 1;
363 				len -= 1;
364 			}
365 			break;
366 
367 		case LIBUSB20_ME_INT16:
368 			pd_offset = -((-pd_offset) & ~1);	/* align */
369 			while (pd_count--) {
370 				uint16_t temp;
371 
372 				if (len < 2)	/* overflow */
373 					goto done;
374 
375 				if (buf) {
376 					temp = *((const uint16_t *)
377 					    LIBUSB20_ADD_BYTES(pd, pd_offset));
378 					buf[1] = (temp >> 8) & 0xFF;
379 					buf[0] = temp & 0xFF;
380 					buf += 2;
381 				}
382 				pd_offset += 2;
383 				len -= 2;
384 			}
385 			break;
386 
387 		case LIBUSB20_ME_INT32:
388 			pd_offset = -((-pd_offset) & ~3);	/* align */
389 			while (pd_count--) {
390 				uint32_t temp;
391 
392 				if (len < 4)	/* overflow */
393 					goto done;
394 				if (buf) {
395 					temp = *((const uint32_t *)
396 					    LIBUSB20_ADD_BYTES(pd, pd_offset));
397 					buf[3] = (temp >> 24) & 0xFF;
398 					buf[2] = (temp >> 16) & 0xFF;
399 					buf[1] = (temp >> 8) & 0xFF;
400 					buf[0] = temp & 0xFF;
401 					buf += 4;
402 				}
403 				pd_offset += 4;
404 				len -= 4;
405 			}
406 			break;
407 
408 		case LIBUSB20_ME_INT64:
409 			pd_offset = -((-pd_offset) & ~7);	/* align */
410 			while (pd_count--) {
411 				uint64_t temp;
412 
413 				if (len < 8)	/* overflow */
414 					goto done;
415 				if (buf) {
416 
417 					temp = *((const uint64_t *)
418 					    LIBUSB20_ADD_BYTES(pd, pd_offset));
419 					buf[7] = (temp >> 56) & 0xFF;
420 					buf[6] = (temp >> 48) & 0xFF;
421 					buf[5] = (temp >> 40) & 0xFF;
422 					buf[4] = (temp >> 32) & 0xFF;
423 					buf[3] = (temp >> 24) & 0xFF;
424 					buf[2] = (temp >> 16) & 0xFF;
425 					buf[1] = (temp >> 8) & 0xFF;
426 					buf[0] = temp & 0xFF;
427 					buf += 8;
428 				}
429 				pd_offset += 8;
430 				len -= 8;
431 			}
432 			break;
433 
434 		case LIBUSB20_ME_STRUCT:
435 			pd_offset = -rounddown2(-pd_offset, LIBUSB20_ME_STRUCT_ALIGN);	/* align */
436 			while (pd_count--) {
437 				void *src_ptr;
438 				uint16_t src_len;
439 				struct libusb20_me_struct *ps;
440 
441 				ps = LIBUSB20_ADD_BYTES(pd, pd_offset);
442 
443 				switch (ps->type) {
444 				case LIBUSB20_ME_IS_RAW:
445 					src_len = ps->len;
446 					src_ptr = ps->ptr;
447 					break;
448 
449 				case LIBUSB20_ME_IS_ENCODED:
450 					if (ps->len == 0) {
451 						/*
452 						 * Length is encoded
453 						 * in the data itself
454 						 * and should be
455 						 * correct:
456 						 */
457 						ps->len = 0xFFFF;
458 					}
459 					src_len = libusb20_me_get_1(pd, 0);
460 					src_ptr = LIBUSB20_ADD_BYTES(ps->ptr, 1);
461 					if (src_len == 0xFF) {
462 						/* length is escaped */
463 						src_len = libusb20_me_get_2(pd, 1);
464 						src_ptr =
465 						    LIBUSB20_ADD_BYTES(ps->ptr, 3);
466 					}
467 					break;
468 
469 				case LIBUSB20_ME_IS_DECODED:
470 					/* reserve 3 length bytes */
471 					src_len = libusb20_me_encode(NULL,
472 					    0xFFFF - 3, ps->ptr);
473 					src_ptr = NULL;
474 					break;
475 
476 				default:	/* empty structure */
477 					src_len = 0;
478 					src_ptr = NULL;
479 					break;
480 				}
481 
482 				if (src_len > 0xFE) {
483 					if (src_len > (0xFFFF - 3))
484 						/* overflow */
485 						goto done;
486 
487 					if (len < (src_len + 3))
488 						/* overflow */
489 						goto done;
490 
491 					if (buf) {
492 						buf[0] = 0xFF;
493 						buf[1] = (src_len & 0xFF);
494 						buf[2] = (src_len >> 8) & 0xFF;
495 						buf += 3;
496 					}
497 					len -= (src_len + 3);
498 				} else {
499 					if (len < (src_len + 1))
500 						/* overflow */
501 						goto done;
502 
503 					if (buf) {
504 						buf[0] = (src_len & 0xFF);
505 						buf += 1;
506 					}
507 					len -= (src_len + 1);
508 				}
509 
510 				/* check for buffer and non-zero length */
511 
512 				if (buf && src_len) {
513 					if (ps->type == LIBUSB20_ME_IS_DECODED) {
514 						/*
515 						 * Repeat encode
516 						 * procedure - we have
517 						 * room for the
518 						 * complete structure:
519 						 */
520 						uint16_t dummy;
521 
522 						dummy = libusb20_me_encode(buf,
523 						    0xFFFF - 3, ps->ptr);
524 					} else {
525 						bcopy(src_ptr, buf, src_len);
526 					}
527 					buf += src_len;
528 				}
529 				pd_offset += sizeof(struct libusb20_me_struct);
530 			}
531 			break;
532 
533 		default:
534 			goto done;
535 		}
536 	}
537 done:
538 	return (len_old - len);
539 }
540 
541 /*------------------------------------------------------------------------*
542  *	libusb20_me_decode - decode a message into a decoded structure
543  *
544  * Description of parameters:
545  * "ptr" - message pointer
546  * "len" - message length
547  * "pd" - pointer to decoded structure
548  *
549  * Returns:
550  * "0..65535" - number of bytes decoded, limited by "len"
551  *------------------------------------------------------------------------*/
552 uint16_t
553 libusb20_me_decode(const void *ptr, uint16_t len, void *pd)
554 {
555 	const uint8_t *pf;		/* pointer to format data */
556 	const uint8_t *buf;		/* pointer to input buffer */
557 
558 	uint32_t pd_offset;		/* decoded structure offset */
559 	uint16_t len_old;		/* old length */
560 	uint16_t pd_count;		/* decoded element count */
561 	uint8_t me;			/* message element */
562 
563 	/* initialise */
564 
565 	len_old = len;
566 	buf = ptr;
567 	pd_offset = sizeof(void *);
568 	pf = (*((struct libusb20_me_format **)pd))->format;
569 
570 	/* scan */
571 
572 	while (1) {
573 
574 		/* get information element */
575 
576 		me = (pf[0]) & LIBUSB20_ME_MASK;
577 		pd_count = pf[1] | (pf[2] << 8);
578 		pf += 3;
579 
580 		/* decode the message element by type */
581 
582 		switch (me) {
583 		case LIBUSB20_ME_INT8:
584 			while (pd_count--) {
585 				uint8_t temp;
586 
587 				if (len < 1) {
588 					len = 0;
589 					temp = 0;
590 				} else {
591 					len -= 1;
592 					temp = buf[0];
593 					buf++;
594 				}
595 				*((uint8_t *)LIBUSB20_ADD_BYTES(pd,
596 				    pd_offset)) = temp;
597 				pd_offset += 1;
598 			}
599 			break;
600 
601 		case LIBUSB20_ME_INT16:
602 			pd_offset = -((-pd_offset) & ~1);	/* align */
603 			while (pd_count--) {
604 				uint16_t temp;
605 
606 				if (len < 2) {
607 					len = 0;
608 					temp = 0;
609 				} else {
610 					len -= 2;
611 					temp = buf[1] << 8;
612 					temp |= buf[0];
613 					buf += 2;
614 				}
615 				*((uint16_t *)LIBUSB20_ADD_BYTES(pd,
616 				    pd_offset)) = temp;
617 				pd_offset += 2;
618 			}
619 			break;
620 
621 		case LIBUSB20_ME_INT32:
622 			pd_offset = -((-pd_offset) & ~3);	/* align */
623 			while (pd_count--) {
624 				uint32_t temp;
625 
626 				if (len < 4) {
627 					len = 0;
628 					temp = 0;
629 				} else {
630 					len -= 4;
631 					temp = buf[3] << 24;
632 					temp |= buf[2] << 16;
633 					temp |= buf[1] << 8;
634 					temp |= buf[0];
635 					buf += 4;
636 				}
637 
638 				*((uint32_t *)LIBUSB20_ADD_BYTES(pd,
639 				    pd_offset)) = temp;
640 				pd_offset += 4;
641 			}
642 			break;
643 
644 		case LIBUSB20_ME_INT64:
645 			pd_offset = -((-pd_offset) & ~7);	/* align */
646 			while (pd_count--) {
647 				uint64_t temp;
648 
649 				if (len < 8) {
650 					len = 0;
651 					temp = 0;
652 				} else {
653 					len -= 8;
654 					temp = ((uint64_t)buf[7]) << 56;
655 					temp |= ((uint64_t)buf[6]) << 48;
656 					temp |= ((uint64_t)buf[5]) << 40;
657 					temp |= ((uint64_t)buf[4]) << 32;
658 					temp |= buf[3] << 24;
659 					temp |= buf[2] << 16;
660 					temp |= buf[1] << 8;
661 					temp |= buf[0];
662 					buf += 8;
663 				}
664 
665 				*((uint64_t *)LIBUSB20_ADD_BYTES(pd,
666 				    pd_offset)) = temp;
667 				pd_offset += 8;
668 			}
669 			break;
670 
671 		case LIBUSB20_ME_STRUCT:
672 			pd_offset = -rounddown2(-pd_offset, LIBUSB20_ME_STRUCT_ALIGN);	/* align */
673 			while (pd_count--) {
674 				uint16_t temp;
675 				uint16_t dummy;
676 				struct libusb20_me_struct *ps;
677 
678 				ps = LIBUSB20_ADD_BYTES(pd, pd_offset);
679 
680 				if (ps->type == LIBUSB20_ME_IS_ENCODED) {
681 					/*
682 					 * Pre-store a de-constified
683 					 * pointer to the raw
684 					 * structure:
685 					 */
686 					ps->ptr = LIBUSB20_ADD_BYTES(buf, 0);
687 
688 					/*
689 					 * Get the correct number of
690 					 * length bytes:
691 					 */
692 					if (len != 0) {
693 						if (buf[0] == 0xFF) {
694 							ps->len = 3;
695 						} else {
696 							ps->len = 1;
697 						}
698 					} else {
699 						ps->len = 0;
700 					}
701 				}
702 				/* get the structure length */
703 
704 				if (len != 0) {
705 					if (buf[0] == 0xFF) {
706 						if (len < 3) {
707 							len = 0;
708 							temp = 0;
709 						} else {
710 							len -= 3;
711 							temp = buf[1] |
712 							    (buf[2] << 8);
713 							buf += 3;
714 						}
715 					} else {
716 						len -= 1;
717 						temp = buf[0];
718 						buf += 1;
719 					}
720 				} else {
721 					len = 0;
722 					temp = 0;
723 				}
724 				/* check for invalid length */
725 
726 				if (temp > len) {
727 					len = 0;
728 					temp = 0;
729 				}
730 				/* check wanted structure type */
731 
732 				switch (ps->type) {
733 				case LIBUSB20_ME_IS_ENCODED:
734 					/* check for zero length */
735 					if (temp == 0) {
736 						/*
737 						 * The pointer must
738 						 * be valid:
739 						 */
740 						ps->ptr = LIBUSB20_ADD_BYTES(
741 						    libusb20_me_encode_empty, 0);
742 						ps->len = 1;
743 					} else {
744 						ps->len += temp;
745 					}
746 					break;
747 
748 				case LIBUSB20_ME_IS_RAW:
749 					/* update length and pointer */
750 					ps->len = temp;
751 					ps->ptr = LIBUSB20_ADD_BYTES(buf, 0);
752 					break;
753 
754 				case LIBUSB20_ME_IS_EMPTY:
755 				case LIBUSB20_ME_IS_DECODED:
756 					/* check for non-zero length */
757 					if (temp != 0) {
758 						/* update type */
759 						ps->type = LIBUSB20_ME_IS_DECODED;
760 						ps->len = 0;
761 						/*
762 						 * Recursivly decode
763 						 * the next structure
764 						 */
765 						dummy = libusb20_me_decode(buf,
766 						    temp, ps->ptr);
767 					} else {
768 						/* update type */
769 						ps->type = LIBUSB20_ME_IS_EMPTY;
770 						ps->len = 0;
771 					}
772 					break;
773 
774 				default:
775 					/*
776 					 * nothing to do - should
777 					 * not happen
778 					 */
779 					ps->ptr = NULL;
780 					ps->len = 0;
781 					break;
782 				}
783 				buf += temp;
784 				len -= temp;
785 				pd_offset += sizeof(struct libusb20_me_struct);
786 			}
787 			break;
788 
789 		default:
790 			goto done;
791 		}
792 	}
793 done:
794 	return (len_old - len);
795 }
796