1194676Sthompsa/* $FreeBSD$ */
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)
335194676Sthompsa		return (strlen(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
413227404Shselaskylibusb_parse_bos_descriptor(const void *buf, int len,
414227404Shselasky    struct libusb_bos_descriptor **bos)
415227404Shselasky{
416227404Shselasky	struct libusb_bos_descriptor *ptr;
417234491Shselasky	struct libusb_usb_2_0_device_capability_descriptor *dcap_20 = NULL;
418234491Shselasky	struct libusb_ss_usb_device_capability_descriptor *ss_cap = NULL;
419227404Shselasky
420227404Shselasky	if (buf == NULL || bos == NULL || len < 1)
421227404Shselasky		return (LIBUSB_ERROR_INVALID_PARAM);
422227404Shselasky
423227404Shselasky	if (len > 65535)
424227404Shselasky		len = 65535;
425227404Shselasky
426227404Shselasky	*bos = ptr = NULL;
427227404Shselasky
428227404Shselasky	while (len != 0) {
429227404Shselasky		uint8_t dlen;
430227404Shselasky		uint8_t dtype;
431227404Shselasky
432227404Shselasky		dlen = ((const uint8_t *)buf)[0];
433227404Shselasky		dtype = ((const uint8_t *)buf)[1];
434227404Shselasky
435227404Shselasky		if (dlen < 2 || dlen > len)
436227404Shselasky			break;
437227404Shselasky
438227404Shselasky		if (dlen >= LIBUSB_DT_BOS_SIZE &&
439227404Shselasky		    dtype == LIBUSB_DT_BOS) {
440227404Shselasky
441227404Shselasky			ptr = malloc(sizeof(*ptr) + sizeof(*dcap_20) +
442227404Shselasky			    sizeof(*ss_cap));
443227404Shselasky
444227404Shselasky			if (ptr == NULL)
445227404Shselasky				return (LIBUSB_ERROR_NO_MEM);
446227404Shselasky
447227404Shselasky			*bos = ptr;
448227404Shselasky
449227404Shselasky			ptr->bLength = LIBUSB_DT_BOS_SIZE;
450227404Shselasky			ptr->bDescriptorType = dtype;
451227404Shselasky			ptr->wTotalLength = ((const uint8_t *)buf)[2] |
452227404Shselasky			    (((const uint8_t *)buf)[3] << 8);
453227404Shselasky			ptr->bNumDeviceCapabilities = ((const uint8_t *)buf)[4];
454227404Shselasky			ptr->usb_2_0_ext_cap = NULL;
455227404Shselasky			ptr->ss_usb_cap = NULL;
456227404Shselasky
457227404Shselasky			dcap_20 = (void *)(ptr + 1);
458227404Shselasky			ss_cap = (void *)(dcap_20 + 1);
459227404Shselasky		}
460227404Shselasky		if (dlen >= 3 &&
461227404Shselasky		    ptr != NULL &&
462227404Shselasky		    dtype == LIBUSB_DT_DEVICE_CAPABILITY) {
463227404Shselasky			switch (((const uint8_t *)buf)[2]) {
464227404Shselasky			case LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY:
465234491Shselasky				if (ptr->usb_2_0_ext_cap != NULL || dcap_20 == NULL)
466227404Shselasky					break;
467227404Shselasky				if (dlen < LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY_SIZE)
468227404Shselasky					break;
469227404Shselasky
470227404Shselasky				ptr->usb_2_0_ext_cap = dcap_20;
471227404Shselasky
472227404Shselasky				dcap_20->bLength = LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY_SIZE;
473227404Shselasky				dcap_20->bDescriptorType = dtype;
474227404Shselasky				dcap_20->bDevCapabilityType = ((const uint8_t *)buf)[2];
475227404Shselasky				dcap_20->bmAttributes = ((const uint8_t *)buf)[3] |
476227404Shselasky				    (((const uint8_t *)buf)[4] << 8) |
477227404Shselasky				    (((const uint8_t *)buf)[5] << 16) |
478227404Shselasky				    (((const uint8_t *)buf)[6] << 24);
479227404Shselasky				break;
480227404Shselasky
481227404Shselasky			case LIBUSB_SS_USB_DEVICE_CAPABILITY:
482234491Shselasky				if (ptr->ss_usb_cap != NULL || ss_cap == NULL)
483227404Shselasky					break;
484227404Shselasky				if (dlen < LIBUSB_SS_USB_DEVICE_CAPABILITY_SIZE)
485227404Shselasky					break;
486227404Shselasky
487227404Shselasky				ptr->ss_usb_cap = ss_cap;
488227404Shselasky
489227404Shselasky				ss_cap->bLength = LIBUSB_SS_USB_DEVICE_CAPABILITY_SIZE;
490227404Shselasky				ss_cap->bDescriptorType = dtype;
491227404Shselasky				ss_cap->bDevCapabilityType = ((const uint8_t *)buf)[2];
492227404Shselasky				ss_cap->bmAttributes = ((const uint8_t *)buf)[3];
493227404Shselasky				ss_cap->wSpeedSupported = ((const uint8_t *)buf)[4] |
494227404Shselasky				    (((const uint8_t *)buf)[5] << 8);
495227404Shselasky				ss_cap->bFunctionalitySupport = ((const uint8_t *)buf)[6];
496227404Shselasky				ss_cap->bU1DevExitLat = ((const uint8_t *)buf)[7];
497227404Shselasky				ss_cap->wU2DevExitLat = ((const uint8_t *)buf)[8] |
498227404Shselasky				    (((const uint8_t *)buf)[9] << 8);
499227404Shselasky				break;
500227404Shselasky
501227404Shselasky			default:
502227404Shselasky				break;
503227404Shselasky			}
504227404Shselasky		}
505227404Shselasky
506227404Shselasky		buf = ((const uint8_t *)buf) + dlen;
507227404Shselasky		len -= dlen;
508227404Shselasky	}
509227404Shselasky	if (ptr != NULL)
510227404Shselasky		return (0);		/* success */
511227404Shselasky
512227404Shselasky	return (LIBUSB_ERROR_IO);
513227404Shselasky}
514227404Shselasky
515227404Shselaskyvoid
516227404Shselaskylibusb_free_bos_descriptor(struct libusb_bos_descriptor *bos)
517227404Shselasky{
518227404Shselasky	if (bos == NULL)
519227404Shselasky		return;
520227404Shselasky
521227404Shselasky	free(bos);
522227404Shselasky}
523