libusb20_desc.c revision 234491
1252391Sray/* $FreeBSD: head/lib/libusb/libusb20_desc.c 234491 2012-04-20 14:29:45Z hselasky $ */
2252391Sray/*-
3252391Sray * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
4252391Sray *
5252391Sray * Redistribution and use in source and binary forms, with or without
6252391Sray * modification, are permitted provided that the following conditions
7252391Sray * are met:
8252391Sray * 1. Redistributions of source code must retain the above copyright
9252391Sray *    notice, this list of conditions and the following disclaimer.
10252391Sray * 2. Redistributions in binary form must reproduce the above copyright
11252391Sray *    notice, this list of conditions and the following disclaimer in the
12252391Sray *    documentation and/or other materials provided with the distribution.
13252391Sray *
14252391Sray * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15252391Sray * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16252391Sray * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17252391Sray * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18252391Sray * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19252391Sray * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20252391Sray * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21252391Sray * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22252391Sray * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23252391Sray * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24252391Sray * SUCH DAMAGE.
25252391Sray */
26252391Sray
27252391Sray#include <sys/queue.h>
28252391Sray
29252391Sray#include <stdio.h>
30252391Sray#include <stdlib.h>
31252391Sray#include <string.h>
32252391Sray
33258096Sbr#include "libusb20.h"
34252391Sray#include "libusb20_desc.h"
35252391Sray#include "libusb20_int.h"
36252391Sray
37298627Sbrstatic const uint32_t libusb20_me_encode_empty[2];	/* dummy */
38252391Sray
39252391SrayLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_DEVICE_DESC);
40252391SrayLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_ENDPOINT_DESC);
41258096SbrLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_INTERFACE_DESC);
42252391SrayLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONFIG_DESC);
43252391SrayLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONTROL_SETUP);
44266301SandrewLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_ENDPT_COMP_DESC);
45252391SrayLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_USB_20_DEVCAP_DESC);
46252391SrayLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_USB_DEVCAP_DESC);
47266301SandrewLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_BOS_DESCRIPTOR);
48252391Sray
49252391Sray/*------------------------------------------------------------------------*
50298627Sbr *	libusb20_parse_config_desc
51252391Sray *
52252391Sray * Return values:
53252391Sray * NULL: Out of memory.
54266301Sandrew * Else: A valid config structure pointer which must be passed to "free()"
55257669Sian *------------------------------------------------------------------------*/
56257669Sianstruct libusb20_config *
57257669Sianlibusb20_parse_config_desc(const void *config_desc)
58257669Sian{
59257669Sian	struct libusb20_config *lub_config;
60266301Sandrew	struct libusb20_interface *lub_interface;
61252391Sray	struct libusb20_interface *lub_alt_interface;
62258096Sbr	struct libusb20_interface *last_if;
63252391Sray	struct libusb20_endpoint *lub_endpoint;
64252391Sray	struct libusb20_endpoint *last_ep;
65252391Sray
66266301Sandrew	struct libusb20_me_struct pcdesc;
67252391Sray	const uint8_t *ptr;
68258096Sbr	uint32_t size;
69252391Sray	uint16_t niface_no_alt;
70252391Sray	uint16_t niface;
71252391Sray	uint16_t nendpoint;
72266301Sandrew	uint16_t iface_no;
73252391Sray
74252391Sray	ptr = config_desc;
75267388Sbr	if (ptr[1] != LIBUSB20_DT_CONFIG) {
76298627Sbr		return (NULL);		/* not config descriptor */
77267388Sbr	}
78258096Sbr	/*
79298627Sbr	 * The first "bInterfaceNumber" should never have the value 0xff.
80252391Sray	 * Then it is corrupt.
81272712Sbr	 */
82298627Sbr	niface_no_alt = 0;
83272712Sbr	nendpoint = 0;
84252391Sray	niface = 0;
85252391Sray	iface_no = 0xFFFF;
86252391Sray	ptr = NULL;
87252391Sray
88252391Sray	/* get "wTotalLength" and setup "pcdesc" */
89252391Sray	pcdesc.ptr = LIBUSB20_ADD_BYTES(config_desc, 0);
90252391Sray	pcdesc.len =
91252391Sray	    ((const uint8_t *)config_desc)[2] |
92252391Sray	    (((const uint8_t *)config_desc)[3] << 8);
93252391Sray	pcdesc.type = LIBUSB20_ME_IS_RAW;
94252391Sray
95252391Sray	/* descriptor pre-scan */
96252391Sray	while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) {
97252391Sray		if (ptr[1] == LIBUSB20_DT_ENDPOINT) {
98252391Sray			nendpoint++;
99252391Sray		} else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) {
100			niface++;
101			/* check "bInterfaceNumber" */
102			if (ptr[2] != iface_no) {
103				iface_no = ptr[2];
104				niface_no_alt++;
105			}
106		}
107	}
108
109	/* sanity checking */
110	if (niface >= 256) {
111		return (NULL);		/* corrupt */
112	}
113	if (nendpoint >= 256) {
114		return (NULL);		/* corrupt */
115	}
116	size = sizeof(*lub_config) +
117	    (niface * sizeof(*lub_interface)) +
118	    (nendpoint * sizeof(*lub_endpoint)) +
119	    pcdesc.len;
120
121	lub_config = malloc(size);
122	if (lub_config == NULL) {
123		return (NULL);		/* out of memory */
124	}
125	/* make sure memory is initialised */
126	memset(lub_config, 0, size);
127
128	lub_interface = (void *)(lub_config + 1);
129	lub_alt_interface = (void *)(lub_interface + niface_no_alt);
130	lub_endpoint = (void *)(lub_interface + niface);
131
132	/*
133	 * Make a copy of the config descriptor, so that the caller can free
134	 * the inital config descriptor pointer!
135	 */
136	ptr = (void *)(lub_endpoint + nendpoint);
137	memcpy(LIBUSB20_ADD_BYTES(ptr, 0), config_desc, pcdesc.len);
138	pcdesc.ptr = LIBUSB20_ADD_BYTES(ptr, 0);
139	config_desc = LIBUSB20_ADD_BYTES(ptr, 0);
140
141	/* init config structure */
142
143	ptr = config_desc;
144
145	LIBUSB20_INIT(LIBUSB20_CONFIG_DESC, &lub_config->desc);
146
147	if (libusb20_me_decode(ptr, ptr[0], &lub_config->desc)) {
148		/* ignore */
149	}
150	lub_config->num_interface = 0;
151	lub_config->interface = lub_interface;
152	lub_config->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
153	lub_config->extra.len = -ptr[0];
154	lub_config->extra.type = LIBUSB20_ME_IS_RAW;
155
156	/* reset states */
157	niface = 0;
158	iface_no = 0xFFFF;
159	ptr = NULL;
160	lub_interface--;
161	lub_endpoint--;
162	last_if = NULL;
163	last_ep = NULL;
164
165	/* descriptor pre-scan */
166	while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) {
167		if (ptr[1] == LIBUSB20_DT_ENDPOINT) {
168			if (last_if) {
169				lub_endpoint++;
170				last_ep = lub_endpoint;
171				last_if->num_endpoints++;
172
173				LIBUSB20_INIT(LIBUSB20_ENDPOINT_DESC, &last_ep->desc);
174
175				if (libusb20_me_decode(ptr, ptr[0], &last_ep->desc)) {
176					/* ignore */
177				}
178				last_ep->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
179				last_ep->extra.len = 0;
180				last_ep->extra.type = LIBUSB20_ME_IS_RAW;
181			} else {
182				lub_config->extra.len += ptr[0];
183			}
184
185		} else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) {
186			if (ptr[2] != iface_no) {
187				/* new interface */
188				iface_no = ptr[2];
189				lub_interface++;
190				lub_config->num_interface++;
191				last_if = lub_interface;
192				niface++;
193			} else {
194				/* one more alternate setting */
195				lub_interface->num_altsetting++;
196				last_if = lub_alt_interface;
197				lub_alt_interface++;
198			}
199
200			LIBUSB20_INIT(LIBUSB20_INTERFACE_DESC, &last_if->desc);
201
202			if (libusb20_me_decode(ptr, ptr[0], &last_if->desc)) {
203				/* ignore */
204			}
205			/*
206			 * Sometimes USB devices have corrupt interface
207			 * descriptors and we need to overwrite the provided
208			 * interface number!
209			 */
210			last_if->desc.bInterfaceNumber = niface - 1;
211			last_if->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
212			last_if->extra.len = 0;
213			last_if->extra.type = LIBUSB20_ME_IS_RAW;
214			last_if->endpoints = lub_endpoint + 1;
215			last_if->altsetting = lub_alt_interface;
216			last_if->num_altsetting = 0;
217			last_if->num_endpoints = 0;
218			last_ep = NULL;
219		} else {
220			/* unknown descriptor */
221			if (last_if) {
222				if (last_ep) {
223					last_ep->extra.len += ptr[0];
224				} else {
225					last_if->extra.len += ptr[0];
226				}
227			} else {
228				lub_config->extra.len += ptr[0];
229			}
230		}
231	}
232	return (lub_config);
233}
234
235/*------------------------------------------------------------------------*
236 *	libusb20_desc_foreach
237 *
238 * Safe traversal of USB descriptors.
239 *
240 * Return values:
241 * NULL: End of descriptors
242 * Else: Pointer to next descriptor
243 *------------------------------------------------------------------------*/
244const uint8_t *
245libusb20_desc_foreach(const struct libusb20_me_struct *pdesc,
246    const uint8_t *psubdesc)
247{
248	const uint8_t *start;
249	const uint8_t *end;
250	const uint8_t *desc_next;
251
252	/* be NULL safe */
253	if (pdesc == NULL)
254		return (NULL);
255
256	start = (const uint8_t *)pdesc->ptr;
257	end = LIBUSB20_ADD_BYTES(start, pdesc->len);
258
259	/* get start of next descriptor */
260	if (psubdesc == NULL)
261		psubdesc = start;
262	else
263		psubdesc = psubdesc + psubdesc[0];
264
265	/* check that the next USB descriptor is within the range */
266	if ((psubdesc < start) || (psubdesc >= end))
267		return (NULL);		/* out of range, or EOD */
268
269	/* check start of the second next USB descriptor, if any */
270	desc_next = psubdesc + psubdesc[0];
271	if ((desc_next < start) || (desc_next > end))
272		return (NULL);		/* out of range */
273
274	/* check minimum descriptor length */
275	if (psubdesc[0] < 3)
276		return (NULL);		/* too short descriptor */
277
278	return (psubdesc);		/* return start of next descriptor */
279}
280
281/*------------------------------------------------------------------------*
282 *	libusb20_me_get_1 - safety wrapper to read out one byte
283 *------------------------------------------------------------------------*/
284uint8_t
285libusb20_me_get_1(const struct libusb20_me_struct *ie, uint16_t offset)
286{
287	if (offset < ie->len) {
288		return (*((uint8_t *)LIBUSB20_ADD_BYTES(ie->ptr, offset)));
289	}
290	return (0);
291}
292
293/*------------------------------------------------------------------------*
294 *	libusb20_me_get_2 - safety wrapper to read out one word
295 *------------------------------------------------------------------------*/
296uint16_t
297libusb20_me_get_2(const struct libusb20_me_struct *ie, uint16_t offset)
298{
299	return (libusb20_me_get_1(ie, offset) |
300	    (libusb20_me_get_1(ie, offset + 1) << 8));
301}
302
303/*------------------------------------------------------------------------*
304 *	libusb20_me_encode - encode a message structure
305 *
306 * Description of parameters:
307 * "len" - maximum length of output buffer
308 * "ptr" - pointer to output buffer. If NULL, no data will be written
309 * "pd" - source structure
310 *
311 * Return values:
312 * 0..65535 - Number of bytes used, limited by the "len" input parameter.
313 *------------------------------------------------------------------------*/
314uint16_t
315libusb20_me_encode(void *ptr, uint16_t len, const void *pd)
316{
317	const uint8_t *pf;		/* pointer to format data */
318	uint8_t *buf;			/* pointer to output buffer */
319
320	uint32_t pd_offset;		/* decoded structure offset */
321	uint16_t len_old;		/* old length */
322	uint16_t pd_count;		/* decoded element count */
323	uint8_t me;			/* message element */
324
325	/* initialise */
326
327	len_old = len;
328	buf = ptr;
329	pd_offset = sizeof(void *);
330	pf = (*((struct libusb20_me_format *const *)pd))->format;
331
332	/* scan */
333
334	while (1) {
335
336		/* get information element */
337
338		me = (pf[0]) & LIBUSB20_ME_MASK;
339		pd_count = pf[1] | (pf[2] << 8);
340		pf += 3;
341
342		/* encode the message element */
343
344		switch (me) {
345		case LIBUSB20_ME_INT8:
346			while (pd_count--) {
347				uint8_t temp;
348
349				if (len < 1)	/* overflow */
350					goto done;
351				if (buf) {
352					temp = *((const uint8_t *)
353					    LIBUSB20_ADD_BYTES(pd, pd_offset));
354					buf[0] = temp;
355					buf += 1;
356				}
357				pd_offset += 1;
358				len -= 1;
359			}
360			break;
361
362		case LIBUSB20_ME_INT16:
363			pd_offset = -((-pd_offset) & ~1);	/* align */
364			while (pd_count--) {
365				uint16_t temp;
366
367				if (len < 2)	/* overflow */
368					goto done;
369
370				if (buf) {
371					temp = *((const uint16_t *)
372					    LIBUSB20_ADD_BYTES(pd, pd_offset));
373					buf[1] = (temp >> 8) & 0xFF;
374					buf[0] = temp & 0xFF;
375					buf += 2;
376				}
377				pd_offset += 2;
378				len -= 2;
379			}
380			break;
381
382		case LIBUSB20_ME_INT32:
383			pd_offset = -((-pd_offset) & ~3);	/* align */
384			while (pd_count--) {
385				uint32_t temp;
386
387				if (len < 4)	/* overflow */
388					goto done;
389				if (buf) {
390					temp = *((const uint32_t *)
391					    LIBUSB20_ADD_BYTES(pd, pd_offset));
392					buf[3] = (temp >> 24) & 0xFF;
393					buf[2] = (temp >> 16) & 0xFF;
394					buf[1] = (temp >> 8) & 0xFF;
395					buf[0] = temp & 0xFF;
396					buf += 4;
397				}
398				pd_offset += 4;
399				len -= 4;
400			}
401			break;
402
403		case LIBUSB20_ME_INT64:
404			pd_offset = -((-pd_offset) & ~7);	/* align */
405			while (pd_count--) {
406				uint64_t temp;
407
408				if (len < 8)	/* overflow */
409					goto done;
410				if (buf) {
411
412					temp = *((const uint64_t *)
413					    LIBUSB20_ADD_BYTES(pd, pd_offset));
414					buf[7] = (temp >> 56) & 0xFF;
415					buf[6] = (temp >> 48) & 0xFF;
416					buf[5] = (temp >> 40) & 0xFF;
417					buf[4] = (temp >> 32) & 0xFF;
418					buf[3] = (temp >> 24) & 0xFF;
419					buf[2] = (temp >> 16) & 0xFF;
420					buf[1] = (temp >> 8) & 0xFF;
421					buf[0] = temp & 0xFF;
422					buf += 8;
423				}
424				pd_offset += 8;
425				len -= 8;
426			}
427			break;
428
429		case LIBUSB20_ME_STRUCT:
430			pd_offset = -((-pd_offset) &
431			    ~(LIBUSB20_ME_STRUCT_ALIGN - 1));	/* align */
432			while (pd_count--) {
433				void *src_ptr;
434				uint16_t src_len;
435				struct libusb20_me_struct *ps;
436
437				ps = LIBUSB20_ADD_BYTES(pd, pd_offset);
438
439				switch (ps->type) {
440				case LIBUSB20_ME_IS_RAW:
441					src_len = ps->len;
442					src_ptr = ps->ptr;
443					break;
444
445				case LIBUSB20_ME_IS_ENCODED:
446					if (ps->len == 0) {
447						/*
448						 * Length is encoded
449						 * in the data itself
450						 * and should be
451						 * correct:
452						 */
453						ps->len = 0xFFFF;
454					}
455					src_len = libusb20_me_get_1(pd, 0);
456					src_ptr = LIBUSB20_ADD_BYTES(ps->ptr, 1);
457					if (src_len == 0xFF) {
458						/* length is escaped */
459						src_len = libusb20_me_get_2(pd, 1);
460						src_ptr =
461						    LIBUSB20_ADD_BYTES(ps->ptr, 3);
462					}
463					break;
464
465				case LIBUSB20_ME_IS_DECODED:
466					/* reserve 3 length bytes */
467					src_len = libusb20_me_encode(NULL,
468					    0xFFFF - 3, ps->ptr);
469					src_ptr = NULL;
470					break;
471
472				default:	/* empty structure */
473					src_len = 0;
474					src_ptr = NULL;
475					break;
476				}
477
478				if (src_len > 0xFE) {
479					if (src_len > (0xFFFF - 3))
480						/* overflow */
481						goto done;
482
483					if (len < (src_len + 3))
484						/* overflow */
485						goto done;
486
487					if (buf) {
488						buf[0] = 0xFF;
489						buf[1] = (src_len & 0xFF);
490						buf[2] = (src_len >> 8) & 0xFF;
491						buf += 3;
492					}
493					len -= (src_len + 3);
494				} else {
495					if (len < (src_len + 1))
496						/* overflow */
497						goto done;
498
499					if (buf) {
500						buf[0] = (src_len & 0xFF);
501						buf += 1;
502					}
503					len -= (src_len + 1);
504				}
505
506				/* check for buffer and non-zero length */
507
508				if (buf && src_len) {
509					if (ps->type == LIBUSB20_ME_IS_DECODED) {
510						/*
511						 * Repeat encode
512						 * procedure - we have
513						 * room for the
514						 * complete structure:
515						 */
516						uint16_t dummy;
517
518						dummy = 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				uint16_t dummy;
673				struct libusb20_me_struct *ps;
674
675				ps = LIBUSB20_ADD_BYTES(pd, pd_offset);
676
677				if (ps->type == LIBUSB20_ME_IS_ENCODED) {
678					/*
679					 * Pre-store a de-constified
680					 * pointer to the raw
681					 * structure:
682					 */
683					ps->ptr = LIBUSB20_ADD_BYTES(buf, 0);
684
685					/*
686					 * Get the correct number of
687					 * length bytes:
688					 */
689					if (len != 0) {
690						if (buf[0] == 0xFF) {
691							ps->len = 3;
692						} else {
693							ps->len = 1;
694						}
695					} else {
696						ps->len = 0;
697					}
698				}
699				/* get the structure length */
700
701				if (len != 0) {
702					if (buf[0] == 0xFF) {
703						if (len < 3) {
704							len = 0;
705							temp = 0;
706						} else {
707							len -= 3;
708							temp = buf[1] |
709							    (buf[2] << 8);
710							buf += 3;
711						}
712					} else {
713						len -= 1;
714						temp = buf[0];
715						buf += 1;
716					}
717				} else {
718					len = 0;
719					temp = 0;
720				}
721				/* check for invalid length */
722
723				if (temp > len) {
724					len = 0;
725					temp = 0;
726				}
727				/* check wanted structure type */
728
729				switch (ps->type) {
730				case LIBUSB20_ME_IS_ENCODED:
731					/* check for zero length */
732					if (temp == 0) {
733						/*
734						 * The pointer must
735						 * be valid:
736						 */
737						ps->ptr = LIBUSB20_ADD_BYTES(
738						    libusb20_me_encode_empty, 0);
739						ps->len = 1;
740					} else {
741						ps->len += temp;
742					}
743					break;
744
745				case LIBUSB20_ME_IS_RAW:
746					/* update length and pointer */
747					ps->len = temp;
748					ps->ptr = LIBUSB20_ADD_BYTES(buf, 0);
749					break;
750
751				case LIBUSB20_ME_IS_EMPTY:
752				case LIBUSB20_ME_IS_DECODED:
753					/* check for non-zero length */
754					if (temp != 0) {
755						/* update type */
756						ps->type = LIBUSB20_ME_IS_DECODED;
757						ps->len = 0;
758						/*
759						 * Recursivly decode
760						 * the next structure
761						 */
762						dummy = libusb20_me_decode(buf,
763						    temp, ps->ptr);
764					} else {
765						/* update type */
766						ps->type = LIBUSB20_ME_IS_EMPTY;
767						ps->len = 0;
768					}
769					break;
770
771				default:
772					/*
773					 * nothing to do - should
774					 * not happen
775					 */
776					ps->ptr = NULL;
777					ps->len = 0;
778					break;
779				}
780				buf += temp;
781				len -= temp;
782				pd_offset += sizeof(struct libusb20_me_struct);
783			}
784			break;
785
786		default:
787			goto done;
788		}
789	}
790done:
791	return (len_old - len);
792}
793