xref: /dflybsd-src/lib/libusb/libusb20_desc.c (revision 3f7b72606e8fadb0b2e9e32302c89298c107534b)
19b0c1abeSSascha Wildner /* $FreeBSD: head/lib/libusb/libusb20_desc.c 248236 2013-03-13 12:23:14Z hselasky $ */
21d96047eSMarkus Pfeiffer /*-
31d96047eSMarkus Pfeiffer  * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
41d96047eSMarkus Pfeiffer  *
51d96047eSMarkus Pfeiffer  * Redistribution and use in source and binary forms, with or without
61d96047eSMarkus Pfeiffer  * modification, are permitted provided that the following conditions
71d96047eSMarkus Pfeiffer  * are met:
81d96047eSMarkus Pfeiffer  * 1. Redistributions of source code must retain the above copyright
91d96047eSMarkus Pfeiffer  *    notice, this list of conditions and the following disclaimer.
101d96047eSMarkus Pfeiffer  * 2. Redistributions in binary form must reproduce the above copyright
111d96047eSMarkus Pfeiffer  *    notice, this list of conditions and the following disclaimer in the
121d96047eSMarkus Pfeiffer  *    documentation and/or other materials provided with the distribution.
131d96047eSMarkus Pfeiffer  *
141d96047eSMarkus Pfeiffer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
151d96047eSMarkus Pfeiffer  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161d96047eSMarkus Pfeiffer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171d96047eSMarkus Pfeiffer  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
181d96047eSMarkus Pfeiffer  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191d96047eSMarkus Pfeiffer  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201d96047eSMarkus Pfeiffer  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211d96047eSMarkus Pfeiffer  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221d96047eSMarkus Pfeiffer  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231d96047eSMarkus Pfeiffer  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241d96047eSMarkus Pfeiffer  * SUCH DAMAGE.
251d96047eSMarkus Pfeiffer  */
261d96047eSMarkus Pfeiffer 
279b0c1abeSSascha Wildner #ifdef LIBUSB_GLOBAL_INCLUDE_FILE
289b0c1abeSSascha Wildner #include LIBUSB_GLOBAL_INCLUDE_FILE
299b0c1abeSSascha Wildner #else
301d96047eSMarkus Pfeiffer #include <stdio.h>
311d96047eSMarkus Pfeiffer #include <stdlib.h>
321d96047eSMarkus Pfeiffer #include <string.h>
339b0c1abeSSascha Wildner #include <time.h>
34*3f7b7260SSascha Wildner #include <sys/param.h>
359b0c1abeSSascha Wildner #include <sys/queue.h>
369b0c1abeSSascha Wildner #endif
371d96047eSMarkus Pfeiffer 
381d96047eSMarkus Pfeiffer #include "libusb20.h"
391d96047eSMarkus Pfeiffer #include "libusb20_desc.h"
401d96047eSMarkus Pfeiffer #include "libusb20_int.h"
411d96047eSMarkus Pfeiffer 
421d96047eSMarkus Pfeiffer static const uint32_t libusb20_me_encode_empty[2];	/* dummy */
431d96047eSMarkus Pfeiffer 
441d96047eSMarkus Pfeiffer LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_DEVICE_DESC);
451d96047eSMarkus Pfeiffer LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_ENDPOINT_DESC);
461d96047eSMarkus Pfeiffer LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_INTERFACE_DESC);
471d96047eSMarkus Pfeiffer LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONFIG_DESC);
481d96047eSMarkus Pfeiffer LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONTROL_SETUP);
491d96047eSMarkus Pfeiffer LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_ENDPT_COMP_DESC);
501d96047eSMarkus Pfeiffer LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_USB_20_DEVCAP_DESC);
511d96047eSMarkus Pfeiffer LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_USB_DEVCAP_DESC);
521d96047eSMarkus Pfeiffer LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_BOS_DESCRIPTOR);
531d96047eSMarkus Pfeiffer 
541d96047eSMarkus Pfeiffer /*------------------------------------------------------------------------*
551d96047eSMarkus Pfeiffer  *	libusb20_parse_config_desc
561d96047eSMarkus Pfeiffer  *
571d96047eSMarkus Pfeiffer  * Return values:
581d96047eSMarkus Pfeiffer  * NULL: Out of memory.
591d96047eSMarkus Pfeiffer  * Else: A valid config structure pointer which must be passed to "free()"
601d96047eSMarkus Pfeiffer  *------------------------------------------------------------------------*/
611d96047eSMarkus Pfeiffer struct libusb20_config *
libusb20_parse_config_desc(const void * config_desc)621d96047eSMarkus Pfeiffer libusb20_parse_config_desc(const void *config_desc)
631d96047eSMarkus Pfeiffer {
641d96047eSMarkus Pfeiffer 	struct libusb20_config *lub_config;
651d96047eSMarkus Pfeiffer 	struct libusb20_interface *lub_interface;
661d96047eSMarkus Pfeiffer 	struct libusb20_interface *lub_alt_interface;
671d96047eSMarkus Pfeiffer 	struct libusb20_interface *last_if;
681d96047eSMarkus Pfeiffer 	struct libusb20_endpoint *lub_endpoint;
691d96047eSMarkus Pfeiffer 	struct libusb20_endpoint *last_ep;
701d96047eSMarkus Pfeiffer 
711d96047eSMarkus Pfeiffer 	struct libusb20_me_struct pcdesc;
721d96047eSMarkus Pfeiffer 	const uint8_t *ptr;
731d96047eSMarkus Pfeiffer 	uint32_t size;
741d96047eSMarkus Pfeiffer 	uint16_t niface_no_alt;
751d96047eSMarkus Pfeiffer 	uint16_t niface;
761d96047eSMarkus Pfeiffer 	uint16_t nendpoint;
77aa3e5c14SSascha Wildner 	uint16_t iface_no;
781d96047eSMarkus Pfeiffer 
791d96047eSMarkus Pfeiffer 	ptr = config_desc;
801d96047eSMarkus Pfeiffer 	if (ptr[1] != LIBUSB20_DT_CONFIG) {
811d96047eSMarkus Pfeiffer 		return (NULL);		/* not config descriptor */
821d96047eSMarkus Pfeiffer 	}
831d96047eSMarkus Pfeiffer 	/*
841d96047eSMarkus Pfeiffer 	 * The first "bInterfaceNumber" should never have the value 0xff.
851d96047eSMarkus Pfeiffer 	 * Then it is corrupt.
861d96047eSMarkus Pfeiffer 	 */
871d96047eSMarkus Pfeiffer 	niface_no_alt = 0;
881d96047eSMarkus Pfeiffer 	nendpoint = 0;
891d96047eSMarkus Pfeiffer 	niface = 0;
90aa3e5c14SSascha Wildner 	iface_no = 0xFFFF;
911d96047eSMarkus Pfeiffer 	ptr = NULL;
921d96047eSMarkus Pfeiffer 
931d96047eSMarkus Pfeiffer 	/* get "wTotalLength" and setup "pcdesc" */
941d96047eSMarkus Pfeiffer 	pcdesc.ptr = LIBUSB20_ADD_BYTES(config_desc, 0);
951d96047eSMarkus Pfeiffer 	pcdesc.len =
961d96047eSMarkus Pfeiffer 	    ((const uint8_t *)config_desc)[2] |
971d96047eSMarkus Pfeiffer 	    (((const uint8_t *)config_desc)[3] << 8);
981d96047eSMarkus Pfeiffer 	pcdesc.type = LIBUSB20_ME_IS_RAW;
991d96047eSMarkus Pfeiffer 
1001d96047eSMarkus Pfeiffer 	/* descriptor pre-scan */
1011d96047eSMarkus Pfeiffer 	while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) {
1021d96047eSMarkus Pfeiffer 		if (ptr[1] == LIBUSB20_DT_ENDPOINT) {
1031d96047eSMarkus Pfeiffer 			nendpoint++;
1041d96047eSMarkus Pfeiffer 		} else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) {
1051d96047eSMarkus Pfeiffer 			niface++;
1061d96047eSMarkus Pfeiffer 			/* check "bInterfaceNumber" */
1071d96047eSMarkus Pfeiffer 			if (ptr[2] != iface_no) {
1081d96047eSMarkus Pfeiffer 				iface_no = ptr[2];
1091d96047eSMarkus Pfeiffer 				niface_no_alt++;
1101d96047eSMarkus Pfeiffer 			}
1111d96047eSMarkus Pfeiffer 		}
1121d96047eSMarkus Pfeiffer 	}
1131d96047eSMarkus Pfeiffer 
1141d96047eSMarkus Pfeiffer 	/* sanity checking */
1151d96047eSMarkus Pfeiffer 	if (niface >= 256) {
1161d96047eSMarkus Pfeiffer 		return (NULL);		/* corrupt */
1171d96047eSMarkus Pfeiffer 	}
1181d96047eSMarkus Pfeiffer 	if (nendpoint >= 256) {
1191d96047eSMarkus Pfeiffer 		return (NULL);		/* corrupt */
1201d96047eSMarkus Pfeiffer 	}
1211d96047eSMarkus Pfeiffer 	size = sizeof(*lub_config) +
1221d96047eSMarkus Pfeiffer 	    (niface * sizeof(*lub_interface)) +
1231d96047eSMarkus Pfeiffer 	    (nendpoint * sizeof(*lub_endpoint)) +
1241d96047eSMarkus Pfeiffer 	    pcdesc.len;
1251d96047eSMarkus Pfeiffer 
1261d96047eSMarkus Pfeiffer 	lub_config = malloc(size);
1271d96047eSMarkus Pfeiffer 	if (lub_config == NULL) {
1281d96047eSMarkus Pfeiffer 		return (NULL);		/* out of memory */
1291d96047eSMarkus Pfeiffer 	}
1301d96047eSMarkus Pfeiffer 	/* make sure memory is initialised */
1311d96047eSMarkus Pfeiffer 	memset(lub_config, 0, size);
1321d96047eSMarkus Pfeiffer 
1331d96047eSMarkus Pfeiffer 	lub_interface = (void *)(lub_config + 1);
1341d96047eSMarkus Pfeiffer 	lub_alt_interface = (void *)(lub_interface + niface_no_alt);
1351d96047eSMarkus Pfeiffer 	lub_endpoint = (void *)(lub_interface + niface);
1361d96047eSMarkus Pfeiffer 
1371d96047eSMarkus Pfeiffer 	/*
1381d96047eSMarkus Pfeiffer 	 * Make a copy of the config descriptor, so that the caller can free
1391d96047eSMarkus Pfeiffer 	 * the inital config descriptor pointer!
1401d96047eSMarkus Pfeiffer 	 */
1411d96047eSMarkus Pfeiffer 	ptr = (void *)(lub_endpoint + nendpoint);
1421d96047eSMarkus Pfeiffer 	memcpy(LIBUSB20_ADD_BYTES(ptr, 0), config_desc, pcdesc.len);
1431d96047eSMarkus Pfeiffer 	pcdesc.ptr = LIBUSB20_ADD_BYTES(ptr, 0);
1441d96047eSMarkus Pfeiffer 	config_desc = LIBUSB20_ADD_BYTES(ptr, 0);
1451d96047eSMarkus Pfeiffer 
1461d96047eSMarkus Pfeiffer 	/* init config structure */
1471d96047eSMarkus Pfeiffer 
1481d96047eSMarkus Pfeiffer 	ptr = config_desc;
1491d96047eSMarkus Pfeiffer 
1501d96047eSMarkus Pfeiffer 	LIBUSB20_INIT(LIBUSB20_CONFIG_DESC, &lub_config->desc);
1511d96047eSMarkus Pfeiffer 
1521d96047eSMarkus Pfeiffer 	if (libusb20_me_decode(ptr, ptr[0], &lub_config->desc)) {
1531d96047eSMarkus Pfeiffer 		/* ignore */
1541d96047eSMarkus Pfeiffer 	}
1551d96047eSMarkus Pfeiffer 	lub_config->num_interface = 0;
1561d96047eSMarkus Pfeiffer 	lub_config->interface = lub_interface;
1571d96047eSMarkus Pfeiffer 	lub_config->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
1581d96047eSMarkus Pfeiffer 	lub_config->extra.len = -ptr[0];
1591d96047eSMarkus Pfeiffer 	lub_config->extra.type = LIBUSB20_ME_IS_RAW;
1601d96047eSMarkus Pfeiffer 
1611d96047eSMarkus Pfeiffer 	/* reset states */
1621d96047eSMarkus Pfeiffer 	niface = 0;
163aa3e5c14SSascha Wildner 	iface_no = 0xFFFF;
1641d96047eSMarkus Pfeiffer 	ptr = NULL;
1651d96047eSMarkus Pfeiffer 	lub_interface--;
1661d96047eSMarkus Pfeiffer 	lub_endpoint--;
1671d96047eSMarkus Pfeiffer 	last_if = NULL;
1681d96047eSMarkus Pfeiffer 	last_ep = NULL;
1691d96047eSMarkus Pfeiffer 
1701d96047eSMarkus Pfeiffer 	/* descriptor pre-scan */
1711d96047eSMarkus Pfeiffer 	while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) {
1721d96047eSMarkus Pfeiffer 		if (ptr[1] == LIBUSB20_DT_ENDPOINT) {
1731d96047eSMarkus Pfeiffer 			if (last_if) {
1741d96047eSMarkus Pfeiffer 				lub_endpoint++;
1751d96047eSMarkus Pfeiffer 				last_ep = lub_endpoint;
1761d96047eSMarkus Pfeiffer 				last_if->num_endpoints++;
1771d96047eSMarkus Pfeiffer 
1781d96047eSMarkus Pfeiffer 				LIBUSB20_INIT(LIBUSB20_ENDPOINT_DESC, &last_ep->desc);
1791d96047eSMarkus Pfeiffer 
1801d96047eSMarkus Pfeiffer 				if (libusb20_me_decode(ptr, ptr[0], &last_ep->desc)) {
1811d96047eSMarkus Pfeiffer 					/* ignore */
1821d96047eSMarkus Pfeiffer 				}
1831d96047eSMarkus Pfeiffer 				last_ep->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
1841d96047eSMarkus Pfeiffer 				last_ep->extra.len = 0;
1851d96047eSMarkus Pfeiffer 				last_ep->extra.type = LIBUSB20_ME_IS_RAW;
1861d96047eSMarkus Pfeiffer 			} else {
1871d96047eSMarkus Pfeiffer 				lub_config->extra.len += ptr[0];
1881d96047eSMarkus Pfeiffer 			}
1891d96047eSMarkus Pfeiffer 
1901d96047eSMarkus Pfeiffer 		} else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) {
1911d96047eSMarkus Pfeiffer 			if (ptr[2] != iface_no) {
1921d96047eSMarkus Pfeiffer 				/* new interface */
1931d96047eSMarkus Pfeiffer 				iface_no = ptr[2];
1941d96047eSMarkus Pfeiffer 				lub_interface++;
1951d96047eSMarkus Pfeiffer 				lub_config->num_interface++;
1961d96047eSMarkus Pfeiffer 				last_if = lub_interface;
1971d96047eSMarkus Pfeiffer 				niface++;
1981d96047eSMarkus Pfeiffer 			} else {
1991d96047eSMarkus Pfeiffer 				/* one more alternate setting */
2001d96047eSMarkus Pfeiffer 				lub_interface->num_altsetting++;
2011d96047eSMarkus Pfeiffer 				last_if = lub_alt_interface;
2021d96047eSMarkus Pfeiffer 				lub_alt_interface++;
2031d96047eSMarkus Pfeiffer 			}
2041d96047eSMarkus Pfeiffer 
2051d96047eSMarkus Pfeiffer 			LIBUSB20_INIT(LIBUSB20_INTERFACE_DESC, &last_if->desc);
2061d96047eSMarkus Pfeiffer 
2071d96047eSMarkus Pfeiffer 			if (libusb20_me_decode(ptr, ptr[0], &last_if->desc)) {
2081d96047eSMarkus Pfeiffer 				/* ignore */
2091d96047eSMarkus Pfeiffer 			}
2101d96047eSMarkus Pfeiffer 			/*
2111d96047eSMarkus Pfeiffer 			 * Sometimes USB devices have corrupt interface
2121d96047eSMarkus Pfeiffer 			 * descriptors and we need to overwrite the provided
2131d96047eSMarkus Pfeiffer 			 * interface number!
2141d96047eSMarkus Pfeiffer 			 */
2151d96047eSMarkus Pfeiffer 			last_if->desc.bInterfaceNumber = niface - 1;
2161d96047eSMarkus Pfeiffer 			last_if->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
2171d96047eSMarkus Pfeiffer 			last_if->extra.len = 0;
2181d96047eSMarkus Pfeiffer 			last_if->extra.type = LIBUSB20_ME_IS_RAW;
2191d96047eSMarkus Pfeiffer 			last_if->endpoints = lub_endpoint + 1;
2201d96047eSMarkus Pfeiffer 			last_if->altsetting = lub_alt_interface;
2211d96047eSMarkus Pfeiffer 			last_if->num_altsetting = 0;
2221d96047eSMarkus Pfeiffer 			last_if->num_endpoints = 0;
2231d96047eSMarkus Pfeiffer 			last_ep = NULL;
2241d96047eSMarkus Pfeiffer 		} else {
2251d96047eSMarkus Pfeiffer 			/* unknown descriptor */
2261d96047eSMarkus Pfeiffer 			if (last_if) {
2271d96047eSMarkus Pfeiffer 				if (last_ep) {
2281d96047eSMarkus Pfeiffer 					last_ep->extra.len += ptr[0];
2291d96047eSMarkus Pfeiffer 				} else {
2301d96047eSMarkus Pfeiffer 					last_if->extra.len += ptr[0];
2311d96047eSMarkus Pfeiffer 				}
2321d96047eSMarkus Pfeiffer 			} else {
2331d96047eSMarkus Pfeiffer 				lub_config->extra.len += ptr[0];
2341d96047eSMarkus Pfeiffer 			}
2351d96047eSMarkus Pfeiffer 		}
2361d96047eSMarkus Pfeiffer 	}
2371d96047eSMarkus Pfeiffer 	return (lub_config);
2381d96047eSMarkus Pfeiffer }
2391d96047eSMarkus Pfeiffer 
2401d96047eSMarkus Pfeiffer /*------------------------------------------------------------------------*
2411d96047eSMarkus Pfeiffer  *	libusb20_desc_foreach
2421d96047eSMarkus Pfeiffer  *
2431d96047eSMarkus Pfeiffer  * Safe traversal of USB descriptors.
2441d96047eSMarkus Pfeiffer  *
2451d96047eSMarkus Pfeiffer  * Return values:
2461d96047eSMarkus Pfeiffer  * NULL: End of descriptors
2471d96047eSMarkus Pfeiffer  * Else: Pointer to next descriptor
2481d96047eSMarkus Pfeiffer  *------------------------------------------------------------------------*/
2491d96047eSMarkus Pfeiffer const uint8_t *
libusb20_desc_foreach(const struct libusb20_me_struct * pdesc,const uint8_t * psubdesc)2501d96047eSMarkus Pfeiffer libusb20_desc_foreach(const struct libusb20_me_struct *pdesc,
2511d96047eSMarkus Pfeiffer     const uint8_t *psubdesc)
2521d96047eSMarkus Pfeiffer {
2531d96047eSMarkus Pfeiffer 	const uint8_t *start;
2541d96047eSMarkus Pfeiffer 	const uint8_t *end;
2551d96047eSMarkus Pfeiffer 	const uint8_t *desc_next;
2561d96047eSMarkus Pfeiffer 
2571d96047eSMarkus Pfeiffer 	/* be NULL safe */
2581d96047eSMarkus Pfeiffer 	if (pdesc == NULL)
2591d96047eSMarkus Pfeiffer 		return (NULL);
2601d96047eSMarkus Pfeiffer 
2611d96047eSMarkus Pfeiffer 	start = (const uint8_t *)pdesc->ptr;
2621d96047eSMarkus Pfeiffer 	end = LIBUSB20_ADD_BYTES(start, pdesc->len);
2631d96047eSMarkus Pfeiffer 
2641d96047eSMarkus Pfeiffer 	/* get start of next descriptor */
2651d96047eSMarkus Pfeiffer 	if (psubdesc == NULL)
2661d96047eSMarkus Pfeiffer 		psubdesc = start;
2671d96047eSMarkus Pfeiffer 	else
2681d96047eSMarkus Pfeiffer 		psubdesc = psubdesc + psubdesc[0];
2691d96047eSMarkus Pfeiffer 
2701d96047eSMarkus Pfeiffer 	/* check that the next USB descriptor is within the range */
2711d96047eSMarkus Pfeiffer 	if ((psubdesc < start) || (psubdesc >= end))
2721d96047eSMarkus Pfeiffer 		return (NULL);		/* out of range, or EOD */
2731d96047eSMarkus Pfeiffer 
2741d96047eSMarkus Pfeiffer 	/* check start of the second next USB descriptor, if any */
2751d96047eSMarkus Pfeiffer 	desc_next = psubdesc + psubdesc[0];
2761d96047eSMarkus Pfeiffer 	if ((desc_next < start) || (desc_next > end))
2771d96047eSMarkus Pfeiffer 		return (NULL);		/* out of range */
2781d96047eSMarkus Pfeiffer 
2791d96047eSMarkus Pfeiffer 	/* check minimum descriptor length */
2801d96047eSMarkus Pfeiffer 	if (psubdesc[0] < 3)
2811d96047eSMarkus Pfeiffer 		return (NULL);		/* too short descriptor */
2821d96047eSMarkus Pfeiffer 
2831d96047eSMarkus Pfeiffer 	return (psubdesc);		/* return start of next descriptor */
2841d96047eSMarkus Pfeiffer }
2851d96047eSMarkus Pfeiffer 
2861d96047eSMarkus Pfeiffer /*------------------------------------------------------------------------*
2871d96047eSMarkus Pfeiffer  *	libusb20_me_get_1 - safety wrapper to read out one byte
2881d96047eSMarkus Pfeiffer  *------------------------------------------------------------------------*/
2891d96047eSMarkus Pfeiffer uint8_t
libusb20_me_get_1(const struct libusb20_me_struct * ie,uint16_t offset)2901d96047eSMarkus Pfeiffer libusb20_me_get_1(const struct libusb20_me_struct *ie, uint16_t offset)
2911d96047eSMarkus Pfeiffer {
2921d96047eSMarkus Pfeiffer 	if (offset < ie->len) {
2931d96047eSMarkus Pfeiffer 		return (*((uint8_t *)LIBUSB20_ADD_BYTES(ie->ptr, offset)));
2941d96047eSMarkus Pfeiffer 	}
2951d96047eSMarkus Pfeiffer 	return (0);
2961d96047eSMarkus Pfeiffer }
2971d96047eSMarkus Pfeiffer 
2981d96047eSMarkus Pfeiffer /*------------------------------------------------------------------------*
2991d96047eSMarkus Pfeiffer  *	libusb20_me_get_2 - safety wrapper to read out one word
3001d96047eSMarkus Pfeiffer  *------------------------------------------------------------------------*/
3011d96047eSMarkus Pfeiffer uint16_t
libusb20_me_get_2(const struct libusb20_me_struct * ie,uint16_t offset)3021d96047eSMarkus Pfeiffer libusb20_me_get_2(const struct libusb20_me_struct *ie, uint16_t offset)
3031d96047eSMarkus Pfeiffer {
3041d96047eSMarkus Pfeiffer 	return (libusb20_me_get_1(ie, offset) |
3051d96047eSMarkus Pfeiffer 	    (libusb20_me_get_1(ie, offset + 1) << 8));
3061d96047eSMarkus Pfeiffer }
3071d96047eSMarkus Pfeiffer 
3081d96047eSMarkus Pfeiffer /*------------------------------------------------------------------------*
3091d96047eSMarkus Pfeiffer  *	libusb20_me_encode - encode a message structure
3101d96047eSMarkus Pfeiffer  *
3111d96047eSMarkus Pfeiffer  * Description of parameters:
3121d96047eSMarkus Pfeiffer  * "len" - maximum length of output buffer
3131d96047eSMarkus Pfeiffer  * "ptr" - pointer to output buffer. If NULL, no data will be written
3141d96047eSMarkus Pfeiffer  * "pd" - source structure
3151d96047eSMarkus Pfeiffer  *
3161d96047eSMarkus Pfeiffer  * Return values:
3171d96047eSMarkus Pfeiffer  * 0..65535 - Number of bytes used, limited by the "len" input parameter.
3181d96047eSMarkus Pfeiffer  *------------------------------------------------------------------------*/
3191d96047eSMarkus Pfeiffer uint16_t
libusb20_me_encode(void * ptr,uint16_t len,const void * pd)3201d96047eSMarkus Pfeiffer libusb20_me_encode(void *ptr, uint16_t len, const void *pd)
3211d96047eSMarkus Pfeiffer {
3221d96047eSMarkus Pfeiffer 	const uint8_t *pf;		/* pointer to format data */
3231d96047eSMarkus Pfeiffer 	uint8_t *buf;			/* pointer to output buffer */
3241d96047eSMarkus Pfeiffer 
3251d96047eSMarkus Pfeiffer 	uint32_t pd_offset;		/* decoded structure offset */
3261d96047eSMarkus Pfeiffer 	uint16_t len_old;		/* old length */
3271d96047eSMarkus Pfeiffer 	uint16_t pd_count;		/* decoded element count */
3281d96047eSMarkus Pfeiffer 	uint8_t me;			/* message element */
3291d96047eSMarkus Pfeiffer 
3301d96047eSMarkus Pfeiffer 	/* initialise */
3311d96047eSMarkus Pfeiffer 
3321d96047eSMarkus Pfeiffer 	len_old = len;
3331d96047eSMarkus Pfeiffer 	buf = ptr;
3341d96047eSMarkus Pfeiffer 	pd_offset = sizeof(void *);
3351d96047eSMarkus Pfeiffer 	pf = (*((struct libusb20_me_format *const *)pd))->format;
3361d96047eSMarkus Pfeiffer 
3371d96047eSMarkus Pfeiffer 	/* scan */
3381d96047eSMarkus Pfeiffer 
3391d96047eSMarkus Pfeiffer 	while (1) {
3401d96047eSMarkus Pfeiffer 
3411d96047eSMarkus Pfeiffer 		/* get information element */
3421d96047eSMarkus Pfeiffer 
3431d96047eSMarkus Pfeiffer 		me = (pf[0]) & LIBUSB20_ME_MASK;
3441d96047eSMarkus Pfeiffer 		pd_count = pf[1] | (pf[2] << 8);
3451d96047eSMarkus Pfeiffer 		pf += 3;
3461d96047eSMarkus Pfeiffer 
3471d96047eSMarkus Pfeiffer 		/* encode the message element */
3481d96047eSMarkus Pfeiffer 
3491d96047eSMarkus Pfeiffer 		switch (me) {
3501d96047eSMarkus Pfeiffer 		case LIBUSB20_ME_INT8:
3511d96047eSMarkus Pfeiffer 			while (pd_count--) {
3521d96047eSMarkus Pfeiffer 				uint8_t temp;
3531d96047eSMarkus Pfeiffer 
3541d96047eSMarkus Pfeiffer 				if (len < 1)	/* overflow */
3551d96047eSMarkus Pfeiffer 					goto done;
3561d96047eSMarkus Pfeiffer 				if (buf) {
3571d96047eSMarkus Pfeiffer 					temp = *((const uint8_t *)
3581d96047eSMarkus Pfeiffer 					    LIBUSB20_ADD_BYTES(pd, pd_offset));
3591d96047eSMarkus Pfeiffer 					buf[0] = temp;
3601d96047eSMarkus Pfeiffer 					buf += 1;
3611d96047eSMarkus Pfeiffer 				}
3621d96047eSMarkus Pfeiffer 				pd_offset += 1;
3631d96047eSMarkus Pfeiffer 				len -= 1;
3641d96047eSMarkus Pfeiffer 			}
3651d96047eSMarkus Pfeiffer 			break;
3661d96047eSMarkus Pfeiffer 
3671d96047eSMarkus Pfeiffer 		case LIBUSB20_ME_INT16:
3681d96047eSMarkus Pfeiffer 			pd_offset = -((-pd_offset) & ~1);	/* align */
3691d96047eSMarkus Pfeiffer 			while (pd_count--) {
3701d96047eSMarkus Pfeiffer 				uint16_t temp;
3711d96047eSMarkus Pfeiffer 
3721d96047eSMarkus Pfeiffer 				if (len < 2)	/* overflow */
3731d96047eSMarkus Pfeiffer 					goto done;
3741d96047eSMarkus Pfeiffer 
3751d96047eSMarkus Pfeiffer 				if (buf) {
3761d96047eSMarkus Pfeiffer 					temp = *((const uint16_t *)
3771d96047eSMarkus Pfeiffer 					    LIBUSB20_ADD_BYTES(pd, pd_offset));
3781d96047eSMarkus Pfeiffer 					buf[1] = (temp >> 8) & 0xFF;
3791d96047eSMarkus Pfeiffer 					buf[0] = temp & 0xFF;
3801d96047eSMarkus Pfeiffer 					buf += 2;
3811d96047eSMarkus Pfeiffer 				}
3821d96047eSMarkus Pfeiffer 				pd_offset += 2;
3831d96047eSMarkus Pfeiffer 				len -= 2;
3841d96047eSMarkus Pfeiffer 			}
3851d96047eSMarkus Pfeiffer 			break;
3861d96047eSMarkus Pfeiffer 
3871d96047eSMarkus Pfeiffer 		case LIBUSB20_ME_INT32:
3881d96047eSMarkus Pfeiffer 			pd_offset = -((-pd_offset) & ~3);	/* align */
3891d96047eSMarkus Pfeiffer 			while (pd_count--) {
3901d96047eSMarkus Pfeiffer 				uint32_t temp;
3911d96047eSMarkus Pfeiffer 
3921d96047eSMarkus Pfeiffer 				if (len < 4)	/* overflow */
3931d96047eSMarkus Pfeiffer 					goto done;
3941d96047eSMarkus Pfeiffer 				if (buf) {
3951d96047eSMarkus Pfeiffer 					temp = *((const uint32_t *)
3961d96047eSMarkus Pfeiffer 					    LIBUSB20_ADD_BYTES(pd, pd_offset));
3971d96047eSMarkus Pfeiffer 					buf[3] = (temp >> 24) & 0xFF;
3981d96047eSMarkus Pfeiffer 					buf[2] = (temp >> 16) & 0xFF;
3991d96047eSMarkus Pfeiffer 					buf[1] = (temp >> 8) & 0xFF;
4001d96047eSMarkus Pfeiffer 					buf[0] = temp & 0xFF;
4011d96047eSMarkus Pfeiffer 					buf += 4;
4021d96047eSMarkus Pfeiffer 				}
4031d96047eSMarkus Pfeiffer 				pd_offset += 4;
4041d96047eSMarkus Pfeiffer 				len -= 4;
4051d96047eSMarkus Pfeiffer 			}
4061d96047eSMarkus Pfeiffer 			break;
4071d96047eSMarkus Pfeiffer 
4081d96047eSMarkus Pfeiffer 		case LIBUSB20_ME_INT64:
4091d96047eSMarkus Pfeiffer 			pd_offset = -((-pd_offset) & ~7);	/* align */
4101d96047eSMarkus Pfeiffer 			while (pd_count--) {
4111d96047eSMarkus Pfeiffer 				uint64_t temp;
4121d96047eSMarkus Pfeiffer 
4131d96047eSMarkus Pfeiffer 				if (len < 8)	/* overflow */
4141d96047eSMarkus Pfeiffer 					goto done;
4151d96047eSMarkus Pfeiffer 				if (buf) {
4161d96047eSMarkus Pfeiffer 
4171d96047eSMarkus Pfeiffer 					temp = *((const uint64_t *)
4181d96047eSMarkus Pfeiffer 					    LIBUSB20_ADD_BYTES(pd, pd_offset));
4191d96047eSMarkus Pfeiffer 					buf[7] = (temp >> 56) & 0xFF;
4201d96047eSMarkus Pfeiffer 					buf[6] = (temp >> 48) & 0xFF;
4211d96047eSMarkus Pfeiffer 					buf[5] = (temp >> 40) & 0xFF;
4221d96047eSMarkus Pfeiffer 					buf[4] = (temp >> 32) & 0xFF;
4231d96047eSMarkus Pfeiffer 					buf[3] = (temp >> 24) & 0xFF;
4241d96047eSMarkus Pfeiffer 					buf[2] = (temp >> 16) & 0xFF;
4251d96047eSMarkus Pfeiffer 					buf[1] = (temp >> 8) & 0xFF;
4261d96047eSMarkus Pfeiffer 					buf[0] = temp & 0xFF;
4271d96047eSMarkus Pfeiffer 					buf += 8;
4281d96047eSMarkus Pfeiffer 				}
4291d96047eSMarkus Pfeiffer 				pd_offset += 8;
4301d96047eSMarkus Pfeiffer 				len -= 8;
4311d96047eSMarkus Pfeiffer 			}
4321d96047eSMarkus Pfeiffer 			break;
4331d96047eSMarkus Pfeiffer 
4341d96047eSMarkus Pfeiffer 		case LIBUSB20_ME_STRUCT:
435*3f7b7260SSascha Wildner 			pd_offset = -rounddown2(-pd_offset, LIBUSB20_ME_STRUCT_ALIGN);	/* align */
4361d96047eSMarkus Pfeiffer 			while (pd_count--) {
4371d96047eSMarkus Pfeiffer 				void *src_ptr;
4381d96047eSMarkus Pfeiffer 				uint16_t src_len;
4391d96047eSMarkus Pfeiffer 				struct libusb20_me_struct *ps;
4401d96047eSMarkus Pfeiffer 
4411d96047eSMarkus Pfeiffer 				ps = LIBUSB20_ADD_BYTES(pd, pd_offset);
4421d96047eSMarkus Pfeiffer 
4431d96047eSMarkus Pfeiffer 				switch (ps->type) {
4441d96047eSMarkus Pfeiffer 				case LIBUSB20_ME_IS_RAW:
4451d96047eSMarkus Pfeiffer 					src_len = ps->len;
4461d96047eSMarkus Pfeiffer 					src_ptr = ps->ptr;
4471d96047eSMarkus Pfeiffer 					break;
4481d96047eSMarkus Pfeiffer 
4491d96047eSMarkus Pfeiffer 				case LIBUSB20_ME_IS_ENCODED:
4501d96047eSMarkus Pfeiffer 					if (ps->len == 0) {
4511d96047eSMarkus Pfeiffer 						/*
4521d96047eSMarkus Pfeiffer 						 * Length is encoded
4531d96047eSMarkus Pfeiffer 						 * in the data itself
4541d96047eSMarkus Pfeiffer 						 * and should be
4551d96047eSMarkus Pfeiffer 						 * correct:
4561d96047eSMarkus Pfeiffer 						 */
457aa3e5c14SSascha Wildner 						ps->len = 0xFFFF;
4581d96047eSMarkus Pfeiffer 					}
4591d96047eSMarkus Pfeiffer 					src_len = libusb20_me_get_1(pd, 0);
4601d96047eSMarkus Pfeiffer 					src_ptr = LIBUSB20_ADD_BYTES(ps->ptr, 1);
4611d96047eSMarkus Pfeiffer 					if (src_len == 0xFF) {
4621d96047eSMarkus Pfeiffer 						/* length is escaped */
4631d96047eSMarkus Pfeiffer 						src_len = libusb20_me_get_2(pd, 1);
4641d96047eSMarkus Pfeiffer 						src_ptr =
4651d96047eSMarkus Pfeiffer 						    LIBUSB20_ADD_BYTES(ps->ptr, 3);
4661d96047eSMarkus Pfeiffer 					}
4671d96047eSMarkus Pfeiffer 					break;
4681d96047eSMarkus Pfeiffer 
4691d96047eSMarkus Pfeiffer 				case LIBUSB20_ME_IS_DECODED:
4701d96047eSMarkus Pfeiffer 					/* reserve 3 length bytes */
4711d96047eSMarkus Pfeiffer 					src_len = libusb20_me_encode(NULL,
472aa3e5c14SSascha Wildner 					    0xFFFF - 3, ps->ptr);
4731d96047eSMarkus Pfeiffer 					src_ptr = NULL;
4741d96047eSMarkus Pfeiffer 					break;
4751d96047eSMarkus Pfeiffer 
4761d96047eSMarkus Pfeiffer 				default:	/* empty structure */
4771d96047eSMarkus Pfeiffer 					src_len = 0;
4781d96047eSMarkus Pfeiffer 					src_ptr = NULL;
4791d96047eSMarkus Pfeiffer 					break;
4801d96047eSMarkus Pfeiffer 				}
4811d96047eSMarkus Pfeiffer 
4821d96047eSMarkus Pfeiffer 				if (src_len > 0xFE) {
483aa3e5c14SSascha Wildner 					if (src_len > (0xFFFF - 3))
4841d96047eSMarkus Pfeiffer 						/* overflow */
4851d96047eSMarkus Pfeiffer 						goto done;
4861d96047eSMarkus Pfeiffer 
4871d96047eSMarkus Pfeiffer 					if (len < (src_len + 3))
4881d96047eSMarkus Pfeiffer 						/* overflow */
4891d96047eSMarkus Pfeiffer 						goto done;
4901d96047eSMarkus Pfeiffer 
4911d96047eSMarkus Pfeiffer 					if (buf) {
4921d96047eSMarkus Pfeiffer 						buf[0] = 0xFF;
4931d96047eSMarkus Pfeiffer 						buf[1] = (src_len & 0xFF);
4941d96047eSMarkus Pfeiffer 						buf[2] = (src_len >> 8) & 0xFF;
4951d96047eSMarkus Pfeiffer 						buf += 3;
4961d96047eSMarkus Pfeiffer 					}
4971d96047eSMarkus Pfeiffer 					len -= (src_len + 3);
4981d96047eSMarkus Pfeiffer 				} else {
4991d96047eSMarkus Pfeiffer 					if (len < (src_len + 1))
5001d96047eSMarkus Pfeiffer 						/* overflow */
5011d96047eSMarkus Pfeiffer 						goto done;
5021d96047eSMarkus Pfeiffer 
5031d96047eSMarkus Pfeiffer 					if (buf) {
5041d96047eSMarkus Pfeiffer 						buf[0] = (src_len & 0xFF);
5051d96047eSMarkus Pfeiffer 						buf += 1;
5061d96047eSMarkus Pfeiffer 					}
5071d96047eSMarkus Pfeiffer 					len -= (src_len + 1);
5081d96047eSMarkus Pfeiffer 				}
5091d96047eSMarkus Pfeiffer 
5101d96047eSMarkus Pfeiffer 				/* check for buffer and non-zero length */
5111d96047eSMarkus Pfeiffer 
5121d96047eSMarkus Pfeiffer 				if (buf && src_len) {
5131d96047eSMarkus Pfeiffer 					if (ps->type == LIBUSB20_ME_IS_DECODED) {
5141d96047eSMarkus Pfeiffer 						/*
5151d96047eSMarkus Pfeiffer 						 * Repeat encode
5161d96047eSMarkus Pfeiffer 						 * procedure - we have
5171d96047eSMarkus Pfeiffer 						 * room for the
5181d96047eSMarkus Pfeiffer 						 * complete structure:
5191d96047eSMarkus Pfeiffer 						 */
5209b0c1abeSSascha Wildner 						uint16_t dummy;
5211d96047eSMarkus Pfeiffer 
5221d96047eSMarkus Pfeiffer 						dummy = libusb20_me_encode(buf,
523aa3e5c14SSascha Wildner 						    0xFFFF - 3, ps->ptr);
5241d96047eSMarkus Pfeiffer 					} else {
5251d96047eSMarkus Pfeiffer 						bcopy(src_ptr, buf, src_len);
5261d96047eSMarkus Pfeiffer 					}
5271d96047eSMarkus Pfeiffer 					buf += src_len;
5281d96047eSMarkus Pfeiffer 				}
5291d96047eSMarkus Pfeiffer 				pd_offset += sizeof(struct libusb20_me_struct);
5301d96047eSMarkus Pfeiffer 			}
5311d96047eSMarkus Pfeiffer 			break;
5321d96047eSMarkus Pfeiffer 
5331d96047eSMarkus Pfeiffer 		default:
5341d96047eSMarkus Pfeiffer 			goto done;
5351d96047eSMarkus Pfeiffer 		}
5361d96047eSMarkus Pfeiffer 	}
5371d96047eSMarkus Pfeiffer done:
5381d96047eSMarkus Pfeiffer 	return (len_old - len);
5391d96047eSMarkus Pfeiffer }
5401d96047eSMarkus Pfeiffer 
5411d96047eSMarkus Pfeiffer /*------------------------------------------------------------------------*
5421d96047eSMarkus Pfeiffer  *	libusb20_me_decode - decode a message into a decoded structure
5431d96047eSMarkus Pfeiffer  *
5441d96047eSMarkus Pfeiffer  * Description of parameters:
5451d96047eSMarkus Pfeiffer  * "ptr" - message pointer
5461d96047eSMarkus Pfeiffer  * "len" - message length
5471d96047eSMarkus Pfeiffer  * "pd" - pointer to decoded structure
5481d96047eSMarkus Pfeiffer  *
5491d96047eSMarkus Pfeiffer  * Returns:
5501d96047eSMarkus Pfeiffer  * "0..65535" - number of bytes decoded, limited by "len"
5511d96047eSMarkus Pfeiffer  *------------------------------------------------------------------------*/
5521d96047eSMarkus Pfeiffer uint16_t
libusb20_me_decode(const void * ptr,uint16_t len,void * pd)5531d96047eSMarkus Pfeiffer libusb20_me_decode(const void *ptr, uint16_t len, void *pd)
5541d96047eSMarkus Pfeiffer {
5551d96047eSMarkus Pfeiffer 	const uint8_t *pf;		/* pointer to format data */
5561d96047eSMarkus Pfeiffer 	const uint8_t *buf;		/* pointer to input buffer */
5571d96047eSMarkus Pfeiffer 
5581d96047eSMarkus Pfeiffer 	uint32_t pd_offset;		/* decoded structure offset */
5591d96047eSMarkus Pfeiffer 	uint16_t len_old;		/* old length */
5601d96047eSMarkus Pfeiffer 	uint16_t pd_count;		/* decoded element count */
5611d96047eSMarkus Pfeiffer 	uint8_t me;			/* message element */
5621d96047eSMarkus Pfeiffer 
5631d96047eSMarkus Pfeiffer 	/* initialise */
5641d96047eSMarkus Pfeiffer 
5651d96047eSMarkus Pfeiffer 	len_old = len;
5661d96047eSMarkus Pfeiffer 	buf = ptr;
5671d96047eSMarkus Pfeiffer 	pd_offset = sizeof(void *);
5681d96047eSMarkus Pfeiffer 	pf = (*((struct libusb20_me_format **)pd))->format;
5691d96047eSMarkus Pfeiffer 
5701d96047eSMarkus Pfeiffer 	/* scan */
5711d96047eSMarkus Pfeiffer 
5721d96047eSMarkus Pfeiffer 	while (1) {
5731d96047eSMarkus Pfeiffer 
5741d96047eSMarkus Pfeiffer 		/* get information element */
5751d96047eSMarkus Pfeiffer 
5761d96047eSMarkus Pfeiffer 		me = (pf[0]) & LIBUSB20_ME_MASK;
5771d96047eSMarkus Pfeiffer 		pd_count = pf[1] | (pf[2] << 8);
5781d96047eSMarkus Pfeiffer 		pf += 3;
5791d96047eSMarkus Pfeiffer 
5801d96047eSMarkus Pfeiffer 		/* decode the message element by type */
5811d96047eSMarkus Pfeiffer 
5821d96047eSMarkus Pfeiffer 		switch (me) {
5831d96047eSMarkus Pfeiffer 		case LIBUSB20_ME_INT8:
5841d96047eSMarkus Pfeiffer 			while (pd_count--) {
5851d96047eSMarkus Pfeiffer 				uint8_t temp;
5861d96047eSMarkus Pfeiffer 
5871d96047eSMarkus Pfeiffer 				if (len < 1) {
5881d96047eSMarkus Pfeiffer 					len = 0;
5891d96047eSMarkus Pfeiffer 					temp = 0;
5901d96047eSMarkus Pfeiffer 				} else {
5911d96047eSMarkus Pfeiffer 					len -= 1;
5921d96047eSMarkus Pfeiffer 					temp = buf[0];
5931d96047eSMarkus Pfeiffer 					buf++;
5941d96047eSMarkus Pfeiffer 				}
5951d96047eSMarkus Pfeiffer 				*((uint8_t *)LIBUSB20_ADD_BYTES(pd,
5961d96047eSMarkus Pfeiffer 				    pd_offset)) = temp;
5971d96047eSMarkus Pfeiffer 				pd_offset += 1;
5981d96047eSMarkus Pfeiffer 			}
5991d96047eSMarkus Pfeiffer 			break;
6001d96047eSMarkus Pfeiffer 
6011d96047eSMarkus Pfeiffer 		case LIBUSB20_ME_INT16:
6021d96047eSMarkus Pfeiffer 			pd_offset = -((-pd_offset) & ~1);	/* align */
6031d96047eSMarkus Pfeiffer 			while (pd_count--) {
6041d96047eSMarkus Pfeiffer 				uint16_t temp;
6051d96047eSMarkus Pfeiffer 
6061d96047eSMarkus Pfeiffer 				if (len < 2) {
6071d96047eSMarkus Pfeiffer 					len = 0;
6081d96047eSMarkus Pfeiffer 					temp = 0;
6091d96047eSMarkus Pfeiffer 				} else {
6101d96047eSMarkus Pfeiffer 					len -= 2;
6111d96047eSMarkus Pfeiffer 					temp = buf[1] << 8;
6121d96047eSMarkus Pfeiffer 					temp |= buf[0];
6131d96047eSMarkus Pfeiffer 					buf += 2;
6141d96047eSMarkus Pfeiffer 				}
6151d96047eSMarkus Pfeiffer 				*((uint16_t *)LIBUSB20_ADD_BYTES(pd,
6161d96047eSMarkus Pfeiffer 				    pd_offset)) = temp;
6171d96047eSMarkus Pfeiffer 				pd_offset += 2;
6181d96047eSMarkus Pfeiffer 			}
6191d96047eSMarkus Pfeiffer 			break;
6201d96047eSMarkus Pfeiffer 
6211d96047eSMarkus Pfeiffer 		case LIBUSB20_ME_INT32:
6221d96047eSMarkus Pfeiffer 			pd_offset = -((-pd_offset) & ~3);	/* align */
6231d96047eSMarkus Pfeiffer 			while (pd_count--) {
6241d96047eSMarkus Pfeiffer 				uint32_t temp;
6251d96047eSMarkus Pfeiffer 
6261d96047eSMarkus Pfeiffer 				if (len < 4) {
6271d96047eSMarkus Pfeiffer 					len = 0;
6281d96047eSMarkus Pfeiffer 					temp = 0;
6291d96047eSMarkus Pfeiffer 				} else {
6301d96047eSMarkus Pfeiffer 					len -= 4;
6311d96047eSMarkus Pfeiffer 					temp = buf[3] << 24;
6321d96047eSMarkus Pfeiffer 					temp |= buf[2] << 16;
6331d96047eSMarkus Pfeiffer 					temp |= buf[1] << 8;
6341d96047eSMarkus Pfeiffer 					temp |= buf[0];
6351d96047eSMarkus Pfeiffer 					buf += 4;
6361d96047eSMarkus Pfeiffer 				}
6371d96047eSMarkus Pfeiffer 
6381d96047eSMarkus Pfeiffer 				*((uint32_t *)LIBUSB20_ADD_BYTES(pd,
6391d96047eSMarkus Pfeiffer 				    pd_offset)) = temp;
6401d96047eSMarkus Pfeiffer 				pd_offset += 4;
6411d96047eSMarkus Pfeiffer 			}
6421d96047eSMarkus Pfeiffer 			break;
6431d96047eSMarkus Pfeiffer 
6441d96047eSMarkus Pfeiffer 		case LIBUSB20_ME_INT64:
6451d96047eSMarkus Pfeiffer 			pd_offset = -((-pd_offset) & ~7);	/* align */
6461d96047eSMarkus Pfeiffer 			while (pd_count--) {
6471d96047eSMarkus Pfeiffer 				uint64_t temp;
6481d96047eSMarkus Pfeiffer 
6491d96047eSMarkus Pfeiffer 				if (len < 8) {
6501d96047eSMarkus Pfeiffer 					len = 0;
6511d96047eSMarkus Pfeiffer 					temp = 0;
6521d96047eSMarkus Pfeiffer 				} else {
6531d96047eSMarkus Pfeiffer 					len -= 8;
6541d96047eSMarkus Pfeiffer 					temp = ((uint64_t)buf[7]) << 56;
6551d96047eSMarkus Pfeiffer 					temp |= ((uint64_t)buf[6]) << 48;
6561d96047eSMarkus Pfeiffer 					temp |= ((uint64_t)buf[5]) << 40;
6571d96047eSMarkus Pfeiffer 					temp |= ((uint64_t)buf[4]) << 32;
6581d96047eSMarkus Pfeiffer 					temp |= buf[3] << 24;
6591d96047eSMarkus Pfeiffer 					temp |= buf[2] << 16;
6601d96047eSMarkus Pfeiffer 					temp |= buf[1] << 8;
6611d96047eSMarkus Pfeiffer 					temp |= buf[0];
6621d96047eSMarkus Pfeiffer 					buf += 8;
6631d96047eSMarkus Pfeiffer 				}
6641d96047eSMarkus Pfeiffer 
6651d96047eSMarkus Pfeiffer 				*((uint64_t *)LIBUSB20_ADD_BYTES(pd,
6661d96047eSMarkus Pfeiffer 				    pd_offset)) = temp;
6671d96047eSMarkus Pfeiffer 				pd_offset += 8;
6681d96047eSMarkus Pfeiffer 			}
6691d96047eSMarkus Pfeiffer 			break;
6701d96047eSMarkus Pfeiffer 
6711d96047eSMarkus Pfeiffer 		case LIBUSB20_ME_STRUCT:
672*3f7b7260SSascha Wildner 			pd_offset = -rounddown2(-pd_offset, LIBUSB20_ME_STRUCT_ALIGN);	/* align */
6731d96047eSMarkus Pfeiffer 			while (pd_count--) {
6741d96047eSMarkus Pfeiffer 				uint16_t temp;
6759b0c1abeSSascha Wildner 				uint16_t dummy;
6761d96047eSMarkus Pfeiffer 				struct libusb20_me_struct *ps;
6771d96047eSMarkus Pfeiffer 
6781d96047eSMarkus Pfeiffer 				ps = LIBUSB20_ADD_BYTES(pd, pd_offset);
6791d96047eSMarkus Pfeiffer 
6801d96047eSMarkus Pfeiffer 				if (ps->type == LIBUSB20_ME_IS_ENCODED) {
6811d96047eSMarkus Pfeiffer 					/*
6821d96047eSMarkus Pfeiffer 					 * Pre-store a de-constified
6831d96047eSMarkus Pfeiffer 					 * pointer to the raw
6841d96047eSMarkus Pfeiffer 					 * structure:
6851d96047eSMarkus Pfeiffer 					 */
6861d96047eSMarkus Pfeiffer 					ps->ptr = LIBUSB20_ADD_BYTES(buf, 0);
6871d96047eSMarkus Pfeiffer 
6881d96047eSMarkus Pfeiffer 					/*
6891d96047eSMarkus Pfeiffer 					 * Get the correct number of
6901d96047eSMarkus Pfeiffer 					 * length bytes:
6911d96047eSMarkus Pfeiffer 					 */
6921d96047eSMarkus Pfeiffer 					if (len != 0) {
6931d96047eSMarkus Pfeiffer 						if (buf[0] == 0xFF) {
6941d96047eSMarkus Pfeiffer 							ps->len = 3;
6951d96047eSMarkus Pfeiffer 						} else {
6961d96047eSMarkus Pfeiffer 							ps->len = 1;
6971d96047eSMarkus Pfeiffer 						}
6981d96047eSMarkus Pfeiffer 					} else {
6991d96047eSMarkus Pfeiffer 						ps->len = 0;
7001d96047eSMarkus Pfeiffer 					}
7011d96047eSMarkus Pfeiffer 				}
7021d96047eSMarkus Pfeiffer 				/* get the structure length */
7031d96047eSMarkus Pfeiffer 
7041d96047eSMarkus Pfeiffer 				if (len != 0) {
7051d96047eSMarkus Pfeiffer 					if (buf[0] == 0xFF) {
7061d96047eSMarkus Pfeiffer 						if (len < 3) {
7071d96047eSMarkus Pfeiffer 							len = 0;
7081d96047eSMarkus Pfeiffer 							temp = 0;
7091d96047eSMarkus Pfeiffer 						} else {
7101d96047eSMarkus Pfeiffer 							len -= 3;
7111d96047eSMarkus Pfeiffer 							temp = buf[1] |
7121d96047eSMarkus Pfeiffer 							    (buf[2] << 8);
7131d96047eSMarkus Pfeiffer 							buf += 3;
7141d96047eSMarkus Pfeiffer 						}
7151d96047eSMarkus Pfeiffer 					} else {
7161d96047eSMarkus Pfeiffer 						len -= 1;
7171d96047eSMarkus Pfeiffer 						temp = buf[0];
7181d96047eSMarkus Pfeiffer 						buf += 1;
7191d96047eSMarkus Pfeiffer 					}
7201d96047eSMarkus Pfeiffer 				} else {
7211d96047eSMarkus Pfeiffer 					len = 0;
7221d96047eSMarkus Pfeiffer 					temp = 0;
7231d96047eSMarkus Pfeiffer 				}
7241d96047eSMarkus Pfeiffer 				/* check for invalid length */
7251d96047eSMarkus Pfeiffer 
7261d96047eSMarkus Pfeiffer 				if (temp > len) {
7271d96047eSMarkus Pfeiffer 					len = 0;
7281d96047eSMarkus Pfeiffer 					temp = 0;
7291d96047eSMarkus Pfeiffer 				}
7301d96047eSMarkus Pfeiffer 				/* check wanted structure type */
7311d96047eSMarkus Pfeiffer 
7321d96047eSMarkus Pfeiffer 				switch (ps->type) {
7331d96047eSMarkus Pfeiffer 				case LIBUSB20_ME_IS_ENCODED:
7341d96047eSMarkus Pfeiffer 					/* check for zero length */
7351d96047eSMarkus Pfeiffer 					if (temp == 0) {
7361d96047eSMarkus Pfeiffer 						/*
7371d96047eSMarkus Pfeiffer 						 * The pointer must
7381d96047eSMarkus Pfeiffer 						 * be valid:
7391d96047eSMarkus Pfeiffer 						 */
7401d96047eSMarkus Pfeiffer 						ps->ptr = LIBUSB20_ADD_BYTES(
7411d96047eSMarkus Pfeiffer 						    libusb20_me_encode_empty, 0);
7421d96047eSMarkus Pfeiffer 						ps->len = 1;
7431d96047eSMarkus Pfeiffer 					} else {
7441d96047eSMarkus Pfeiffer 						ps->len += temp;
7451d96047eSMarkus Pfeiffer 					}
7461d96047eSMarkus Pfeiffer 					break;
7471d96047eSMarkus Pfeiffer 
7481d96047eSMarkus Pfeiffer 				case LIBUSB20_ME_IS_RAW:
7491d96047eSMarkus Pfeiffer 					/* update length and pointer */
7501d96047eSMarkus Pfeiffer 					ps->len = temp;
7511d96047eSMarkus Pfeiffer 					ps->ptr = LIBUSB20_ADD_BYTES(buf, 0);
7521d96047eSMarkus Pfeiffer 					break;
7531d96047eSMarkus Pfeiffer 
7541d96047eSMarkus Pfeiffer 				case LIBUSB20_ME_IS_EMPTY:
7551d96047eSMarkus Pfeiffer 				case LIBUSB20_ME_IS_DECODED:
7561d96047eSMarkus Pfeiffer 					/* check for non-zero length */
7571d96047eSMarkus Pfeiffer 					if (temp != 0) {
7581d96047eSMarkus Pfeiffer 						/* update type */
7591d96047eSMarkus Pfeiffer 						ps->type = LIBUSB20_ME_IS_DECODED;
7601d96047eSMarkus Pfeiffer 						ps->len = 0;
7611d96047eSMarkus Pfeiffer 						/*
7621d96047eSMarkus Pfeiffer 						 * Recursivly decode
7631d96047eSMarkus Pfeiffer 						 * the next structure
7641d96047eSMarkus Pfeiffer 						 */
7651d96047eSMarkus Pfeiffer 						dummy = libusb20_me_decode(buf,
7661d96047eSMarkus Pfeiffer 						    temp, ps->ptr);
7671d96047eSMarkus Pfeiffer 					} else {
7681d96047eSMarkus Pfeiffer 						/* update type */
7691d96047eSMarkus Pfeiffer 						ps->type = LIBUSB20_ME_IS_EMPTY;
7701d96047eSMarkus Pfeiffer 						ps->len = 0;
7711d96047eSMarkus Pfeiffer 					}
7721d96047eSMarkus Pfeiffer 					break;
7731d96047eSMarkus Pfeiffer 
7741d96047eSMarkus Pfeiffer 				default:
7751d96047eSMarkus Pfeiffer 					/*
7761d96047eSMarkus Pfeiffer 					 * nothing to do - should
7771d96047eSMarkus Pfeiffer 					 * not happen
7781d96047eSMarkus Pfeiffer 					 */
7791d96047eSMarkus Pfeiffer 					ps->ptr = NULL;
7801d96047eSMarkus Pfeiffer 					ps->len = 0;
7811d96047eSMarkus Pfeiffer 					break;
7821d96047eSMarkus Pfeiffer 				}
7831d96047eSMarkus Pfeiffer 				buf += temp;
7841d96047eSMarkus Pfeiffer 				len -= temp;
7851d96047eSMarkus Pfeiffer 				pd_offset += sizeof(struct libusb20_me_struct);
7861d96047eSMarkus Pfeiffer 			}
7871d96047eSMarkus Pfeiffer 			break;
7881d96047eSMarkus Pfeiffer 
7891d96047eSMarkus Pfeiffer 		default:
7901d96047eSMarkus Pfeiffer 			goto done;
7911d96047eSMarkus Pfeiffer 		}
7921d96047eSMarkus Pfeiffer 	}
7931d96047eSMarkus Pfeiffer done:
7941d96047eSMarkus Pfeiffer 	return (len_old - len);
7951d96047eSMarkus Pfeiffer }
796