And with the previous bunch of patches out of the way, we can move
further on the 1.0.12 agenda, with Trac #15 [1] (or old libusb #110 [2]).
If you look at section 9.6 of the USB 3.0 specs (Standard USB
Descriptors), you'll see that it lists the Binary Device Object Store
(BOS) and SuperSpeed Endpoint Companion as a new set of descriptors that
didn't exist in the USB 2.0 specs (well technically, the USB 2.0
Extension Descriptor did exist but let's not try to cut hairs here).
Because we provide a generic library, that we we want to support the
retrieval of those ASAP is a given.
The good thing is that a patch was sent to libusb to do some of that,
exactly a year ago...
The bad thing, of course, is that it was sent to libusb, which means
that, here we are, one frigging year later, only starting to look at it...
For now, please be mindful that the attached is a preliminary patch,
with some missing bits and the expectation that the final version will
have a few changes. Still, if you test on Windows, it should produce the
following xusb output against an USB 3.0 device:
--------------------------------------------------------------------
D:\libusbx\x64\Debug\examples>xusb 125f:312a
Using libusbx v1.0.11.10520
Opening device...
bus: 1, port path from HCD: 2
speed: 5000 Mbit/s (USB 3.0 SuperSpeed)
Reading device descriptor:
length: 18
device class: 0
S/N: 3
VID:PID: 125F:312A
bcdDevice: 0A00
iMan:iProd:iSer: 1:2:3
nb confs: 1
Reading BOS Descriptor:
00000000 05 0f 16 00 02 07 10 02 02 00 00 00 0a 10 03 00
00000010 0e 00 01 0a 20 00
Reading configuration descriptors:
nb interfaces: 1
interface[0]: id = 0
interface[0].altsetting[0]: num endpoints = 2
Class.SubClass.Protocol: 08.06.50
endpoint[0].address: 02
max packet size: 0400
polling interval: 00
bMaxBurst: 0F
bmAttributes: 00
wBytesPerInterval: 0000
endpoint[1].address: 81
max packet size: 0400
polling interval: 00
bMaxBurst: 0F
bmAttributes: 00
wBytesPerInterval: 0000
Claiming interface 0...
Reading string descriptors:
String (0x01): "ADATA"
String (0x02): "ADATA USB Flash Drive"
String (0x03): "09021000000000000000000999"
(...)
--------------------------------------------------------------------
The interesting bits from the above are of course the BOS and the
bMaxBurst, bmAttributes and wBytesPerInterval attributes, which come
from the SS EP companion. Unlike BOS (see below) SS EP companion support
should work by default on all supported platforms.
Apart from formatting and updating some of the macro names, this is
mostly what I changed from the original patch:
- add libusb_get_bos_descriptor() and libusb_free_bos_descriptor() calls
as well as their implementation for the Windows backend
- drop the libusb_parse_bos_desc() call
- use an open byte array for the BOS data rather than:
struct libusb_usb_ext_cap_descriptor *usb_ext_cap;
struct libusb_ss_usb_cap_descriptor *ss_usb_cap;
Note that we'll probably need a BOS parser one way or another, but I
think we first should sort out raw BOS retrieval on most platforms
before doing that (though endian-agnocism may change the deal - see below).
But let's cut to the chase: here are the various issues we still need to
look into:
1. BOS retrieval requires a specific backend implementation. We are
currently missing Linux, OS-X and *BSD (though I won't really have an
issue going to 1.0.12 without OS-X and *BSD. If we can't have Linux
however, I'll delay it to 1.0.13). Note that I can't do much for OS-X
since I don't have USB 3.0 support on my test platform there, and I
would also very much prefer if somebody else could take over Linux.
2. As with other descriptors, the expectation is that the BOS are cached
during enum. Because we don't have good visibility on the backend
implementations I preferred playing it safe and doing the caching in the
backend private structure for the device. However it's likely that
caching can be done in the global device structure, with
libusb_get_bos_descriptor() factorized across platform as well.
3. As indicated above, we follow the USB 3.0 specs (9.6.2) and therefore
try to be as generic and future-proof as possible with regards to the
Device Capability Descriptors, by storing a DevCap data array that can
be any size. This is different from the original patch which proposed
limiting ourselves to filling known DevCaps structs. What this means
however is that it adds a need for a libusb_free_bos_descriptor() call,
and using a raw byte array may very well come bite us with regards to
endianness support, as we're supposed to always return data in the host
endian format. But the latter basically means we're supposed to know how
the data is laid out everywhere, and re-endianize it if needed before
serving it back...
Now, my thinking would be that, if we a use public parsing functions to
populate the SS Dev Caps or USB 2.0 Ext descriptor, or any other DevCap
we know, we can easily straighten endianness here, from a semi-raw BOS
descriptor obtained with libusb_get_bos_descriptor(), so I don't see it
as a big deal. And for what is worth, in case anybody want to start
crying foul with regards to this approach, this isn't really a new issue
we would have in the library - If you look at the
libusb_endpoint_descriptor struct from libusb.h, you'll see that the
library already stores any extra endpoint data as a byte array of
unknown format (and therefore unknown endianness), so not much different
from what we'd be doing for unprocessed DevCaps data.
With BOS clearly established by the USB committee as the generic way to
provide additional descriptors, I think it's fair to expect DevCaps to
be expanded in the future, so leaving libusbx with the ability to
provide unprocessed BOS data, rather than limit ourselves to only the
DevCaps we know how to handle makes a lot of sense to me. Instead, we
can just add another parsing function for a new DevCap when the need
arises and get the both of both worlds.
4. With regards to zero sized arrays, we're already using them in struct
libusb_transfer of libusb.h, so I don't expect much of an issue.
Francesco once expressed concerns about using a zero sized array rather
than a pointer [3], but I think the benefit outweighs the drawback,
especially as we can then serve a BOS descriptor that is the same as
what is described in the specs.
Note: I'm CC'ing Maya Erez, the original author of the patch, as well as
Amit Blay, who participated in our discussion at the time. Maya, Amit,
please be mindful that libusbx is a fork of libusb (and one of the
reasons we forked is precisely so that we got tired of seeing
contributions such as yours falling by the wayside), and that you may
have to subscribe to this mailing list to be able to reply. Please see
https://lists.sourceforge.net/lists/listinfo/libusbx-devel or
http://libusbx.org for more info.
Regards,
/Pete
[1] https://sourceforge.net/apps/trac/libusbx/ticket/15
[2] http://www.libusb.org/ticket/110
[3]
http://libusb.6.n5.nabble.com/msvc-warning-in-libusb-h-about-zero-sized-array-td2730.html
>From f688d97409e83fceb436c3d0025710bcd0b78c88 Mon Sep 17 00:00:00 2001
From: Pete Batard <[email protected]>
Date: Mon, 4 Jun 2012 01:13:51 +0100
Subject: [PATCH] Core: Add support for BOS and Endpoint Companion USB 3.0
descriptors
* Adds new libusb_get_bos_descriptor and libusb_free_bos_descriptor
calls to the API
* Original patch by Maya Erez <[email protected]>
* BOS readout currently limited to Windows backend only
---
examples/xusb.c | 13 ++++++
libusb/descriptor.c | 107 +++++++++++++++++++++++++++++++++++++++++++++--
libusb/libusb-1.0.def | 4 ++
libusb/libusb.h | 78 +++++++++++++++++++++++++++++++++-
libusb/libusbi.h | 22 ++++++++++
libusb/os/darwin_usb.c | 6 +++
libusb/os/linux_usbfs.c | 8 ++++
libusb/os/openbsd_usb.c | 10 +++++
libusb/os/windows_usb.c | 95 +++++++++++++++++++++++++++++++++++++++++
libusb/os/windows_usb.h | 37 ++++++++++++++++
10 files changed, 376 insertions(+), 6 deletions(-)
diff --git a/examples/xusb.c b/examples/xusb.c
index c158727..06ef3a6 100644
--- a/examples/xusb.c
+++ b/examples/xusb.c
@@ -734,6 +734,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;
@@ -785,6 +786,12 @@ static int test_device(uint16_t vid, uint16_t pid)
string_index[1] = dev_desc.iProduct;
string_index[2] = dev_desc.iSerialNumber;
+ if (libusb_get_bos_descriptor(dev, &bos_desc) == LIBUSB_SUCCESS) {
+ printf("\nReading BOS Descriptor:");
+ display_buffer_hex((unsigned char*)bos_desc,
bos_desc->wTotalLength);
+ libusb_free_bos_descriptor(bos_desc);
+ }
+
printf("\nReading configuration descriptors:\n");
CALL_CHECK(libusb_get_config_descriptor(dev, 0, &conf_desc));
nb_ifaces = conf_desc->bNumInterfaces;
@@ -823,6 +830,12 @@ 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);
+ // Endpoint Companion
+ if (endpoint->ep_comp) {
+ printf(" bMaxBurst:
%02X\n", endpoint->ep_comp->bMaxBurst);
+ printf(" bmAttributes:
%02X\n", endpoint->ep_comp->bmAttributes);
+ printf(" wBytesPerInterval:
%04X\n", endpoint->ep_comp->wBytesPerInterval);
+ }
}
}
}
diff --git a/libusb/descriptor.c b/libusb/descriptor.c
index 590d1dc..5e27549 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"
@@ -75,6 +76,36 @@ static void clear_endpoint(struct libusb_endpoint_descriptor
*endpoint)
free((unsigned char *) endpoint->extra);
}
+static int parse_endpoint_comp(struct libusb_context *ctx,
+ struct libusb_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);
+
+ /* Everything should be fine being passed into here, but we sanity
check JIC */
+ if (header.bLength > size) {
+ usbi_err(ctx, "ran out of descriptors parsing");
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ if (header.bDescriptorType != LIBUSB_DT_ENDPOINT_COMPANION) {
+ usbi_err(ctx, "unexpected descriptor %x (expected %x)",
+ header.bDescriptorType, LIBUSB_DT_ENDPOINT_COMPANION);
+ return parsed;
+ }
+
+ if (header.bLength >= LIBUSB_DT_ENDPOINT_COMP_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 +114,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 +140,26 @@ static int parse_endpoint(struct libusb_context *ctx,
size -= header.bLength;
parsed += header.bLength;
+ /* check if we have a companion descriptor */
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
+ if (header.bDescriptorType == LIBUSB_DT_ENDPOINT_COMPANION) {
+ endpoint->ep_comp = (struct
libusb_endpoint_companion_descriptor *)
+ malloc(sizeof(struct
libusb_endpoint_companion_descriptor));
+ if (!endpoint->ep_comp) {
+ usbi_err(ctx, "couldn't allocate memory for endpoint
companion");
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ memset(endpoint->ep_comp, 0, sizeof(struct
libusb_endpoint_companion_descriptor));
+ retval = parse_endpoint_comp(ctx, endpoint->ep_comp, 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 +175,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_ENDPOINT_COMPANION))
break;
usbi_dbg("skipping descriptor %x", header.bDescriptorType);
@@ -235,7 +287,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_ENDPOINT_COMPANION))
break;
buffer += header.bLength;
@@ -383,7 +436,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_ENDPOINT_COMPANION))
break;
usbi_dbg("skipping descriptor 0x%x\n",
header.bDescriptorType);
@@ -455,6 +509,51 @@ int API_EXPORTED
libusb_get_device_descriptor(libusb_device *dev,
}
/** \ingroup desc
+ * Get the USB 3.0 BOS descriptor for a given device.
+ *
+ * This is a non-blocking function; the BOS descriptor is cached in memory.
+ *
+ * \param dev the device
+ * \param desc output location for the USB BOS descriptor. Only
+ * valid if 0 was returned. Must be freed with libusb_free_bos_descriptor()
+ * after use.
+ * \returns 0 on success or a LIBUSB_ERROR code on failure
+ */
+int API_EXPORTED libusb_get_bos_descriptor(libusb_device *dev,
+ struct libusb_bos_descriptor **bos)
+{
+ int host_endian = 0;
+ int r;
+
+ usbi_dbg("");
+ r = usbi_backend->get_bos_descriptor(dev, bos, &host_endian);
+ if (r < 0)
+ return r;
+
+ if (!host_endian) {
+ (*bos)->wTotalLength = libusb_le16_to_cpu((*bos)->wTotalLength);
+ }
+ return 0;
+}
+
+/** \ingroup desc
+ * Free a configuration descriptor obtained from
+ * libusb_get_active_config_descriptor() or libusb_get_config_descriptor().
+ * It is safe to call this function with a NULL config parameter, in which
+ * case the function simply returns.
+ *
+ * \param config the configuration descriptor to free
+ */
+void API_EXPORTED libusb_free_bos_descriptor(
+ struct libusb_bos_descriptor *bos)
+{
+ if (!bos)
+ return;
+
+ free(bos);
+}
+
+/** \ingroup desc
* Get the USB configuration descriptor for the currently active configuration.
* This is a non-blocking function which does not involve any requests being
* sent to the device.
diff --git a/libusb/libusb-1.0.def b/libusb/libusb-1.0.def
index 847b785..0fc5245 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 fd231ea..ad1bd8a 100644
--- a/libusb/libusb.h
+++ b/libusb/libusb.h
@@ -229,6 +229,15 @@ enum libusb_descriptor_type {
/** Hub descriptor */
LIBUSB_DT_HUB = 0x29,
+
+ /** BOS descriptor */
+ LIBUSB_DT_BOS = 0x0f,
+
+ /** Device Capability descriptor */
+ LIBUSB_DT_DEVICE_CAPABILITY = 0x10,
+
+ /** SuperSpeed Endpoint Companion descriptor */
+ LIBUSB_DT_ENDPOINT_COMPANION = 0x30
};
/* Descriptor sizes per descriptor type */
@@ -238,6 +247,7 @@ enum libusb_descriptor_type {
#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_ENDPOINT_COMP_SIZE 6
#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */
#define LIBUSB_ENDPOINT_DIR_MASK 0x80
@@ -444,6 +454,32 @@ struct libusb_device_descriptor {
uint8_t bNumConfigurations;
};
+/* USB_DT_ENDPOINT_COMPANION: SuperSpeed Endpoint Companion descriptor */
+struct libusb_endpoint_companion_descriptor {
+
+ /** Size of this descriptor (in bytes) */
+ uint8_t bLength;
+
+ /** Descriptor type. Will have value
+ * \ref libusb_descriptor_type::LIBUSB_DT_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.3 of the USB 2.0 specification.
@@ -486,7 +522,10 @@ struct libusb_endpoint_descriptor {
/** For audio devices only: the address if the synch endpoint */
uint8_t bSynchAddress;
- /** Extra descriptors. If libusbx encounters unknown endpoint
descriptors,
+ /** The endpoint companion descriptor */
+ struct libusb_endpoint_companion_descriptor *ep_comp;
+
+ /** Extra descriptors. If libusb encounters unknown endpoint
descriptors,
* it will store them here, should you wish to parse them. */
const unsigned char *extra;
@@ -602,6 +641,39 @@ 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 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;
+
+ /** Total length of data returned for this configuration */
+ uint16_t wTotalLength;
+
+ /** The number of separate device capability descriptors in the BOS */
+ uint8_t bNumDeviceCaps;
+
+ /** device capability descriptors data. As per the USB 3.0 specs,
+ * can be one or a concatenation of Wireless USB, USB 2.0 Extension
+ * (9.6.2.1), SuperSpeed USB Device Capability (9.6.2.2) or Container
+ * ID for USB 3.0 hubs (9.6.2.3) */
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+ const unsigned char device_caps[]; /* valid C99 code */
+#else
+ const unsigned char device_caps[0]; /* non-standard, but usually
working code */
+#endif
+};
+
/** \ingroup asyncio
* Setup packet for control transfers. */
struct libusb_control_setup {
@@ -985,6 +1057,10 @@ int LIBUSB_CALL
libusb_get_configuration(libusb_device_handle *dev,
int *config);
int LIBUSB_CALL libusb_get_device_descriptor(libusb_device *dev,
struct libusb_device_descriptor *desc);
+int LIBUSB_CALL libusb_get_bos_descriptor(libusb_device *dev,
+ struct libusb_bos_descriptor **bos);
+void LIBUSB_CALL libusb_free_bos_descriptor(
+ struct libusb_bos_descriptor *bos);
int LIBUSB_CALL libusb_get_active_config_descriptor(libusb_device *dev,
struct libusb_config_descriptor **config);
int LIBUSB_CALL libusb_get_config_descriptor(libusb_device *dev,
diff --git a/libusb/libusbi.h b/libusb/libusbi.h
index 9de56a1..b188954 100644
--- a/libusb/libusbi.h
+++ b/libusb/libusbi.h
@@ -563,6 +563,28 @@ struct usbi_os_backend {
int (*get_device_descriptor)(struct libusb_device *device,
unsigned char *buffer, int *host_endian);
+ /* Retrieve the BOS descriptor from a device.
+ *
+ * The descriptor should be retrieved from memory, NOT via bus I/O to
the
+ * device. This means that you may have to cache it in a private
structure
+ * during get_device_list enumeration. Alternatively, you may be able
+ * to retrieve it from a kernel interface.
+ *
+ * Because a BOS descriptor is variable in size, this function is
required
+ * to allocate its own BOS buffer (which will then be freed by calling
+ * libusb_free_bos_descriptor).
+ *
+ * This function is called when the user requests to read the BOS
descriptor.
+ *
+ * This function is expected to return the descriptor in bus-endian
format
+ * (LE). If it returns the multi-byte values in host-endian format,
+ * set the host_endian output parameter to "1".
+ *
+ * Return 0 on success or a LIBUSB_ERROR code on failure.
+ */
+ int (*get_bos_descriptor)(struct libusb_device *device,
+ struct libusb_bos_descriptor **bos, int *host_endian);
+
/* Get the ACTIVE configuration descriptor for a device.
*
* The descriptor should be retrieved from memory, NOT via bus I/O to
the
diff --git a/libusb/os/darwin_usb.c b/libusb/os/darwin_usb.c
index ac67b35..a742f4a 100644
--- a/libusb/os/darwin_usb.c
+++ b/libusb/os/darwin_usb.c
@@ -460,6 +460,11 @@ static int darwin_get_device_descriptor(struct
libusb_device *dev, unsigned char
return 0;
}
+static int darwin_get_bos_descriptor(struct libusb_device *dev, struct
libusb_bos_descriptor **bos, int *host_endian) {
+
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
static int get_configuration_index (struct libusb_device *dev, int
config_value) {
struct darwin_device_priv *priv = (struct darwin_device_priv *)dev->os_priv;
UInt8 i, numConfig;
@@ -1770,6 +1775,7 @@ const struct usbi_os_backend darwin_backend = {
.exit = darwin_exit,
.get_device_list = darwin_get_device_list,
.get_device_descriptor = darwin_get_device_descriptor,
+ .get_bos_descriptor = darwin_get_bos_descriptor,
.get_active_config_descriptor = darwin_get_active_config_descriptor,
.get_config_descriptor = darwin_get_config_descriptor,
diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c
index a01fff8..54856e9 100644
--- a/libusb/os/linux_usbfs.c
+++ b/libusb/os/linux_usbfs.c
@@ -111,6 +111,7 @@ static int sysfs_has_descriptors = 0;
struct linux_device_priv {
char *sysfs_dir;
unsigned char *dev_descriptor;
+ struct libusb_bos_descriptor *bos_descriptor;
unsigned char *config_descriptor;
};
@@ -518,6 +519,12 @@ static int op_get_device_descriptor(struct libusb_device
*dev,
}
}
+static int op_get_bos_descriptor(struct libusb_device *dev,
+ struct libusb_bos_descriptor **bos, int *host_endian)
+{
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
static int usbfs_get_active_config_descriptor(struct libusb_device *dev,
unsigned char *buffer, size_t len)
{
@@ -2467,6 +2474,7 @@ const struct usbi_os_backend linux_usbfs_backend = {
.exit = NULL,
.get_device_list = op_get_device_list,
.get_device_descriptor = op_get_device_descriptor,
+ .get_bos_descriptor = op_get_bos_descriptor,
.get_active_config_descriptor = op_get_active_config_descriptor,
.get_config_descriptor = op_get_config_descriptor,
diff --git a/libusb/os/openbsd_usb.c b/libusb/os/openbsd_usb.c
index 88e2c7a..9755869 100644
--- a/libusb/os/openbsd_usb.c
+++ b/libusb/os/openbsd_usb.c
@@ -54,6 +54,8 @@ static void obsd_close(struct libusb_device_handle *);
static int obsd_get_device_descriptor(struct libusb_device *, unsigned char *,
int *);
+static int obsd_get_bos_descriptor(struct libusb_device *,
+ struct libusb_bos_descriptor **, int *);
static int obsd_get_active_config_descriptor(struct libusb_device *,
unsigned char *, size_t, int *);
static int obsd_get_config_descriptor(struct libusb_device *, uint8_t,
@@ -96,6 +98,7 @@ const struct usbi_os_backend openbsd_backend = {
obsd_close,
obsd_get_device_descriptor,
+ obsd_get_bos_descriptor,
obsd_get_active_config_descriptor,
obsd_get_config_descriptor,
@@ -253,6 +256,13 @@ obsd_get_device_descriptor(struct libusb_device *dev,
unsigned char *buf,
}
int
+obsd_get_bos_descriptor(struct libusb_device *dev, struct
libusb_bos_descriptor **bos,
+ int *host_endian)
+{
+ return (LIBUSB_ERROR_NOT_SUPPORTED);
+}
+
+int
obsd_get_active_config_descriptor(struct libusb_device *dev,
unsigned char *buf, size_t len, int *host_endian)
{
diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c
index dbebfaf..1893aa2 100644
--- a/libusb/os/windows_usb.c
+++ b/libusb/os/windows_usb.c
@@ -985,6 +985,84 @@ static int cache_config_descriptors(struct libusb_device
*dev, HANDLE hub_handle
}
/*
+ * fetch and cache all the BOS descriptors through I/O
+ */
+static int cache_bos_descriptors(struct libusb_device *dev, HANDLE hub_handle,
char* device_id)
+{
+ DWORD size, ret_size;
+ struct libusb_context *ctx = DEVICE_CTX(dev);
+ struct windows_device_priv *priv = _device_priv(dev);
+ int r = LIBUSB_ERROR_OTHER;
+
+ USB_BOS_DESCRIPTOR_SHORT bos_buf_short;
// dummy request
+ PUSB_DESCRIPTOR_REQUEST_WITH_DATA bos_buf_actual = NULL; //
actual request
+
+ size = sizeof(bos_buf_short);
+ memset(&bos_buf_short, 0, size);
+
+ bos_buf_short.req.ConnectionIndex = (ULONG)priv->port;
+ bos_buf_short.req.SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN;
+ bos_buf_short.req.SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR;
+ bos_buf_short.req.SetupPacket.wValue = (USB_BOS_DESCRIPTOR_TYPE << 8) |
0;
+ bos_buf_short.req.SetupPacket.wIndex = 0;
+ bos_buf_short.req.SetupPacket.wLength =
sizeof(USB_BOS_DESCRIPTOR_HEADER); // sizeof(bos_buf_short);
+
+ // Dummy call to get the required data size. Initial failures are
reported as info rather
+ // than error as they can occur for non-penalizing situations, such as
with some hubs.
+ if (!DeviceIoControl(hub_handle,
IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, &bos_buf_short, size,
+ &bos_buf_short, size, &ret_size, NULL)) {
+ // An error is to be expected for non 3.0 devices
+ return LIBUSB_ERROR_IO;
+ }
+
+ if ((ret_size != size) || (bos_buf_short.data.wTotalLength <
sizeof(USB_BOS_DESCRIPTOR_HEADER))) {
+ usbi_info(ctx, "unexpected BOS descriptor size (dummy) for
'%s'.", device_id);
+ return LIBUSB_ERROR_IO;
+ }
+
+ size = sizeof(USB_DESCRIPTOR_REQUEST) + bos_buf_short.data.wTotalLength;
+ if ((bos_buf_actual = (PUSB_DESCRIPTOR_REQUEST_WITH_DATA) calloc(1,
size)) == NULL) {
+ usbi_err(ctx, "could not allocate BOS descriptor buffer for
'%s'.", device_id);
+ return LIBUSB_ERROR_NO_MEM;
+ }
+
+ // Actual call
+ bos_buf_actual->ConnectionIndex = (ULONG)priv->port;
+ bos_buf_actual->SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN;
+ bos_buf_actual->SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR;
+ bos_buf_actual->SetupPacket.wValue = (USB_BOS_DESCRIPTOR_TYPE << 8) | 0;
+ bos_buf_actual->SetupPacket.wIndex = 0;
+ bos_buf_actual->SetupPacket.wLength = (USHORT)(size -
sizeof(USB_DESCRIPTOR_REQUEST));
+
+ if (!DeviceIoControl(hub_handle,
IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, bos_buf_actual, size,
+ bos_buf_actual, size, &ret_size, NULL)) {
+ usbi_err(ctx, "could not access BOS descriptor (actual) for
'%s': %s", device_id, windows_error_str(0));
+ r = LIBUSB_ERROR_IO;
+ goto err;
+ }
+
+ if (size != ret_size) {
+ usbi_err(ctx, "unexpected configuration descriptor size
(actual) for '%s'.", device_id);
+ r = LIBUSB_ERROR_IO;
+ goto err;
+ }
+
+ priv->bos_descriptor = (struct
libusb_bos_descriptor*)malloc(bos_buf_actual->SetupPacket.wLength);
+ if (priv->bos_descriptor == NULL) {
+ r = LIBUSB_ERROR_NO_MEM;
+ goto err;
+ }
+
+ memcpy(priv->bos_descriptor, &bos_buf_actual->Data[0],
bos_buf_actual->SetupPacket.wLength);
+ usbi_dbg("cached BOS descriptor (%d bytes)",
bos_buf_actual->SetupPacket.wLength);
+ r = LIBUSB_SUCCESS;
+
+err:
+ safe_free(bos_buf_actual);
+ return r;
+}
+
+/*
* Populate a libusbx device structure
*/
static int init_device(struct libusb_device* dev, struct libusb_device*
parent_dev,
@@ -1067,6 +1145,7 @@ static int init_device(struct libusb_device* dev, struct
libusb_device* parent_d
dev->num_configurations = 0;
priv->dev_descriptor.bNumConfigurations = 0;
}
+ cache_bos_descriptors(dev, handle, device_id);
safe_closehandle(handle);
if (conn_info.DeviceAddress > UINT8_MAX) {
@@ -1652,6 +1731,21 @@ static int windows_get_device_descriptor(struct
libusb_device *dev, unsigned cha
return LIBUSB_SUCCESS;
}
+static int windows_get_bos_descriptor(struct libusb_device *dev, struct
libusb_bos_descriptor **bos, int *host_endian)
+{
+ struct windows_device_priv *priv = _device_priv(dev);
+
+ if (priv->bos_descriptor == NULL)
+ return LIBUSB_ERROR_NOT_FOUND;
+
+ *bos = (struct libusb_bos_descriptor
*)malloc(priv->bos_descriptor->wTotalLength);
+ if (*bos == NULL)
+ return LIBUSB_ERROR_NO_MEM;
+ memcpy(*bos, priv->bos_descriptor, priv->bos_descriptor->wTotalLength);
+ *host_endian = 0;
+ return LIBUSB_SUCCESS;
+}
+
static int windows_get_config_descriptor(struct libusb_device *dev, uint8_t
config_index, unsigned char *buffer, size_t len, int *host_endian)
{
struct windows_device_priv *priv = _device_priv(dev);
@@ -2182,6 +2276,7 @@ const struct usbi_os_backend windows_backend = {
windows_close,
windows_get_device_descriptor,
+ windows_get_bos_descriptor,
windows_get_active_config_descriptor,
windows_get_config_descriptor,
diff --git a/libusb/os/windows_usb.h b/libusb/os/windows_usb.h
index 331f75c..0cb2a39 100644
--- a/libusb/os/windows_usb.h
+++ b/libusb/os/windows_usb.h
@@ -245,6 +245,7 @@ struct windows_device_priv {
uint8_t active_config;
USB_DEVICE_DESCRIPTOR dev_descriptor;
unsigned char **config_descriptor; // list of pointers to the
cached config descriptors
+ struct libusb_bos_descriptor *bos_descriptor;
};
static inline struct windows_device_priv *_device_priv(struct libusb_device
*dev) {
@@ -263,6 +264,7 @@ static inline void windows_device_priv_init(libusb_device*
dev) {
p->hid = NULL;
p->active_config = 0;
p->config_descriptor = NULL;
+ p->bos_descriptor = NULL;
memset(&(p->dev_descriptor), 0, sizeof(USB_DEVICE_DESCRIPTOR));
for (i=0; i<USB_MAXINTERFACES; i++) {
p->usb_interface[i].path = NULL;
@@ -282,6 +284,7 @@ static inline void
windows_device_priv_release(libusb_device* dev) {
safe_free(p->config_descriptor[i]);
}
safe_free(p->config_descriptor);
+ safe_free(p->bos_descriptor);
safe_free(p->hid);
for (i=0; i<USB_MAXINTERFACES; i++) {
safe_free(p->usb_interface[i].path);
@@ -386,6 +389,7 @@ typedef RETURN_TYPE CONFIGRET;
#define USB_STRING_DESCRIPTOR_TYPE LIBUSB_DT_STRING
#define USB_INTERFACE_DESCRIPTOR_TYPE LIBUSB_DT_INTERFACE
#define USB_ENDPOINT_DESCRIPTOR_TYPE LIBUSB_DT_ENDPOINT
+#define USB_BOS_DESCRIPTOR_TYPE LIBUSB_DT_BOS
#define USB_REQUEST_GET_STATUS LIBUSB_REQUEST_GET_STATUS
#define USB_REQUEST_CLEAR_FEATURE LIBUSB_REQUEST_CLEAR_FEATURE
@@ -515,6 +519,27 @@ typedef struct USB_CONFIGURATION_DESCRIPTOR_SHORT {
USB_CONFIGURATION_DESCRIPTOR data;
} USB_CONFIGURATION_DESCRIPTOR_SHORT;
+typedef struct USB_BOS_DESCRIPTOR_HEADER {
+ UCHAR bLength;
+ UCHAR bDescriptorType;
+ USHORT wTotalLength;
+ UCHAR bNumDeviceCaps;
+} USB_BOS_DESCRIPTOR_HEADER;
+
+typedef struct USB_BOS_DESCRIPTOR_SHORT {
+ struct {
+ ULONG ConnectionIndex;
+ struct {
+ UCHAR bmRequest;
+ UCHAR bRequest;
+ USHORT wValue;
+ USHORT wIndex;
+ USHORT wLength;
+ } SetupPacket;
+ } req;
+ USB_BOS_DESCRIPTOR_HEADER data;
+} USB_BOS_DESCRIPTOR_SHORT;
+
typedef struct USB_ENDPOINT_DESCRIPTOR {
UCHAR bLength;
UCHAR bDescriptorType;
@@ -536,6 +561,18 @@ typedef struct USB_DESCRIPTOR_REQUEST {
// UCHAR Data[0];
} USB_DESCRIPTOR_REQUEST, *PUSB_DESCRIPTOR_REQUEST;
+typedef struct USB_DESCRIPTOR_REQUEST_WITH_DATA {
+ ULONG ConnectionIndex;
+ struct {
+ UCHAR bmRequest;
+ UCHAR bRequest;
+ USHORT wValue;
+ USHORT wIndex;
+ USHORT wLength;
+ } SetupPacket;
+ UCHAR Data[1];
+} USB_DESCRIPTOR_REQUEST_WITH_DATA, *PUSB_DESCRIPTOR_REQUEST_WITH_DATA;
+
typedef struct USB_HUB_DESCRIPTOR {
UCHAR bDescriptorLength;
UCHAR bDescriptorType;
--
1.7.10.msysgit.1
------------------------------------------------------------------------------
Live Security Virtual Conference
Exclusive live event will cover all the ways today's security and
threat landscape has changed and how IT managers can respond. Discussions
will include endpoint security, mobile security and the latest in malware
threats. http://www.accelacomm.com/jaw/sfrnl04242012/114/50122263/
_______________________________________________
libusbx-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/libusbx-devel