1194676Sthompsa/* $FreeBSD: stable/11/lib/libusb/libusb10_desc.c 328142 2018-01-18 21:39:03Z kevans $ */
2194676Sthompsa/*-
3194676Sthompsa * Copyright (c) 2009 Sylvestre Gallon. All rights reserved.
4194676Sthompsa *
5194676Sthompsa * Redistribution and use in source and binary forms, with or without
6194676Sthompsa * modification, are permitted provided that the following conditions
7194676Sthompsa * are met:
8194676Sthompsa * 1. Redistributions of source code must retain the above copyright
9194676Sthompsa *    notice, this list of conditions and the following disclaimer.
10194676Sthompsa * 2. Redistributions in binary form must reproduce the above copyright
11194676Sthompsa *    notice, this list of conditions and the following disclaimer in the
12194676Sthompsa *    documentation and/or other materials provided with the distribution.
13194676Sthompsa *
14194676Sthompsa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15194676Sthompsa * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16194676Sthompsa * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17194676Sthompsa * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18194676Sthompsa * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19194676Sthompsa * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20194676Sthompsa * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21194676Sthompsa * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22194676Sthompsa * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23194676Sthompsa * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24194676Sthompsa * SUCH DAMAGE.
25194676Sthompsa */
26194676Sthompsa
27248236Shselasky#ifdef LIBUSB_GLOBAL_INCLUDE_FILE
28248236Shselasky#include LIBUSB_GLOBAL_INCLUDE_FILE
29248236Shselasky#else
30203815Swkoszek#include <stdio.h>
31203815Swkoszek#include <stdlib.h>
32248236Shselasky#include <string.h>
33248236Shselasky#include <time.h>
34248236Shselasky#include <sys/queue.h>
35248236Shselasky#endif
36203815Swkoszek
37208020Sthompsa#define	libusb_device_handle libusb20_device
38208020Sthompsa
39194676Sthompsa#include "libusb20.h"
40194676Sthompsa#include "libusb20_desc.h"
41194676Sthompsa#include "libusb20_int.h"
42194676Sthompsa#include "libusb.h"
43194676Sthompsa#include "libusb10.h"
44194676Sthompsa
45199055Sthompsa#define	N_ALIGN(n) (-((-(n)) & (-8UL)))
46199055Sthompsa
47194676Sthompsa/* USB descriptors */
48194676Sthompsa
49194676Sthompsaint
50195957Salfredlibusb_get_device_descriptor(libusb_device *dev,
51194676Sthompsa    struct libusb_device_descriptor *desc)
52194676Sthompsa{
53194676Sthompsa	struct LIBUSB20_DEVICE_DESC_DECODED *pdesc;
54194676Sthompsa	struct libusb20_device *pdev;
55194676Sthompsa
56194676Sthompsa	if ((dev == NULL) || (desc == NULL))
57194676Sthompsa		return (LIBUSB_ERROR_INVALID_PARAM);
58194676Sthompsa
59194676Sthompsa	pdev = dev->os_priv;
60194676Sthompsa	pdesc = libusb20_dev_get_device_desc(pdev);
61194676Sthompsa
62194676Sthompsa	desc->bLength = pdesc->bLength;
63194676Sthompsa	desc->bDescriptorType = pdesc->bDescriptorType;
64194676Sthompsa	desc->bcdUSB = pdesc->bcdUSB;
65194676Sthompsa	desc->bDeviceClass = pdesc->bDeviceClass;
66194676Sthompsa	desc->bDeviceSubClass = pdesc->bDeviceSubClass;
67194676Sthompsa	desc->bDeviceProtocol = pdesc->bDeviceProtocol;
68194676Sthompsa	desc->bMaxPacketSize0 = pdesc->bMaxPacketSize0;
69194676Sthompsa	desc->idVendor = pdesc->idVendor;
70194676Sthompsa	desc->idProduct = pdesc->idProduct;
71194676Sthompsa	desc->bcdDevice = pdesc->bcdDevice;
72194676Sthompsa	desc->iManufacturer = pdesc->iManufacturer;
73194676Sthompsa	desc->iProduct = pdesc->iProduct;
74194676Sthompsa	desc->iSerialNumber = pdesc->iSerialNumber;
75194676Sthompsa	desc->bNumConfigurations = pdesc->bNumConfigurations;
76194676Sthompsa
77194676Sthompsa	return (0);
78194676Sthompsa}
79194676Sthompsa
80194676Sthompsaint
81195957Salfredlibusb_get_active_config_descriptor(libusb_device *dev,
82194676Sthompsa    struct libusb_config_descriptor **config)
83194676Sthompsa{
84194676Sthompsa	struct libusb20_device *pdev;
85195957Salfred	uint8_t config_index;
86194676Sthompsa
87194676Sthompsa	pdev = dev->os_priv;
88195957Salfred	config_index = libusb20_dev_get_config_index(pdev);
89194676Sthompsa
90195957Salfred	return (libusb_get_config_descriptor(dev, config_index, config));
91194676Sthompsa}
92194676Sthompsa
93194676Sthompsaint
94195957Salfredlibusb_get_config_descriptor(libusb_device *dev, uint8_t config_index,
95194676Sthompsa    struct libusb_config_descriptor **config)
96194676Sthompsa{
97194676Sthompsa	struct libusb20_device *pdev;
98194676Sthompsa	struct libusb20_config *pconf;
99194676Sthompsa	struct libusb20_interface *pinf;
100194676Sthompsa	struct libusb20_endpoint *pend;
101195957Salfred	struct libusb_config_descriptor *pconfd;
102195957Salfred	struct libusb_interface_descriptor *ifd;
103195957Salfred	struct libusb_endpoint_descriptor *endd;
104195957Salfred	uint8_t *pextra;
105195957Salfred	uint16_t nextra;
106195957Salfred	uint8_t nif;
107195957Salfred	uint8_t nep;
108195957Salfred	uint8_t nalt;
109195957Salfred	uint8_t i;
110195957Salfred	uint8_t j;
111195957Salfred	uint8_t k;
112194676Sthompsa
113194676Sthompsa	if (dev == NULL || config == NULL)
114194676Sthompsa		return (LIBUSB_ERROR_INVALID_PARAM);
115194676Sthompsa
116195957Salfred	*config = NULL;
117195957Salfred
118194676Sthompsa	pdev = dev->os_priv;
119194676Sthompsa	pconf = libusb20_dev_alloc_config(pdev, config_index);
120194676Sthompsa
121194676Sthompsa	if (pconf == NULL)
122194676Sthompsa		return (LIBUSB_ERROR_NOT_FOUND);
123194676Sthompsa
124194676Sthompsa	nalt = nif = pconf->num_interface;
125195957Salfred	nep = 0;
126199055Sthompsa	nextra = N_ALIGN(pconf->extra.len);
127195957Salfred
128195957Salfred	for (i = 0; i < nif; i++) {
129195957Salfred
130195957Salfred		pinf = pconf->interface + i;
131199055Sthompsa		nextra += N_ALIGN(pinf->extra.len);
132195957Salfred		nep += pinf->num_endpoints;
133195957Salfred		k = pinf->num_endpoints;
134195957Salfred		pend = pinf->endpoints;
135195957Salfred		while (k--) {
136199055Sthompsa			nextra += N_ALIGN(pend->extra.len);
137195957Salfred			pend++;
138195957Salfred		}
139195957Salfred
140195957Salfred		j = pinf->num_altsetting;
141195957Salfred		nalt += pinf->num_altsetting;
142195957Salfred		pinf = pinf->altsetting;
143195957Salfred		while (j--) {
144199055Sthompsa			nextra += N_ALIGN(pinf->extra.len);
145195957Salfred			nep += pinf->num_endpoints;
146195957Salfred			k = pinf->num_endpoints;
147195957Salfred			pend = pinf->endpoints;
148195957Salfred			while (k--) {
149199055Sthompsa				nextra += N_ALIGN(pend->extra.len);
150195957Salfred				pend++;
151194676Sthompsa			}
152195957Salfred			pinf++;
153194676Sthompsa		}
154194676Sthompsa	}
155194676Sthompsa
156195957Salfred	nextra = nextra +
157195957Salfred	    (1 * sizeof(libusb_config_descriptor)) +
158194676Sthompsa	    (nif * sizeof(libusb_interface)) +
159194676Sthompsa	    (nalt * sizeof(libusb_interface_descriptor)) +
160195957Salfred	    (nep * sizeof(libusb_endpoint_descriptor));
161195957Salfred
162199055Sthompsa	nextra = N_ALIGN(nextra);
163199055Sthompsa
164195957Salfred	pconfd = malloc(nextra);
165195957Salfred
166195957Salfred	if (pconfd == NULL) {
167194676Sthompsa		free(pconf);
168194676Sthompsa		return (LIBUSB_ERROR_NO_MEM);
169194676Sthompsa	}
170199055Sthompsa	/* make sure memory is initialised */
171195957Salfred	memset(pconfd, 0, nextra);
172194676Sthompsa
173199055Sthompsa	pconfd->interface = (libusb_interface *) (pconfd + 1);
174194676Sthompsa
175195957Salfred	ifd = (libusb_interface_descriptor *) (pconfd->interface + nif);
176195957Salfred	endd = (libusb_endpoint_descriptor *) (ifd + nalt);
177195957Salfred	pextra = (uint8_t *)(endd + nep);
178195957Salfred
179195957Salfred	/* fill in config descriptor */
180195957Salfred
181195957Salfred	pconfd->bLength = pconf->desc.bLength;
182195957Salfred	pconfd->bDescriptorType = pconf->desc.bDescriptorType;
183195957Salfred	pconfd->wTotalLength = pconf->desc.wTotalLength;
184195957Salfred	pconfd->bNumInterfaces = pconf->desc.bNumInterfaces;
185195957Salfred	pconfd->bConfigurationValue = pconf->desc.bConfigurationValue;
186195957Salfred	pconfd->iConfiguration = pconf->desc.iConfiguration;
187195957Salfred	pconfd->bmAttributes = pconf->desc.bmAttributes;
188195957Salfred	pconfd->MaxPower = pconf->desc.bMaxPower;
189195957Salfred
190195957Salfred	if (pconf->extra.len != 0) {
191195957Salfred		pconfd->extra_length = pconf->extra.len;
192195957Salfred		pconfd->extra = pextra;
193195957Salfred		memcpy(pextra, pconf->extra.ptr, pconfd->extra_length);
194199055Sthompsa		pextra += N_ALIGN(pconfd->extra_length);
195195957Salfred	}
196195957Salfred	/* setup all interface and endpoint pointers */
197195957Salfred
198195957Salfred	for (i = 0; i < nif; i++) {
199195957Salfred
200195957Salfred		pconfd->interface[i].altsetting = ifd;
201195957Salfred		ifd->endpoint = endd;
202195957Salfred		endd += pconf->interface[i].num_endpoints;
203195957Salfred		ifd++;
204195957Salfred
205195957Salfred		for (j = 0; j < pconf->interface[i].num_altsetting; j++) {
206195957Salfred			ifd->endpoint = endd;
207195957Salfred			endd += pconf->interface[i].altsetting[j].num_endpoints;
208195957Salfred			ifd++;
209194676Sthompsa		}
210194676Sthompsa	}
211194676Sthompsa
212195957Salfred	/* fill in all interface and endpoint data */
213194676Sthompsa
214195957Salfred	for (i = 0; i < nif; i++) {
215194676Sthompsa		pinf = &pconf->interface[i];
216195957Salfred		pconfd->interface[i].num_altsetting = pinf->num_altsetting + 1;
217195957Salfred		for (j = 0; j < pconfd->interface[i].num_altsetting; j++) {
218194676Sthompsa			if (j != 0)
219194676Sthompsa				pinf = &pconf->interface[i].altsetting[j - 1];
220195957Salfred			ifd = &pconfd->interface[i].altsetting[j];
221194676Sthompsa			ifd->bLength = pinf->desc.bLength;
222194676Sthompsa			ifd->bDescriptorType = pinf->desc.bDescriptorType;
223194676Sthompsa			ifd->bInterfaceNumber = pinf->desc.bInterfaceNumber;
224194676Sthompsa			ifd->bAlternateSetting = pinf->desc.bAlternateSetting;
225194676Sthompsa			ifd->bNumEndpoints = pinf->desc.bNumEndpoints;
226194676Sthompsa			ifd->bInterfaceClass = pinf->desc.bInterfaceClass;
227194676Sthompsa			ifd->bInterfaceSubClass = pinf->desc.bInterfaceSubClass;
228194676Sthompsa			ifd->bInterfaceProtocol = pinf->desc.bInterfaceProtocol;
229194676Sthompsa			ifd->iInterface = pinf->desc.iInterface;
230195957Salfred			if (pinf->extra.len != 0) {
231195957Salfred				ifd->extra_length = pinf->extra.len;
232195957Salfred				ifd->extra = pextra;
233195957Salfred				memcpy(pextra, pinf->extra.ptr, pinf->extra.len);
234199055Sthompsa				pextra += N_ALIGN(pinf->extra.len);
235195957Salfred			}
236195957Salfred			for (k = 0; k < pinf->num_endpoints; k++) {
237194676Sthompsa				pend = &pinf->endpoints[k];
238194676Sthompsa				endd = &ifd->endpoint[k];
239194676Sthompsa				endd->bLength = pend->desc.bLength;
240194676Sthompsa				endd->bDescriptorType = pend->desc.bDescriptorType;
241194676Sthompsa				endd->bEndpointAddress = pend->desc.bEndpointAddress;
242194676Sthompsa				endd->bmAttributes = pend->desc.bmAttributes;
243194676Sthompsa				endd->wMaxPacketSize = pend->desc.wMaxPacketSize;
244194676Sthompsa				endd->bInterval = pend->desc.bInterval;
245194676Sthompsa				endd->bRefresh = pend->desc.bRefresh;
246194676Sthompsa				endd->bSynchAddress = pend->desc.bSynchAddress;
247195957Salfred				if (pend->extra.len != 0) {
248195957Salfred					endd->extra_length = pend->extra.len;
249195957Salfred					endd->extra = pextra;
250195957Salfred					memcpy(pextra, pend->extra.ptr, pend->extra.len);
251199055Sthompsa					pextra += N_ALIGN(pend->extra.len);
252195957Salfred				}
253194676Sthompsa			}
254195957Salfred		}
255194676Sthompsa	}
256194676Sthompsa
257194676Sthompsa	free(pconf);
258195957Salfred
259195957Salfred	*config = pconfd;
260195957Salfred
261195957Salfred	return (0);			/* success */
262194676Sthompsa}
263194676Sthompsa
264194676Sthompsaint
265195957Salfredlibusb_get_config_descriptor_by_value(libusb_device *dev,
266194676Sthompsa    uint8_t bConfigurationValue, struct libusb_config_descriptor **config)
267194676Sthompsa{
268194676Sthompsa	struct LIBUSB20_DEVICE_DESC_DECODED *pdesc;
269194676Sthompsa	struct libusb20_device *pdev;
270194676Sthompsa	int i;
271195957Salfred	int err;
272194676Sthompsa
273194676Sthompsa	if (dev == NULL || config == NULL)
274194676Sthompsa		return (LIBUSB_ERROR_INVALID_PARAM);
275195957Salfred
276194676Sthompsa	pdev = dev->os_priv;
277194676Sthompsa	pdesc = libusb20_dev_get_device_desc(pdev);
278194676Sthompsa
279195957Salfred	for (i = 0; i < pdesc->bNumConfigurations; i++) {
280195957Salfred		err = libusb_get_config_descriptor(dev, i, config);
281195957Salfred		if (err)
282195957Salfred			return (err);
283194676Sthompsa
284195957Salfred		if ((*config)->bConfigurationValue == bConfigurationValue)
285195957Salfred			return (0);	/* success */
286195957Salfred
287195957Salfred		libusb_free_config_descriptor(*config);
288194676Sthompsa	}
289194676Sthompsa
290195957Salfred	*config = NULL;
291195957Salfred
292194676Sthompsa	return (LIBUSB_ERROR_NOT_FOUND);
293194676Sthompsa}
294194676Sthompsa
295194676Sthompsavoid
296194676Sthompsalibusb_free_config_descriptor(struct libusb_config_descriptor *config)
297194676Sthompsa{
298194676Sthompsa	free(config);
299194676Sthompsa}
300194676Sthompsa
301194676Sthompsaint
302235128Shselaskylibusb_get_string_descriptor(libusb_device_handle *pdev,
303235128Shselasky    uint8_t desc_index, uint16_t langid, unsigned char *data,
304235128Shselasky    int length)
305235128Shselasky{
306235128Shselasky	if (pdev == NULL || data == NULL || length < 1)
307235128Shselasky		return (LIBUSB_ERROR_INVALID_PARAM);
308235128Shselasky
309235128Shselasky	if (length > 65535)
310235128Shselasky		length = 65535;
311235128Shselasky
312235128Shselasky	/* put some default data into the destination buffer */
313235128Shselasky	data[0] = 0;
314235128Shselasky
315235128Shselasky	return (libusb_control_transfer(pdev, LIBUSB_ENDPOINT_IN,
316235128Shselasky	    LIBUSB_REQUEST_GET_DESCRIPTOR, (LIBUSB_DT_STRING << 8) | desc_index,
317235128Shselasky	    langid, data, length, 1000));
318235128Shselasky}
319235128Shselasky
320235128Shselaskyint
321195957Salfredlibusb_get_string_descriptor_ascii(libusb_device_handle *pdev,
322194676Sthompsa    uint8_t desc_index, unsigned char *data, int length)
323194676Sthompsa{
324195957Salfred	if (pdev == NULL || data == NULL || length < 1)
325227404Shselasky		return (LIBUSB_ERROR_INVALID_PARAM);
326194676Sthompsa
327224085Shselasky	if (length > 65535)
328224085Shselasky		length = 65535;
329224085Shselasky
330195957Salfred	/* put some default data into the destination buffer */
331195957Salfred	data[0] = 0;
332194676Sthompsa
333195957Salfred	if (libusb20_dev_req_string_simple_sync(pdev, desc_index,
334194676Sthompsa	    data, length) == 0)
335301846Shselasky		return (strlen((char *)data));
336194676Sthompsa
337194676Sthompsa	return (LIBUSB_ERROR_OTHER);
338194676Sthompsa}
339199055Sthompsa
340199055Sthompsaint
341199055Sthompsalibusb_get_descriptor(libusb_device_handle * devh, uint8_t desc_type,
342199055Sthompsa    uint8_t desc_index, uint8_t *data, int length)
343199055Sthompsa{
344224085Shselasky	if (devh == NULL || data == NULL || length < 1)
345227404Shselasky		return (LIBUSB_ERROR_INVALID_PARAM);
346224085Shselasky
347224085Shselasky	if (length > 65535)
348224085Shselasky		length = 65535;
349224085Shselasky
350199055Sthompsa	return (libusb_control_transfer(devh, LIBUSB_ENDPOINT_IN,
351199055Sthompsa	    LIBUSB_REQUEST_GET_DESCRIPTOR, (desc_type << 8) | desc_index, 0, data,
352199055Sthompsa	    length, 1000));
353199055Sthompsa}
354227404Shselasky
355227404Shselaskyint
356227404Shselaskylibusb_parse_ss_endpoint_comp(const void *buf, int len,
357227404Shselasky    struct libusb_ss_endpoint_companion_descriptor **ep_comp)
358227404Shselasky{
359227404Shselasky	if (buf == NULL || ep_comp == NULL || len < 1)
360227404Shselasky		return (LIBUSB_ERROR_INVALID_PARAM);
361227404Shselasky
362227404Shselasky	if (len > 65535)
363227404Shselasky		len = 65535;
364227404Shselasky
365227404Shselasky	*ep_comp = NULL;
366227404Shselasky
367227404Shselasky	while (len != 0) {
368227404Shselasky		uint8_t dlen;
369227404Shselasky		uint8_t dtype;
370227404Shselasky
371227404Shselasky		dlen = ((const uint8_t *)buf)[0];
372227404Shselasky		dtype = ((const uint8_t *)buf)[1];
373227404Shselasky
374227404Shselasky		if (dlen < 2 || dlen > len)
375227404Shselasky			break;
376227404Shselasky
377227404Shselasky		if (dlen >= LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE &&
378227404Shselasky		    dtype == LIBUSB_DT_SS_ENDPOINT_COMPANION) {
379227404Shselasky			struct libusb_ss_endpoint_companion_descriptor *ptr;
380227404Shselasky
381227404Shselasky			ptr = malloc(sizeof(*ptr));
382227404Shselasky			if (ptr == NULL)
383227404Shselasky				return (LIBUSB_ERROR_NO_MEM);
384227404Shselasky
385227404Shselasky			ptr->bLength = LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE;
386227404Shselasky			ptr->bDescriptorType = dtype;
387227404Shselasky			ptr->bMaxBurst = ((const uint8_t *)buf)[2];
388227404Shselasky			ptr->bmAttributes = ((const uint8_t *)buf)[3];
389227404Shselasky			ptr->wBytesPerInterval = ((const uint8_t *)buf)[4] |
390227404Shselasky			    (((const uint8_t *)buf)[5] << 8);
391227404Shselasky
392227404Shselasky			*ep_comp = ptr;
393227404Shselasky
394227404Shselasky			return (0);	/* success */
395227404Shselasky		}
396227404Shselasky
397227404Shselasky		buf = ((const uint8_t *)buf) + dlen;
398227404Shselasky		len -= dlen;
399227404Shselasky	}
400227404Shselasky	return (LIBUSB_ERROR_IO);
401227404Shselasky}
402227404Shselasky
403227404Shselaskyvoid
404227404Shselaskylibusb_free_ss_endpoint_comp(struct libusb_ss_endpoint_companion_descriptor *ep_comp)
405227404Shselasky{
406227404Shselasky	if (ep_comp == NULL)
407227404Shselasky		return;
408227404Shselasky
409227404Shselasky	free(ep_comp);
410227404Shselasky}
411227404Shselasky
412227404Shselaskyint
413301968Shselaskylibusb_get_ss_endpoint_companion_descriptor(struct libusb_context *ctx,
414301968Shselasky    const struct libusb_endpoint_descriptor *endpoint,
415301968Shselasky    struct libusb_ss_endpoint_companion_descriptor **ep_comp)
416301968Shselasky{
417301968Shselasky	if (endpoint == NULL)
418301968Shselasky		return (LIBUSB_ERROR_INVALID_PARAM);
419301968Shselasky	return (libusb_parse_ss_endpoint_comp(endpoint->extra, endpoint->extra_length, ep_comp));
420301968Shselasky}
421301968Shselasky
422301968Shselaskyvoid
423301968Shselaskylibusb_free_ss_endpoint_companion_descriptor(struct libusb_ss_endpoint_companion_descriptor *ep_comp)
424301968Shselasky{
425301968Shselasky
426301968Shselasky	libusb_free_ss_endpoint_comp(ep_comp);
427301968Shselasky}
428301968Shselasky
429301968Shselaskyint
430227404Shselaskylibusb_parse_bos_descriptor(const void *buf, int len,
431227404Shselasky    struct libusb_bos_descriptor **bos)
432227404Shselasky{
433227404Shselasky	struct libusb_bos_descriptor *ptr;
434234491Shselasky	struct libusb_usb_2_0_device_capability_descriptor *dcap_20 = NULL;
435234491Shselasky	struct libusb_ss_usb_device_capability_descriptor *ss_cap = NULL;
436328142Skevans	uint8_t index = 0;
437227404Shselasky
438227404Shselasky	if (buf == NULL || bos == NULL || len < 1)
439227404Shselasky		return (LIBUSB_ERROR_INVALID_PARAM);
440227404Shselasky
441227404Shselasky	if (len > 65535)
442227404Shselasky		len = 65535;
443227404Shselasky
444227404Shselasky	*bos = ptr = NULL;
445227404Shselasky
446227404Shselasky	while (len != 0) {
447227404Shselasky		uint8_t dlen;
448227404Shselasky		uint8_t dtype;
449227404Shselasky
450227404Shselasky		dlen = ((const uint8_t *)buf)[0];
451227404Shselasky		dtype = ((const uint8_t *)buf)[1];
452227404Shselasky
453227404Shselasky		if (dlen < 2 || dlen > len)
454227404Shselasky			break;
455227404Shselasky
456227404Shselasky		if (dlen >= LIBUSB_DT_BOS_SIZE &&
457328142Skevans		    dtype == LIBUSB_DT_BOS &&
458328142Skevans		    ptr == NULL) {
459227404Shselasky
460227404Shselasky			ptr = malloc(sizeof(*ptr) + sizeof(*dcap_20) +
461227404Shselasky			    sizeof(*ss_cap));
462227404Shselasky
463227404Shselasky			if (ptr == NULL)
464227404Shselasky				return (LIBUSB_ERROR_NO_MEM);
465227404Shselasky
466227404Shselasky			*bos = ptr;
467227404Shselasky
468227404Shselasky			ptr->bLength = LIBUSB_DT_BOS_SIZE;
469227404Shselasky			ptr->bDescriptorType = dtype;
470227404Shselasky			ptr->wTotalLength = ((const uint8_t *)buf)[2] |
471227404Shselasky			    (((const uint8_t *)buf)[3] << 8);
472227404Shselasky			ptr->bNumDeviceCapabilities = ((const uint8_t *)buf)[4];
473227404Shselasky			ptr->usb_2_0_ext_cap = NULL;
474227404Shselasky			ptr->ss_usb_cap = NULL;
475328142Skevans			ptr->dev_capability = calloc(ptr->bNumDeviceCapabilities, sizeof(void *));
476328142Skevans			if (ptr->dev_capability == NULL) {
477328142Skevans				free(ptr);
478328142Skevans				return (LIBUSB_ERROR_NO_MEM);
479328142Skevans			}
480227404Shselasky
481227404Shselasky			dcap_20 = (void *)(ptr + 1);
482227404Shselasky			ss_cap = (void *)(dcap_20 + 1);
483227404Shselasky		}
484227404Shselasky		if (dlen >= 3 &&
485227404Shselasky		    ptr != NULL &&
486227404Shselasky		    dtype == LIBUSB_DT_DEVICE_CAPABILITY) {
487328142Skevans			if (index != ptr->bNumDeviceCapabilities) {
488328142Skevans				ptr->dev_capability[index] = malloc(dlen);
489328142Skevans				if (ptr->dev_capability[index] == NULL) {
490328142Skevans					libusb_free_bos_descriptor(ptr);
491328142Skevans					return LIBUSB_ERROR_NO_MEM;
492328142Skevans				}
493328142Skevans				memcpy(ptr->dev_capability[index], buf, dlen);
494328142Skevans				index++;
495328142Skevans			}
496227404Shselasky			switch (((const uint8_t *)buf)[2]) {
497227404Shselasky			case LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY:
498234491Shselasky				if (ptr->usb_2_0_ext_cap != NULL || dcap_20 == NULL)
499227404Shselasky					break;
500227404Shselasky				if (dlen < LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY_SIZE)
501227404Shselasky					break;
502227404Shselasky
503227404Shselasky				ptr->usb_2_0_ext_cap = dcap_20;
504227404Shselasky
505227404Shselasky				dcap_20->bLength = LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY_SIZE;
506227404Shselasky				dcap_20->bDescriptorType = dtype;
507227404Shselasky				dcap_20->bDevCapabilityType = ((const uint8_t *)buf)[2];
508227404Shselasky				dcap_20->bmAttributes = ((const uint8_t *)buf)[3] |
509227404Shselasky				    (((const uint8_t *)buf)[4] << 8) |
510227404Shselasky				    (((const uint8_t *)buf)[5] << 16) |
511227404Shselasky				    (((const uint8_t *)buf)[6] << 24);
512227404Shselasky				break;
513227404Shselasky
514227404Shselasky			case LIBUSB_SS_USB_DEVICE_CAPABILITY:
515234491Shselasky				if (ptr->ss_usb_cap != NULL || ss_cap == NULL)
516227404Shselasky					break;
517227404Shselasky				if (dlen < LIBUSB_SS_USB_DEVICE_CAPABILITY_SIZE)
518227404Shselasky					break;
519227404Shselasky
520227404Shselasky				ptr->ss_usb_cap = ss_cap;
521227404Shselasky
522227404Shselasky				ss_cap->bLength = LIBUSB_SS_USB_DEVICE_CAPABILITY_SIZE;
523227404Shselasky				ss_cap->bDescriptorType = dtype;
524227404Shselasky				ss_cap->bDevCapabilityType = ((const uint8_t *)buf)[2];
525227404Shselasky				ss_cap->bmAttributes = ((const uint8_t *)buf)[3];
526227404Shselasky				ss_cap->wSpeedSupported = ((const uint8_t *)buf)[4] |
527227404Shselasky				    (((const uint8_t *)buf)[5] << 8);
528227404Shselasky				ss_cap->bFunctionalitySupport = ((const uint8_t *)buf)[6];
529227404Shselasky				ss_cap->bU1DevExitLat = ((const uint8_t *)buf)[7];
530227404Shselasky				ss_cap->wU2DevExitLat = ((const uint8_t *)buf)[8] |
531227404Shselasky				    (((const uint8_t *)buf)[9] << 8);
532227404Shselasky				break;
533227404Shselasky
534227404Shselasky			default:
535227404Shselasky				break;
536227404Shselasky			}
537227404Shselasky		}
538227404Shselasky
539227404Shselasky		buf = ((const uint8_t *)buf) + dlen;
540227404Shselasky		len -= dlen;
541227404Shselasky	}
542328142Skevans
543328142Skevans	if (ptr != NULL) {
544328142Skevans		ptr->bNumDeviceCapabilities = index;
545227404Shselasky		return (0);		/* success */
546328142Skevans	}
547227404Shselasky
548227404Shselasky	return (LIBUSB_ERROR_IO);
549227404Shselasky}
550227404Shselasky
551227404Shselaskyvoid
552227404Shselaskylibusb_free_bos_descriptor(struct libusb_bos_descriptor *bos)
553227404Shselasky{
554328142Skevans	uint8_t i;
555328142Skevans
556227404Shselasky	if (bos == NULL)
557227404Shselasky		return;
558227404Shselasky
559328142Skevans	for (i = 0; i != bos->bNumDeviceCapabilities; i++)
560328142Skevans		free(bos->dev_capability[i]);
561328142Skevans	free(bos->dev_capability);
562227404Shselasky	free(bos);
563227404Shselasky}
564301968Shselasky
565301968Shselaskyint
566301968Shselaskylibusb_get_bos_descriptor(libusb_device_handle *handle,
567301968Shselasky    struct libusb_bos_descriptor **bos)
568301968Shselasky{
569301968Shselasky	uint8_t bos_header[LIBUSB_DT_BOS_SIZE] = {0};
570301968Shselasky	uint16_t wTotalLength;
571301968Shselasky	uint8_t *bos_data;
572301968Shselasky	int err;
573301968Shselasky
574301968Shselasky	err = libusb_get_descriptor(handle, LIBUSB_DT_BOS, 0,
575301968Shselasky	    bos_header, sizeof(bos_header));
576301968Shselasky	if (err < 0)
577301968Shselasky		return (err);
578301968Shselasky
579301968Shselasky	wTotalLength = bos_header[2] | (bos_header[3] << 8);
580301968Shselasky	if (wTotalLength < LIBUSB_DT_BOS_SIZE)
581301968Shselasky		return (LIBUSB_ERROR_INVALID_PARAM);
582301968Shselasky
583301968Shselasky	bos_data = calloc(wTotalLength, 1);
584301968Shselasky	if (bos_data == NULL)
585301968Shselasky		return (LIBUSB_ERROR_NO_MEM);
586301968Shselasky
587301968Shselasky	err = libusb_get_descriptor(handle, LIBUSB_DT_BOS, 0,
588301968Shselasky	    bos_data, wTotalLength);
589301968Shselasky	if (err < 0)
590301968Shselasky		goto done;
591301968Shselasky
592301968Shselasky	/* avoid descriptor length mismatches */
593301968Shselasky	bos_data[2] = (wTotalLength & 0xFF);
594301968Shselasky	bos_data[3] = (wTotalLength >> 8);
595301968Shselasky
596301968Shselasky	err = libusb_parse_bos_descriptor(bos_data, wTotalLength, bos);
597301968Shselaskydone:
598301968Shselasky	free(bos_data);
599301968Shselasky	return (err);
600301968Shselasky}
601301968Shselasky
602301968Shselaskyint
603301968Shselaskylibusb_get_usb_2_0_extension_descriptor(struct libusb_context *ctx,
604301968Shselasky    struct libusb_bos_dev_capability_descriptor *dev_cap,
605301968Shselasky    struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension)
606301968Shselasky{
607301968Shselasky	struct libusb_usb_2_0_extension_descriptor *desc;
608301968Shselasky
609301968Shselasky	if (dev_cap == NULL || usb_2_0_extension == NULL ||
610301968Shselasky	    dev_cap->bDevCapabilityType != LIBUSB_BT_USB_2_0_EXTENSION)
611301968Shselasky		return (LIBUSB_ERROR_INVALID_PARAM);
612301968Shselasky	if (dev_cap->bLength < LIBUSB_BT_USB_2_0_EXTENSION_SIZE)
613301968Shselasky		return (LIBUSB_ERROR_IO);
614301968Shselasky
615301968Shselasky	desc = malloc(sizeof(*desc));
616301968Shselasky	if (desc == NULL)
617301968Shselasky		return (LIBUSB_ERROR_NO_MEM);
618301968Shselasky
619301968Shselasky	desc->bLength = LIBUSB_BT_USB_2_0_EXTENSION_SIZE;
620301968Shselasky	desc->bDescriptorType = dev_cap->bDescriptorType;
621301968Shselasky	desc->bDevCapabilityType = dev_cap->bDevCapabilityType;
622301968Shselasky	desc->bmAttributes =
623301968Shselasky	    (dev_cap->dev_capability_data[0]) |
624301968Shselasky	    (dev_cap->dev_capability_data[1] << 8) |
625301968Shselasky	    (dev_cap->dev_capability_data[2] << 16) |
626301968Shselasky	    (dev_cap->dev_capability_data[3] << 24);
627301968Shselasky
628301968Shselasky	*usb_2_0_extension = desc;
629301968Shselasky	return (0);
630301968Shselasky}
631301968Shselasky
632301968Shselaskyvoid
633301968Shselaskylibusb_free_usb_2_0_extension_descriptor(
634301968Shselasky    struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension)
635301968Shselasky{
636301968Shselasky
637301968Shselasky	free(usb_2_0_extension);
638301968Shselasky}
639301968Shselasky
640301968Shselaskyint
641301968Shselaskylibusb_get_ss_usb_device_capability_descriptor(struct libusb_context *ctx,
642301968Shselasky    struct libusb_bos_dev_capability_descriptor *dev_cap,
643301968Shselasky    struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_capability)
644301968Shselasky{
645301968Shselasky	struct libusb_ss_usb_device_capability_descriptor *desc;
646301968Shselasky
647301968Shselasky	if (dev_cap == NULL || ss_usb_device_capability == NULL ||
648301968Shselasky	    dev_cap->bDevCapabilityType != LIBUSB_BT_SS_USB_DEVICE_CAPABILITY)
649301968Shselasky		return (LIBUSB_ERROR_INVALID_PARAM);
650301968Shselasky	if (dev_cap->bLength < LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE)
651301968Shselasky		return (LIBUSB_ERROR_IO);
652301968Shselasky
653301968Shselasky	desc = malloc(sizeof(*desc));
654301968Shselasky	if (desc == NULL)
655301968Shselasky		return (LIBUSB_ERROR_NO_MEM);
656301968Shselasky
657301968Shselasky	desc->bLength = LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE;
658301968Shselasky	desc->bDescriptorType = dev_cap->bDescriptorType;
659301968Shselasky	desc->bDevCapabilityType = dev_cap->bDevCapabilityType;
660301968Shselasky	desc->bmAttributes = dev_cap->dev_capability_data[0];
661301968Shselasky	desc->wSpeedSupported = dev_cap->dev_capability_data[1] |
662301968Shselasky	    (dev_cap->dev_capability_data[2] << 8);
663301968Shselasky	desc->bFunctionalitySupport = dev_cap->dev_capability_data[3];
664301968Shselasky	desc->bU1DevExitLat = dev_cap->dev_capability_data[4];
665301968Shselasky	desc->wU2DevExitLat = dev_cap->dev_capability_data[5] |
666301968Shselasky	    (dev_cap->dev_capability_data[6] << 8);
667301968Shselasky
668301968Shselasky	*ss_usb_device_capability = desc;
669301968Shselasky	return (0);
670301968Shselasky}
671301968Shselasky
672301968Shselaskyvoid
673301968Shselaskylibusb_free_ss_usb_device_capability_descriptor(
674301968Shselasky    struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_capability)
675301968Shselasky{
676301968Shselasky
677301968Shselasky	free(ss_usb_device_capability);
678301968Shselasky}
679301968Shselasky
680301968Shselaskyint
681301968Shselaskylibusb_get_container_id_descriptor(struct libusb_context *ctx,
682301968Shselasky    struct libusb_bos_dev_capability_descriptor *dev_cap,
683301968Shselasky    struct libusb_container_id_descriptor **container_id)
684301968Shselasky{
685301968Shselasky	struct libusb_container_id_descriptor *desc;
686301968Shselasky
687301968Shselasky	if (dev_cap == NULL || container_id == NULL ||
688301968Shselasky	    dev_cap->bDevCapabilityType != LIBUSB_BT_CONTAINER_ID)
689301968Shselasky		return (LIBUSB_ERROR_INVALID_PARAM);
690301968Shselasky	if (dev_cap->bLength < LIBUSB_BT_CONTAINER_ID_SIZE)
691301968Shselasky		return (LIBUSB_ERROR_IO);
692301968Shselasky
693301968Shselasky	desc = malloc(sizeof(*desc));
694301968Shselasky	if (desc == NULL)
695301968Shselasky		return (LIBUSB_ERROR_NO_MEM);
696301968Shselasky
697301968Shselasky	desc->bLength = LIBUSB_BT_CONTAINER_ID_SIZE;
698301968Shselasky	desc->bDescriptorType = dev_cap->bDescriptorType;
699301968Shselasky	desc->bDevCapabilityType = dev_cap->bDevCapabilityType;
700301968Shselasky	desc->bReserved = dev_cap->dev_capability_data[0];
701301968Shselasky	memcpy(desc->ContainerID, dev_cap->dev_capability_data + 1,
702301968Shselasky	    sizeof(desc->ContainerID));
703301968Shselasky
704301968Shselasky	*container_id = desc;
705301968Shselasky	return (0);
706301968Shselasky}
707301968Shselasky
708301968Shselaskyvoid
709301968Shselaskylibusb_free_container_id_descriptor(
710301968Shselasky    struct libusb_container_id_descriptor *container_id)
711301968Shselasky{
712301968Shselasky
713301968Shselasky	free(container_id);
714301968Shselasky}
715