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