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	 */
140286333Spfg	memcpy((void *)(lub_endpoint + nendpoint), config_desc, pcdesc.len);
141286333Spfg
142286333Spfg	ptr = (const void *)(lub_endpoint + nendpoint);
143184610Salfred	pcdesc.ptr = LIBUSB20_ADD_BYTES(ptr, 0);
144184610Salfred
145184610Salfred	/* init config structure */
146184610Salfred
147184610Salfred	LIBUSB20_INIT(LIBUSB20_CONFIG_DESC, &lub_config->desc);
148184610Salfred
149184610Salfred	if (libusb20_me_decode(ptr, ptr[0], &lub_config->desc)) {
150184610Salfred		/* ignore */
151184610Salfred	}
152184610Salfred	lub_config->num_interface = 0;
153184610Salfred	lub_config->interface = lub_interface;
154184610Salfred	lub_config->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
155184610Salfred	lub_config->extra.len = -ptr[0];
156184610Salfred	lub_config->extra.type = LIBUSB20_ME_IS_RAW;
157184610Salfred
158184610Salfred	/* reset states */
159184610Salfred	niface = 0;
160234491Shselasky	iface_no = 0xFFFF;
161184610Salfred	ptr = NULL;
162184610Salfred	lub_interface--;
163184610Salfred	lub_endpoint--;
164184610Salfred	last_if = NULL;
165184610Salfred	last_ep = NULL;
166184610Salfred
167184610Salfred	/* descriptor pre-scan */
168184610Salfred	while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) {
169184610Salfred		if (ptr[1] == LIBUSB20_DT_ENDPOINT) {
170184610Salfred			if (last_if) {
171184610Salfred				lub_endpoint++;
172184610Salfred				last_ep = lub_endpoint;
173184610Salfred				last_if->num_endpoints++;
174184610Salfred
175184610Salfred				LIBUSB20_INIT(LIBUSB20_ENDPOINT_DESC, &last_ep->desc);
176184610Salfred
177184610Salfred				if (libusb20_me_decode(ptr, ptr[0], &last_ep->desc)) {
178184610Salfred					/* ignore */
179184610Salfred				}
180184610Salfred				last_ep->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
181184610Salfred				last_ep->extra.len = 0;
182184610Salfred				last_ep->extra.type = LIBUSB20_ME_IS_RAW;
183184610Salfred			} else {
184184610Salfred				lub_config->extra.len += ptr[0];
185184610Salfred			}
186184610Salfred
187184610Salfred		} else if ((ptr[1] == LIBUSB20_DT_INTERFACE) && (ptr[0] >= 4)) {
188184610Salfred			if (ptr[2] != iface_no) {
189184610Salfred				/* new interface */
190184610Salfred				iface_no = ptr[2];
191184610Salfred				lub_interface++;
192184610Salfred				lub_config->num_interface++;
193184610Salfred				last_if = lub_interface;
194184610Salfred				niface++;
195184610Salfred			} else {
196184610Salfred				/* one more alternate setting */
197184610Salfred				lub_interface->num_altsetting++;
198184610Salfred				last_if = lub_alt_interface;
199184610Salfred				lub_alt_interface++;
200184610Salfred			}
201184610Salfred
202184610Salfred			LIBUSB20_INIT(LIBUSB20_INTERFACE_DESC, &last_if->desc);
203184610Salfred
204184610Salfred			if (libusb20_me_decode(ptr, ptr[0], &last_if->desc)) {
205184610Salfred				/* ignore */
206184610Salfred			}
207184610Salfred			/*
208184610Salfred			 * Sometimes USB devices have corrupt interface
209184610Salfred			 * descriptors and we need to overwrite the provided
210184610Salfred			 * interface number!
211184610Salfred			 */
212184610Salfred			last_if->desc.bInterfaceNumber = niface - 1;
213184610Salfred			last_if->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
214184610Salfred			last_if->extra.len = 0;
215184610Salfred			last_if->extra.type = LIBUSB20_ME_IS_RAW;
216184610Salfred			last_if->endpoints = lub_endpoint + 1;
217184610Salfred			last_if->altsetting = lub_alt_interface;
218184610Salfred			last_if->num_altsetting = 0;
219184610Salfred			last_if->num_endpoints = 0;
220184610Salfred			last_ep = NULL;
221184610Salfred		} else {
222184610Salfred			/* unknown descriptor */
223184610Salfred			if (last_if) {
224184610Salfred				if (last_ep) {
225184610Salfred					last_ep->extra.len += ptr[0];
226184610Salfred				} else {
227184610Salfred					last_if->extra.len += ptr[0];
228184610Salfred				}
229184610Salfred			} else {
230184610Salfred				lub_config->extra.len += ptr[0];
231184610Salfred			}
232184610Salfred		}
233184610Salfred	}
234184610Salfred	return (lub_config);
235184610Salfred}
236184610Salfred
237184610Salfred/*------------------------------------------------------------------------*
238184610Salfred *	libusb20_desc_foreach
239184610Salfred *
240184610Salfred * Safe traversal of USB descriptors.
241184610Salfred *
242184610Salfred * Return values:
243184610Salfred * NULL: End of descriptors
244184610Salfred * Else: Pointer to next descriptor
245184610Salfred *------------------------------------------------------------------------*/
246184610Salfredconst uint8_t *
247184610Salfredlibusb20_desc_foreach(const struct libusb20_me_struct *pdesc,
248184610Salfred    const uint8_t *psubdesc)
249184610Salfred{
250186730Salfred	const uint8_t *start;
251186730Salfred	const uint8_t *end;
252186730Salfred	const uint8_t *desc_next;
253184610Salfred
254186730Salfred	/* be NULL safe */
255186730Salfred	if (pdesc == NULL)
256184610Salfred		return (NULL);
257184610Salfred
258186730Salfred	start = (const uint8_t *)pdesc->ptr;
259186730Salfred	end = LIBUSB20_ADD_BYTES(start, pdesc->len);
260186730Salfred
261186730Salfred	/* get start of next descriptor */
262186730Salfred	if (psubdesc == NULL)
263186730Salfred		psubdesc = start;
264186730Salfred	else
265186730Salfred		psubdesc = psubdesc + psubdesc[0];
266186730Salfred
267186730Salfred	/* check that the next USB descriptor is within the range */
268186730Salfred	if ((psubdesc < start) || (psubdesc >= end))
269186730Salfred		return (NULL);		/* out of range, or EOD */
270186730Salfred
271186730Salfred	/* check start of the second next USB descriptor, if any */
272186730Salfred	desc_next = psubdesc + psubdesc[0];
273186730Salfred	if ((desc_next < start) || (desc_next > end))
274186730Salfred		return (NULL);		/* out of range */
275186730Salfred
276186730Salfred	/* check minimum descriptor length */
277186730Salfred	if (psubdesc[0] < 3)
278186730Salfred		return (NULL);		/* too short descriptor */
279186730Salfred
280186730Salfred	return (psubdesc);		/* return start of next descriptor */
281184610Salfred}
282184610Salfred
283184610Salfred/*------------------------------------------------------------------------*
284184610Salfred *	libusb20_me_get_1 - safety wrapper to read out one byte
285184610Salfred *------------------------------------------------------------------------*/
286184610Salfreduint8_t
287184610Salfredlibusb20_me_get_1(const struct libusb20_me_struct *ie, uint16_t offset)
288184610Salfred{
289184610Salfred	if (offset < ie->len) {
290184610Salfred		return (*((uint8_t *)LIBUSB20_ADD_BYTES(ie->ptr, offset)));
291184610Salfred	}
292184610Salfred	return (0);
293184610Salfred}
294184610Salfred
295184610Salfred/*------------------------------------------------------------------------*
296184610Salfred *	libusb20_me_get_2 - safety wrapper to read out one word
297184610Salfred *------------------------------------------------------------------------*/
298184610Salfreduint16_t
299184610Salfredlibusb20_me_get_2(const struct libusb20_me_struct *ie, uint16_t offset)
300184610Salfred{
301184610Salfred	return (libusb20_me_get_1(ie, offset) |
302184610Salfred	    (libusb20_me_get_1(ie, offset + 1) << 8));
303184610Salfred}
304184610Salfred
305184610Salfred/*------------------------------------------------------------------------*
306184610Salfred *	libusb20_me_encode - encode a message structure
307184610Salfred *
308184610Salfred * Description of parameters:
309184610Salfred * "len" - maximum length of output buffer
310184610Salfred * "ptr" - pointer to output buffer. If NULL, no data will be written
311184610Salfred * "pd" - source structure
312184610Salfred *
313184610Salfred * Return values:
314184610Salfred * 0..65535 - Number of bytes used, limited by the "len" input parameter.
315184610Salfred *------------------------------------------------------------------------*/
316184610Salfreduint16_t
317184610Salfredlibusb20_me_encode(void *ptr, uint16_t len, const void *pd)
318184610Salfred{
319184610Salfred	const uint8_t *pf;		/* pointer to format data */
320184610Salfred	uint8_t *buf;			/* pointer to output buffer */
321184610Salfred
322184610Salfred	uint32_t pd_offset;		/* decoded structure offset */
323184610Salfred	uint16_t len_old;		/* old length */
324184610Salfred	uint16_t pd_count;		/* decoded element count */
325184610Salfred	uint8_t me;			/* message element */
326184610Salfred
327184610Salfred	/* initialise */
328184610Salfred
329184610Salfred	len_old = len;
330184610Salfred	buf = ptr;
331184610Salfred	pd_offset = sizeof(void *);
332185087Salfred	pf = (*((struct libusb20_me_format *const *)pd))->format;
333184610Salfred
334184610Salfred	/* scan */
335184610Salfred
336184610Salfred	while (1) {
337184610Salfred
338184610Salfred		/* get information element */
339184610Salfred
340184610Salfred		me = (pf[0]) & LIBUSB20_ME_MASK;
341184610Salfred		pd_count = pf[1] | (pf[2] << 8);
342184610Salfred		pf += 3;
343184610Salfred
344184610Salfred		/* encode the message element */
345184610Salfred
346184610Salfred		switch (me) {
347184610Salfred		case LIBUSB20_ME_INT8:
348184610Salfred			while (pd_count--) {
349184610Salfred				uint8_t temp;
350184610Salfred
351184610Salfred				if (len < 1)	/* overflow */
352184610Salfred					goto done;
353184610Salfred				if (buf) {
354184610Salfred					temp = *((const uint8_t *)
355184610Salfred					    LIBUSB20_ADD_BYTES(pd, pd_offset));
356184610Salfred					buf[0] = temp;
357184610Salfred					buf += 1;
358184610Salfred				}
359184610Salfred				pd_offset += 1;
360184610Salfred				len -= 1;
361184610Salfred			}
362184610Salfred			break;
363184610Salfred
364184610Salfred		case LIBUSB20_ME_INT16:
365184610Salfred			pd_offset = -((-pd_offset) & ~1);	/* align */
366184610Salfred			while (pd_count--) {
367184610Salfred				uint16_t temp;
368184610Salfred
369184610Salfred				if (len < 2)	/* overflow */
370184610Salfred					goto done;
371184610Salfred
372184610Salfred				if (buf) {
373184610Salfred					temp = *((const uint16_t *)
374184610Salfred					    LIBUSB20_ADD_BYTES(pd, pd_offset));
375184610Salfred					buf[1] = (temp >> 8) & 0xFF;
376184610Salfred					buf[0] = temp & 0xFF;
377184610Salfred					buf += 2;
378184610Salfred				}
379184610Salfred				pd_offset += 2;
380184610Salfred				len -= 2;
381184610Salfred			}
382184610Salfred			break;
383184610Salfred
384184610Salfred		case LIBUSB20_ME_INT32:
385184610Salfred			pd_offset = -((-pd_offset) & ~3);	/* align */
386184610Salfred			while (pd_count--) {
387184610Salfred				uint32_t temp;
388184610Salfred
389184610Salfred				if (len < 4)	/* overflow */
390184610Salfred					goto done;
391184610Salfred				if (buf) {
392184610Salfred					temp = *((const uint32_t *)
393184610Salfred					    LIBUSB20_ADD_BYTES(pd, pd_offset));
394184610Salfred					buf[3] = (temp >> 24) & 0xFF;
395184610Salfred					buf[2] = (temp >> 16) & 0xFF;
396184610Salfred					buf[1] = (temp >> 8) & 0xFF;
397184610Salfred					buf[0] = temp & 0xFF;
398184610Salfred					buf += 4;
399184610Salfred				}
400184610Salfred				pd_offset += 4;
401184610Salfred				len -= 4;
402184610Salfred			}
403184610Salfred			break;
404184610Salfred
405184610Salfred		case LIBUSB20_ME_INT64:
406184610Salfred			pd_offset = -((-pd_offset) & ~7);	/* align */
407184610Salfred			while (pd_count--) {
408184610Salfred				uint64_t temp;
409184610Salfred
410184610Salfred				if (len < 8)	/* overflow */
411184610Salfred					goto done;
412184610Salfred				if (buf) {
413184610Salfred
414184610Salfred					temp = *((const uint64_t *)
415184610Salfred					    LIBUSB20_ADD_BYTES(pd, pd_offset));
416184610Salfred					buf[7] = (temp >> 56) & 0xFF;
417184610Salfred					buf[6] = (temp >> 48) & 0xFF;
418184610Salfred					buf[5] = (temp >> 40) & 0xFF;
419184610Salfred					buf[4] = (temp >> 32) & 0xFF;
420184610Salfred					buf[3] = (temp >> 24) & 0xFF;
421184610Salfred					buf[2] = (temp >> 16) & 0xFF;
422184610Salfred					buf[1] = (temp >> 8) & 0xFF;
423184610Salfred					buf[0] = temp & 0xFF;
424184610Salfred					buf += 8;
425184610Salfred				}
426184610Salfred				pd_offset += 8;
427184610Salfred				len -= 8;
428184610Salfred			}
429184610Salfred			break;
430184610Salfred
431184610Salfred		case LIBUSB20_ME_STRUCT:
432184610Salfred			pd_offset = -((-pd_offset) &
433184610Salfred			    ~(LIBUSB20_ME_STRUCT_ALIGN - 1));	/* align */
434184610Salfred			while (pd_count--) {
435184610Salfred				void *src_ptr;
436184610Salfred				uint16_t src_len;
437184610Salfred				struct libusb20_me_struct *ps;
438184610Salfred
439184610Salfred				ps = LIBUSB20_ADD_BYTES(pd, pd_offset);
440184610Salfred
441184610Salfred				switch (ps->type) {
442184610Salfred				case LIBUSB20_ME_IS_RAW:
443184610Salfred					src_len = ps->len;
444184610Salfred					src_ptr = ps->ptr;
445184610Salfred					break;
446184610Salfred
447184610Salfred				case LIBUSB20_ME_IS_ENCODED:
448184610Salfred					if (ps->len == 0) {
449184610Salfred						/*
450184610Salfred						 * Length is encoded
451184610Salfred						 * in the data itself
452184610Salfred						 * and should be
453184610Salfred						 * correct:
454184610Salfred						 */
455234491Shselasky						ps->len = 0xFFFF;
456184610Salfred					}
457184610Salfred					src_len = libusb20_me_get_1(pd, 0);
458184610Salfred					src_ptr = LIBUSB20_ADD_BYTES(ps->ptr, 1);
459184610Salfred					if (src_len == 0xFF) {
460184610Salfred						/* length is escaped */
461184610Salfred						src_len = libusb20_me_get_2(pd, 1);
462184610Salfred						src_ptr =
463184610Salfred						    LIBUSB20_ADD_BYTES(ps->ptr, 3);
464184610Salfred					}
465184610Salfred					break;
466184610Salfred
467184610Salfred				case LIBUSB20_ME_IS_DECODED:
468184610Salfred					/* reserve 3 length bytes */
469184610Salfred					src_len = libusb20_me_encode(NULL,
470234491Shselasky					    0xFFFF - 3, ps->ptr);
471184610Salfred					src_ptr = NULL;
472184610Salfred					break;
473184610Salfred
474184610Salfred				default:	/* empty structure */
475184610Salfred					src_len = 0;
476184610Salfred					src_ptr = NULL;
477184610Salfred					break;
478184610Salfred				}
479184610Salfred
480184610Salfred				if (src_len > 0xFE) {
481234491Shselasky					if (src_len > (0xFFFF - 3))
482184610Salfred						/* overflow */
483184610Salfred						goto done;
484184610Salfred
485184610Salfred					if (len < (src_len + 3))
486184610Salfred						/* overflow */
487184610Salfred						goto done;
488184610Salfred
489184610Salfred					if (buf) {
490184610Salfred						buf[0] = 0xFF;
491184610Salfred						buf[1] = (src_len & 0xFF);
492184610Salfred						buf[2] = (src_len >> 8) & 0xFF;
493184610Salfred						buf += 3;
494184610Salfred					}
495184610Salfred					len -= (src_len + 3);
496184610Salfred				} else {
497184610Salfred					if (len < (src_len + 1))
498184610Salfred						/* overflow */
499184610Salfred						goto done;
500184610Salfred
501184610Salfred					if (buf) {
502184610Salfred						buf[0] = (src_len & 0xFF);
503184610Salfred						buf += 1;
504184610Salfred					}
505184610Salfred					len -= (src_len + 1);
506184610Salfred				}
507184610Salfred
508184610Salfred				/* check for buffer and non-zero length */
509184610Salfred
510184610Salfred				if (buf && src_len) {
511184610Salfred					if (ps->type == LIBUSB20_ME_IS_DECODED) {
512184610Salfred						/*
513184610Salfred						 * Repeat encode
514184610Salfred						 * procedure - we have
515184610Salfred						 * room for the
516184610Salfred						 * complete structure:
517184610Salfred						 */
518184610Salfred						uint16_t dummy;
519184610Salfred
520184610Salfred						dummy = libusb20_me_encode(buf,
521234491Shselasky						    0xFFFF - 3, ps->ptr);
522184610Salfred					} else {
523184610Salfred						bcopy(src_ptr, buf, src_len);
524184610Salfred					}
525184610Salfred					buf += src_len;
526184610Salfred				}
527184610Salfred				pd_offset += sizeof(struct libusb20_me_struct);
528184610Salfred			}
529184610Salfred			break;
530184610Salfred
531184610Salfred		default:
532184610Salfred			goto done;
533184610Salfred		}
534184610Salfred	}
535184610Salfreddone:
536184610Salfred	return (len_old - len);
537184610Salfred}
538184610Salfred
539184610Salfred/*------------------------------------------------------------------------*
540184610Salfred *	libusb20_me_decode - decode a message into a decoded structure
541184610Salfred *
542184610Salfred * Description of parameters:
543184610Salfred * "ptr" - message pointer
544184610Salfred * "len" - message length
545184610Salfred * "pd" - pointer to decoded structure
546184610Salfred *
547184610Salfred * Returns:
548184610Salfred * "0..65535" - number of bytes decoded, limited by "len"
549184610Salfred *------------------------------------------------------------------------*/
550184610Salfreduint16_t
551184610Salfredlibusb20_me_decode(const void *ptr, uint16_t len, void *pd)
552184610Salfred{
553184610Salfred	const uint8_t *pf;		/* pointer to format data */
554184610Salfred	const uint8_t *buf;		/* pointer to input buffer */
555184610Salfred
556184610Salfred	uint32_t pd_offset;		/* decoded structure offset */
557184610Salfred	uint16_t len_old;		/* old length */
558184610Salfred	uint16_t pd_count;		/* decoded element count */
559184610Salfred	uint8_t me;			/* message element */
560184610Salfred
561184610Salfred	/* initialise */
562184610Salfred
563184610Salfred	len_old = len;
564184610Salfred	buf = ptr;
565184610Salfred	pd_offset = sizeof(void *);
566184610Salfred	pf = (*((struct libusb20_me_format **)pd))->format;
567184610Salfred
568184610Salfred	/* scan */
569184610Salfred
570184610Salfred	while (1) {
571184610Salfred
572184610Salfred		/* get information element */
573184610Salfred
574184610Salfred		me = (pf[0]) & LIBUSB20_ME_MASK;
575184610Salfred		pd_count = pf[1] | (pf[2] << 8);
576184610Salfred		pf += 3;
577184610Salfred
578184610Salfred		/* decode the message element by type */
579184610Salfred
580184610Salfred		switch (me) {
581184610Salfred		case LIBUSB20_ME_INT8:
582184610Salfred			while (pd_count--) {
583184610Salfred				uint8_t temp;
584184610Salfred
585184610Salfred				if (len < 1) {
586184610Salfred					len = 0;
587184610Salfred					temp = 0;
588184610Salfred				} else {
589184610Salfred					len -= 1;
590184610Salfred					temp = buf[0];
591184610Salfred					buf++;
592184610Salfred				}
593184610Salfred				*((uint8_t *)LIBUSB20_ADD_BYTES(pd,
594184610Salfred				    pd_offset)) = temp;
595184610Salfred				pd_offset += 1;
596184610Salfred			}
597184610Salfred			break;
598184610Salfred
599184610Salfred		case LIBUSB20_ME_INT16:
600184610Salfred			pd_offset = -((-pd_offset) & ~1);	/* align */
601184610Salfred			while (pd_count--) {
602184610Salfred				uint16_t temp;
603184610Salfred
604184610Salfred				if (len < 2) {
605184610Salfred					len = 0;
606184610Salfred					temp = 0;
607184610Salfred				} else {
608184610Salfred					len -= 2;
609184610Salfred					temp = buf[1] << 8;
610184610Salfred					temp |= buf[0];
611184610Salfred					buf += 2;
612184610Salfred				}
613184610Salfred				*((uint16_t *)LIBUSB20_ADD_BYTES(pd,
614184610Salfred				    pd_offset)) = temp;
615184610Salfred				pd_offset += 2;
616184610Salfred			}
617184610Salfred			break;
618184610Salfred
619184610Salfred		case LIBUSB20_ME_INT32:
620184610Salfred			pd_offset = -((-pd_offset) & ~3);	/* align */
621184610Salfred			while (pd_count--) {
622184610Salfred				uint32_t temp;
623184610Salfred
624184610Salfred				if (len < 4) {
625184610Salfred					len = 0;
626184610Salfred					temp = 0;
627184610Salfred				} else {
628184610Salfred					len -= 4;
629184610Salfred					temp = buf[3] << 24;
630184610Salfred					temp |= buf[2] << 16;
631184610Salfred					temp |= buf[1] << 8;
632184610Salfred					temp |= buf[0];
633184610Salfred					buf += 4;
634184610Salfred				}
635184610Salfred
636184610Salfred				*((uint32_t *)LIBUSB20_ADD_BYTES(pd,
637184610Salfred				    pd_offset)) = temp;
638184610Salfred				pd_offset += 4;
639184610Salfred			}
640184610Salfred			break;
641184610Salfred
642184610Salfred		case LIBUSB20_ME_INT64:
643184610Salfred			pd_offset = -((-pd_offset) & ~7);	/* align */
644184610Salfred			while (pd_count--) {
645184610Salfred				uint64_t temp;
646184610Salfred
647184610Salfred				if (len < 8) {
648184610Salfred					len = 0;
649184610Salfred					temp = 0;
650184610Salfred				} else {
651184610Salfred					len -= 8;
652184610Salfred					temp = ((uint64_t)buf[7]) << 56;
653184610Salfred					temp |= ((uint64_t)buf[6]) << 48;
654184610Salfred					temp |= ((uint64_t)buf[5]) << 40;
655184610Salfred					temp |= ((uint64_t)buf[4]) << 32;
656184610Salfred					temp |= buf[3] << 24;
657184610Salfred					temp |= buf[2] << 16;
658184610Salfred					temp |= buf[1] << 8;
659184610Salfred					temp |= buf[0];
660184610Salfred					buf += 8;
661184610Salfred				}
662184610Salfred
663184610Salfred				*((uint64_t *)LIBUSB20_ADD_BYTES(pd,
664184610Salfred				    pd_offset)) = temp;
665184610Salfred				pd_offset += 8;
666184610Salfred			}
667184610Salfred			break;
668184610Salfred
669184610Salfred		case LIBUSB20_ME_STRUCT:
670184610Salfred			pd_offset = -((-pd_offset) &
671184610Salfred			    ~(LIBUSB20_ME_STRUCT_ALIGN - 1));	/* align */
672184610Salfred			while (pd_count--) {
673184610Salfred				uint16_t temp;
674184610Salfred				uint16_t dummy;
675184610Salfred				struct libusb20_me_struct *ps;
676184610Salfred
677184610Salfred				ps = LIBUSB20_ADD_BYTES(pd, pd_offset);
678184610Salfred
679184610Salfred				if (ps->type == LIBUSB20_ME_IS_ENCODED) {
680184610Salfred					/*
681184610Salfred					 * Pre-store a de-constified
682184610Salfred					 * pointer to the raw
683184610Salfred					 * structure:
684184610Salfred					 */
685184610Salfred					ps->ptr = LIBUSB20_ADD_BYTES(buf, 0);
686184610Salfred
687184610Salfred					/*
688184610Salfred					 * Get the correct number of
689184610Salfred					 * length bytes:
690184610Salfred					 */
691184610Salfred					if (len != 0) {
692184610Salfred						if (buf[0] == 0xFF) {
693184610Salfred							ps->len = 3;
694184610Salfred						} else {
695184610Salfred							ps->len = 1;
696184610Salfred						}
697184610Salfred					} else {
698184610Salfred						ps->len = 0;
699184610Salfred					}
700184610Salfred				}
701184610Salfred				/* get the structure length */
702184610Salfred
703184610Salfred				if (len != 0) {
704184610Salfred					if (buf[0] == 0xFF) {
705184610Salfred						if (len < 3) {
706184610Salfred							len = 0;
707184610Salfred							temp = 0;
708184610Salfred						} else {
709184610Salfred							len -= 3;
710184610Salfred							temp = buf[1] |
711184610Salfred							    (buf[2] << 8);
712184610Salfred							buf += 3;
713184610Salfred						}
714184610Salfred					} else {
715184610Salfred						len -= 1;
716184610Salfred						temp = buf[0];
717184610Salfred						buf += 1;
718184610Salfred					}
719184610Salfred				} else {
720184610Salfred					len = 0;
721184610Salfred					temp = 0;
722184610Salfred				}
723184610Salfred				/* check for invalid length */
724184610Salfred
725184610Salfred				if (temp > len) {
726184610Salfred					len = 0;
727184610Salfred					temp = 0;
728184610Salfred				}
729184610Salfred				/* check wanted structure type */
730184610Salfred
731184610Salfred				switch (ps->type) {
732184610Salfred				case LIBUSB20_ME_IS_ENCODED:
733184610Salfred					/* check for zero length */
734184610Salfred					if (temp == 0) {
735184610Salfred						/*
736184610Salfred						 * The pointer must
737184610Salfred						 * be valid:
738184610Salfred						 */
739184610Salfred						ps->ptr = LIBUSB20_ADD_BYTES(
740184610Salfred						    libusb20_me_encode_empty, 0);
741184610Salfred						ps->len = 1;
742184610Salfred					} else {
743184610Salfred						ps->len += temp;
744184610Salfred					}
745184610Salfred					break;
746184610Salfred
747184610Salfred				case LIBUSB20_ME_IS_RAW:
748184610Salfred					/* update length and pointer */
749184610Salfred					ps->len = temp;
750184610Salfred					ps->ptr = LIBUSB20_ADD_BYTES(buf, 0);
751184610Salfred					break;
752184610Salfred
753184610Salfred				case LIBUSB20_ME_IS_EMPTY:
754184610Salfred				case LIBUSB20_ME_IS_DECODED:
755184610Salfred					/* check for non-zero length */
756184610Salfred					if (temp != 0) {
757184610Salfred						/* update type */
758184610Salfred						ps->type = LIBUSB20_ME_IS_DECODED;
759184610Salfred						ps->len = 0;
760184610Salfred						/*
761184610Salfred						 * Recursivly decode
762184610Salfred						 * the next structure
763184610Salfred						 */
764184610Salfred						dummy = libusb20_me_decode(buf,
765184610Salfred						    temp, ps->ptr);
766184610Salfred					} else {
767184610Salfred						/* update type */
768184610Salfred						ps->type = LIBUSB20_ME_IS_EMPTY;
769184610Salfred						ps->len = 0;
770184610Salfred					}
771184610Salfred					break;
772184610Salfred
773184610Salfred				default:
774184610Salfred					/*
775184610Salfred					 * nothing to do - should
776184610Salfred					 * not happen
777184610Salfred					 */
778184610Salfred					ps->ptr = NULL;
779184610Salfred					ps->len = 0;
780184610Salfred					break;
781184610Salfred				}
782184610Salfred				buf += temp;
783184610Salfred				len -= temp;
784184610Salfred				pd_offset += sizeof(struct libusb20_me_struct);
785184610Salfred			}
786184610Salfred			break;
787184610Salfred
788184610Salfred		default:
789184610Salfred			goto done;
790184610Salfred		}
791184610Salfred	}
792184610Salfreddone:
793184610Salfred	return (len_old - len);
794184610Salfred}
795