1184610Salfred/* $FreeBSD$ */
2184610Salfred/*-
3184610Salfred * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
4184610Salfred *
5184610Salfred * Redistribution and use in source and binary forms, with or without
6184610Salfred * modification, are permitted provided that the following conditions
7184610Salfred * are met:
8184610Salfred * 1. Redistributions of source code must retain the above copyright
9184610Salfred *    notice, this list of conditions and the following disclaimer.
10184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
11184610Salfred *    notice, this list of conditions and the following disclaimer in the
12184610Salfred *    documentation and/or other materials provided with the distribution.
13184610Salfred *
14184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17184610Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24184610Salfred * SUCH DAMAGE.
25184610Salfred */
26184610Salfred
27248236Shselasky#ifdef LIBUSB_GLOBAL_INCLUDE_FILE
28248236Shselasky#include LIBUSB_GLOBAL_INCLUDE_FILE
29248236Shselasky#else
30184610Salfred#include <stdio.h>
31184610Salfred#include <stdlib.h>
32184610Salfred#include <string.h>
33248236Shselasky#include <time.h>
34248236Shselasky#include <sys/queue.h>
35248236Shselasky#endif
36184610Salfred
37184610Salfred#include "libusb20.h"
38184610Salfred#include "libusb20_desc.h"
39184610Salfred#include "libusb20_int.h"
40184610Salfred
41184610Salfredstatic const uint32_t libusb20_me_encode_empty[2];	/* dummy */
42184610Salfred
43184610SalfredLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_DEVICE_DESC);
44184610SalfredLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_ENDPOINT_DESC);
45184610SalfredLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_INTERFACE_DESC);
46184610SalfredLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONFIG_DESC);
47184610SalfredLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONTROL_SETUP);
48227404ShselaskyLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_ENDPT_COMP_DESC);
49227404ShselaskyLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_USB_20_DEVCAP_DESC);
50227404ShselaskyLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_USB_DEVCAP_DESC);
51227404ShselaskyLIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_BOS_DESCRIPTOR);
52184610Salfred
53184610Salfred/*------------------------------------------------------------------------*
54184610Salfred *	libusb20_parse_config_desc
55184610Salfred *
56184610Salfred * Return values:
57184610Salfred * NULL: Out of memory.
58184610Salfred * Else: A valid config structure pointer which must be passed to "free()"
59184610Salfred *------------------------------------------------------------------------*/
60184610Salfredstruct libusb20_config *
61184610Salfredlibusb20_parse_config_desc(const void *config_desc)
62184610Salfred{
63184610Salfred	struct libusb20_config *lub_config;
64184610Salfred	struct libusb20_interface *lub_interface;
65184610Salfred	struct libusb20_interface *lub_alt_interface;
66184610Salfred	struct libusb20_interface *last_if;
67184610Salfred	struct libusb20_endpoint *lub_endpoint;
68184610Salfred	struct libusb20_endpoint *last_ep;
69184610Salfred
70184610Salfred	struct libusb20_me_struct pcdesc;
71184610Salfred	const uint8_t *ptr;
72184610Salfred	uint32_t size;
73184610Salfred	uint16_t niface_no_alt;
74184610Salfred	uint16_t niface;
75184610Salfred	uint16_t nendpoint;
76234491Shselasky	uint16_t iface_no;
77184610Salfred
78184610Salfred	ptr = config_desc;
79184610Salfred	if (ptr[1] != LIBUSB20_DT_CONFIG) {
80184610Salfred		return (NULL);		/* not config descriptor */
81184610Salfred	}
82184610Salfred	/*
83184610Salfred	 * The first "bInterfaceNumber" should never have the value 0xff.
84184610Salfred	 * Then it is corrupt.
85184610Salfred	 */
86184610Salfred	niface_no_alt = 0;
87184610Salfred	nendpoint = 0;
88184610Salfred	niface = 0;
89234491Shselasky	iface_no = 0xFFFF;
90184610Salfred	ptr = NULL;
91184610Salfred
92184610Salfred	/* get "wTotalLength" and setup "pcdesc" */
93184610Salfred	pcdesc.ptr = LIBUSB20_ADD_BYTES(config_desc, 0);
94184610Salfred	pcdesc.len =
95185087Salfred	    ((const uint8_t *)config_desc)[2] |
96185087Salfred	    (((const uint8_t *)config_desc)[3] << 8);
97184610Salfred	pcdesc.type = LIBUSB20_ME_IS_RAW;
98184610Salfred
99184610Salfred	/* descriptor pre-scan */
100184610Salfred	while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) {
101184610Salfred		if (ptr[1] == LIBUSB20_DT_ENDPOINT) {
102184610Salfred			nendpoint++;
103184610Salfred		} else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) {
104184610Salfred			niface++;
105184610Salfred			/* check "bInterfaceNumber" */
106184610Salfred			if (ptr[2] != iface_no) {
107184610Salfred				iface_no = ptr[2];
108184610Salfred				niface_no_alt++;
109184610Salfred			}
110184610Salfred		}
111184610Salfred	}
112184610Salfred
113184610Salfred	/* sanity checking */
114184610Salfred	if (niface >= 256) {
115184610Salfred		return (NULL);		/* corrupt */
116184610Salfred	}
117184610Salfred	if (nendpoint >= 256) {
118184610Salfred		return (NULL);		/* corrupt */
119184610Salfred	}
120184610Salfred	size = sizeof(*lub_config) +
121184610Salfred	    (niface * sizeof(*lub_interface)) +
122184610Salfred	    (nendpoint * sizeof(*lub_endpoint)) +
123184610Salfred	    pcdesc.len;
124184610Salfred
125184610Salfred	lub_config = malloc(size);
126184610Salfred	if (lub_config == NULL) {
127184610Salfred		return (NULL);		/* out of memory */
128184610Salfred	}
129199055Sthompsa	/* make sure memory is initialised */
130199055Sthompsa	memset(lub_config, 0, size);
131199055Sthompsa
132184610Salfred	lub_interface = (void *)(lub_config + 1);
133184610Salfred	lub_alt_interface = (void *)(lub_interface + niface_no_alt);
134184610Salfred	lub_endpoint = (void *)(lub_interface + niface);
135184610Salfred
136184610Salfred	/*
137184610Salfred	 * Make a copy of the config descriptor, so that the caller can free
138184610Salfred	 * the inital config descriptor pointer!
139184610Salfred	 */
140184610Salfred	ptr = (void *)(lub_endpoint + nendpoint);
141184610Salfred	memcpy(LIBUSB20_ADD_BYTES(ptr, 0), config_desc, pcdesc.len);
142184610Salfred	pcdesc.ptr = LIBUSB20_ADD_BYTES(ptr, 0);
143184610Salfred	config_desc = LIBUSB20_ADD_BYTES(ptr, 0);
144184610Salfred
145184610Salfred	/* init config structure */
146184610Salfred
147184610Salfred	ptr = config_desc;
148184610Salfred
149184610Salfred	LIBUSB20_INIT(LIBUSB20_CONFIG_DESC, &lub_config->desc);
150184610Salfred
151184610Salfred	if (libusb20_me_decode(ptr, ptr[0], &lub_config->desc)) {
152184610Salfred		/* ignore */
153184610Salfred	}
154184610Salfred	lub_config->num_interface = 0;
155184610Salfred	lub_config->interface = lub_interface;
156184610Salfred	lub_config->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
157184610Salfred	lub_config->extra.len = -ptr[0];
158184610Salfred	lub_config->extra.type = LIBUSB20_ME_IS_RAW;
159184610Salfred
160184610Salfred	/* reset states */
161184610Salfred	niface = 0;
162234491Shselasky	iface_no = 0xFFFF;
163184610Salfred	ptr = NULL;
164184610Salfred	lub_interface--;
165184610Salfred	lub_endpoint--;
166184610Salfred	last_if = NULL;
167184610Salfred	last_ep = NULL;
168184610Salfred
169184610Salfred	/* descriptor pre-scan */
170184610Salfred	while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) {
171184610Salfred		if (ptr[1] == LIBUSB20_DT_ENDPOINT) {
172184610Salfred			if (last_if) {
173184610Salfred				lub_endpoint++;
174184610Salfred				last_ep = lub_endpoint;
175184610Salfred				last_if->num_endpoints++;
176184610Salfred
177184610Salfred				LIBUSB20_INIT(LIBUSB20_ENDPOINT_DESC, &last_ep->desc);
178184610Salfred
179184610Salfred				if (libusb20_me_decode(ptr, ptr[0], &last_ep->desc)) {
180184610Salfred					/* ignore */
181184610Salfred				}
182184610Salfred				last_ep->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
183184610Salfred				last_ep->extra.len = 0;
184184610Salfred				last_ep->extra.type = LIBUSB20_ME_IS_RAW;
185184610Salfred			} else {
186184610Salfred				lub_config->extra.len += ptr[0];
187184610Salfred			}
188184610Salfred
189184610Salfred		} else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) {
190184610Salfred			if (ptr[2] != iface_no) {
191184610Salfred				/* new interface */
192184610Salfred				iface_no = ptr[2];
193184610Salfred				lub_interface++;
194184610Salfred				lub_config->num_interface++;
195184610Salfred				last_if = lub_interface;
196184610Salfred				niface++;
197184610Salfred			} else {
198184610Salfred				/* one more alternate setting */
199184610Salfred				lub_interface->num_altsetting++;
200184610Salfred				last_if = lub_alt_interface;
201184610Salfred				lub_alt_interface++;
202184610Salfred			}
203184610Salfred
204184610Salfred			LIBUSB20_INIT(LIBUSB20_INTERFACE_DESC, &last_if->desc);
205184610Salfred
206184610Salfred			if (libusb20_me_decode(ptr, ptr[0], &last_if->desc)) {
207184610Salfred				/* ignore */
208184610Salfred			}
209184610Salfred			/*
210184610Salfred			 * Sometimes USB devices have corrupt interface
211184610Salfred			 * descriptors and we need to overwrite the provided
212184610Salfred			 * interface number!
213184610Salfred			 */
214184610Salfred			last_if->desc.bInterfaceNumber = niface - 1;
215184610Salfred			last_if->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
216184610Salfred			last_if->extra.len = 0;
217184610Salfred			last_if->extra.type = LIBUSB20_ME_IS_RAW;
218184610Salfred			last_if->endpoints = lub_endpoint + 1;
219184610Salfred			last_if->altsetting = lub_alt_interface;
220184610Salfred			last_if->num_altsetting = 0;
221184610Salfred			last_if->num_endpoints = 0;
222184610Salfred			last_ep = NULL;
223184610Salfred		} else {
224184610Salfred			/* unknown descriptor */
225184610Salfred			if (last_if) {
226184610Salfred				if (last_ep) {
227184610Salfred					last_ep->extra.len += ptr[0];
228184610Salfred				} else {
229184610Salfred					last_if->extra.len += ptr[0];
230184610Salfred				}
231184610Salfred			} else {
232184610Salfred				lub_config->extra.len += ptr[0];
233184610Salfred			}
234184610Salfred		}
235184610Salfred	}
236184610Salfred	return (lub_config);
237184610Salfred}
238184610Salfred
239184610Salfred/*------------------------------------------------------------------------*
240184610Salfred *	libusb20_desc_foreach
241184610Salfred *
242184610Salfred * Safe traversal of USB descriptors.
243184610Salfred *
244184610Salfred * Return values:
245184610Salfred * NULL: End of descriptors
246184610Salfred * Else: Pointer to next descriptor
247184610Salfred *------------------------------------------------------------------------*/
248184610Salfredconst uint8_t *
249184610Salfredlibusb20_desc_foreach(const struct libusb20_me_struct *pdesc,
250184610Salfred    const uint8_t *psubdesc)
251184610Salfred{
252186730Salfred	const uint8_t *start;
253186730Salfred	const uint8_t *end;
254186730Salfred	const uint8_t *desc_next;
255184610Salfred
256186730Salfred	/* be NULL safe */
257186730Salfred	if (pdesc == NULL)
258184610Salfred		return (NULL);
259184610Salfred
260186730Salfred	start = (const uint8_t *)pdesc->ptr;
261186730Salfred	end = LIBUSB20_ADD_BYTES(start, pdesc->len);
262186730Salfred
263186730Salfred	/* get start of next descriptor */
264186730Salfred	if (psubdesc == NULL)
265186730Salfred		psubdesc = start;
266186730Salfred	else
267186730Salfred		psubdesc = psubdesc + psubdesc[0];
268186730Salfred
269186730Salfred	/* check that the next USB descriptor is within the range */
270186730Salfred	if ((psubdesc < start) || (psubdesc >= end))
271186730Salfred		return (NULL);		/* out of range, or EOD */
272186730Salfred
273186730Salfred	/* check start of the second next USB descriptor, if any */
274186730Salfred	desc_next = psubdesc + psubdesc[0];
275186730Salfred	if ((desc_next < start) || (desc_next > end))
276186730Salfred		return (NULL);		/* out of range */
277186730Salfred
278186730Salfred	/* check minimum descriptor length */
279186730Salfred	if (psubdesc[0] < 3)
280186730Salfred		return (NULL);		/* too short descriptor */
281186730Salfred
282186730Salfred	return (psubdesc);		/* return start of next descriptor */
283184610Salfred}
284184610Salfred
285184610Salfred/*------------------------------------------------------------------------*
286184610Salfred *	libusb20_me_get_1 - safety wrapper to read out one byte
287184610Salfred *------------------------------------------------------------------------*/
288184610Salfreduint8_t
289184610Salfredlibusb20_me_get_1(const struct libusb20_me_struct *ie, uint16_t offset)
290184610Salfred{
291184610Salfred	if (offset < ie->len) {
292184610Salfred		return (*((uint8_t *)LIBUSB20_ADD_BYTES(ie->ptr, offset)));
293184610Salfred	}
294184610Salfred	return (0);
295184610Salfred}
296184610Salfred
297184610Salfred/*------------------------------------------------------------------------*
298184610Salfred *	libusb20_me_get_2 - safety wrapper to read out one word
299184610Salfred *------------------------------------------------------------------------*/
300184610Salfreduint16_t
301184610Salfredlibusb20_me_get_2(const struct libusb20_me_struct *ie, uint16_t offset)
302184610Salfred{
303184610Salfred	return (libusb20_me_get_1(ie, offset) |
304184610Salfred	    (libusb20_me_get_1(ie, offset + 1) << 8));
305184610Salfred}
306184610Salfred
307184610Salfred/*------------------------------------------------------------------------*
308184610Salfred *	libusb20_me_encode - encode a message structure
309184610Salfred *
310184610Salfred * Description of parameters:
311184610Salfred * "len" - maximum length of output buffer
312184610Salfred * "ptr" - pointer to output buffer. If NULL, no data will be written
313184610Salfred * "pd" - source structure
314184610Salfred *
315184610Salfred * Return values:
316184610Salfred * 0..65535 - Number of bytes used, limited by the "len" input parameter.
317184610Salfred *------------------------------------------------------------------------*/
318184610Salfreduint16_t
319184610Salfredlibusb20_me_encode(void *ptr, uint16_t len, const void *pd)
320184610Salfred{
321184610Salfred	const uint8_t *pf;		/* pointer to format data */
322184610Salfred	uint8_t *buf;			/* pointer to output buffer */
323184610Salfred
324184610Salfred	uint32_t pd_offset;		/* decoded structure offset */
325184610Salfred	uint16_t len_old;		/* old length */
326184610Salfred	uint16_t pd_count;		/* decoded element count */
327184610Salfred	uint8_t me;			/* message element */
328184610Salfred
329184610Salfred	/* initialise */
330184610Salfred
331184610Salfred	len_old = len;
332184610Salfred	buf = ptr;
333184610Salfred	pd_offset = sizeof(void *);
334185087Salfred	pf = (*((struct libusb20_me_format *const *)pd))->format;
335184610Salfred
336184610Salfred	/* scan */
337184610Salfred
338184610Salfred	while (1) {
339184610Salfred
340184610Salfred		/* get information element */
341184610Salfred
342184610Salfred		me = (pf[0]) & LIBUSB20_ME_MASK;
343184610Salfred		pd_count = pf[1] | (pf[2] << 8);
344184610Salfred		pf += 3;
345184610Salfred
346184610Salfred		/* encode the message element */
347184610Salfred
348184610Salfred		switch (me) {
349184610Salfred		case LIBUSB20_ME_INT8:
350184610Salfred			while (pd_count--) {
351184610Salfred				uint8_t temp;
352184610Salfred
353184610Salfred				if (len < 1)	/* overflow */
354184610Salfred					goto done;
355184610Salfred				if (buf) {
356184610Salfred					temp = *((const uint8_t *)
357184610Salfred					    LIBUSB20_ADD_BYTES(pd, pd_offset));
358184610Salfred					buf[0] = temp;
359184610Salfred					buf += 1;
360184610Salfred				}
361184610Salfred				pd_offset += 1;
362184610Salfred				len -= 1;
363184610Salfred			}
364184610Salfred			break;
365184610Salfred
366184610Salfred		case LIBUSB20_ME_INT16:
367184610Salfred			pd_offset = -((-pd_offset) & ~1);	/* align */
368184610Salfred			while (pd_count--) {
369184610Salfred				uint16_t temp;
370184610Salfred
371184610Salfred				if (len < 2)	/* overflow */
372184610Salfred					goto done;
373184610Salfred
374184610Salfred				if (buf) {
375184610Salfred					temp = *((const uint16_t *)
376184610Salfred					    LIBUSB20_ADD_BYTES(pd, pd_offset));
377184610Salfred					buf[1] = (temp >> 8) & 0xFF;
378184610Salfred					buf[0] = temp & 0xFF;
379184610Salfred					buf += 2;
380184610Salfred				}
381184610Salfred				pd_offset += 2;
382184610Salfred				len -= 2;
383184610Salfred			}
384184610Salfred			break;
385184610Salfred
386184610Salfred		case LIBUSB20_ME_INT32:
387184610Salfred			pd_offset = -((-pd_offset) & ~3);	/* align */
388184610Salfred			while (pd_count--) {
389184610Salfred				uint32_t temp;
390184610Salfred
391184610Salfred				if (len < 4)	/* overflow */
392184610Salfred					goto done;
393184610Salfred				if (buf) {
394184610Salfred					temp = *((const uint32_t *)
395184610Salfred					    LIBUSB20_ADD_BYTES(pd, pd_offset));
396184610Salfred					buf[3] = (temp >> 24) & 0xFF;
397184610Salfred					buf[2] = (temp >> 16) & 0xFF;
398184610Salfred					buf[1] = (temp >> 8) & 0xFF;
399184610Salfred					buf[0] = temp & 0xFF;
400184610Salfred					buf += 4;
401184610Salfred				}
402184610Salfred				pd_offset += 4;
403184610Salfred				len -= 4;
404184610Salfred			}
405184610Salfred			break;
406184610Salfred
407184610Salfred		case LIBUSB20_ME_INT64:
408184610Salfred			pd_offset = -((-pd_offset) & ~7);	/* align */
409184610Salfred			while (pd_count--) {
410184610Salfred				uint64_t temp;
411184610Salfred
412184610Salfred				if (len < 8)	/* overflow */
413184610Salfred					goto done;
414184610Salfred				if (buf) {
415184610Salfred
416184610Salfred					temp = *((const uint64_t *)
417184610Salfred					    LIBUSB20_ADD_BYTES(pd, pd_offset));
418184610Salfred					buf[7] = (temp >> 56) & 0xFF;
419184610Salfred					buf[6] = (temp >> 48) & 0xFF;
420184610Salfred					buf[5] = (temp >> 40) & 0xFF;
421184610Salfred					buf[4] = (temp >> 32) & 0xFF;
422184610Salfred					buf[3] = (temp >> 24) & 0xFF;
423184610Salfred					buf[2] = (temp >> 16) & 0xFF;
424184610Salfred					buf[1] = (temp >> 8) & 0xFF;
425184610Salfred					buf[0] = temp & 0xFF;
426184610Salfred					buf += 8;
427184610Salfred				}
428184610Salfred				pd_offset += 8;
429184610Salfred				len -= 8;
430184610Salfred			}
431184610Salfred			break;
432184610Salfred
433184610Salfred		case LIBUSB20_ME_STRUCT:
434184610Salfred			pd_offset = -((-pd_offset) &
435184610Salfred			    ~(LIBUSB20_ME_STRUCT_ALIGN - 1));	/* align */
436184610Salfred			while (pd_count--) {
437184610Salfred				void *src_ptr;
438184610Salfred				uint16_t src_len;
439184610Salfred				struct libusb20_me_struct *ps;
440184610Salfred
441184610Salfred				ps = LIBUSB20_ADD_BYTES(pd, pd_offset);
442184610Salfred
443184610Salfred				switch (ps->type) {
444184610Salfred				case LIBUSB20_ME_IS_RAW:
445184610Salfred					src_len = ps->len;
446184610Salfred					src_ptr = ps->ptr;
447184610Salfred					break;
448184610Salfred
449184610Salfred				case LIBUSB20_ME_IS_ENCODED:
450184610Salfred					if (ps->len == 0) {
451184610Salfred						/*
452184610Salfred						 * Length is encoded
453184610Salfred						 * in the data itself
454184610Salfred						 * and should be
455184610Salfred						 * correct:
456184610Salfred						 */
457234491Shselasky						ps->len = 0xFFFF;
458184610Salfred					}
459184610Salfred					src_len = libusb20_me_get_1(pd, 0);
460184610Salfred					src_ptr = LIBUSB20_ADD_BYTES(ps->ptr, 1);
461184610Salfred					if (src_len == 0xFF) {
462184610Salfred						/* length is escaped */
463184610Salfred						src_len = libusb20_me_get_2(pd, 1);
464184610Salfred						src_ptr =
465184610Salfred						    LIBUSB20_ADD_BYTES(ps->ptr, 3);
466184610Salfred					}
467184610Salfred					break;
468184610Salfred
469184610Salfred				case LIBUSB20_ME_IS_DECODED:
470184610Salfred					/* reserve 3 length bytes */
471184610Salfred					src_len = libusb20_me_encode(NULL,
472234491Shselasky					    0xFFFF - 3, ps->ptr);
473184610Salfred					src_ptr = NULL;
474184610Salfred					break;
475184610Salfred
476184610Salfred				default:	/* empty structure */
477184610Salfred					src_len = 0;
478184610Salfred					src_ptr = NULL;
479184610Salfred					break;
480184610Salfred				}
481184610Salfred
482184610Salfred				if (src_len > 0xFE) {
483234491Shselasky					if (src_len > (0xFFFF - 3))
484184610Salfred						/* overflow */
485184610Salfred						goto done;
486184610Salfred
487184610Salfred					if (len < (src_len + 3))
488184610Salfred						/* overflow */
489184610Salfred						goto done;
490184610Salfred
491184610Salfred					if (buf) {
492184610Salfred						buf[0] = 0xFF;
493184610Salfred						buf[1] = (src_len & 0xFF);
494184610Salfred						buf[2] = (src_len >> 8) & 0xFF;
495184610Salfred						buf += 3;
496184610Salfred					}
497184610Salfred					len -= (src_len + 3);
498184610Salfred				} else {
499184610Salfred					if (len < (src_len + 1))
500184610Salfred						/* overflow */
501184610Salfred						goto done;
502184610Salfred
503184610Salfred					if (buf) {
504184610Salfred						buf[0] = (src_len & 0xFF);
505184610Salfred						buf += 1;
506184610Salfred					}
507184610Salfred					len -= (src_len + 1);
508184610Salfred				}
509184610Salfred
510184610Salfred				/* check for buffer and non-zero length */
511184610Salfred
512184610Salfred				if (buf && src_len) {
513184610Salfred					if (ps->type == LIBUSB20_ME_IS_DECODED) {
514184610Salfred						/*
515184610Salfred						 * Repeat encode
516184610Salfred						 * procedure - we have
517184610Salfred						 * room for the
518184610Salfred						 * complete structure:
519184610Salfred						 */
520184610Salfred						uint16_t dummy;
521184610Salfred
522184610Salfred						dummy = libusb20_me_encode(buf,
523234491Shselasky						    0xFFFF - 3, ps->ptr);
524184610Salfred					} else {
525184610Salfred						bcopy(src_ptr, buf, src_len);
526184610Salfred					}
527184610Salfred					buf += src_len;
528184610Salfred				}
529184610Salfred				pd_offset += sizeof(struct libusb20_me_struct);
530184610Salfred			}
531184610Salfred			break;
532184610Salfred
533184610Salfred		default:
534184610Salfred			goto done;
535184610Salfred		}
536184610Salfred	}
537184610Salfreddone:
538184610Salfred	return (len_old - len);
539184610Salfred}
540184610Salfred
541184610Salfred/*------------------------------------------------------------------------*
542184610Salfred *	libusb20_me_decode - decode a message into a decoded structure
543184610Salfred *
544184610Salfred * Description of parameters:
545184610Salfred * "ptr" - message pointer
546184610Salfred * "len" - message length
547184610Salfred * "pd" - pointer to decoded structure
548184610Salfred *
549184610Salfred * Returns:
550184610Salfred * "0..65535" - number of bytes decoded, limited by "len"
551184610Salfred *------------------------------------------------------------------------*/
552184610Salfreduint16_t
553184610Salfredlibusb20_me_decode(const void *ptr, uint16_t len, void *pd)
554184610Salfred{
555184610Salfred	const uint8_t *pf;		/* pointer to format data */
556184610Salfred	const uint8_t *buf;		/* pointer to input buffer */
557184610Salfred
558184610Salfred	uint32_t pd_offset;		/* decoded structure offset */
559184610Salfred	uint16_t len_old;		/* old length */
560184610Salfred	uint16_t pd_count;		/* decoded element count */
561184610Salfred	uint8_t me;			/* message element */
562184610Salfred
563184610Salfred	/* initialise */
564184610Salfred
565184610Salfred	len_old = len;
566184610Salfred	buf = ptr;
567184610Salfred	pd_offset = sizeof(void *);
568184610Salfred	pf = (*((struct libusb20_me_format **)pd))->format;
569184610Salfred
570184610Salfred	/* scan */
571184610Salfred
572184610Salfred	while (1) {
573184610Salfred
574184610Salfred		/* get information element */
575184610Salfred
576184610Salfred		me = (pf[0]) & LIBUSB20_ME_MASK;
577184610Salfred		pd_count = pf[1] | (pf[2] << 8);
578184610Salfred		pf += 3;
579184610Salfred
580184610Salfred		/* decode the message element by type */
581184610Salfred
582184610Salfred		switch (me) {
583184610Salfred		case LIBUSB20_ME_INT8:
584184610Salfred			while (pd_count--) {
585184610Salfred				uint8_t temp;
586184610Salfred
587184610Salfred				if (len < 1) {
588184610Salfred					len = 0;
589184610Salfred					temp = 0;
590184610Salfred				} else {
591184610Salfred					len -= 1;
592184610Salfred					temp = buf[0];
593184610Salfred					buf++;
594184610Salfred				}
595184610Salfred				*((uint8_t *)LIBUSB20_ADD_BYTES(pd,
596184610Salfred				    pd_offset)) = temp;
597184610Salfred				pd_offset += 1;
598184610Salfred			}
599184610Salfred			break;
600184610Salfred
601184610Salfred		case LIBUSB20_ME_INT16:
602184610Salfred			pd_offset = -((-pd_offset) & ~1);	/* align */
603184610Salfred			while (pd_count--) {
604184610Salfred				uint16_t temp;
605184610Salfred
606184610Salfred				if (len < 2) {
607184610Salfred					len = 0;
608184610Salfred					temp = 0;
609184610Salfred				} else {
610184610Salfred					len -= 2;
611184610Salfred					temp = buf[1] << 8;
612184610Salfred					temp |= buf[0];
613184610Salfred					buf += 2;
614184610Salfred				}
615184610Salfred				*((uint16_t *)LIBUSB20_ADD_BYTES(pd,
616184610Salfred				    pd_offset)) = temp;
617184610Salfred				pd_offset += 2;
618184610Salfred			}
619184610Salfred			break;
620184610Salfred
621184610Salfred		case LIBUSB20_ME_INT32:
622184610Salfred			pd_offset = -((-pd_offset) & ~3);	/* align */
623184610Salfred			while (pd_count--) {
624184610Salfred				uint32_t temp;
625184610Salfred
626184610Salfred				if (len < 4) {
627184610Salfred					len = 0;
628184610Salfred					temp = 0;
629184610Salfred				} else {
630184610Salfred					len -= 4;
631184610Salfred					temp = buf[3] << 24;
632184610Salfred					temp |= buf[2] << 16;
633184610Salfred					temp |= buf[1] << 8;
634184610Salfred					temp |= buf[0];
635184610Salfred					buf += 4;
636184610Salfred				}
637184610Salfred
638184610Salfred				*((uint32_t *)LIBUSB20_ADD_BYTES(pd,
639184610Salfred				    pd_offset)) = temp;
640184610Salfred				pd_offset += 4;
641184610Salfred			}
642184610Salfred			break;
643184610Salfred
644184610Salfred		case LIBUSB20_ME_INT64:
645184610Salfred			pd_offset = -((-pd_offset) & ~7);	/* align */
646184610Salfred			while (pd_count--) {
647184610Salfred				uint64_t temp;
648184610Salfred
649184610Salfred				if (len < 8) {
650184610Salfred					len = 0;
651184610Salfred					temp = 0;
652184610Salfred				} else {
653184610Salfred					len -= 8;
654184610Salfred					temp = ((uint64_t)buf[7]) << 56;
655184610Salfred					temp |= ((uint64_t)buf[6]) << 48;
656184610Salfred					temp |= ((uint64_t)buf[5]) << 40;
657184610Salfred					temp |= ((uint64_t)buf[4]) << 32;
658184610Salfred					temp |= buf[3] << 24;
659184610Salfred					temp |= buf[2] << 16;
660184610Salfred					temp |= buf[1] << 8;
661184610Salfred					temp |= buf[0];
662184610Salfred					buf += 8;
663184610Salfred				}
664184610Salfred
665184610Salfred				*((uint64_t *)LIBUSB20_ADD_BYTES(pd,
666184610Salfred				    pd_offset)) = temp;
667184610Salfred				pd_offset += 8;
668184610Salfred			}
669184610Salfred			break;
670184610Salfred
671184610Salfred		case LIBUSB20_ME_STRUCT:
672184610Salfred			pd_offset = -((-pd_offset) &
673184610Salfred			    ~(LIBUSB20_ME_STRUCT_ALIGN - 1));	/* align */
674184610Salfred			while (pd_count--) {
675184610Salfred				uint16_t temp;
676184610Salfred				uint16_t dummy;
677184610Salfred				struct libusb20_me_struct *ps;
678184610Salfred
679184610Salfred				ps = LIBUSB20_ADD_BYTES(pd, pd_offset);
680184610Salfred
681184610Salfred				if (ps->type == LIBUSB20_ME_IS_ENCODED) {
682184610Salfred					/*
683184610Salfred					 * Pre-store a de-constified
684184610Salfred					 * pointer to the raw
685184610Salfred					 * structure:
686184610Salfred					 */
687184610Salfred					ps->ptr = LIBUSB20_ADD_BYTES(buf, 0);
688184610Salfred
689184610Salfred					/*
690184610Salfred					 * Get the correct number of
691184610Salfred					 * length bytes:
692184610Salfred					 */
693184610Salfred					if (len != 0) {
694184610Salfred						if (buf[0] == 0xFF) {
695184610Salfred							ps->len = 3;
696184610Salfred						} else {
697184610Salfred							ps->len = 1;
698184610Salfred						}
699184610Salfred					} else {
700184610Salfred						ps->len = 0;
701184610Salfred					}
702184610Salfred				}
703184610Salfred				/* get the structure length */
704184610Salfred
705184610Salfred				if (len != 0) {
706184610Salfred					if (buf[0] == 0xFF) {
707184610Salfred						if (len < 3) {
708184610Salfred							len = 0;
709184610Salfred							temp = 0;
710184610Salfred						} else {
711184610Salfred							len -= 3;
712184610Salfred							temp = buf[1] |
713184610Salfred							    (buf[2] << 8);
714184610Salfred							buf += 3;
715184610Salfred						}
716184610Salfred					} else {
717184610Salfred						len -= 1;
718184610Salfred						temp = buf[0];
719184610Salfred						buf += 1;
720184610Salfred					}
721184610Salfred				} else {
722184610Salfred					len = 0;
723184610Salfred					temp = 0;
724184610Salfred				}
725184610Salfred				/* check for invalid length */
726184610Salfred
727184610Salfred				if (temp > len) {
728184610Salfred					len = 0;
729184610Salfred					temp = 0;
730184610Salfred				}
731184610Salfred				/* check wanted structure type */
732184610Salfred
733184610Salfred				switch (ps->type) {
734184610Salfred				case LIBUSB20_ME_IS_ENCODED:
735184610Salfred					/* check for zero length */
736184610Salfred					if (temp == 0) {
737184610Salfred						/*
738184610Salfred						 * The pointer must
739184610Salfred						 * be valid:
740184610Salfred						 */
741184610Salfred						ps->ptr = LIBUSB20_ADD_BYTES(
742184610Salfred						    libusb20_me_encode_empty, 0);
743184610Salfred						ps->len = 1;
744184610Salfred					} else {
745184610Salfred						ps->len += temp;
746184610Salfred					}
747184610Salfred					break;
748184610Salfred
749184610Salfred				case LIBUSB20_ME_IS_RAW:
750184610Salfred					/* update length and pointer */
751184610Salfred					ps->len = temp;
752184610Salfred					ps->ptr = LIBUSB20_ADD_BYTES(buf, 0);
753184610Salfred					break;
754184610Salfred
755184610Salfred				case LIBUSB20_ME_IS_EMPTY:
756184610Salfred				case LIBUSB20_ME_IS_DECODED:
757184610Salfred					/* check for non-zero length */
758184610Salfred					if (temp != 0) {
759184610Salfred						/* update type */
760184610Salfred						ps->type = LIBUSB20_ME_IS_DECODED;
761184610Salfred						ps->len = 0;
762184610Salfred						/*
763184610Salfred						 * Recursivly decode
764184610Salfred						 * the next structure
765184610Salfred						 */
766184610Salfred						dummy = libusb20_me_decode(buf,
767184610Salfred						    temp, ps->ptr);
768184610Salfred					} else {
769184610Salfred						/* update type */
770184610Salfred						ps->type = LIBUSB20_ME_IS_EMPTY;
771184610Salfred						ps->len = 0;
772184610Salfred					}
773184610Salfred					break;
774184610Salfred
775184610Salfred				default:
776184610Salfred					/*
777184610Salfred					 * nothing to do - should
778184610Salfred					 * not happen
779184610Salfred					 */
780184610Salfred					ps->ptr = NULL;
781184610Salfred					ps->len = 0;
782184610Salfred					break;
783184610Salfred				}
784184610Salfred				buf += temp;
785184610Salfred				len -= temp;
786184610Salfred				pd_offset += sizeof(struct libusb20_me_struct);
787184610Salfred			}
788184610Salfred			break;
789184610Salfred
790184610Salfred		default:
791184610Salfred			goto done;
792184610Salfred		}
793184610Salfred	}
794184610Salfreddone:
795184610Salfred	return (len_old - len);
796184610Salfred}
797