libusb20_desc.c revision 248236
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
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	 * 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 *------------------------------------------------------------------------*/
248const uint8_t *
249libusb20_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 *------------------------------------------------------------------------*/
288uint8_t
289libusb20_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 *------------------------------------------------------------------------*/
300uint16_t
301libusb20_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 *------------------------------------------------------------------------*/
318uint16_t
319libusb20_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	}
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				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	}
794done:
795	return (len_old - len);
796}
797