summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2013-05-27 11:12:28 +0200
committerHans de Goede <hdegoede@redhat.com>2013-05-30 14:20:44 +0200
commitef698c633d2ecb597be58deccfa2795cc6c88e25 (patch)
tree574ffb7ed0d5dc5bd7fba7185b190fdec151c724
parentcedc7f6e289c427c84a9175045b06614be56ec5a (diff)
downloadlibusb-ef698c633d2ecb597be58deccfa2795cc6c88e25.tar.gz
Add superspeed endpoint companion descriptor support
Based on earlier work done on this by Maya Erez <merez@codeaurora.org>, Nathan Hjelm <hjelmn@me.com> and Pete Batard <pete@akeo.ie>. Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-rw-r--r--examples/xusb.c7
-rw-r--r--libusb/descriptor.c88
-rw-r--r--libusb/libusb-1.0.def4
-rw-r--r--libusb/libusb.h61
-rw-r--r--libusb/libusbi.h2
-rw-r--r--libusb/version_nano.h2
6 files changed, 157 insertions, 7 deletions
diff --git a/examples/xusb.c b/examples/xusb.c
index 5bb92a6..e1b1931 100644
--- a/examples/xusb.c
+++ b/examples/xusb.c
@@ -808,6 +808,7 @@ static int test_device(uint16_t vid, uint16_t pid)
test_mode = USE_SCSI;
}
for (k=0; k<conf_desc->usb_interface[i].altsetting[j].bNumEndpoints; k++) {
+ struct libusb_ss_endpoint_companion_descriptor *ep_comp = NULL;
endpoint = &conf_desc->usb_interface[i].altsetting[j].endpoint[k];
printf(" endpoint[%d].address: %02X\n", k, endpoint->bEndpointAddress);
// Use the first interrupt or bulk IN/OUT endpoints as default for testing
@@ -822,6 +823,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);
+ libusb_get_ss_endpoint_companion_descriptor(NULL, endpoint, &ep_comp);
+ if (ep_comp) {
+ printf(" max burst: %02X (USB 3.0)\n", ep_comp->bMaxBurst);
+ printf(" bytes per interval: %04X (USB 3.0)\n", ep_comp->wBytesPerInterval);
+ libusb_free_ss_endpoint_companion_descriptor(ep_comp);
+ }
}
}
}
diff --git a/libusb/descriptor.c b/libusb/descriptor.c
index b53df77..14482ca 100644
--- a/libusb/descriptor.c
+++ b/libusb/descriptor.c
@@ -40,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) {
@@ -64,6 +66,24 @@ int usbi_parse_descriptor(unsigned char *source, const char *descriptor,
sp += 2;
dp += 2;
break;
+ case 'd': /* 32-bit word, convert from little endian to CPU */
+ dp += ((uintptr_t)dp & 1); /* Align to word boundary */
+
+ 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 */
+ memcpy(dp, sp, 16);
+ sp += 16;
+ dp += 16;
+ break;
}
}
@@ -721,6 +741,70 @@ void API_EXPORTED libusb_free_config_descriptor(
}
/** \ingroup desc
+ * Get an endpoints superspeed endpoint companion descriptor (if any)
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param endpoint endpoint descriptor from which to get the superspeed
+ * endpoint companion descriptor
+ * \param ep_comp output location for the superspeed endpoint companion
+ * descriptor. Only valid if 0 was returned. Must be freed with
+ * libusb_free_ss_endpoint_companion_descriptor() after use.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist
+ * \returns another LIBUSB_ERROR code on error
+ */
+int API_EXPORTED libusb_get_ss_endpoint_companion_descriptor(
+ struct libusb_context *ctx,
+ const struct libusb_endpoint_descriptor *endpoint,
+ struct libusb_ss_endpoint_companion_descriptor **ep_comp)
+{
+ struct usb_descriptor_header header;
+ int size = endpoint->extra_length;
+ const unsigned char *buffer = endpoint->extra;
+
+ *ep_comp = NULL;
+
+ while (size >= DESC_HEADER_LENGTH) {
+ usbi_parse_descriptor(buffer, "bb", &header, 0);
+ if (header.bLength < 2 || header.bLength > size) {
+ usbi_err(ctx, "invalid descriptor length %d",
+ header.bLength);
+ return LIBUSB_ERROR_IO;
+ }
+ if (header.bDescriptorType != LIBUSB_DT_SS_ENDPOINT_COMPANION) {
+ buffer += header.bLength;
+ size -= header.bLength;
+ continue;
+ }
+ if (header.bLength < LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE) {
+ usbi_err(ctx, "invalid ss-ep-comp-desc length %d",
+ header.bLength);
+ return LIBUSB_ERROR_IO;
+ }
+ *ep_comp = malloc(sizeof(**ep_comp));
+ if (*ep_comp == NULL)
+ return LIBUSB_ERROR_NO_MEM;
+ usbi_parse_descriptor(buffer, "bbbbw", *ep_comp, 0);
+ return LIBUSB_SUCCESS;
+ }
+ return LIBUSB_ERROR_NOT_FOUND;
+}
+
+/** \ingroup desc
+ * Free a superspeed endpoint companion descriptor obtained from
+ * libusb_get_ss_endpoint_companion_descriptor().
+ * It is safe to call this function with a NULL ep_comp parameter, in which
+ * case the function simply returns.
+ *
+ * \param ep_comp the superspeed endpoint companion descriptor to free
+ */
+void API_EXPORTED libusb_free_ss_endpoint_companion_descriptor(
+ struct libusb_ss_endpoint_companion_descriptor *ep_comp)
+{
+ free(ep_comp);
+}
+
+/** \ingroup desc
* Retrieve a string descriptor in C style ASCII.
*
* Wrapper around libusb_get_string_descriptor(). Uses the first language
diff --git a/libusb/libusb-1.0.def b/libusb/libusb-1.0.def
index 0343116..52047ea 100644
--- a/libusb/libusb-1.0.def
+++ b/libusb/libusb-1.0.def
@@ -30,6 +30,8 @@ EXPORTS
libusb_free_config_descriptor@4 = libusb_free_config_descriptor
libusb_free_device_list
libusb_free_device_list@8 = libusb_free_device_list
+ libusb_free_ss_endpoint_companion_descriptor
+ libusb_free_ss_endpoint_companion_descriptor@4 = libusb_free_ss_endpoint_companion_descriptor
libusb_free_transfer
libusb_free_transfer@4 = libusb_free_transfer
libusb_get_active_config_descriptor
@@ -66,6 +68,8 @@ EXPORTS
libusb_get_port_number@4 = libusb_get_port_number
libusb_get_port_path
libusb_get_port_path@16 = libusb_get_port_path
+ libusb_get_ss_endpoint_companion_descriptor
+ libusb_get_ss_endpoint_companion_descriptor@12 = libusb_get_ss_endpoint_companion_descriptor
libusb_get_string_descriptor_ascii
libusb_get_string_descriptor_ascii@16 = libusb_get_string_descriptor_ascii
libusb_get_version
diff --git a/libusb/libusb.h b/libusb/libusb.h
index a67cbcb..593dc09 100644
--- a/libusb/libusb.h
+++ b/libusb/libusb.h
@@ -274,16 +274,33 @@ 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_AUDIO_SIZE 9 /* Audio extension */
+#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
+#define LIBUSB_DT_DEVICE_CAPABILITY_SIZE 3
+
+/* 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
@@ -655,6 +672,38 @@ struct libusb_config_descriptor {
int extra_length;
};
+/** \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 asyncio
* Setup packet for control transfers. */
struct libusb_control_setup {
@@ -1060,6 +1109,12 @@ 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_ss_endpoint_companion_descriptor(
+ struct libusb_context *ctx,
+ const struct libusb_endpoint_descriptor *endpoint,
+ struct libusb_ss_endpoint_companion_descriptor **ep_comp);
+void LIBUSB_CALL libusb_free_ss_endpoint_companion_descriptor(
+ struct libusb_ss_endpoint_companion_descriptor *ep_comp);
uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev);
uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev);
int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_t* port_numbers, int port_numbers_len);
diff --git a/libusb/libusbi.h b/libusb/libusbi.h
index 4dda97e..eabf33d 100644
--- a/libusb/libusbi.h
+++ b/libusb/libusbi.h
@@ -413,7 +413,7 @@ 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,
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index a4f4d24..d69b0cc 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 10724
+#define LIBUSB_NANO 10725