1df4b8c2aSAndrew Thompson /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
35e53a4f9SPedro F. Giffuni *
4df4b8c2aSAndrew Thompson * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
5df4b8c2aSAndrew Thompson *
6df4b8c2aSAndrew Thompson * Redistribution and use in source and binary forms, with or without
7df4b8c2aSAndrew Thompson * modification, are permitted provided that the following conditions
8df4b8c2aSAndrew Thompson * are met:
9df4b8c2aSAndrew Thompson * 1. Redistributions of source code must retain the above copyright
10df4b8c2aSAndrew Thompson * notice, this list of conditions and the following disclaimer.
11df4b8c2aSAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright
12df4b8c2aSAndrew Thompson * notice, this list of conditions and the following disclaimer in the
13df4b8c2aSAndrew Thompson * documentation and/or other materials provided with the distribution.
14df4b8c2aSAndrew Thompson *
15df4b8c2aSAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16df4b8c2aSAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17df4b8c2aSAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18df4b8c2aSAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19df4b8c2aSAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20df4b8c2aSAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21df4b8c2aSAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22df4b8c2aSAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23df4b8c2aSAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24df4b8c2aSAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25df4b8c2aSAndrew Thompson * SUCH DAMAGE.
26df4b8c2aSAndrew Thompson */
27df4b8c2aSAndrew Thompson
2866194130SHans Petter Selasky #ifdef LIBUSB_GLOBAL_INCLUDE_FILE
2966194130SHans Petter Selasky #include LIBUSB_GLOBAL_INCLUDE_FILE
3066194130SHans Petter Selasky #else
31df4b8c2aSAndrew Thompson #include <stdio.h>
32df4b8c2aSAndrew Thompson #include <stdlib.h>
33df4b8c2aSAndrew Thompson #include <string.h>
3466194130SHans Petter Selasky #include <time.h>
3566194130SHans Petter Selasky #include <sys/queue.h>
3666194130SHans Petter Selasky #endif
37df4b8c2aSAndrew Thompson
38df4b8c2aSAndrew Thompson #include "libusb20.h"
39df4b8c2aSAndrew Thompson #include "libusb20_desc.h"
40df4b8c2aSAndrew Thompson #include "libusb20_int.h"
41df4b8c2aSAndrew Thompson
42df4b8c2aSAndrew Thompson static const uint32_t libusb20_me_encode_empty[2]; /* dummy */
43df4b8c2aSAndrew Thompson
44df4b8c2aSAndrew Thompson LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_DEVICE_DESC);
45df4b8c2aSAndrew Thompson LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_ENDPOINT_DESC);
46df4b8c2aSAndrew Thompson LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_INTERFACE_DESC);
47df4b8c2aSAndrew Thompson LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONFIG_DESC);
48df4b8c2aSAndrew Thompson LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONTROL_SETUP);
494c0392e6SHans Petter Selasky LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_ENDPT_COMP_DESC);
504c0392e6SHans Petter Selasky LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_USB_20_DEVCAP_DESC);
514c0392e6SHans Petter Selasky LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_USB_DEVCAP_DESC);
524c0392e6SHans Petter Selasky LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_BOS_DESCRIPTOR);
53df4b8c2aSAndrew Thompson
54df4b8c2aSAndrew Thompson /*------------------------------------------------------------------------*
55df4b8c2aSAndrew Thompson * libusb20_parse_config_desc
56df4b8c2aSAndrew Thompson *
57df4b8c2aSAndrew Thompson * Return values:
58df4b8c2aSAndrew Thompson * NULL: Out of memory.
59df4b8c2aSAndrew Thompson * Else: A valid config structure pointer which must be passed to "free()"
60df4b8c2aSAndrew Thompson *------------------------------------------------------------------------*/
61df4b8c2aSAndrew Thompson struct libusb20_config *
libusb20_parse_config_desc(const void * config_desc)62df4b8c2aSAndrew Thompson libusb20_parse_config_desc(const void *config_desc)
63df4b8c2aSAndrew Thompson {
64df4b8c2aSAndrew Thompson struct libusb20_config *lub_config;
65df4b8c2aSAndrew Thompson struct libusb20_interface *lub_interface;
66df4b8c2aSAndrew Thompson struct libusb20_interface *lub_alt_interface;
67df4b8c2aSAndrew Thompson struct libusb20_interface *last_if;
68df4b8c2aSAndrew Thompson struct libusb20_endpoint *lub_endpoint;
69df4b8c2aSAndrew Thompson struct libusb20_endpoint *last_ep;
70df4b8c2aSAndrew Thompson
71df4b8c2aSAndrew Thompson struct libusb20_me_struct pcdesc;
72df4b8c2aSAndrew Thompson const uint8_t *ptr;
73df4b8c2aSAndrew Thompson uint32_t size;
74df4b8c2aSAndrew Thompson uint16_t niface_no_alt;
75df4b8c2aSAndrew Thompson uint16_t niface;
76df4b8c2aSAndrew Thompson uint16_t nendpoint;
77d81535d1SHans Petter Selasky uint16_t iface_no;
78df4b8c2aSAndrew Thompson
79df4b8c2aSAndrew Thompson ptr = config_desc;
80df4b8c2aSAndrew Thompson if (ptr[1] != LIBUSB20_DT_CONFIG) {
81df4b8c2aSAndrew Thompson return (NULL); /* not config descriptor */
82df4b8c2aSAndrew Thompson }
8310557931SHans Petter Selasky
84df4b8c2aSAndrew Thompson /*
8510557931SHans Petter Selasky * The first "bInterfaceNumber" cannot start at 0xFFFF
8610557931SHans Petter Selasky * because the field is 8-bit.
87df4b8c2aSAndrew Thompson */
88df4b8c2aSAndrew Thompson niface_no_alt = 0;
89df4b8c2aSAndrew Thompson nendpoint = 0;
90df4b8c2aSAndrew Thompson niface = 0;
91d81535d1SHans Petter Selasky iface_no = 0xFFFF;
92df4b8c2aSAndrew Thompson ptr = NULL;
93df4b8c2aSAndrew Thompson
94df4b8c2aSAndrew Thompson /* get "wTotalLength" and setup "pcdesc" */
95df4b8c2aSAndrew Thompson pcdesc.ptr = LIBUSB20_ADD_BYTES(config_desc, 0);
96df4b8c2aSAndrew Thompson pcdesc.len =
97df4b8c2aSAndrew Thompson ((const uint8_t *)config_desc)[2] |
98df4b8c2aSAndrew Thompson (((const uint8_t *)config_desc)[3] << 8);
99df4b8c2aSAndrew Thompson pcdesc.type = LIBUSB20_ME_IS_RAW;
100df4b8c2aSAndrew Thompson
101df4b8c2aSAndrew Thompson /* descriptor pre-scan */
102df4b8c2aSAndrew Thompson while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) {
103df4b8c2aSAndrew Thompson if (ptr[1] == LIBUSB20_DT_ENDPOINT) {
104df4b8c2aSAndrew Thompson nendpoint++;
105df4b8c2aSAndrew Thompson } else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) {
106df4b8c2aSAndrew Thompson niface++;
107df4b8c2aSAndrew Thompson /* check "bInterfaceNumber" */
108df4b8c2aSAndrew Thompson if (ptr[2] != iface_no) {
109df4b8c2aSAndrew Thompson iface_no = ptr[2];
110df4b8c2aSAndrew Thompson niface_no_alt++;
111df4b8c2aSAndrew Thompson }
112df4b8c2aSAndrew Thompson }
113df4b8c2aSAndrew Thompson }
114df4b8c2aSAndrew Thompson
115df4b8c2aSAndrew Thompson /* sanity checking */
116df4b8c2aSAndrew Thompson if (niface >= 256) {
117df4b8c2aSAndrew Thompson return (NULL); /* corrupt */
118df4b8c2aSAndrew Thompson }
119df4b8c2aSAndrew Thompson if (nendpoint >= 256) {
120df4b8c2aSAndrew Thompson return (NULL); /* corrupt */
121df4b8c2aSAndrew Thompson }
122df4b8c2aSAndrew Thompson size = sizeof(*lub_config) +
123df4b8c2aSAndrew Thompson (niface * sizeof(*lub_interface)) +
124df4b8c2aSAndrew Thompson (nendpoint * sizeof(*lub_endpoint)) +
125df4b8c2aSAndrew Thompson pcdesc.len;
126df4b8c2aSAndrew Thompson
127df4b8c2aSAndrew Thompson lub_config = malloc(size);
128df4b8c2aSAndrew Thompson if (lub_config == NULL) {
129df4b8c2aSAndrew Thompson return (NULL); /* out of memory */
130df4b8c2aSAndrew Thompson }
131ccef4ddfSAndrew Thompson /* make sure memory is initialised */
132ccef4ddfSAndrew Thompson memset(lub_config, 0, size);
133ccef4ddfSAndrew Thompson
134df4b8c2aSAndrew Thompson lub_interface = (void *)(lub_config + 1);
135df4b8c2aSAndrew Thompson lub_alt_interface = (void *)(lub_interface + niface_no_alt);
136df4b8c2aSAndrew Thompson lub_endpoint = (void *)(lub_interface + niface);
137df4b8c2aSAndrew Thompson
138df4b8c2aSAndrew Thompson /*
139df4b8c2aSAndrew Thompson * Make a copy of the config descriptor, so that the caller can free
14075f46cf6SPedro F. Giffuni * the initial config descriptor pointer!
141df4b8c2aSAndrew Thompson */
1425a5c6e99SPedro F. Giffuni memcpy((void *)(lub_endpoint + nendpoint), config_desc, pcdesc.len);
1435a5c6e99SPedro F. Giffuni
1445a5c6e99SPedro F. Giffuni ptr = (const void *)(lub_endpoint + nendpoint);
145df4b8c2aSAndrew Thompson pcdesc.ptr = LIBUSB20_ADD_BYTES(ptr, 0);
146df4b8c2aSAndrew Thompson
147df4b8c2aSAndrew Thompson /* init config structure */
148df4b8c2aSAndrew Thompson
149df4b8c2aSAndrew Thompson LIBUSB20_INIT(LIBUSB20_CONFIG_DESC, &lub_config->desc);
150df4b8c2aSAndrew Thompson
151df4b8c2aSAndrew Thompson if (libusb20_me_decode(ptr, ptr[0], &lub_config->desc)) {
152df4b8c2aSAndrew Thompson /* ignore */
153df4b8c2aSAndrew Thompson }
154df4b8c2aSAndrew Thompson lub_config->num_interface = 0;
155df4b8c2aSAndrew Thompson lub_config->interface = lub_interface;
156df4b8c2aSAndrew Thompson lub_config->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
157df4b8c2aSAndrew Thompson lub_config->extra.len = -ptr[0];
158df4b8c2aSAndrew Thompson lub_config->extra.type = LIBUSB20_ME_IS_RAW;
159df4b8c2aSAndrew Thompson
160df4b8c2aSAndrew Thompson /* reset states */
161df4b8c2aSAndrew Thompson niface = 0;
162d81535d1SHans Petter Selasky iface_no = 0xFFFF;
163df4b8c2aSAndrew Thompson ptr = NULL;
164df4b8c2aSAndrew Thompson lub_interface--;
165df4b8c2aSAndrew Thompson lub_endpoint--;
166df4b8c2aSAndrew Thompson last_if = NULL;
167df4b8c2aSAndrew Thompson last_ep = NULL;
168df4b8c2aSAndrew Thompson
169df4b8c2aSAndrew Thompson /* descriptor pre-scan */
170df4b8c2aSAndrew Thompson while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) {
171df4b8c2aSAndrew Thompson if (ptr[1] == LIBUSB20_DT_ENDPOINT) {
172df4b8c2aSAndrew Thompson if (last_if) {
173df4b8c2aSAndrew Thompson lub_endpoint++;
174df4b8c2aSAndrew Thompson last_ep = lub_endpoint;
175df4b8c2aSAndrew Thompson last_if->num_endpoints++;
176df4b8c2aSAndrew Thompson
177df4b8c2aSAndrew Thompson LIBUSB20_INIT(LIBUSB20_ENDPOINT_DESC, &last_ep->desc);
178df4b8c2aSAndrew Thompson
179df4b8c2aSAndrew Thompson if (libusb20_me_decode(ptr, ptr[0], &last_ep->desc)) {
180df4b8c2aSAndrew Thompson /* ignore */
181df4b8c2aSAndrew Thompson }
182df4b8c2aSAndrew Thompson last_ep->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
183df4b8c2aSAndrew Thompson last_ep->extra.len = 0;
184df4b8c2aSAndrew Thompson last_ep->extra.type = LIBUSB20_ME_IS_RAW;
185df4b8c2aSAndrew Thompson } else {
186df4b8c2aSAndrew Thompson lub_config->extra.len += ptr[0];
187df4b8c2aSAndrew Thompson }
188df4b8c2aSAndrew Thompson
189df4b8c2aSAndrew Thompson } else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) {
190df4b8c2aSAndrew Thompson if (ptr[2] != iface_no) {
191df4b8c2aSAndrew Thompson /* new interface */
192df4b8c2aSAndrew Thompson iface_no = ptr[2];
193df4b8c2aSAndrew Thompson lub_interface++;
194df4b8c2aSAndrew Thompson lub_config->num_interface++;
195df4b8c2aSAndrew Thompson last_if = lub_interface;
196df4b8c2aSAndrew Thompson niface++;
197df4b8c2aSAndrew Thompson } else {
198df4b8c2aSAndrew Thompson /* one more alternate setting */
199df4b8c2aSAndrew Thompson lub_interface->num_altsetting++;
200df4b8c2aSAndrew Thompson last_if = lub_alt_interface;
201df4b8c2aSAndrew Thompson lub_alt_interface++;
202df4b8c2aSAndrew Thompson }
203df4b8c2aSAndrew Thompson
204df4b8c2aSAndrew Thompson LIBUSB20_INIT(LIBUSB20_INTERFACE_DESC, &last_if->desc);
205df4b8c2aSAndrew Thompson
206df4b8c2aSAndrew Thompson if (libusb20_me_decode(ptr, ptr[0], &last_if->desc)) {
207df4b8c2aSAndrew Thompson /* ignore */
208df4b8c2aSAndrew Thompson }
20910557931SHans Petter Selasky
21010557931SHans Petter Selasky /* detect broken USB descriptors when USB debugging is enabled */
21110557931SHans Petter Selasky if (last_if->desc.bInterfaceNumber != (uint8_t)(niface - 1)) {
21210557931SHans Petter Selasky const char *str = getenv("LIBUSB_DEBUG");
21310557931SHans Petter Selasky if (str != NULL && str[0] != '\0' && str[0] != '0') {
21410557931SHans Petter Selasky printf("LIBUSB_DEBUG: bInterfaceNumber(%u) is not sequential(%u)\n",
21510557931SHans Petter Selasky last_if->desc.bInterfaceNumber, niface - 1);
21610557931SHans Petter Selasky }
21710557931SHans Petter Selasky }
218df4b8c2aSAndrew Thompson last_if->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
219df4b8c2aSAndrew Thompson last_if->extra.len = 0;
220df4b8c2aSAndrew Thompson last_if->extra.type = LIBUSB20_ME_IS_RAW;
221df4b8c2aSAndrew Thompson last_if->endpoints = lub_endpoint + 1;
222df4b8c2aSAndrew Thompson last_if->altsetting = lub_alt_interface;
223df4b8c2aSAndrew Thompson last_if->num_altsetting = 0;
224df4b8c2aSAndrew Thompson last_if->num_endpoints = 0;
225df4b8c2aSAndrew Thompson last_ep = NULL;
226df4b8c2aSAndrew Thompson } else {
227df4b8c2aSAndrew Thompson /* unknown descriptor */
228df4b8c2aSAndrew Thompson if (last_if) {
229df4b8c2aSAndrew Thompson if (last_ep) {
230df4b8c2aSAndrew Thompson last_ep->extra.len += ptr[0];
231df4b8c2aSAndrew Thompson } else {
232df4b8c2aSAndrew Thompson last_if->extra.len += ptr[0];
233df4b8c2aSAndrew Thompson }
234df4b8c2aSAndrew Thompson } else {
235df4b8c2aSAndrew Thompson lub_config->extra.len += ptr[0];
236df4b8c2aSAndrew Thompson }
237df4b8c2aSAndrew Thompson }
238df4b8c2aSAndrew Thompson }
239df4b8c2aSAndrew Thompson return (lub_config);
240df4b8c2aSAndrew Thompson }
241df4b8c2aSAndrew Thompson
242df4b8c2aSAndrew Thompson /*------------------------------------------------------------------------*
243df4b8c2aSAndrew Thompson * libusb20_desc_foreach
244df4b8c2aSAndrew Thompson *
245df4b8c2aSAndrew Thompson * Safe traversal of USB descriptors.
246df4b8c2aSAndrew Thompson *
247df4b8c2aSAndrew Thompson * Return values:
248df4b8c2aSAndrew Thompson * NULL: End of descriptors
249df4b8c2aSAndrew Thompson * Else: Pointer to next descriptor
250df4b8c2aSAndrew Thompson *------------------------------------------------------------------------*/
251df4b8c2aSAndrew Thompson const uint8_t *
libusb20_desc_foreach(const struct libusb20_me_struct * pdesc,const uint8_t * psubdesc)252df4b8c2aSAndrew Thompson libusb20_desc_foreach(const struct libusb20_me_struct *pdesc,
253df4b8c2aSAndrew Thompson const uint8_t *psubdesc)
254df4b8c2aSAndrew Thompson {
255df4b8c2aSAndrew Thompson const uint8_t *start;
256df4b8c2aSAndrew Thompson const uint8_t *end;
257df4b8c2aSAndrew Thompson const uint8_t *desc_next;
258df4b8c2aSAndrew Thompson
259df4b8c2aSAndrew Thompson /* be NULL safe */
260df4b8c2aSAndrew Thompson if (pdesc == NULL)
261df4b8c2aSAndrew Thompson return (NULL);
262df4b8c2aSAndrew Thompson
263df4b8c2aSAndrew Thompson start = (const uint8_t *)pdesc->ptr;
264df4b8c2aSAndrew Thompson end = LIBUSB20_ADD_BYTES(start, pdesc->len);
265df4b8c2aSAndrew Thompson
266df4b8c2aSAndrew Thompson /* get start of next descriptor */
267df4b8c2aSAndrew Thompson if (psubdesc == NULL)
268df4b8c2aSAndrew Thompson psubdesc = start;
269df4b8c2aSAndrew Thompson else
270df4b8c2aSAndrew Thompson psubdesc = psubdesc + psubdesc[0];
271df4b8c2aSAndrew Thompson
272df4b8c2aSAndrew Thompson /* check that the next USB descriptor is within the range */
273df4b8c2aSAndrew Thompson if ((psubdesc < start) || (psubdesc >= end))
274df4b8c2aSAndrew Thompson return (NULL); /* out of range, or EOD */
275df4b8c2aSAndrew Thompson
276df4b8c2aSAndrew Thompson /* check start of the second next USB descriptor, if any */
277df4b8c2aSAndrew Thompson desc_next = psubdesc + psubdesc[0];
278df4b8c2aSAndrew Thompson if ((desc_next < start) || (desc_next > end))
279df4b8c2aSAndrew Thompson return (NULL); /* out of range */
280df4b8c2aSAndrew Thompson
281df4b8c2aSAndrew Thompson /* check minimum descriptor length */
282df4b8c2aSAndrew Thompson if (psubdesc[0] < 3)
283df4b8c2aSAndrew Thompson return (NULL); /* too short descriptor */
284df4b8c2aSAndrew Thompson
285df4b8c2aSAndrew Thompson return (psubdesc); /* return start of next descriptor */
286df4b8c2aSAndrew Thompson }
287df4b8c2aSAndrew Thompson
288df4b8c2aSAndrew Thompson /*------------------------------------------------------------------------*
289df4b8c2aSAndrew Thompson * libusb20_me_get_1 - safety wrapper to read out one byte
290df4b8c2aSAndrew Thompson *------------------------------------------------------------------------*/
291df4b8c2aSAndrew Thompson uint8_t
libusb20_me_get_1(const struct libusb20_me_struct * ie,uint16_t offset)292df4b8c2aSAndrew Thompson libusb20_me_get_1(const struct libusb20_me_struct *ie, uint16_t offset)
293df4b8c2aSAndrew Thompson {
294df4b8c2aSAndrew Thompson if (offset < ie->len) {
295df4b8c2aSAndrew Thompson return (*((uint8_t *)LIBUSB20_ADD_BYTES(ie->ptr, offset)));
296df4b8c2aSAndrew Thompson }
297df4b8c2aSAndrew Thompson return (0);
298df4b8c2aSAndrew Thompson }
299df4b8c2aSAndrew Thompson
300df4b8c2aSAndrew Thompson /*------------------------------------------------------------------------*
301df4b8c2aSAndrew Thompson * libusb20_me_get_2 - safety wrapper to read out one word
302df4b8c2aSAndrew Thompson *------------------------------------------------------------------------*/
303df4b8c2aSAndrew Thompson uint16_t
libusb20_me_get_2(const struct libusb20_me_struct * ie,uint16_t offset)304df4b8c2aSAndrew Thompson libusb20_me_get_2(const struct libusb20_me_struct *ie, uint16_t offset)
305df4b8c2aSAndrew Thompson {
306df4b8c2aSAndrew Thompson return (libusb20_me_get_1(ie, offset) |
307df4b8c2aSAndrew Thompson (libusb20_me_get_1(ie, offset + 1) << 8));
308df4b8c2aSAndrew Thompson }
309df4b8c2aSAndrew Thompson
310df4b8c2aSAndrew Thompson /*------------------------------------------------------------------------*
311df4b8c2aSAndrew Thompson * libusb20_me_encode - encode a message structure
312df4b8c2aSAndrew Thompson *
313df4b8c2aSAndrew Thompson * Description of parameters:
314df4b8c2aSAndrew Thompson * "len" - maximum length of output buffer
315df4b8c2aSAndrew Thompson * "ptr" - pointer to output buffer. If NULL, no data will be written
316df4b8c2aSAndrew Thompson * "pd" - source structure
317df4b8c2aSAndrew Thompson *
318df4b8c2aSAndrew Thompson * Return values:
319df4b8c2aSAndrew Thompson * 0..65535 - Number of bytes used, limited by the "len" input parameter.
320df4b8c2aSAndrew Thompson *------------------------------------------------------------------------*/
321df4b8c2aSAndrew Thompson uint16_t
libusb20_me_encode(void * ptr,uint16_t len,const void * pd)322df4b8c2aSAndrew Thompson libusb20_me_encode(void *ptr, uint16_t len, const void *pd)
323df4b8c2aSAndrew Thompson {
324df4b8c2aSAndrew Thompson const uint8_t *pf; /* pointer to format data */
325df4b8c2aSAndrew Thompson uint8_t *buf; /* pointer to output buffer */
326df4b8c2aSAndrew Thompson
327df4b8c2aSAndrew Thompson uint32_t pd_offset; /* decoded structure offset */
328df4b8c2aSAndrew Thompson uint16_t len_old; /* old length */
329df4b8c2aSAndrew Thompson uint16_t pd_count; /* decoded element count */
330df4b8c2aSAndrew Thompson uint8_t me; /* message element */
331df4b8c2aSAndrew Thompson
332df4b8c2aSAndrew Thompson /* initialise */
333df4b8c2aSAndrew Thompson
334df4b8c2aSAndrew Thompson len_old = len;
335df4b8c2aSAndrew Thompson buf = ptr;
336df4b8c2aSAndrew Thompson pd_offset = sizeof(void *);
337df4b8c2aSAndrew Thompson pf = (*((struct libusb20_me_format *const *)pd))->format;
338df4b8c2aSAndrew Thompson
339df4b8c2aSAndrew Thompson /* scan */
340df4b8c2aSAndrew Thompson
341df4b8c2aSAndrew Thompson while (1) {
342df4b8c2aSAndrew Thompson
343df4b8c2aSAndrew Thompson /* get information element */
344df4b8c2aSAndrew Thompson
345df4b8c2aSAndrew Thompson me = (pf[0]) & LIBUSB20_ME_MASK;
346df4b8c2aSAndrew Thompson pd_count = pf[1] | (pf[2] << 8);
347df4b8c2aSAndrew Thompson pf += 3;
348df4b8c2aSAndrew Thompson
349df4b8c2aSAndrew Thompson /* encode the message element */
350df4b8c2aSAndrew Thompson
351df4b8c2aSAndrew Thompson switch (me) {
352df4b8c2aSAndrew Thompson case LIBUSB20_ME_INT8:
353df4b8c2aSAndrew Thompson while (pd_count--) {
354df4b8c2aSAndrew Thompson uint8_t temp;
355df4b8c2aSAndrew Thompson
356df4b8c2aSAndrew Thompson if (len < 1) /* overflow */
357df4b8c2aSAndrew Thompson goto done;
358df4b8c2aSAndrew Thompson if (buf) {
359df4b8c2aSAndrew Thompson temp = *((const uint8_t *)
360df4b8c2aSAndrew Thompson LIBUSB20_ADD_BYTES(pd, pd_offset));
361df4b8c2aSAndrew Thompson buf[0] = temp;
362df4b8c2aSAndrew Thompson buf += 1;
363df4b8c2aSAndrew Thompson }
364df4b8c2aSAndrew Thompson pd_offset += 1;
365df4b8c2aSAndrew Thompson len -= 1;
366df4b8c2aSAndrew Thompson }
367df4b8c2aSAndrew Thompson break;
368df4b8c2aSAndrew Thompson
369df4b8c2aSAndrew Thompson case LIBUSB20_ME_INT16:
370df4b8c2aSAndrew Thompson pd_offset = -((-pd_offset) & ~1); /* align */
371df4b8c2aSAndrew Thompson while (pd_count--) {
372df4b8c2aSAndrew Thompson uint16_t temp;
373df4b8c2aSAndrew Thompson
374df4b8c2aSAndrew Thompson if (len < 2) /* overflow */
375df4b8c2aSAndrew Thompson goto done;
376df4b8c2aSAndrew Thompson
377df4b8c2aSAndrew Thompson if (buf) {
378df4b8c2aSAndrew Thompson temp = *((const uint16_t *)
379df4b8c2aSAndrew Thompson LIBUSB20_ADD_BYTES(pd, pd_offset));
380df4b8c2aSAndrew Thompson buf[1] = (temp >> 8) & 0xFF;
381df4b8c2aSAndrew Thompson buf[0] = temp & 0xFF;
382df4b8c2aSAndrew Thompson buf += 2;
383df4b8c2aSAndrew Thompson }
384df4b8c2aSAndrew Thompson pd_offset += 2;
385df4b8c2aSAndrew Thompson len -= 2;
386df4b8c2aSAndrew Thompson }
387df4b8c2aSAndrew Thompson break;
388df4b8c2aSAndrew Thompson
389df4b8c2aSAndrew Thompson case LIBUSB20_ME_INT32:
390df4b8c2aSAndrew Thompson pd_offset = -((-pd_offset) & ~3); /* align */
391df4b8c2aSAndrew Thompson while (pd_count--) {
392df4b8c2aSAndrew Thompson uint32_t temp;
393df4b8c2aSAndrew Thompson
394df4b8c2aSAndrew Thompson if (len < 4) /* overflow */
395df4b8c2aSAndrew Thompson goto done;
396df4b8c2aSAndrew Thompson if (buf) {
397df4b8c2aSAndrew Thompson temp = *((const uint32_t *)
398df4b8c2aSAndrew Thompson LIBUSB20_ADD_BYTES(pd, pd_offset));
399df4b8c2aSAndrew Thompson buf[3] = (temp >> 24) & 0xFF;
400df4b8c2aSAndrew Thompson buf[2] = (temp >> 16) & 0xFF;
401df4b8c2aSAndrew Thompson buf[1] = (temp >> 8) & 0xFF;
402df4b8c2aSAndrew Thompson buf[0] = temp & 0xFF;
403df4b8c2aSAndrew Thompson buf += 4;
404df4b8c2aSAndrew Thompson }
405df4b8c2aSAndrew Thompson pd_offset += 4;
406df4b8c2aSAndrew Thompson len -= 4;
407df4b8c2aSAndrew Thompson }
408df4b8c2aSAndrew Thompson break;
409df4b8c2aSAndrew Thompson
410df4b8c2aSAndrew Thompson case LIBUSB20_ME_INT64:
411df4b8c2aSAndrew Thompson pd_offset = -((-pd_offset) & ~7); /* align */
412df4b8c2aSAndrew Thompson while (pd_count--) {
413df4b8c2aSAndrew Thompson uint64_t temp;
414df4b8c2aSAndrew Thompson
415df4b8c2aSAndrew Thompson if (len < 8) /* overflow */
416df4b8c2aSAndrew Thompson goto done;
417df4b8c2aSAndrew Thompson if (buf) {
418df4b8c2aSAndrew Thompson
419df4b8c2aSAndrew Thompson temp = *((const uint64_t *)
420df4b8c2aSAndrew Thompson LIBUSB20_ADD_BYTES(pd, pd_offset));
421df4b8c2aSAndrew Thompson buf[7] = (temp >> 56) & 0xFF;
422df4b8c2aSAndrew Thompson buf[6] = (temp >> 48) & 0xFF;
423df4b8c2aSAndrew Thompson buf[5] = (temp >> 40) & 0xFF;
424df4b8c2aSAndrew Thompson buf[4] = (temp >> 32) & 0xFF;
425df4b8c2aSAndrew Thompson buf[3] = (temp >> 24) & 0xFF;
426df4b8c2aSAndrew Thompson buf[2] = (temp >> 16) & 0xFF;
427df4b8c2aSAndrew Thompson buf[1] = (temp >> 8) & 0xFF;
428df4b8c2aSAndrew Thompson buf[0] = temp & 0xFF;
429df4b8c2aSAndrew Thompson buf += 8;
430df4b8c2aSAndrew Thompson }
431df4b8c2aSAndrew Thompson pd_offset += 8;
432df4b8c2aSAndrew Thompson len -= 8;
433df4b8c2aSAndrew Thompson }
434df4b8c2aSAndrew Thompson break;
435df4b8c2aSAndrew Thompson
436df4b8c2aSAndrew Thompson case LIBUSB20_ME_STRUCT:
437df4b8c2aSAndrew Thompson pd_offset = -((-pd_offset) &
438df4b8c2aSAndrew Thompson ~(LIBUSB20_ME_STRUCT_ALIGN - 1)); /* align */
439df4b8c2aSAndrew Thompson while (pd_count--) {
440df4b8c2aSAndrew Thompson void *src_ptr;
441df4b8c2aSAndrew Thompson uint16_t src_len;
442df4b8c2aSAndrew Thompson struct libusb20_me_struct *ps;
443df4b8c2aSAndrew Thompson
444df4b8c2aSAndrew Thompson ps = LIBUSB20_ADD_BYTES(pd, pd_offset);
445df4b8c2aSAndrew Thompson
446df4b8c2aSAndrew Thompson switch (ps->type) {
447df4b8c2aSAndrew Thompson case LIBUSB20_ME_IS_RAW:
448df4b8c2aSAndrew Thompson src_len = ps->len;
449df4b8c2aSAndrew Thompson src_ptr = ps->ptr;
450df4b8c2aSAndrew Thompson break;
451df4b8c2aSAndrew Thompson
452df4b8c2aSAndrew Thompson case LIBUSB20_ME_IS_ENCODED:
453df4b8c2aSAndrew Thompson if (ps->len == 0) {
454df4b8c2aSAndrew Thompson /*
455df4b8c2aSAndrew Thompson * Length is encoded
456df4b8c2aSAndrew Thompson * in the data itself
457df4b8c2aSAndrew Thompson * and should be
458df4b8c2aSAndrew Thompson * correct:
459df4b8c2aSAndrew Thompson */
460d81535d1SHans Petter Selasky ps->len = 0xFFFF;
461df4b8c2aSAndrew Thompson }
462df4b8c2aSAndrew Thompson src_len = libusb20_me_get_1(pd, 0);
463df4b8c2aSAndrew Thompson src_ptr = LIBUSB20_ADD_BYTES(ps->ptr, 1);
464df4b8c2aSAndrew Thompson if (src_len == 0xFF) {
465df4b8c2aSAndrew Thompson /* length is escaped */
466df4b8c2aSAndrew Thompson src_len = libusb20_me_get_2(pd, 1);
467df4b8c2aSAndrew Thompson src_ptr =
468df4b8c2aSAndrew Thompson LIBUSB20_ADD_BYTES(ps->ptr, 3);
469df4b8c2aSAndrew Thompson }
470df4b8c2aSAndrew Thompson break;
471df4b8c2aSAndrew Thompson
472df4b8c2aSAndrew Thompson case LIBUSB20_ME_IS_DECODED:
473df4b8c2aSAndrew Thompson /* reserve 3 length bytes */
474df4b8c2aSAndrew Thompson src_len = libusb20_me_encode(NULL,
475d81535d1SHans Petter Selasky 0xFFFF - 3, ps->ptr);
476df4b8c2aSAndrew Thompson src_ptr = NULL;
477df4b8c2aSAndrew Thompson break;
478df4b8c2aSAndrew Thompson
479df4b8c2aSAndrew Thompson default: /* empty structure */
480df4b8c2aSAndrew Thompson src_len = 0;
481df4b8c2aSAndrew Thompson src_ptr = NULL;
482df4b8c2aSAndrew Thompson break;
483df4b8c2aSAndrew Thompson }
484df4b8c2aSAndrew Thompson
485df4b8c2aSAndrew Thompson if (src_len > 0xFE) {
486d81535d1SHans Petter Selasky if (src_len > (0xFFFF - 3))
487df4b8c2aSAndrew Thompson /* overflow */
488df4b8c2aSAndrew Thompson goto done;
489df4b8c2aSAndrew Thompson
490df4b8c2aSAndrew Thompson if (len < (src_len + 3))
491df4b8c2aSAndrew Thompson /* overflow */
492df4b8c2aSAndrew Thompson goto done;
493df4b8c2aSAndrew Thompson
494df4b8c2aSAndrew Thompson if (buf) {
495df4b8c2aSAndrew Thompson buf[0] = 0xFF;
496df4b8c2aSAndrew Thompson buf[1] = (src_len & 0xFF);
497df4b8c2aSAndrew Thompson buf[2] = (src_len >> 8) & 0xFF;
498df4b8c2aSAndrew Thompson buf += 3;
499df4b8c2aSAndrew Thompson }
500df4b8c2aSAndrew Thompson len -= (src_len + 3);
501df4b8c2aSAndrew Thompson } else {
502df4b8c2aSAndrew Thompson if (len < (src_len + 1))
503df4b8c2aSAndrew Thompson /* overflow */
504df4b8c2aSAndrew Thompson goto done;
505df4b8c2aSAndrew Thompson
506df4b8c2aSAndrew Thompson if (buf) {
507df4b8c2aSAndrew Thompson buf[0] = (src_len & 0xFF);
508df4b8c2aSAndrew Thompson buf += 1;
509df4b8c2aSAndrew Thompson }
510df4b8c2aSAndrew Thompson len -= (src_len + 1);
511df4b8c2aSAndrew Thompson }
512df4b8c2aSAndrew Thompson
513df4b8c2aSAndrew Thompson /* check for buffer and non-zero length */
514df4b8c2aSAndrew Thompson
515df4b8c2aSAndrew Thompson if (buf && src_len) {
516df4b8c2aSAndrew Thompson if (ps->type == LIBUSB20_ME_IS_DECODED) {
517df4b8c2aSAndrew Thompson /*
518df4b8c2aSAndrew Thompson * Repeat encode
519df4b8c2aSAndrew Thompson * procedure - we have
520df4b8c2aSAndrew Thompson * room for the
521df4b8c2aSAndrew Thompson * complete structure:
522df4b8c2aSAndrew Thompson */
523721d6da7SMarcelo Araujo (void) libusb20_me_encode(buf,
524d81535d1SHans Petter Selasky 0xFFFF - 3, ps->ptr);
525df4b8c2aSAndrew Thompson } else {
526df4b8c2aSAndrew Thompson bcopy(src_ptr, buf, src_len);
527df4b8c2aSAndrew Thompson }
528df4b8c2aSAndrew Thompson buf += src_len;
529df4b8c2aSAndrew Thompson }
530df4b8c2aSAndrew Thompson pd_offset += sizeof(struct libusb20_me_struct);
531df4b8c2aSAndrew Thompson }
532df4b8c2aSAndrew Thompson break;
533df4b8c2aSAndrew Thompson
534df4b8c2aSAndrew Thompson default:
535df4b8c2aSAndrew Thompson goto done;
536df4b8c2aSAndrew Thompson }
537df4b8c2aSAndrew Thompson }
538df4b8c2aSAndrew Thompson done:
539df4b8c2aSAndrew Thompson return (len_old - len);
540df4b8c2aSAndrew Thompson }
541df4b8c2aSAndrew Thompson
542df4b8c2aSAndrew Thompson /*------------------------------------------------------------------------*
543df4b8c2aSAndrew Thompson * libusb20_me_decode - decode a message into a decoded structure
544df4b8c2aSAndrew Thompson *
545df4b8c2aSAndrew Thompson * Description of parameters:
546df4b8c2aSAndrew Thompson * "ptr" - message pointer
547df4b8c2aSAndrew Thompson * "len" - message length
548df4b8c2aSAndrew Thompson * "pd" - pointer to decoded structure
549df4b8c2aSAndrew Thompson *
550df4b8c2aSAndrew Thompson * Returns:
551df4b8c2aSAndrew Thompson * "0..65535" - number of bytes decoded, limited by "len"
552df4b8c2aSAndrew Thompson *------------------------------------------------------------------------*/
553df4b8c2aSAndrew Thompson uint16_t
libusb20_me_decode(const void * ptr,uint16_t len,void * pd)554df4b8c2aSAndrew Thompson libusb20_me_decode(const void *ptr, uint16_t len, void *pd)
555df4b8c2aSAndrew Thompson {
556df4b8c2aSAndrew Thompson const uint8_t *pf; /* pointer to format data */
557df4b8c2aSAndrew Thompson const uint8_t *buf; /* pointer to input buffer */
558df4b8c2aSAndrew Thompson
559df4b8c2aSAndrew Thompson uint32_t pd_offset; /* decoded structure offset */
560df4b8c2aSAndrew Thompson uint16_t len_old; /* old length */
561df4b8c2aSAndrew Thompson uint16_t pd_count; /* decoded element count */
562df4b8c2aSAndrew Thompson uint8_t me; /* message element */
563df4b8c2aSAndrew Thompson
564df4b8c2aSAndrew Thompson /* initialise */
565df4b8c2aSAndrew Thompson
566df4b8c2aSAndrew Thompson len_old = len;
567df4b8c2aSAndrew Thompson buf = ptr;
568df4b8c2aSAndrew Thompson pd_offset = sizeof(void *);
569df4b8c2aSAndrew Thompson pf = (*((struct libusb20_me_format **)pd))->format;
570df4b8c2aSAndrew Thompson
571df4b8c2aSAndrew Thompson /* scan */
572df4b8c2aSAndrew Thompson
573df4b8c2aSAndrew Thompson while (1) {
574df4b8c2aSAndrew Thompson
575df4b8c2aSAndrew Thompson /* get information element */
576df4b8c2aSAndrew Thompson
577df4b8c2aSAndrew Thompson me = (pf[0]) & LIBUSB20_ME_MASK;
578df4b8c2aSAndrew Thompson pd_count = pf[1] | (pf[2] << 8);
579df4b8c2aSAndrew Thompson pf += 3;
580df4b8c2aSAndrew Thompson
581df4b8c2aSAndrew Thompson /* decode the message element by type */
582df4b8c2aSAndrew Thompson
583df4b8c2aSAndrew Thompson switch (me) {
584df4b8c2aSAndrew Thompson case LIBUSB20_ME_INT8:
585df4b8c2aSAndrew Thompson while (pd_count--) {
586df4b8c2aSAndrew Thompson uint8_t temp;
587df4b8c2aSAndrew Thompson
588df4b8c2aSAndrew Thompson if (len < 1) {
589df4b8c2aSAndrew Thompson len = 0;
590df4b8c2aSAndrew Thompson temp = 0;
591df4b8c2aSAndrew Thompson } else {
592df4b8c2aSAndrew Thompson len -= 1;
593df4b8c2aSAndrew Thompson temp = buf[0];
594df4b8c2aSAndrew Thompson buf++;
595df4b8c2aSAndrew Thompson }
596df4b8c2aSAndrew Thompson *((uint8_t *)LIBUSB20_ADD_BYTES(pd,
597df4b8c2aSAndrew Thompson pd_offset)) = temp;
598df4b8c2aSAndrew Thompson pd_offset += 1;
599df4b8c2aSAndrew Thompson }
600df4b8c2aSAndrew Thompson break;
601df4b8c2aSAndrew Thompson
602df4b8c2aSAndrew Thompson case LIBUSB20_ME_INT16:
603df4b8c2aSAndrew Thompson pd_offset = -((-pd_offset) & ~1); /* align */
604df4b8c2aSAndrew Thompson while (pd_count--) {
605df4b8c2aSAndrew Thompson uint16_t temp;
606df4b8c2aSAndrew Thompson
607df4b8c2aSAndrew Thompson if (len < 2) {
608df4b8c2aSAndrew Thompson len = 0;
609df4b8c2aSAndrew Thompson temp = 0;
610df4b8c2aSAndrew Thompson } else {
611df4b8c2aSAndrew Thompson len -= 2;
612df4b8c2aSAndrew Thompson temp = buf[1] << 8;
613df4b8c2aSAndrew Thompson temp |= buf[0];
614df4b8c2aSAndrew Thompson buf += 2;
615df4b8c2aSAndrew Thompson }
616df4b8c2aSAndrew Thompson *((uint16_t *)LIBUSB20_ADD_BYTES(pd,
617df4b8c2aSAndrew Thompson pd_offset)) = temp;
618df4b8c2aSAndrew Thompson pd_offset += 2;
619df4b8c2aSAndrew Thompson }
620df4b8c2aSAndrew Thompson break;
621df4b8c2aSAndrew Thompson
622df4b8c2aSAndrew Thompson case LIBUSB20_ME_INT32:
623df4b8c2aSAndrew Thompson pd_offset = -((-pd_offset) & ~3); /* align */
624df4b8c2aSAndrew Thompson while (pd_count--) {
625df4b8c2aSAndrew Thompson uint32_t temp;
626df4b8c2aSAndrew Thompson
627df4b8c2aSAndrew Thompson if (len < 4) {
628df4b8c2aSAndrew Thompson len = 0;
629df4b8c2aSAndrew Thompson temp = 0;
630df4b8c2aSAndrew Thompson } else {
631df4b8c2aSAndrew Thompson len -= 4;
632df4b8c2aSAndrew Thompson temp = buf[3] << 24;
633df4b8c2aSAndrew Thompson temp |= buf[2] << 16;
634df4b8c2aSAndrew Thompson temp |= buf[1] << 8;
635df4b8c2aSAndrew Thompson temp |= buf[0];
636df4b8c2aSAndrew Thompson buf += 4;
637df4b8c2aSAndrew Thompson }
638df4b8c2aSAndrew Thompson
639df4b8c2aSAndrew Thompson *((uint32_t *)LIBUSB20_ADD_BYTES(pd,
640df4b8c2aSAndrew Thompson pd_offset)) = temp;
641df4b8c2aSAndrew Thompson pd_offset += 4;
642df4b8c2aSAndrew Thompson }
643df4b8c2aSAndrew Thompson break;
644df4b8c2aSAndrew Thompson
645df4b8c2aSAndrew Thompson case LIBUSB20_ME_INT64:
646df4b8c2aSAndrew Thompson pd_offset = -((-pd_offset) & ~7); /* align */
647df4b8c2aSAndrew Thompson while (pd_count--) {
648df4b8c2aSAndrew Thompson uint64_t temp;
649df4b8c2aSAndrew Thompson
650df4b8c2aSAndrew Thompson if (len < 8) {
651df4b8c2aSAndrew Thompson len = 0;
652df4b8c2aSAndrew Thompson temp = 0;
653df4b8c2aSAndrew Thompson } else {
654df4b8c2aSAndrew Thompson len -= 8;
655df4b8c2aSAndrew Thompson temp = ((uint64_t)buf[7]) << 56;
656df4b8c2aSAndrew Thompson temp |= ((uint64_t)buf[6]) << 48;
657df4b8c2aSAndrew Thompson temp |= ((uint64_t)buf[5]) << 40;
658df4b8c2aSAndrew Thompson temp |= ((uint64_t)buf[4]) << 32;
659df4b8c2aSAndrew Thompson temp |= buf[3] << 24;
660df4b8c2aSAndrew Thompson temp |= buf[2] << 16;
661df4b8c2aSAndrew Thompson temp |= buf[1] << 8;
662df4b8c2aSAndrew Thompson temp |= buf[0];
663df4b8c2aSAndrew Thompson buf += 8;
664df4b8c2aSAndrew Thompson }
665df4b8c2aSAndrew Thompson
666df4b8c2aSAndrew Thompson *((uint64_t *)LIBUSB20_ADD_BYTES(pd,
667df4b8c2aSAndrew Thompson pd_offset)) = temp;
668df4b8c2aSAndrew Thompson pd_offset += 8;
669df4b8c2aSAndrew Thompson }
670df4b8c2aSAndrew Thompson break;
671df4b8c2aSAndrew Thompson
672df4b8c2aSAndrew Thompson case LIBUSB20_ME_STRUCT:
673df4b8c2aSAndrew Thompson pd_offset = -((-pd_offset) &
674df4b8c2aSAndrew Thompson ~(LIBUSB20_ME_STRUCT_ALIGN - 1)); /* align */
675df4b8c2aSAndrew Thompson while (pd_count--) {
676df4b8c2aSAndrew Thompson uint16_t temp;
677df4b8c2aSAndrew Thompson struct libusb20_me_struct *ps;
678df4b8c2aSAndrew Thompson
679df4b8c2aSAndrew Thompson ps = LIBUSB20_ADD_BYTES(pd, pd_offset);
680df4b8c2aSAndrew Thompson
681df4b8c2aSAndrew Thompson if (ps->type == LIBUSB20_ME_IS_ENCODED) {
682df4b8c2aSAndrew Thompson /*
683df4b8c2aSAndrew Thompson * Pre-store a de-constified
684df4b8c2aSAndrew Thompson * pointer to the raw
685df4b8c2aSAndrew Thompson * structure:
686df4b8c2aSAndrew Thompson */
687df4b8c2aSAndrew Thompson ps->ptr = LIBUSB20_ADD_BYTES(buf, 0);
688df4b8c2aSAndrew Thompson
689df4b8c2aSAndrew Thompson /*
690df4b8c2aSAndrew Thompson * Get the correct number of
691df4b8c2aSAndrew Thompson * length bytes:
692df4b8c2aSAndrew Thompson */
693df4b8c2aSAndrew Thompson if (len != 0) {
694df4b8c2aSAndrew Thompson if (buf[0] == 0xFF) {
695df4b8c2aSAndrew Thompson ps->len = 3;
696df4b8c2aSAndrew Thompson } else {
697df4b8c2aSAndrew Thompson ps->len = 1;
698df4b8c2aSAndrew Thompson }
699df4b8c2aSAndrew Thompson } else {
700df4b8c2aSAndrew Thompson ps->len = 0;
701df4b8c2aSAndrew Thompson }
702df4b8c2aSAndrew Thompson }
703df4b8c2aSAndrew Thompson /* get the structure length */
704df4b8c2aSAndrew Thompson
705df4b8c2aSAndrew Thompson if (len != 0) {
706df4b8c2aSAndrew Thompson if (buf[0] == 0xFF) {
707df4b8c2aSAndrew Thompson if (len < 3) {
708df4b8c2aSAndrew Thompson len = 0;
709df4b8c2aSAndrew Thompson temp = 0;
710df4b8c2aSAndrew Thompson } else {
711df4b8c2aSAndrew Thompson len -= 3;
712df4b8c2aSAndrew Thompson temp = buf[1] |
713df4b8c2aSAndrew Thompson (buf[2] << 8);
714df4b8c2aSAndrew Thompson buf += 3;
715df4b8c2aSAndrew Thompson }
716df4b8c2aSAndrew Thompson } else {
717df4b8c2aSAndrew Thompson len -= 1;
718df4b8c2aSAndrew Thompson temp = buf[0];
719df4b8c2aSAndrew Thompson buf += 1;
720df4b8c2aSAndrew Thompson }
721df4b8c2aSAndrew Thompson } else {
722df4b8c2aSAndrew Thompson len = 0;
723df4b8c2aSAndrew Thompson temp = 0;
724df4b8c2aSAndrew Thompson }
725df4b8c2aSAndrew Thompson /* check for invalid length */
726df4b8c2aSAndrew Thompson
727df4b8c2aSAndrew Thompson if (temp > len) {
728df4b8c2aSAndrew Thompson len = 0;
729df4b8c2aSAndrew Thompson temp = 0;
730df4b8c2aSAndrew Thompson }
731df4b8c2aSAndrew Thompson /* check wanted structure type */
732df4b8c2aSAndrew Thompson
733df4b8c2aSAndrew Thompson switch (ps->type) {
734df4b8c2aSAndrew Thompson case LIBUSB20_ME_IS_ENCODED:
735df4b8c2aSAndrew Thompson /* check for zero length */
736df4b8c2aSAndrew Thompson if (temp == 0) {
737df4b8c2aSAndrew Thompson /*
738df4b8c2aSAndrew Thompson * The pointer must
739df4b8c2aSAndrew Thompson * be valid:
740df4b8c2aSAndrew Thompson */
741df4b8c2aSAndrew Thompson ps->ptr = LIBUSB20_ADD_BYTES(
742df4b8c2aSAndrew Thompson libusb20_me_encode_empty, 0);
743df4b8c2aSAndrew Thompson ps->len = 1;
744df4b8c2aSAndrew Thompson } else {
745df4b8c2aSAndrew Thompson ps->len += temp;
746df4b8c2aSAndrew Thompson }
747df4b8c2aSAndrew Thompson break;
748df4b8c2aSAndrew Thompson
749df4b8c2aSAndrew Thompson case LIBUSB20_ME_IS_RAW:
750df4b8c2aSAndrew Thompson /* update length and pointer */
751df4b8c2aSAndrew Thompson ps->len = temp;
752df4b8c2aSAndrew Thompson ps->ptr = LIBUSB20_ADD_BYTES(buf, 0);
753df4b8c2aSAndrew Thompson break;
754df4b8c2aSAndrew Thompson
755df4b8c2aSAndrew Thompson case LIBUSB20_ME_IS_EMPTY:
756df4b8c2aSAndrew Thompson case LIBUSB20_ME_IS_DECODED:
757df4b8c2aSAndrew Thompson /* check for non-zero length */
758df4b8c2aSAndrew Thompson if (temp != 0) {
759df4b8c2aSAndrew Thompson /* update type */
760df4b8c2aSAndrew Thompson ps->type = LIBUSB20_ME_IS_DECODED;
761df4b8c2aSAndrew Thompson ps->len = 0;
762df4b8c2aSAndrew Thompson /*
763df4b8c2aSAndrew Thompson * Recursivly decode
764df4b8c2aSAndrew Thompson * the next structure
765df4b8c2aSAndrew Thompson */
766721d6da7SMarcelo Araujo (void) libusb20_me_decode(buf,
767df4b8c2aSAndrew Thompson temp, ps->ptr);
768df4b8c2aSAndrew Thompson } else {
769df4b8c2aSAndrew Thompson /* update type */
770df4b8c2aSAndrew Thompson ps->type = LIBUSB20_ME_IS_EMPTY;
771df4b8c2aSAndrew Thompson ps->len = 0;
772df4b8c2aSAndrew Thompson }
773df4b8c2aSAndrew Thompson break;
774df4b8c2aSAndrew Thompson
775df4b8c2aSAndrew Thompson default:
776df4b8c2aSAndrew Thompson /*
777df4b8c2aSAndrew Thompson * nothing to do - should
778df4b8c2aSAndrew Thompson * not happen
779df4b8c2aSAndrew Thompson */
780df4b8c2aSAndrew Thompson ps->ptr = NULL;
781df4b8c2aSAndrew Thompson ps->len = 0;
782df4b8c2aSAndrew Thompson break;
783df4b8c2aSAndrew Thompson }
784df4b8c2aSAndrew Thompson buf += temp;
785df4b8c2aSAndrew Thompson len -= temp;
786df4b8c2aSAndrew Thompson pd_offset += sizeof(struct libusb20_me_struct);
787df4b8c2aSAndrew Thompson }
788df4b8c2aSAndrew Thompson break;
789df4b8c2aSAndrew Thompson
790df4b8c2aSAndrew Thompson default:
791df4b8c2aSAndrew Thompson goto done;
792df4b8c2aSAndrew Thompson }
793df4b8c2aSAndrew Thompson }
794df4b8c2aSAndrew Thompson done:
795df4b8c2aSAndrew Thompson return (len_old - len);
796df4b8c2aSAndrew Thompson }
797