xref: /freebsd-src/lib/libusb/libusb20_desc.c (revision 2a63c3be158216222d89a073dcbd6a72ee4aab5a)
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