Sooooo, instead of looking at Hans' patch, as I should have, I decided
to spend some time on BOS and EP Companions, with the result being the
proposed solution above.
Seems to work nicely against an FX3 device (also tested against an USB
3.0 hub), as per the output sample at the end. Tested with Linux and
Windows.
Now, for the usual stream of notes:
- Yes, I'm deviating from the libusb and freebsd implementation. Why?
Because these sure could use some improvements. First, there's no way we
want to expose a parse_descriptor function, as it doesn't align with
anything we do, and second, not having the EP companions being part of
the EP descriptor struct is just nonsense. When newly introduced APIs
fall short of expectations, we shouldn't shy away from breaking them
when we can do something that'll actually serve users better. And while
I was there, I updated the God-awful designations that FreeBSD and
libusb went with, again, to try to align with what we do in the rest of
the library. Consistency - it's not that hard!
- I played with caching the BOS. But unless we go querying the OS
directly for it (rather than issue a generic transfer as we're doing
now), this isn't gonna fly, as it'd mean opening each device in
sequence, get a handle, and the whole shebang (detach driver anyone?).
So, as opposed to our other get_###_descriptor() calls,
get_bos_descriptor() _is_ blocking and will submit traffic (and requires
a handle rather than a dev). My previous proposal did rely on the OS to
query the BOS, so would have been more suitable for caching, but of
course that'd multiply implementations, so I guess we'll just go for
this common one. Maybe in 2.0 I'll switch to caching the BOS, and align
get_bos_desc() to take a dev rather than a handle. We'll see.
- I'm issuing a double request for the BOS (header for size, then full
BOS). Yeah, we could get away with using a large enough buffer, since
you'll be hard pressed to find a BOS that's more than 128 bytes in size
right now, but hey, we're supposed to be generic where we can...
- Since libusb and BSD went with unrolling the BOS, I added the
Container ID. This is used on every single USB 3.0 HUB, and I'm pretty
sure we'll be asked by our users to enable access to it. Whenever
there's an ID, there's somebody who's going to want to read it. Of
course, going with an unrolled BOS means that whatever additional caps
are added we'll have to add them in libusbx as well. Oh, and just for
the record, I haven't bothered with the Wireless cap.
- Since I discovered that my USB 3.0 hub uses the same VID:PID for the
2.0 and 3.0 hub sub-hubs (seen as 2 separate devices), and of course the
2.0 one is the one that comes first, I'm thinking of introducing a
libusb_open_with_vid_pid_instance() call, that would add an instance
parameter that would be an entirely volatile number corresponding to the
order in which libusbx sees devices with same VID:PID (with no guarantee
whatsoever on that order being preserved between sessions).
Regards,
/Pete
----------------------------------------------------------------------
Sample output from xusb with an custom FX£ firmware. Note that, since
I'm not planning to emulate lsusb within xusb, we're only displaying a
selection of notable USB 3.0 attributes.
D:\libusbx\Win32\Debug\examples>xusb 4b4:f0
Using libusbx v1.0.15.10648
Opening device 04B4:00F0...
Reading device descriptor:
length: 18
device class: 0
S/N: 0
VID:PID: 04B4:00F0
bcdDevice: 0000
iMan:iProd:iSer: 1:2:0
nb confs: 1
Reading BOS descriptor: 3 caps
USB 3.0 capabilities:
supported speeds : 000E
supported functionality: 03
USB 2.0 extension:
attributes : 02
Container ID:
{ebcf16e6-e712-425b-8fcf-918b31340f01}
Reading first configuration descriptor:
nb interfaces: 1
interface[0]: id = 0
interface[0].altsetting[0]: num endpoints = 2
Class.SubClass.Protocol: FF.00.00
endpoint[0].address: 01
max packet size: 0400
polling interval: 00
max burst: 01 (USB 3.0)
bytes per interval: 0400 (USB 3.0)
endpoint[1].address: 81
max packet size: 0400
polling interval: 00
max burst: 01 (USB 3.0)
bytes per interval: 0400 (USB 3.0)
Claiming interface 0...
Reading string descriptors:
String (0x01): "libusbx"
String (0x02): "FX3"
Releasing interface 0...
Closing device...
----------------------------------------------------------------------
>From 9ad522f637d9e3fc76655682b46f029f33a2566d Mon Sep 17 00:00:00 2001
From: Pete Batard <p...@akeo.ie>
Date: Sat, 11 May 2013 14:15:20 +0100
Subject: [PATCH 1/3] Core: Add support for BOS and Endpoint Companion USB 3.0
descriptors
* Also add caching for device descriptors
* Based on patches by Maya Erez <me...@codeaurora.org> and Nathan Hjelm
<hje...@me.com>
---
examples/xusb.c | 37 ++++++-
libusb/core.c | 6 +-
libusb/descriptor.c | 273 ++++++++++++++++++++++++++++++++++++++++++++----
libusb/libusb-1.0.def | 4 +
libusb/libusb.h | 236 ++++++++++++++++++++++++++++++++++++++++-
libusb/libusbi.h | 6 +-
libusb/os/windows_usb.c | 2 +
7 files changed, 535 insertions(+), 29 deletions(-)
diff --git a/examples/xusb.c b/examples/xusb.c
index 856f723..20ac52c 100644
--- a/examples/xusb.c
+++ b/examples/xusb.c
@@ -165,6 +165,16 @@ static void display_buffer_hex(unsigned char *buffer,
unsigned size)
printf("\n" );
}
+static char* uuid_to_string(const uint8_t* uuid)
+{
+ static char uuid_string[40];
+ if (uuid == NULL) return NULL;
+ sprintf(uuid_string,
"{%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
+ uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6],
uuid[7],
+ uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13],
uuid[14], uuid[15]);
+ return uuid_string;
+}
+
// The PS3 Controller is really a HID device that got its HID Report
Descriptors
// removed by Sony
static int display_ps3_status(libusb_device_handle *handle)
@@ -731,6 +741,7 @@ static int test_device(uint16_t vid, uint16_t pid)
libusb_device_handle *handle;
libusb_device *dev;
uint8_t bus, port_path[8];
+ struct libusb_bos_descriptor *bos_desc;
struct libusb_config_descriptor *conf_desc;
const struct libusb_endpoint_descriptor *endpoint;
int i, j, k, r;
@@ -784,7 +795,27 @@ static int test_device(uint16_t vid, uint16_t pid)
string_index[1] = dev_desc.iProduct;
string_index[2] = dev_desc.iSerialNumber;
- printf("\nReading configuration descriptors:\n");
+ printf("\nReading BOS descriptor: ");
+ if (libusb_get_bos_descriptor(handle, &bos_desc) == LIBUSB_SUCCESS) {
+ printf("%d caps\n", bos_desc->bNumDeviceCaps);
+ if (bos_desc->ss_usb_dev_cap != NULL) {
+ printf(" USB 3.0 capabilities:\n");
+ printf(" supported speeds : %04X\n",
bos_desc->ss_usb_dev_cap->wSpeedSupported);
+ printf(" supported functionality: %02X\n",
bos_desc->ss_usb_dev_cap->bFunctionalitySupport);
+ }
+ if (bos_desc->usb_2_0_extension != NULL) {
+ printf(" USB 2.0 extension:\n");
+ printf(" attributes : %02X\n",
bos_desc->usb_2_0_extension->bmAttributes);
+ }
+ if (bos_desc->container_id != NULL) {
+ printf(" Container ID:\n %s\n",
uuid_to_string(bos_desc->container_id->ContainerID));
+ }
+ libusb_free_bos_descriptor(bos_desc);
+ } else {
+ printf("no descriptor\n");
+ }
+
+ printf("\nReading first configuration descriptor:\n");
CALL_CHECK(libusb_get_config_descriptor(dev, 0, &conf_desc));
nb_ifaces = conf_desc->bNumInterfaces;
printf(" nb interfaces: %d\n", nb_ifaces);
@@ -822,6 +853,10 @@ static int test_device(uint16_t vid, uint16_t pid)
}
printf(" max packet size: %04X\n",
endpoint->wMaxPacketSize);
printf(" polling interval: %02X\n",
endpoint->bInterval);
+ if (endpoint->ss_endpoint_companion != NULL) {
+ printf(" max burst:
%02X (USB 3.0)\n", endpoint->ss_endpoint_companion->bMaxBurst);
+ printf(" bytes per interval:
%04X (USB 3.0)\n", endpoint->ss_endpoint_companion->wBytesPerInterval);
+ }
}
}
}
diff --git a/libusb/core.c b/libusb/core.c
index 5948689..234963c 100644
--- a/libusb/core.c
+++ b/libusb/core.c
@@ -534,15 +534,13 @@ struct libusb_device *usbi_alloc_device(struct
libusb_context *ctx,
int usbi_sanitize_device(struct libusb_device *dev)
{
int r;
- unsigned char raw_desc[DEVICE_DESC_LENGTH];
uint8_t num_configurations;
- int host_endian;
- r = usbi_backend->get_device_descriptor(dev, raw_desc, &host_endian);
+ r = usbi_device_cache_descriptor(dev);
if (r < 0)
return r;
- num_configurations = raw_desc[DEVICE_DESC_LENGTH - 1];
+ num_configurations = dev->device_descriptor.bNumConfigurations;
if (num_configurations > USB_MAXCONFIG) {
usbi_err(DEVICE_CTX(dev), "too many configurations");
return LIBUSB_ERROR_IO;
diff --git a/libusb/descriptor.c b/libusb/descriptor.c
index 7e47aae..2a7ff5a 100644
--- a/libusb/descriptor.c
+++ b/libusb/descriptor.c
@@ -22,6 +22,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
+#include <stdio.h>
#include "libusbi.h"
@@ -39,12 +40,14 @@
/* set host_endian if the w values are already in host endian format,
* as opposed to bus endian. */
-int usbi_parse_descriptor(unsigned char *source, const char *descriptor,
+int usbi_parse_descriptor(const unsigned char *source, const char *descriptor,
void *dest, int host_endian)
{
- unsigned char *sp = source, *dp = dest;
+ const unsigned char *sp = source;
+ unsigned char *dp = dest;
uint16_t w;
const char *cp;
+ uint32_t d;
for (cp = descriptor; *cp; cp++) {
switch (*cp) {
@@ -63,6 +66,28 @@ int usbi_parse_descriptor(unsigned char *source, const char
*descriptor,
sp += 2;
dp += 2;
break;
+ /* 32-bit word, convert from little endian to CPU */
+ case 'd':
+ /* Align to word boundary */
+ dp += ((unsigned long)dp & 1);
+
+ if (host_endian) {
+ memcpy(dp, sp, 4);
+ } else {
+ d = (sp[3] << 24) | (sp[2] << 16) |
+ (sp[1] << 8) | sp[0];
+ *((uint32_t *)dp) = d;
+ }
+ sp += 4;
+ dp += 4;
+ break;
+ case 'u': /* 16 byte UUID */
+ /* Align to word boundary */
+ dp += ((unsigned long)dp & 1);
+ memcpy(dp, sp, 16);
+ sp += 16;
+ dp += 16;
+ break;
}
}
@@ -71,10 +96,42 @@ int usbi_parse_descriptor(unsigned char *source, const char
*descriptor,
static void clear_endpoint(struct libusb_endpoint_descriptor *endpoint)
{
+ if (endpoint->ss_endpoint_companion)
+ free((unsigned char *) endpoint->ss_endpoint_companion);
if (endpoint->extra)
free((unsigned char *) endpoint->extra);
}
+static int parse_ss_endpoint_companion(struct libusb_context *ctx,
+ struct libusb_ss_endpoint_companion_descriptor *ep_comp, unsigned char
*buffer, int size)
+{
+ struct usb_descriptor_header header;
+ int parsed = 0;
+
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
+
+ /* Sanity check */
+ if (header.bLength > size) {
+ usbi_err(ctx, "ran out of descriptors parsing");
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ if (header.bDescriptorType != LIBUSB_DT_SS_ENDPOINT_COMPANION) {
+ usbi_err(ctx, "unexpected descriptor %x (expected %x)",
+ header.bDescriptorType,
LIBUSB_DT_SS_ENDPOINT_COMPANION);
+ return parsed;
+ }
+
+ if (header.bLength >= LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE)
+ usbi_parse_descriptor(buffer, "bbbbw", ep_comp, 0);
+
+ buffer += header.bLength;
+ size -= header.bLength;
+ parsed += header.bLength;
+
+ return parsed;
+}
+
static int parse_endpoint(struct libusb_context *ctx,
struct libusb_endpoint_descriptor *endpoint, unsigned char *buffer,
int size, int host_endian)
@@ -83,7 +140,7 @@ static int parse_endpoint(struct libusb_context *ctx,
unsigned char *extra;
unsigned char *begin;
int parsed = 0;
- int len;
+ int len, retval;
usbi_parse_descriptor(buffer, "bb", &header, 0);
@@ -109,6 +166,26 @@ static int parse_endpoint(struct libusb_context *ctx,
size -= header.bLength;
parsed += header.bLength;
+ /* check if we have a superspeed companion descriptor */
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
+ if (header.bDescriptorType == LIBUSB_DT_SS_ENDPOINT_COMPANION) {
+ endpoint->ss_endpoint_companion = (struct
libusb_ss_endpoint_companion_descriptor *)
+ malloc(sizeof(struct
libusb_ss_endpoint_companion_descriptor));
+ if (!endpoint->ss_endpoint_companion) {
+ usbi_err(ctx, "couldn't allocate memory for endpoint
companion");
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ memset(endpoint->ss_endpoint_companion, 0, sizeof(struct
libusb_ss_endpoint_companion_descriptor));
+ retval = parse_ss_endpoint_companion(ctx,
endpoint->ss_endpoint_companion, buffer, size);
+ if (retval < 0)
+ return retval;
+
+ buffer += retval;
+ parsed += retval;
+ size -= retval;
+ }
+
/* Skip over the rest of the Class Specific or Vendor Specific */
/* descriptors */
begin = buffer;
@@ -124,7 +201,8 @@ static int parse_endpoint(struct libusb_context *ctx,
if ((header.bDescriptorType == LIBUSB_DT_ENDPOINT) ||
(header.bDescriptorType == LIBUSB_DT_INTERFACE)
||
(header.bDescriptorType == LIBUSB_DT_CONFIG) ||
- (header.bDescriptorType == LIBUSB_DT_DEVICE))
+ (header.bDescriptorType == LIBUSB_DT_DEVICE) ||
+ (header.bDescriptorType ==
LIBUSB_DT_SS_ENDPOINT_COMPANION))
break;
usbi_dbg("skipping descriptor %x", header.bDescriptorType);
@@ -241,7 +319,8 @@ static int parse_interface(libusb_context *ctx,
if ((header.bDescriptorType == LIBUSB_DT_INTERFACE) ||
(header.bDescriptorType ==
LIBUSB_DT_ENDPOINT) ||
(header.bDescriptorType ==
LIBUSB_DT_CONFIG) ||
- (header.bDescriptorType ==
LIBUSB_DT_DEVICE))
+ (header.bDescriptorType ==
LIBUSB_DT_DEVICE) ||
+ (header.bDescriptorType ==
LIBUSB_DT_SS_ENDPOINT_COMPANION))
break;
buffer += header.bLength;
@@ -397,7 +476,8 @@ static int parse_configuration(struct libusb_context *ctx,
if ((header.bDescriptorType == LIBUSB_DT_ENDPOINT) ||
(header.bDescriptorType ==
LIBUSB_DT_INTERFACE) ||
(header.bDescriptorType ==
LIBUSB_DT_CONFIG) ||
- (header.bDescriptorType ==
LIBUSB_DT_DEVICE))
+ (header.bDescriptorType ==
LIBUSB_DT_DEVICE) ||
+ (header.bDescriptorType ==
LIBUSB_DT_SS_ENDPOINT_COMPANION))
break;
usbi_dbg("skipping descriptor 0x%x\n",
header.bDescriptorType);
@@ -437,6 +517,24 @@ err:
return r;
}
+int usbi_device_cache_descriptor(libusb_device *dev)
+{
+ int r, host_endian;
+
+ r = usbi_backend->get_device_descriptor(dev, (unsigned char *)
&dev->device_descriptor, &host_endian);
+ if (r < 0)
+ return r;
+
+ if (!host_endian) {
+ dev->device_descriptor.bcdUSB =
libusb_le16_to_cpu(dev->device_descriptor.bcdUSB);
+ dev->device_descriptor.idVendor =
libusb_le16_to_cpu(dev->device_descriptor.idVendor);
+ dev->device_descriptor.idProduct =
libusb_le16_to_cpu(dev->device_descriptor.idProduct);
+ dev->device_descriptor.bcdDevice =
libusb_le16_to_cpu(dev->device_descriptor.bcdDevice);
+ }
+
+ return LIBUSB_SUCCESS;
+}
+
/** \ingroup desc
* Get the USB device descriptor for a given device.
*
@@ -449,22 +547,9 @@ err:
int API_EXPORTED libusb_get_device_descriptor(libusb_device *dev,
struct libusb_device_descriptor *desc)
{
- unsigned char raw_desc[DEVICE_DESC_LENGTH];
- int host_endian = 0;
- int r;
-
usbi_dbg("");
- r = usbi_backend->get_device_descriptor(dev, raw_desc, &host_endian);
- if (r < 0)
- return r;
-
- memcpy((unsigned char *) desc, raw_desc, sizeof(raw_desc));
- if (!host_endian) {
- desc->bcdUSB = libusb_le16_to_cpu(desc->bcdUSB);
- desc->idVendor = libusb_le16_to_cpu(desc->idVendor);
- desc->idProduct = libusb_le16_to_cpu(desc->idProduct);
- desc->bcdDevice = libusb_le16_to_cpu(desc->bcdDevice);
- }
+ memcpy((unsigned char *) desc, (unsigned char *)
&dev->device_descriptor,
+ sizeof (dev->device_descriptor));
return 0;
}
@@ -743,3 +828,149 @@ int API_EXPORTED
libusb_get_string_descriptor_ascii(libusb_device_handle *dev,
data[di] = 0;
return di;
}
+
+static int parse_bos(struct libusb_context *ctx, struct libusb_bos_descriptor
**bos,
+ unsigned char *bos_raw, int host_endian)
+{
+ struct libusb_bos_descriptor *bos_desc;
+ int i;
+ uint16_t len, parse_len;
+
+ bos_desc = calloc (1, sizeof (*bos_desc));
+ if (!bos_desc) {
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ usbi_parse_descriptor(bos_raw, "bbwb", bos_desc, 0);
+ bos_raw += LIBUSB_DT_BOS_SIZE;
+ len = bos_desc->wTotalLength;
+ parse_len = LIBUSB_DT_BOS_SIZE;
+
+ /* Get the device capability descriptors */
+ for (i = 0; i < bos_desc->bNumDeviceCaps; ++i) {
+ if (bos_raw[2] == LIBUSB_BT_USB_2_0_EXTENSION) {
+ parse_len += LIBUSB_BT_USB_2_0_EXTENSION_SIZE;
+ if (len < parse_len) {
+ libusb_free_bos_descriptor(bos_desc);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+ if (!bos_desc->usb_2_0_extension) {
+ bos_desc->usb_2_0_extension =
+ (struct
libusb_usb_2_0_extension_descriptor *)
+
malloc(sizeof(*bos_desc->usb_2_0_extension));
+ usbi_parse_descriptor(bos_raw, "bbbd",
bos_desc->usb_2_0_extension, host_endian);
+ } else
+ usbi_warn(ctx, "usb_2_0_extension was already
allocated");
+ bos_raw += LIBUSB_BT_USB_2_0_EXTENSION_SIZE;
+ } else if (bos_raw[2] == LIBUSB_BT_SS_USB_DEV_CAP) {
+ parse_len += LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE;
+ if (len < parse_len) {
+ libusb_free_bos_descriptor(bos_desc);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+ if (!bos_desc->ss_usb_dev_cap) {
+ bos_desc->ss_usb_dev_cap =
+ (struct
libusb_ss_usb_device_capability_descriptor *)
+
malloc(sizeof(*bos_desc->ss_usb_dev_cap));
+ usbi_parse_descriptor(bos_raw, "bbbbwbbw",
bos_desc->ss_usb_dev_cap, host_endian);
+ } else
+ usbi_warn(ctx, "ss_usb_dev_cap was already
allocated");
+ bos_raw += LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE;
+ } else if (bos_raw[2] == LIBUSB_BT_CONTAINER_ID) {
+ parse_len += LIBUSB_BT_CONTAINER_ID_SIZE;
+ if (len < parse_len) {
+ libusb_free_bos_descriptor(bos_desc);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+ if (!bos_desc->container_id) {
+ bos_desc->container_id =
+ (struct libusb_container_id_descriptor
*)
+ malloc(sizeof(*bos_desc->container_id));
+ usbi_parse_descriptor(bos_raw, "bbbbu",
bos_desc->container_id, host_endian);
+ } else
+ usbi_warn(ctx, "container_id was already
allocated");
+ bos_raw += LIBUSB_BT_CONTAINER_ID_SIZE;
+ } else {
+ usbi_info(ctx, "wireless/unhandled BOS sub-descriptor");
+ if (len < parse_len+1) {
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
+ parse_len += bos_raw[0];
+ /* move to the next device capability descriptor */
+ bos_raw += bos_raw[0];
+ }
+ }
+
+ *bos = bos_desc;
+
+ return LIBUSB_SUCCESS;
+}
+
+/** \ingroup desc
+ * Get a Binary Object Store (BOS) descriptor
+ * This is a BLOCKING function, which will send requests to the device.
+ *
+ * \param handle, the handle of an open libusb device
+ * \param bos output location for the BOS descriptor. Only valid if 0 was
returned.
+ * Must be freed with \ref libusb_free_bos_descriptor() after use.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the device doesn't have a BOS descriptor
+ * \returns another LIBUSB_ERROR code on error
+ */
+int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *handle,
struct libusb_bos_descriptor **bos)
+{
+ uint8_t bos_header[LIBUSB_DT_BOS_SIZE] = {0};
+ int r, bos_size, host_endian = 0;
+ unsigned char* bos_data = NULL;
+
+ /* Read the BOS. This generates 2 requests on the bus, one for the
header, and one for the full BOS */
+ r = libusb_get_descriptor(handle, LIBUSB_DT_BOS, 0, bos_header,
sizeof(bos_header));
+ bos_size = (bos_header[3]<<8) + bos_header[2];
+ if ((r >= sizeof(bos_header)) && (bos_size >= sizeof(bos_header))) {
+ usbi_dbg("found BOS descriptor: size %d bytes, %d
capabilities", bos_size, bos_header[4]);
+ bos_data = calloc(bos_size, 1);
+ if (bos_data == NULL) {
+ return LIBUSB_ERROR_NO_MEM;
+ }
+ r = libusb_get_descriptor(handle, LIBUSB_DT_BOS, 0, bos_data,
bos_size);
+ if (r == bos_size) {
+ r = parse_bos(handle->dev->ctx, bos, bos_data,
host_endian);
+ } else {
+ usbi_err(handle->dev->ctx, "failed to read BOS (%d)",
r);
+ r = LIBUSB_ERROR_IO;
+ }
+ } else {
+ r = LIBUSB_ERROR_NOT_FOUND;
+ }
+
+ if (bos_data)
+ free(bos_data);
+ return r;
+}
+
+/** \ingroup desc
+ * Free a BOS descriptor obtained from libusb_get_bos_descriptor().
+ * It is safe to call this function with a NULL config parameter, in which
+ * case the function simply returns.
+ *
+ * \param bos the BOS descriptor to free
+ */
+void API_EXPORTED libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos)
+{
+ if (bos == NULL)
+ return;
+
+ if (bos->usb_2_0_extension) {
+ free(bos->usb_2_0_extension);
+ }
+
+ if (bos->ss_usb_dev_cap) {
+ free(bos->ss_usb_dev_cap);
+ }
+
+ if (bos->container_id) {
+ free(bos->container_id);
+ }
+
+ free(bos);
+}
diff --git a/libusb/libusb-1.0.def b/libusb/libusb-1.0.def
index 3aba343..e6b8910 100644
--- a/libusb/libusb-1.0.def
+++ b/libusb/libusb-1.0.def
@@ -26,6 +26,8 @@ EXPORTS
libusb_event_handling_ok@4 = libusb_event_handling_ok
libusb_exit
libusb_exit@4 = libusb_exit
+ libusb_free_bos_descriptor
+ libusb_free_bos_descriptor@4 = libusb_free_bos_descriptor
libusb_free_config_descriptor
libusb_free_config_descriptor@4 = libusb_free_config_descriptor
libusb_free_device_list
@@ -34,6 +36,8 @@ EXPORTS
libusb_free_transfer@4 = libusb_free_transfer
libusb_get_active_config_descriptor
libusb_get_active_config_descriptor@8 = libusb_get_active_config_descriptor
+ libusb_get_bos_descriptor
+ libusb_get_bos_descriptor@8 = libusb_get_bos_descriptor
libusb_get_bus_number
libusb_get_bus_number@4 = libusb_get_bus_number
libusb_get_config_descriptor
diff --git a/libusb/libusb.h b/libusb/libusb.h
index 13fb9a6..e42d381 100644
--- a/libusb/libusb.h
+++ b/libusb/libusb.h
@@ -253,6 +253,12 @@ enum libusb_descriptor_type {
/** Endpoint descriptor. See libusb_endpoint_descriptor. */
LIBUSB_DT_ENDPOINT = 0x05,
+ /** BOS descriptor */
+ LIBUSB_DT_BOS = 0x0f,
+
+ /** Device Capability descriptor */
+ LIBUSB_DT_DEVICE_CAPABILITY = 0x10,
+
/** HID descriptor */
LIBUSB_DT_HID = 0x21,
@@ -266,16 +272,32 @@ enum libusb_descriptor_type {
LIBUSB_DT_HUB = 0x29,
/** SuperSpeed Hub descriptor */
- LIBUSB_DT_SUPERSPEED_HUB = 0x2A,
+ LIBUSB_DT_SUPERSPEED_HUB = 0x2a,
+
+ /** SuperSpeed Endpoint Companion descriptor */
+ LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30
};
/* Descriptor sizes per descriptor type */
#define LIBUSB_DT_DEVICE_SIZE 18
#define LIBUSB_DT_CONFIG_SIZE 9
#define LIBUSB_DT_INTERFACE_SIZE 9
-#define LIBUSB_DT_ENDPOINT_SIZE 7
+#define LIBUSB_DT_ENDPOINT_SIZE 7
#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */
#define LIBUSB_DT_HUB_NONVAR_SIZE 7
+#define LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE 6
+#define LIBUSB_DT_BOS_SIZE 5
+
+/* BOS descriptor sizes */
+#define LIBUSB_BT_USB_2_0_EXTENSION_SIZE 7
+#define LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE 10
+#define LIBUSB_BT_CONTAINER_ID_SIZE 20
+
+/* We unwrap the BOS => define its max size */
+#define LIBUSB_DT_BOS_MAX_SIZE ((LIBUSB_DT_BOS_SIZE) +\
+ (LIBUSB_BT_USB_2_0_EXTENSION_SIZE)
+\
+
(LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) +\
+ (LIBUSB_BT_CONTAINER_ID_SIZE))
#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */
#define LIBUSB_ENDPOINT_DIR_MASK 0x80
@@ -490,6 +512,38 @@ struct libusb_device_descriptor {
};
/** \ingroup desc
+ * A structure representing the superspeed endpoint companion
+ * descriptor. This descriptor is documented in section 9.6.7 of
+ * the USB 3.0 specification. All multiple-byte fields are represented in
+ * host-endian format.
+ */
+struct libusb_ss_endpoint_companion_descriptor {
+
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_SS_ENDPOINT_COMPANION in
+ * this context. */
+ uint8_t bDescriptorType;
+
+
+ /** The maximum number of packets the endpoint can send or
+ * recieve as part of a burst. */
+ uint8_t bMaxBurst;
+
+ /** In bulk EP: bits 4:0 represents the maximum number of
+ * streams the EP supports. In isochronous EP: bits 1:0
+ * represents the Mult - a zero based value that determines
+ * the maximum number of packets within a service interval */
+ uint8_t bmAttributes;
+
+ /** The total number of bytes this EP will transfer every
+ * service interval. valid only for periodic EPs. */
+ uint16_t wBytesPerInterval;
+};
+
+/** \ingroup desc
* A structure representing the standard USB endpoint descriptor. This
* descriptor is documented in section 9.6.6 of the USB 3.0 specification.
* All multiple-byte fields are represented in host-endian format.
@@ -531,6 +585,9 @@ struct libusb_endpoint_descriptor {
/** For audio devices only: the address if the synch endpoint */
uint8_t bSynchAddress;
+ /** SuperSpeed Endpoint Companion descriptor */
+ struct libusb_ss_endpoint_companion_descriptor *ss_endpoint_companion;
+
/** Extra descriptors. If libusbx encounters unknown endpoint
descriptors,
* it will store them here, should you wish to parse them. */
const unsigned char *extra;
@@ -581,6 +638,9 @@ struct libusb_interface_descriptor {
* by the bNumEndpoints field. */
const struct libusb_endpoint_descriptor *endpoint;
+ /** The endpoint companion descriptor */
+ struct libusb_endpoint_companion_descriptor *ep_comp;
+
/** Extra descriptors. If libusbx encounters unknown interface
descriptors,
* it will store them here, should you wish to parse them. */
const unsigned char *extra;
@@ -647,6 +707,134 @@ struct libusb_config_descriptor {
int extra_length;
};
+/** \ingroup desc
+ * A structure representing the Binary Device Object Store (BOS) descriptor.
+ * This descriptor is documented in section 9.6.2 of the USB 3.0 specification.
+ * All multiple-byte fields, except UUIDs, are represented in host-endian
format.
+ */
+struct libusb_bos_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_BOS LIBUSB_DT_BOS
+ * in this context. */
+ uint8_t bDescriptorType;
+
+ /** Length of this descriptor and all of its sub descriptors */
+ uint16_t wTotalLength;
+
+ /** The number of separate device capability descriptors in
+ * the BOS */
+ uint8_t bNumDeviceCaps;
+
+ /* The following assumes that only one descriptor of the following
+ * types will ever be provided by the device for each endpoint... */
+
+ /** USB 2.0 Extension descriptor */
+ struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension;
+
+ /** SuperSpeed USB Device Capability descriptor */
+ struct libusb_ss_usb_device_capability_descriptor *ss_usb_dev_cap;
+
+ /** USB 3.0 Container ID descriptor */
+ struct libusb_container_id_descriptor *container_id;
+};
+
+/** \ingroup desc
+ * A structure representing the USB 2.0 Extension descriptor
+ * This descriptor is documented in section 9.6.2.1 of the USB 3.0
specification.
+ * All multiple-byte fields are represented in host-endian format.
+ */
+struct libusb_usb_2_0_extension_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY
+ * LIBUSB_DT_DEVICE_CAPABILITY in this context. */
+ uint8_t bDescriptorType;
+
+ /** Capability type. Will have value
+ * \ref libusb_capability_type::LIBUSB_BT_USB_2_0_EXTENSION
+ * LIBUSB_BT_USB_2_0_EXTENSION in this context. */
+ uint8_t bDevCapabilityType;
+
+ /** Bitmap encoding of supported device level features.
+ * A value of one in a bit location indicates a feature is
+ * supported; a value of zero indicates it is not supported.
+ * See \ref libusb_capability_attributes. */
+ uint32_t bmAttributes;
+};
+
+/** \ingroup desc
+ * A structure representing the SuperSpeed USB Device Capability descriptor
+ * This descriptor is documented in section 9.6.2.2 of the USB 3.0
specification.
+ * All multiple-byte fields are represented in host-endian format.
+ */
+struct libusb_ss_usb_device_capability_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY
+ * LIBUSB_DT_DEVICE_CAPABILITY in this context. */
+ uint8_t bDescriptorType;
+
+ /** Capability type. Will have value
+ * \ref libusb_capability_type::LIBUSB_BT_SS_USB_DEV_CAP
+ * LIBUSB_BT_SS_USB_DEV_CAP in this context. */
+ uint8_t bDevCapabilityType;
+
+ /** Bitmap encoding of supported device level features.
+ * A value of one in a bit location indicates a feature is
+ * supported; a value of zero indicates it is not supported.
+ * See \ref libusb_capability_attributes. */
+ uint8_t bmAttributes;
+
+ /** Bitmap encoding of the speed supported by this device when
+ * operating in SuperSpeed mode. See \ref libusb_supported_speed. */
+ uint16_t wSpeedSupported;
+
+ /** The lowest speed at which all the functionality supported
+ * by the device is available to the user. For example if the
+ * device supports all its functionality when connected at
+ * full speed and above then it sets this value to 1. */
+ uint8_t bFunctionalitySupport;
+
+ /** U1 Device Exit Latency. */
+ uint8_t bU1DevExitLat;
+
+ /** U2 Device Exit Latency. */
+ uint16_t bU2DevExitLat;
+};
+
+/** \ingroup desc
+ * A structure representing the Container ID descriptor.
+ * This descriptor is documented in section 9.6.2.3 of the USB 3.0
specification.
+ * All multiple-byte fields are represented in host-endian format.
+ */
+struct libusb_container_id_descriptor {
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY
+ * LIBUSB_DT_DEVICE_CAPABILITY in this context. */
+ uint8_t bDescriptorType;
+
+ /** Capability type. Will have value
+ * \ref libusb_capability_type::LIBUSB_BT_CONTAINER_ID
+ * LIBUSB_BT_CONTAINER_ID in this context. */
+ uint8_t bDevCapabilityType;
+
+ /** Reserved field */
+ uint8_t bReserved;
+
+ /** 128 bit UUID */
+ uint8_t ContainerID[16];
+};
+
/** \ingroup asyncio
* Setup packet for control transfers. */
struct libusb_control_setup {
@@ -773,6 +961,48 @@ enum libusb_speed {
LIBUSB_SPEED_SUPER = 4,
};
+/** \ingroup dev
+ * Supported speeds (wSpeedSupported) bitfield. Indicates what
+ * speeds the device supports.
+ */
+enum libusb_supported_speed {
+ /** Low speed operation supported (1.5MBit/s). */
+ LIBUSB_LOW_SPEED_OPERATION = 1,
+
+ /** Full speed operation supported (12MBit/s). */
+ LIBUSB_FULL_SPEED_OPERATION = 2,
+
+ /** High speed operation supported (480MBit/s). */
+ LIBUSB_HIGH_SPEED_OPERATION = 4,
+
+ /** Superspeed operation supported (5000MBit/s). */
+ LIBUSB_SUPER_SPEED_OPERATION = 8,
+};
+
+/** \ingroup dev
+ * Capability attributes
+ */
+enum libusb_bos_attributes {
+ /** Supports Link Power Management (LPM) */
+ LIBUSB_BA_LPM_SUPPORT = 2,
+};
+
+/** \ingroup dev
+ * USB capability types
+ */
+enum libusb_bos_type {
+ /* Wireless USB is ignored */
+
+ /** USB 2.0 extensions */
+ LIBUSB_BT_USB_2_0_EXTENSION = 2,
+
+ /** SuperSpeed capability */
+ LIBUSB_BT_SS_USB_DEV_CAP = 3,
+
+ /** Container ID type */
+ LIBUSB_BT_CONTAINER_ID = 4
+};
+
/** \ingroup misc
* Error codes. Most libusbx functions return 0 on success or one of these
* codes on failure.
@@ -1051,6 +1281,8 @@ int LIBUSB_CALL
libusb_get_config_descriptor_by_value(libusb_device *dev,
uint8_t bConfigurationValue, struct libusb_config_descriptor **config);
void LIBUSB_CALL libusb_free_config_descriptor(
struct libusb_config_descriptor *config);
+int LIBUSB_CALL libusb_get_bos_descriptor(libusb_device_handle *handle, struct
libusb_bos_descriptor **bos);
+void LIBUSB_CALL libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos);
uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev);
uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev);
libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev);
diff --git a/libusb/libusbi.h b/libusb/libusbi.h
index ed95d43..5f6c295 100644
--- a/libusb/libusbi.h
+++ b/libusb/libusbi.h
@@ -286,6 +286,9 @@ struct libusb_device {
struct list_head list;
unsigned long session_data;
+
+ struct libusb_device_descriptor device_descriptor;
+
unsigned char os_priv[0];
};
@@ -390,8 +393,9 @@ int usbi_handle_transfer_completion(struct usbi_transfer
*itransfer,
enum libusb_transfer_status status);
int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer);
-int usbi_parse_descriptor(unsigned char *source, const char *descriptor,
+int usbi_parse_descriptor(const unsigned char *source, const char *descriptor,
void *dest, int host_endian);
+int usbi_device_cache_descriptor(libusb_device *dev);
int usbi_get_config_index_by_value(struct libusb_device *dev,
uint8_t bConfigurationValue, int *idx);
diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c
index e0d2a1c..9c0888a 100644
--- a/libusb/os/windows_usb.c
+++ b/libusb/os/windows_usb.c
@@ -1170,6 +1170,8 @@ static int init_device(struct libusb_device* dev, struct
libusb_device* parent_d
force_hcd_device_descriptor(dev);
}
+ usbi_sanitize_device(dev);
+
usbi_dbg("(bus: %d, addr: %d, depth: %d, port: %d): '%s'",
dev->bus_number, dev->device_address, priv->depth, priv->port,
device_id);
--
1.8.0.msysgit.0
------------------------------------------------------------------------------
Learn Graph Databases - Download FREE O'Reilly Book
"Graph Databases" is the definitive new guide to graph databases and
their applications. This 200-page book is written by three acclaimed
leaders in the field. The early access version is available now.
Download your free book today! http://p.sf.net/sfu/neotech_d2d_may
_______________________________________________
libusbx-devel mailing list
libusbx-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/libusbx-devel