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