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