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