xref: /dragonfly/lib/libusb/libusb20_desc.c (revision 0ca59c34)
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/queue.h>
35 #endif
36 
37 #include "libusb20.h"
38 #include "libusb20_desc.h"
39 #include "libusb20_int.h"
40 
41 static const uint32_t libusb20_me_encode_empty[2];	/* dummy */
42 
43 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_DEVICE_DESC);
44 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_ENDPOINT_DESC);
45 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_INTERFACE_DESC);
46 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONFIG_DESC);
47 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONTROL_SETUP);
48 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_ENDPT_COMP_DESC);
49 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_USB_20_DEVCAP_DESC);
50 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_USB_DEVCAP_DESC);
51 LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_BOS_DESCRIPTOR);
52 
53 /*------------------------------------------------------------------------*
54  *	libusb20_parse_config_desc
55  *
56  * Return values:
57  * NULL: Out of memory.
58  * Else: A valid config structure pointer which must be passed to "free()"
59  *------------------------------------------------------------------------*/
60 struct libusb20_config *
61 libusb20_parse_config_desc(const void *config_desc)
62 {
63 	struct libusb20_config *lub_config;
64 	struct libusb20_interface *lub_interface;
65 	struct libusb20_interface *lub_alt_interface;
66 	struct libusb20_interface *last_if;
67 	struct libusb20_endpoint *lub_endpoint;
68 	struct libusb20_endpoint *last_ep;
69 
70 	struct libusb20_me_struct pcdesc;
71 	const uint8_t *ptr;
72 	uint32_t size;
73 	uint16_t niface_no_alt;
74 	uint16_t niface;
75 	uint16_t nendpoint;
76 	uint16_t iface_no;
77 
78 	ptr = config_desc;
79 	if (ptr[1] != LIBUSB20_DT_CONFIG) {
80 		return (NULL);		/* not config descriptor */
81 	}
82 	/*
83 	 * The first "bInterfaceNumber" should never have the value 0xff.
84 	 * Then it is corrupt.
85 	 */
86 	niface_no_alt = 0;
87 	nendpoint = 0;
88 	niface = 0;
89 	iface_no = 0xFFFF;
90 	ptr = NULL;
91 
92 	/* get "wTotalLength" and setup "pcdesc" */
93 	pcdesc.ptr = LIBUSB20_ADD_BYTES(config_desc, 0);
94 	pcdesc.len =
95 	    ((const uint8_t *)config_desc)[2] |
96 	    (((const uint8_t *)config_desc)[3] << 8);
97 	pcdesc.type = LIBUSB20_ME_IS_RAW;
98 
99 	/* descriptor pre-scan */
100 	while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) {
101 		if (ptr[1] == LIBUSB20_DT_ENDPOINT) {
102 			nendpoint++;
103 		} else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) {
104 			niface++;
105 			/* check "bInterfaceNumber" */
106 			if (ptr[2] != iface_no) {
107 				iface_no = ptr[2];
108 				niface_no_alt++;
109 			}
110 		}
111 	}
112 
113 	/* sanity checking */
114 	if (niface >= 256) {
115 		return (NULL);		/* corrupt */
116 	}
117 	if (nendpoint >= 256) {
118 		return (NULL);		/* corrupt */
119 	}
120 	size = sizeof(*lub_config) +
121 	    (niface * sizeof(*lub_interface)) +
122 	    (nendpoint * sizeof(*lub_endpoint)) +
123 	    pcdesc.len;
124 
125 	lub_config = malloc(size);
126 	if (lub_config == NULL) {
127 		return (NULL);		/* out of memory */
128 	}
129 	/* make sure memory is initialised */
130 	memset(lub_config, 0, size);
131 
132 	lub_interface = (void *)(lub_config + 1);
133 	lub_alt_interface = (void *)(lub_interface + niface_no_alt);
134 	lub_endpoint = (void *)(lub_interface + niface);
135 
136 	/*
137 	 * Make a copy of the config descriptor, so that the caller can free
138 	 * the inital config descriptor pointer!
139 	 */
140 	ptr = (void *)(lub_endpoint + nendpoint);
141 	memcpy(LIBUSB20_ADD_BYTES(ptr, 0), config_desc, pcdesc.len);
142 	pcdesc.ptr = LIBUSB20_ADD_BYTES(ptr, 0);
143 	config_desc = LIBUSB20_ADD_BYTES(ptr, 0);
144 
145 	/* init config structure */
146 
147 	ptr = config_desc;
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 			 * Sometimes USB devices have corrupt interface
211 			 * descriptors and we need to overwrite the provided
212 			 * interface number!
213 			 */
214 			last_if->desc.bInterfaceNumber = niface - 1;
215 			last_if->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
216 			last_if->extra.len = 0;
217 			last_if->extra.type = LIBUSB20_ME_IS_RAW;
218 			last_if->endpoints = lub_endpoint + 1;
219 			last_if->altsetting = lub_alt_interface;
220 			last_if->num_altsetting = 0;
221 			last_if->num_endpoints = 0;
222 			last_ep = NULL;
223 		} else {
224 			/* unknown descriptor */
225 			if (last_if) {
226 				if (last_ep) {
227 					last_ep->extra.len += ptr[0];
228 				} else {
229 					last_if->extra.len += ptr[0];
230 				}
231 			} else {
232 				lub_config->extra.len += ptr[0];
233 			}
234 		}
235 	}
236 	return (lub_config);
237 }
238 
239 /*------------------------------------------------------------------------*
240  *	libusb20_desc_foreach
241  *
242  * Safe traversal of USB descriptors.
243  *
244  * Return values:
245  * NULL: End of descriptors
246  * Else: Pointer to next descriptor
247  *------------------------------------------------------------------------*/
248 const uint8_t *
249 libusb20_desc_foreach(const struct libusb20_me_struct *pdesc,
250     const uint8_t *psubdesc)
251 {
252 	const uint8_t *start;
253 	const uint8_t *end;
254 	const uint8_t *desc_next;
255 
256 	/* be NULL safe */
257 	if (pdesc == NULL)
258 		return (NULL);
259 
260 	start = (const uint8_t *)pdesc->ptr;
261 	end = LIBUSB20_ADD_BYTES(start, pdesc->len);
262 
263 	/* get start of next descriptor */
264 	if (psubdesc == NULL)
265 		psubdesc = start;
266 	else
267 		psubdesc = psubdesc + psubdesc[0];
268 
269 	/* check that the next USB descriptor is within the range */
270 	if ((psubdesc < start) || (psubdesc >= end))
271 		return (NULL);		/* out of range, or EOD */
272 
273 	/* check start of the second next USB descriptor, if any */
274 	desc_next = psubdesc + psubdesc[0];
275 	if ((desc_next < start) || (desc_next > end))
276 		return (NULL);		/* out of range */
277 
278 	/* check minimum descriptor length */
279 	if (psubdesc[0] < 3)
280 		return (NULL);		/* too short descriptor */
281 
282 	return (psubdesc);		/* return start of next descriptor */
283 }
284 
285 /*------------------------------------------------------------------------*
286  *	libusb20_me_get_1 - safety wrapper to read out one byte
287  *------------------------------------------------------------------------*/
288 uint8_t
289 libusb20_me_get_1(const struct libusb20_me_struct *ie, uint16_t offset)
290 {
291 	if (offset < ie->len) {
292 		return (*((uint8_t *)LIBUSB20_ADD_BYTES(ie->ptr, offset)));
293 	}
294 	return (0);
295 }
296 
297 /*------------------------------------------------------------------------*
298  *	libusb20_me_get_2 - safety wrapper to read out one word
299  *------------------------------------------------------------------------*/
300 uint16_t
301 libusb20_me_get_2(const struct libusb20_me_struct *ie, uint16_t offset)
302 {
303 	return (libusb20_me_get_1(ie, offset) |
304 	    (libusb20_me_get_1(ie, offset + 1) << 8));
305 }
306 
307 /*------------------------------------------------------------------------*
308  *	libusb20_me_encode - encode a message structure
309  *
310  * Description of parameters:
311  * "len" - maximum length of output buffer
312  * "ptr" - pointer to output buffer. If NULL, no data will be written
313  * "pd" - source structure
314  *
315  * Return values:
316  * 0..65535 - Number of bytes used, limited by the "len" input parameter.
317  *------------------------------------------------------------------------*/
318 uint16_t
319 libusb20_me_encode(void *ptr, uint16_t len, const void *pd)
320 {
321 	const uint8_t *pf;		/* pointer to format data */
322 	uint8_t *buf;			/* pointer to output buffer */
323 
324 	uint32_t pd_offset;		/* decoded structure offset */
325 	uint16_t len_old;		/* old length */
326 	uint16_t pd_count;		/* decoded element count */
327 	uint8_t me;			/* message element */
328 
329 	/* initialise */
330 
331 	len_old = len;
332 	buf = ptr;
333 	pd_offset = sizeof(void *);
334 	pf = (*((struct libusb20_me_format *const *)pd))->format;
335 
336 	/* scan */
337 
338 	while (1) {
339 
340 		/* get information element */
341 
342 		me = (pf[0]) & LIBUSB20_ME_MASK;
343 		pd_count = pf[1] | (pf[2] << 8);
344 		pf += 3;
345 
346 		/* encode the message element */
347 
348 		switch (me) {
349 		case LIBUSB20_ME_INT8:
350 			while (pd_count--) {
351 				uint8_t temp;
352 
353 				if (len < 1)	/* overflow */
354 					goto done;
355 				if (buf) {
356 					temp = *((const uint8_t *)
357 					    LIBUSB20_ADD_BYTES(pd, pd_offset));
358 					buf[0] = temp;
359 					buf += 1;
360 				}
361 				pd_offset += 1;
362 				len -= 1;
363 			}
364 			break;
365 
366 		case LIBUSB20_ME_INT16:
367 			pd_offset = -((-pd_offset) & ~1);	/* align */
368 			while (pd_count--) {
369 				uint16_t temp;
370 
371 				if (len < 2)	/* overflow */
372 					goto done;
373 
374 				if (buf) {
375 					temp = *((const uint16_t *)
376 					    LIBUSB20_ADD_BYTES(pd, pd_offset));
377 					buf[1] = (temp >> 8) & 0xFF;
378 					buf[0] = temp & 0xFF;
379 					buf += 2;
380 				}
381 				pd_offset += 2;
382 				len -= 2;
383 			}
384 			break;
385 
386 		case LIBUSB20_ME_INT32:
387 			pd_offset = -((-pd_offset) & ~3);	/* align */
388 			while (pd_count--) {
389 				uint32_t temp;
390 
391 				if (len < 4)	/* overflow */
392 					goto done;
393 				if (buf) {
394 					temp = *((const uint32_t *)
395 					    LIBUSB20_ADD_BYTES(pd, pd_offset));
396 					buf[3] = (temp >> 24) & 0xFF;
397 					buf[2] = (temp >> 16) & 0xFF;
398 					buf[1] = (temp >> 8) & 0xFF;
399 					buf[0] = temp & 0xFF;
400 					buf += 4;
401 				}
402 				pd_offset += 4;
403 				len -= 4;
404 			}
405 			break;
406 
407 		case LIBUSB20_ME_INT64:
408 			pd_offset = -((-pd_offset) & ~7);	/* align */
409 			while (pd_count--) {
410 				uint64_t temp;
411 
412 				if (len < 8)	/* overflow */
413 					goto done;
414 				if (buf) {
415 
416 					temp = *((const uint64_t *)
417 					    LIBUSB20_ADD_BYTES(pd, pd_offset));
418 					buf[7] = (temp >> 56) & 0xFF;
419 					buf[6] = (temp >> 48) & 0xFF;
420 					buf[5] = (temp >> 40) & 0xFF;
421 					buf[4] = (temp >> 32) & 0xFF;
422 					buf[3] = (temp >> 24) & 0xFF;
423 					buf[2] = (temp >> 16) & 0xFF;
424 					buf[1] = (temp >> 8) & 0xFF;
425 					buf[0] = temp & 0xFF;
426 					buf += 8;
427 				}
428 				pd_offset += 8;
429 				len -= 8;
430 			}
431 			break;
432 
433 		case LIBUSB20_ME_STRUCT:
434 			pd_offset = -((-pd_offset) &
435 			    ~(LIBUSB20_ME_STRUCT_ALIGN - 1));	/* 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 = -((-pd_offset) &
673 			    ~(LIBUSB20_ME_STRUCT_ALIGN - 1));	/* align */
674 			while (pd_count--) {
675 				uint16_t temp;
676 				uint16_t dummy;
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 						dummy = 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