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