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