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