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