libusb20_desc.c revision 203775
164562Sgshapiro/* $FreeBSD: head/lib/libusb/libusb20_desc.c 203775 2010-02-11 08:34:41Z wkoszek $ */
264562Sgshapiro/*-
364562Sgshapiro * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
464562Sgshapiro *
564562Sgshapiro * Redistribution and use in source and binary forms, with or without
664562Sgshapiro * modification, are permitted provided that the following conditions
764562Sgshapiro * are met:
864562Sgshapiro * 1. Redistributions of source code must retain the above copyright
964562Sgshapiro *    notice, this list of conditions and the following disclaimer.
1064562Sgshapiro * 2. Redistributions in binary form must reproduce the above copyright
1164562Sgshapiro *    notice, this list of conditions and the following disclaimer in the
1266494Sgshapiro *    documentation and/or other materials provided with the distribution.
1364562Sgshapiro *
1464562Sgshapiro * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1564562Sgshapiro * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1664562Sgshapiro * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1764562Sgshapiro * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1864562Sgshapiro * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1966494Sgshapiro * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2066494Sgshapiro * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2166494Sgshapiro * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2266494Sgshapiro * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2366494Sgshapiro * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2464562Sgshapiro * SUCH DAMAGE.
2564562Sgshapiro */
2666494Sgshapiro
2764562Sgshapiro#include <stdio.h>
2864562Sgshapiro#include <stdlib.h>
2964562Sgshapiro#include <string.h>
3064562Sgshapiro#include <sys/queue.h>
3164562Sgshapiro
3264562Sgshapiro#include "libusb20.h"
3364562Sgshapiro#include "libusb20_desc.h"
3464562Sgshapiro#include "libusb20_int.h"
3564562Sgshapiro
3664562Sgshapirostatic const uint32_t libusb20_me_encode_empty[2];	/* dummy */
3764562Sgshapiro
3864562SgshapiroLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_DEVICE_DESC);
3964562SgshapiroLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_ENDPOINT_DESC);
4064562SgshapiroLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_INTERFACE_DESC);
4164562SgshapiroLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONFIG_DESC);
4264562SgshapiroLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONTROL_SETUP);
4364562Sgshapiro
4464562Sgshapiro/*------------------------------------------------------------------------*
4564562Sgshapiro *	libusb20_parse_config_desc
4664562Sgshapiro *
4764562Sgshapiro * Return values:
4864562Sgshapiro * NULL: Out of memory.
4964562Sgshapiro * Else: A valid config structure pointer which must be passed to "free()"
5064562Sgshapiro *------------------------------------------------------------------------*/
5164562Sgshapirostruct libusb20_config *
5264562Sgshapirolibusb20_parse_config_desc(const void *config_desc)
5364562Sgshapiro{
5464562Sgshapiro	struct libusb20_config *lub_config;
5564562Sgshapiro	struct libusb20_interface *lub_interface;
5664562Sgshapiro	struct libusb20_interface *lub_alt_interface;
5764562Sgshapiro	struct libusb20_interface *last_if;
5864562Sgshapiro	struct libusb20_endpoint *lub_endpoint;
5964562Sgshapiro	struct libusb20_endpoint *last_ep;
6064562Sgshapiro
6164562Sgshapiro	struct libusb20_me_struct pcdesc;
6264562Sgshapiro	const uint8_t *ptr;
6364562Sgshapiro	uint32_t size;
6464562Sgshapiro	uint16_t niface_no_alt;
6566494Sgshapiro	uint16_t niface;
6666494Sgshapiro	uint16_t nendpoint;
6766494Sgshapiro	uint8_t iface_no;
6864562Sgshapiro
6964562Sgshapiro	ptr = config_desc;
7064562Sgshapiro	if (ptr[1] != LIBUSB20_DT_CONFIG) {
7164562Sgshapiro		return (NULL);		/* not config descriptor */
7264562Sgshapiro	}
7364562Sgshapiro	/*
7464562Sgshapiro	 * The first "bInterfaceNumber" should never have the value 0xff.
7564562Sgshapiro	 * Then it is corrupt.
7664562Sgshapiro	 */
7764562Sgshapiro	niface_no_alt = 0;
7864562Sgshapiro	nendpoint = 0;
7964562Sgshapiro	niface = 0;
8064562Sgshapiro	iface_no = 0 - 1;
8164562Sgshapiro	ptr = NULL;
8264562Sgshapiro
8364562Sgshapiro	/* get "wTotalLength" and setup "pcdesc" */
8464562Sgshapiro	pcdesc.ptr = LIBUSB20_ADD_BYTES(config_desc, 0);
8564562Sgshapiro	pcdesc.len =
8664562Sgshapiro	    ((const uint8_t *)config_desc)[2] |
8764562Sgshapiro	    (((const uint8_t *)config_desc)[3] << 8);
8864562Sgshapiro	pcdesc.type = LIBUSB20_ME_IS_RAW;
8964562Sgshapiro
9064562Sgshapiro	/* descriptor pre-scan */
9164562Sgshapiro	while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) {
9264562Sgshapiro		if (ptr[1] == LIBUSB20_DT_ENDPOINT) {
9364562Sgshapiro			nendpoint++;
9464562Sgshapiro		} else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) {
9564562Sgshapiro			niface++;
9664562Sgshapiro			/* check "bInterfaceNumber" */
9764562Sgshapiro			if (ptr[2] != iface_no) {
9864562Sgshapiro				iface_no = ptr[2];
9964562Sgshapiro				niface_no_alt++;
10064562Sgshapiro			}
10164562Sgshapiro		}
10264562Sgshapiro	}
10364562Sgshapiro
10464562Sgshapiro	/* sanity checking */
10564562Sgshapiro	if (niface >= 256) {
10664562Sgshapiro		return (NULL);		/* corrupt */
10764562Sgshapiro	}
10864562Sgshapiro	if (nendpoint >= 256) {
10964562Sgshapiro		return (NULL);		/* corrupt */
11064562Sgshapiro	}
11164562Sgshapiro	size = sizeof(*lub_config) +
11264562Sgshapiro	    (niface * sizeof(*lub_interface)) +
11364562Sgshapiro	    (nendpoint * sizeof(*lub_endpoint)) +
11464562Sgshapiro	    pcdesc.len;
11564562Sgshapiro
11664562Sgshapiro	lub_config = malloc(size);
11764562Sgshapiro	if (lub_config == NULL) {
11864562Sgshapiro		return (NULL);		/* out of memory */
11964562Sgshapiro	}
12064562Sgshapiro	/* make sure memory is initialised */
12164562Sgshapiro	memset(lub_config, 0, size);
12264562Sgshapiro
12364562Sgshapiro	lub_interface = (void *)(lub_config + 1);
12464562Sgshapiro	lub_alt_interface = (void *)(lub_interface + niface_no_alt);
12564562Sgshapiro	lub_endpoint = (void *)(lub_interface + niface);
12664562Sgshapiro
12764562Sgshapiro	/*
12864562Sgshapiro	 * Make a copy of the config descriptor, so that the caller can free
12964562Sgshapiro	 * the inital config descriptor pointer!
13064562Sgshapiro	 */
13164562Sgshapiro	ptr = (void *)(lub_endpoint + nendpoint);
13264562Sgshapiro	memcpy(LIBUSB20_ADD_BYTES(ptr, 0), config_desc, pcdesc.len);
13364562Sgshapiro	pcdesc.ptr = LIBUSB20_ADD_BYTES(ptr, 0);
13464562Sgshapiro	config_desc = LIBUSB20_ADD_BYTES(ptr, 0);
13564562Sgshapiro
13664562Sgshapiro	/* init config structure */
13764562Sgshapiro
13864562Sgshapiro	ptr = config_desc;
13964562Sgshapiro
14064562Sgshapiro	LIBUSB20_INIT(LIBUSB20_CONFIG_DESC, &lub_config->desc);
14164562Sgshapiro
14264562Sgshapiro	if (libusb20_me_decode(ptr, ptr[0], &lub_config->desc)) {
14364562Sgshapiro		/* ignore */
14464562Sgshapiro	}
14564562Sgshapiro	lub_config->num_interface = 0;
14664562Sgshapiro	lub_config->interface = lub_interface;
14764562Sgshapiro	lub_config->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
14864562Sgshapiro	lub_config->extra.len = -ptr[0];
14964562Sgshapiro	lub_config->extra.type = LIBUSB20_ME_IS_RAW;
15064562Sgshapiro
15164562Sgshapiro	/* reset states */
15264562Sgshapiro	niface = 0;
15364562Sgshapiro	iface_no = 0 - 1;
15464562Sgshapiro	ptr = NULL;
15564562Sgshapiro	lub_interface--;
15664562Sgshapiro	lub_endpoint--;
15764562Sgshapiro	last_if = NULL;
15864562Sgshapiro	last_ep = NULL;
15964562Sgshapiro
16064562Sgshapiro	/* descriptor pre-scan */
16164562Sgshapiro	while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) {
16264562Sgshapiro		if (ptr[1] == LIBUSB20_DT_ENDPOINT) {
16364562Sgshapiro			if (last_if) {
16464562Sgshapiro				lub_endpoint++;
16564562Sgshapiro				last_ep = lub_endpoint;
16664562Sgshapiro				last_if->num_endpoints++;
16764562Sgshapiro
16864562Sgshapiro				LIBUSB20_INIT(LIBUSB20_ENDPOINT_DESC, &last_ep->desc);
16964562Sgshapiro
17064562Sgshapiro				if (libusb20_me_decode(ptr, ptr[0], &last_ep->desc)) {
17164562Sgshapiro					/* ignore */
17264562Sgshapiro				}
17364562Sgshapiro				last_ep->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
17464562Sgshapiro				last_ep->extra.len = 0;
17564562Sgshapiro				last_ep->extra.type = LIBUSB20_ME_IS_RAW;
17664562Sgshapiro			} else {
17764562Sgshapiro				lub_config->extra.len += ptr[0];
17864562Sgshapiro			}
17964562Sgshapiro
18064562Sgshapiro		} else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) {
18164562Sgshapiro			if (ptr[2] != iface_no) {
18264562Sgshapiro				/* new interface */
18364562Sgshapiro				iface_no = ptr[2];
18464562Sgshapiro				lub_interface++;
18564562Sgshapiro				lub_config->num_interface++;
18664562Sgshapiro				last_if = lub_interface;
18764562Sgshapiro				niface++;
18864562Sgshapiro			} else {
18964562Sgshapiro				/* one more alternate setting */
19064562Sgshapiro				lub_interface->num_altsetting++;
19164562Sgshapiro				last_if = lub_alt_interface;
19264562Sgshapiro				lub_alt_interface++;
19364562Sgshapiro			}
19464562Sgshapiro
19564562Sgshapiro			LIBUSB20_INIT(LIBUSB20_INTERFACE_DESC, &last_if->desc);
19664562Sgshapiro
19764562Sgshapiro			if (libusb20_me_decode(ptr, ptr[0], &last_if->desc)) {
19864562Sgshapiro				/* ignore */
19964562Sgshapiro			}
20064562Sgshapiro			/*
20164562Sgshapiro			 * Sometimes USB devices have corrupt interface
20264562Sgshapiro			 * descriptors and we need to overwrite the provided
20364562Sgshapiro			 * interface number!
20464562Sgshapiro			 */
20564562Sgshapiro			last_if->desc.bInterfaceNumber = niface - 1;
20664562Sgshapiro			last_if->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
20764562Sgshapiro			last_if->extra.len = 0;
20864562Sgshapiro			last_if->extra.type = LIBUSB20_ME_IS_RAW;
20964562Sgshapiro			last_if->endpoints = lub_endpoint + 1;
21064562Sgshapiro			last_if->altsetting = lub_alt_interface;
21164562Sgshapiro			last_if->num_altsetting = 0;
21264562Sgshapiro			last_if->num_endpoints = 0;
21364562Sgshapiro			last_ep = NULL;
21464562Sgshapiro		} else {
21564562Sgshapiro			/* unknown descriptor */
21664562Sgshapiro			if (last_if) {
21764562Sgshapiro				if (last_ep) {
21864562Sgshapiro					last_ep->extra.len += ptr[0];
21964562Sgshapiro				} else {
22064562Sgshapiro					last_if->extra.len += ptr[0];
22164562Sgshapiro				}
22264562Sgshapiro			} else {
223				lub_config->extra.len += ptr[0];
224			}
225		}
226	}
227	return (lub_config);
228}
229
230/*------------------------------------------------------------------------*
231 *	libusb20_desc_foreach
232 *
233 * Safe traversal of USB descriptors.
234 *
235 * Return values:
236 * NULL: End of descriptors
237 * Else: Pointer to next descriptor
238 *------------------------------------------------------------------------*/
239const uint8_t *
240libusb20_desc_foreach(const struct libusb20_me_struct *pdesc,
241    const uint8_t *psubdesc)
242{
243	const uint8_t *start;
244	const uint8_t *end;
245	const uint8_t *desc_next;
246
247	/* be NULL safe */
248	if (pdesc == NULL)
249		return (NULL);
250
251	start = (const uint8_t *)pdesc->ptr;
252	end = LIBUSB20_ADD_BYTES(start, pdesc->len);
253
254	/* get start of next descriptor */
255	if (psubdesc == NULL)
256		psubdesc = start;
257	else
258		psubdesc = psubdesc + psubdesc[0];
259
260	/* check that the next USB descriptor is within the range */
261	if ((psubdesc < start) || (psubdesc >= end))
262		return (NULL);		/* out of range, or EOD */
263
264	/* check start of the second next USB descriptor, if any */
265	desc_next = psubdesc + psubdesc[0];
266	if ((desc_next < start) || (desc_next > end))
267		return (NULL);		/* out of range */
268
269	/* check minimum descriptor length */
270	if (psubdesc[0] < 3)
271		return (NULL);		/* too short descriptor */
272
273	return (psubdesc);		/* return start of next descriptor */
274}
275
276/*------------------------------------------------------------------------*
277 *	libusb20_me_get_1 - safety wrapper to read out one byte
278 *------------------------------------------------------------------------*/
279uint8_t
280libusb20_me_get_1(const struct libusb20_me_struct *ie, uint16_t offset)
281{
282	if (offset < ie->len) {
283		return (*((uint8_t *)LIBUSB20_ADD_BYTES(ie->ptr, offset)));
284	}
285	return (0);
286}
287
288/*------------------------------------------------------------------------*
289 *	libusb20_me_get_2 - safety wrapper to read out one word
290 *------------------------------------------------------------------------*/
291uint16_t
292libusb20_me_get_2(const struct libusb20_me_struct *ie, uint16_t offset)
293{
294	return (libusb20_me_get_1(ie, offset) |
295	    (libusb20_me_get_1(ie, offset + 1) << 8));
296}
297
298/*------------------------------------------------------------------------*
299 *	libusb20_me_encode - encode a message structure
300 *
301 * Description of parameters:
302 * "len" - maximum length of output buffer
303 * "ptr" - pointer to output buffer. If NULL, no data will be written
304 * "pd" - source structure
305 *
306 * Return values:
307 * 0..65535 - Number of bytes used, limited by the "len" input parameter.
308 *------------------------------------------------------------------------*/
309uint16_t
310libusb20_me_encode(void *ptr, uint16_t len, const void *pd)
311{
312	const uint8_t *pf;		/* pointer to format data */
313	uint8_t *buf;			/* pointer to output buffer */
314
315	uint32_t pd_offset;		/* decoded structure offset */
316	uint16_t len_old;		/* old length */
317	uint16_t pd_count;		/* decoded element count */
318	uint8_t me;			/* message element */
319
320	/* initialise */
321
322	len_old = len;
323	buf = ptr;
324	pd_offset = sizeof(void *);
325	pf = (*((struct libusb20_me_format *const *)pd))->format;
326
327	/* scan */
328
329	while (1) {
330
331		/* get information element */
332
333		me = (pf[0]) & LIBUSB20_ME_MASK;
334		pd_count = pf[1] | (pf[2] << 8);
335		pf += 3;
336
337		/* encode the message element */
338
339		switch (me) {
340		case LIBUSB20_ME_INT8:
341			while (pd_count--) {
342				uint8_t temp;
343
344				if (len < 1)	/* overflow */
345					goto done;
346				if (buf) {
347					temp = *((const uint8_t *)
348					    LIBUSB20_ADD_BYTES(pd, pd_offset));
349					buf[0] = temp;
350					buf += 1;
351				}
352				pd_offset += 1;
353				len -= 1;
354			}
355			break;
356
357		case LIBUSB20_ME_INT16:
358			pd_offset = -((-pd_offset) & ~1);	/* align */
359			while (pd_count--) {
360				uint16_t temp;
361
362				if (len < 2)	/* overflow */
363					goto done;
364
365				if (buf) {
366					temp = *((const uint16_t *)
367					    LIBUSB20_ADD_BYTES(pd, pd_offset));
368					buf[1] = (temp >> 8) & 0xFF;
369					buf[0] = temp & 0xFF;
370					buf += 2;
371				}
372				pd_offset += 2;
373				len -= 2;
374			}
375			break;
376
377		case LIBUSB20_ME_INT32:
378			pd_offset = -((-pd_offset) & ~3);	/* align */
379			while (pd_count--) {
380				uint32_t temp;
381
382				if (len < 4)	/* overflow */
383					goto done;
384				if (buf) {
385					temp = *((const uint32_t *)
386					    LIBUSB20_ADD_BYTES(pd, pd_offset));
387					buf[3] = (temp >> 24) & 0xFF;
388					buf[2] = (temp >> 16) & 0xFF;
389					buf[1] = (temp >> 8) & 0xFF;
390					buf[0] = temp & 0xFF;
391					buf += 4;
392				}
393				pd_offset += 4;
394				len -= 4;
395			}
396			break;
397
398		case LIBUSB20_ME_INT64:
399			pd_offset = -((-pd_offset) & ~7);	/* align */
400			while (pd_count--) {
401				uint64_t temp;
402
403				if (len < 8)	/* overflow */
404					goto done;
405				if (buf) {
406
407					temp = *((const uint64_t *)
408					    LIBUSB20_ADD_BYTES(pd, pd_offset));
409					buf[7] = (temp >> 56) & 0xFF;
410					buf[6] = (temp >> 48) & 0xFF;
411					buf[5] = (temp >> 40) & 0xFF;
412					buf[4] = (temp >> 32) & 0xFF;
413					buf[3] = (temp >> 24) & 0xFF;
414					buf[2] = (temp >> 16) & 0xFF;
415					buf[1] = (temp >> 8) & 0xFF;
416					buf[0] = temp & 0xFF;
417					buf += 8;
418				}
419				pd_offset += 8;
420				len -= 8;
421			}
422			break;
423
424		case LIBUSB20_ME_STRUCT:
425			pd_offset = -((-pd_offset) &
426			    ~(LIBUSB20_ME_STRUCT_ALIGN - 1));	/* align */
427			while (pd_count--) {
428				void *src_ptr;
429				uint16_t src_len;
430				struct libusb20_me_struct *ps;
431
432				ps = LIBUSB20_ADD_BYTES(pd, pd_offset);
433
434				switch (ps->type) {
435				case LIBUSB20_ME_IS_RAW:
436					src_len = ps->len;
437					src_ptr = ps->ptr;
438					break;
439
440				case LIBUSB20_ME_IS_ENCODED:
441					if (ps->len == 0) {
442						/*
443						 * Length is encoded
444						 * in the data itself
445						 * and should be
446						 * correct:
447						 */
448						ps->len = 0 - 1;
449					}
450					src_len = libusb20_me_get_1(pd, 0);
451					src_ptr = LIBUSB20_ADD_BYTES(ps->ptr, 1);
452					if (src_len == 0xFF) {
453						/* length is escaped */
454						src_len = libusb20_me_get_2(pd, 1);
455						src_ptr =
456						    LIBUSB20_ADD_BYTES(ps->ptr, 3);
457					}
458					break;
459
460				case LIBUSB20_ME_IS_DECODED:
461					/* reserve 3 length bytes */
462					src_len = libusb20_me_encode(NULL,
463					    0 - 1 - 3, ps->ptr);
464					src_ptr = NULL;
465					break;
466
467				default:	/* empty structure */
468					src_len = 0;
469					src_ptr = NULL;
470					break;
471				}
472
473				if (src_len > 0xFE) {
474					if (src_len > (uint16_t)(0 - 1 - 3))
475						/* overflow */
476						goto done;
477
478					if (len < (src_len + 3))
479						/* overflow */
480						goto done;
481
482					if (buf) {
483						buf[0] = 0xFF;
484						buf[1] = (src_len & 0xFF);
485						buf[2] = (src_len >> 8) & 0xFF;
486						buf += 3;
487					}
488					len -= (src_len + 3);
489				} else {
490					if (len < (src_len + 1))
491						/* overflow */
492						goto done;
493
494					if (buf) {
495						buf[0] = (src_len & 0xFF);
496						buf += 1;
497					}
498					len -= (src_len + 1);
499				}
500
501				/* check for buffer and non-zero length */
502
503				if (buf && src_len) {
504					if (ps->type == LIBUSB20_ME_IS_DECODED) {
505						/*
506						 * Repeat encode
507						 * procedure - we have
508						 * room for the
509						 * complete structure:
510						 */
511						uint16_t dummy;
512
513						dummy = libusb20_me_encode(buf,
514						    0 - 1 - 3, ps->ptr);
515					} else {
516						bcopy(src_ptr, buf, src_len);
517					}
518					buf += src_len;
519				}
520				pd_offset += sizeof(struct libusb20_me_struct);
521			}
522			break;
523
524		default:
525			goto done;
526		}
527	}
528done:
529	return (len_old - len);
530}
531
532/*------------------------------------------------------------------------*
533 *	libusb20_me_decode - decode a message into a decoded structure
534 *
535 * Description of parameters:
536 * "ptr" - message pointer
537 * "len" - message length
538 * "pd" - pointer to decoded structure
539 *
540 * Returns:
541 * "0..65535" - number of bytes decoded, limited by "len"
542 *------------------------------------------------------------------------*/
543uint16_t
544libusb20_me_decode(const void *ptr, uint16_t len, void *pd)
545{
546	const uint8_t *pf;		/* pointer to format data */
547	const uint8_t *buf;		/* pointer to input buffer */
548
549	uint32_t pd_offset;		/* decoded structure offset */
550	uint16_t len_old;		/* old length */
551	uint16_t pd_count;		/* decoded element count */
552	uint8_t me;			/* message element */
553
554	/* initialise */
555
556	len_old = len;
557	buf = ptr;
558	pd_offset = sizeof(void *);
559	pf = (*((struct libusb20_me_format **)pd))->format;
560
561	/* scan */
562
563	while (1) {
564
565		/* get information element */
566
567		me = (pf[0]) & LIBUSB20_ME_MASK;
568		pd_count = pf[1] | (pf[2] << 8);
569		pf += 3;
570
571		/* decode the message element by type */
572
573		switch (me) {
574		case LIBUSB20_ME_INT8:
575			while (pd_count--) {
576				uint8_t temp;
577
578				if (len < 1) {
579					len = 0;
580					temp = 0;
581				} else {
582					len -= 1;
583					temp = buf[0];
584					buf++;
585				}
586				*((uint8_t *)LIBUSB20_ADD_BYTES(pd,
587				    pd_offset)) = temp;
588				pd_offset += 1;
589			}
590			break;
591
592		case LIBUSB20_ME_INT16:
593			pd_offset = -((-pd_offset) & ~1);	/* align */
594			while (pd_count--) {
595				uint16_t temp;
596
597				if (len < 2) {
598					len = 0;
599					temp = 0;
600				} else {
601					len -= 2;
602					temp = buf[1] << 8;
603					temp |= buf[0];
604					buf += 2;
605				}
606				*((uint16_t *)LIBUSB20_ADD_BYTES(pd,
607				    pd_offset)) = temp;
608				pd_offset += 2;
609			}
610			break;
611
612		case LIBUSB20_ME_INT32:
613			pd_offset = -((-pd_offset) & ~3);	/* align */
614			while (pd_count--) {
615				uint32_t temp;
616
617				if (len < 4) {
618					len = 0;
619					temp = 0;
620				} else {
621					len -= 4;
622					temp = buf[3] << 24;
623					temp |= buf[2] << 16;
624					temp |= buf[1] << 8;
625					temp |= buf[0];
626					buf += 4;
627				}
628
629				*((uint32_t *)LIBUSB20_ADD_BYTES(pd,
630				    pd_offset)) = temp;
631				pd_offset += 4;
632			}
633			break;
634
635		case LIBUSB20_ME_INT64:
636			pd_offset = -((-pd_offset) & ~7);	/* align */
637			while (pd_count--) {
638				uint64_t temp;
639
640				if (len < 8) {
641					len = 0;
642					temp = 0;
643				} else {
644					len -= 8;
645					temp = ((uint64_t)buf[7]) << 56;
646					temp |= ((uint64_t)buf[6]) << 48;
647					temp |= ((uint64_t)buf[5]) << 40;
648					temp |= ((uint64_t)buf[4]) << 32;
649					temp |= buf[3] << 24;
650					temp |= buf[2] << 16;
651					temp |= buf[1] << 8;
652					temp |= buf[0];
653					buf += 8;
654				}
655
656				*((uint64_t *)LIBUSB20_ADD_BYTES(pd,
657				    pd_offset)) = temp;
658				pd_offset += 8;
659			}
660			break;
661
662		case LIBUSB20_ME_STRUCT:
663			pd_offset = -((-pd_offset) &
664			    ~(LIBUSB20_ME_STRUCT_ALIGN - 1));	/* align */
665			while (pd_count--) {
666				uint16_t temp;
667				uint16_t dummy;
668				struct libusb20_me_struct *ps;
669
670				ps = LIBUSB20_ADD_BYTES(pd, pd_offset);
671
672				if (ps->type == LIBUSB20_ME_IS_ENCODED) {
673					/*
674					 * Pre-store a de-constified
675					 * pointer to the raw
676					 * structure:
677					 */
678					ps->ptr = LIBUSB20_ADD_BYTES(buf, 0);
679
680					/*
681					 * Get the correct number of
682					 * length bytes:
683					 */
684					if (len != 0) {
685						if (buf[0] == 0xFF) {
686							ps->len = 3;
687						} else {
688							ps->len = 1;
689						}
690					} else {
691						ps->len = 0;
692					}
693				}
694				/* get the structure length */
695
696				if (len != 0) {
697					if (buf[0] == 0xFF) {
698						if (len < 3) {
699							len = 0;
700							temp = 0;
701						} else {
702							len -= 3;
703							temp = buf[1] |
704							    (buf[2] << 8);
705							buf += 3;
706						}
707					} else {
708						len -= 1;
709						temp = buf[0];
710						buf += 1;
711					}
712				} else {
713					len = 0;
714					temp = 0;
715				}
716				/* check for invalid length */
717
718				if (temp > len) {
719					len = 0;
720					temp = 0;
721				}
722				/* check wanted structure type */
723
724				switch (ps->type) {
725				case LIBUSB20_ME_IS_ENCODED:
726					/* check for zero length */
727					if (temp == 0) {
728						/*
729						 * The pointer must
730						 * be valid:
731						 */
732						ps->ptr = LIBUSB20_ADD_BYTES(
733						    libusb20_me_encode_empty, 0);
734						ps->len = 1;
735					} else {
736						ps->len += temp;
737					}
738					break;
739
740				case LIBUSB20_ME_IS_RAW:
741					/* update length and pointer */
742					ps->len = temp;
743					ps->ptr = LIBUSB20_ADD_BYTES(buf, 0);
744					break;
745
746				case LIBUSB20_ME_IS_EMPTY:
747				case LIBUSB20_ME_IS_DECODED:
748					/* check for non-zero length */
749					if (temp != 0) {
750						/* update type */
751						ps->type = LIBUSB20_ME_IS_DECODED;
752						ps->len = 0;
753						/*
754						 * Recursivly decode
755						 * the next structure
756						 */
757						dummy = libusb20_me_decode(buf,
758						    temp, ps->ptr);
759					} else {
760						/* update type */
761						ps->type = LIBUSB20_ME_IS_EMPTY;
762						ps->len = 0;
763					}
764					break;
765
766				default:
767					/*
768					 * nothing to do - should
769					 * not happen
770					 */
771					ps->ptr = NULL;
772					ps->len = 0;
773					break;
774				}
775				buf += temp;
776				len -= temp;
777				pd_offset += sizeof(struct libusb20_me_struct);
778			}
779			break;
780
781		default:
782			goto done;
783		}
784	}
785done:
786	return (len_old - len);
787}
788