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>
This commit is contained in:
Hans de Goede
2013-05-27 11:12:28 +02:00
parent cedc7f6e28
commit ef698c633d
6 changed files with 157 additions and 7 deletions

View File

@@ -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);
}
}
}
}

View File

@@ -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;
}
}
@@ -720,6 +740,70 @@ void API_EXPORTED libusb_free_config_descriptor(
free(config);
}
/** \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.
*

View File

@@ -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

View File

@@ -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);

View File

@@ -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,

View File

@@ -1 +1 @@
#define LIBUSB_NANO 10724
#define LIBUSB_NANO 10725