Add support for SuperSpeed+ Capability Descriptors

See specs in USB 3.1 specs in section:

9.6.2.5 SuperSpeedPlus USB Device Capability

Closes #1428
Fixes #1429
This commit is contained in:
Fabien Sanglard
2024-01-06 00:45:37 -08:00
committed by Tormod Volden
parent f8a6c412f5
commit f00f06e9eb
5 changed files with 229 additions and 3 deletions

View File

@@ -762,6 +762,17 @@ static void read_ms_winsub_feature_descriptors(libusb_device_handle *handle, uin
}
}
static void print_sublink_speed_attribute(struct libusb_ssplus_sublink_attribute* ss_attr) {
static const char exponent[] = " KMG";
printf(" id=%u speed=%u%cbs %s %s SuperSpeed%s",
ss_attr->ssid,
ss_attr->mantisa,
(exponent[ss_attr->exponent]),
(ss_attr->type == LIBUSB_SSPLUS_ATTR_TYPE_ASYM)? "Asym" : "Sym",
(ss_attr->direction == LIBUSB_SSPLUS_ATTR_DIR_TX)? "TX" : "RX",
(ss_attr->protocol == LIBUSB_SSPLUS_ATTR_PROT_SSPLUS)? "+": "" );
}
static void print_device_cap(struct libusb_bos_dev_capability_descriptor *dev_cap)
{
switch(dev_cap->bDevCapabilityType) {
@@ -810,6 +821,25 @@ static void print_device_cap(struct libusb_bos_dev_capability_descriptor *dev_ca
break;
}
case LIBUSB_BT_SUPERSPEED_PLUS_CAPABILITY: {
struct libusb_ssplus_usb_device_capability_descriptor *ssplus_usb_device_cap = NULL;
libusb_get_ssplus_usb_device_capability_descriptor(NULL, dev_cap, &ssplus_usb_device_cap);
if (ssplus_usb_device_cap) {
printf(" USB 3.1 capabilities:\n");
printf(" num speed IDs: %d\n", ssplus_usb_device_cap->numSublinkSpeedIDs);
printf(" minLaneSpeed: %d\n", ssplus_usb_device_cap->ssid);
printf(" minRXLanes: %d\n", ssplus_usb_device_cap->minRxLaneCount);
printf(" minTXLanes: %d\n", ssplus_usb_device_cap->minTxLaneCount);
printf(" num speed attribute IDs: %d\n", ssplus_usb_device_cap->numSublinkSpeedAttributes);
for(uint8_t i=0 ; i < ssplus_usb_device_cap->numSublinkSpeedAttributes ; i++) {
print_sublink_speed_attribute(&ssplus_usb_device_cap->sublinkSpeedAttributes[i]);
printf("\n");
}
libusb_free_ssplus_usb_device_capability_descriptor(ssplus_usb_device_cap);
}
break;
}
default:
printf(" Unknown BOS device capability %02x:\n", dev_cap->bDevCapabilityType);
}

View File

@@ -59,9 +59,14 @@ static void parse_descriptor(const void *source, const char *descriptor, void *d
sp += 2;
dp += 2;
break;
case 'd': /* 32-bit word, convert from little endian to CPU */
case 'd': /* 32-bit word, convert from little endian to CPU (4-byte align dst before write). */
dp += 4 - ((uintptr_t)dp & 3); /* Align to 32-bit word boundary */
*((uint32_t *)dp) = READ_LE32(sp);
sp += 4;
dp += 4;
break;
case 'i': /* 32-bit word, convert from little endian to CPU (no dst alignment before write) */
*((uint32_t *)dp) = READ_LE32(sp);
sp += 4;
dp += 4;
@@ -1001,6 +1006,89 @@ int API_EXPORTED libusb_get_ss_usb_device_capability_descriptor(
return LIBUSB_SUCCESS;
}
// We use this private struct ony to parse a superspeed+ device capability
// descriptor according to section 9.6.2.5 of the USB 3.1 specification.
// We don't expose it.
struct internal_ssplus_capability_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDevCapabilityType;
uint8_t bReserved;
uint32_t bmAttributes;
uint16_t wFunctionalitySupport;
uint16_t wReserved;
};
int API_EXPORTED libusb_get_ssplus_usb_device_capability_descriptor(
libusb_context *ctx,
struct libusb_bos_dev_capability_descriptor *dev_cap,
struct libusb_ssplus_usb_device_capability_descriptor **ssplus_usb_device_cap)
{
struct libusb_ssplus_usb_device_capability_descriptor *_ssplus_cap;
// Use a private struct to re-use our descriptor parsing system.
struct internal_ssplus_capability_descriptor parsedDescriptor;
// Some size/type checks to make sure everything is in order
if (dev_cap->bDevCapabilityType != LIBUSB_BT_SUPERSPEED_PLUS_CAPABILITY) {
usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)",
dev_cap->bDevCapabilityType,
LIBUSB_BT_SUPERSPEED_PLUS_CAPABILITY);
return LIBUSB_ERROR_INVALID_PARAM;
} else if (dev_cap->bLength < LIBUSB_BT_SSPLUS_USB_DEVICE_CAPABILITY_SIZE) {
usbi_err(ctx, "short dev-cap descriptor read %u/%d",
dev_cap->bLength, LIBUSB_BT_SSPLUS_USB_DEVICE_CAPABILITY_SIZE);
return LIBUSB_ERROR_IO;
}
// We can only parse the non-variable size part of the SuperSpeedPlus descriptor. The attributes
// have to be read "manually".
parse_descriptor(dev_cap, "bbbbiww", &parsedDescriptor);
uint8_t numSublikSpeedAttributes = (parsedDescriptor.bmAttributes & 0xF) + 1;
_ssplus_cap = malloc(sizeof(struct libusb_ssplus_usb_device_capability_descriptor) + numSublikSpeedAttributes * sizeof(struct libusb_ssplus_sublink_attribute));
if (!_ssplus_cap)
return LIBUSB_ERROR_NO_MEM;
// Parse bmAttributes
_ssplus_cap->numSublinkSpeedAttributes = numSublikSpeedAttributes;
_ssplus_cap->numSublinkSpeedIDs = ((parsedDescriptor.bmAttributes & 0xF0) >> 4) + 1;
// Parse wFunctionalitySupport
_ssplus_cap->ssid = parsedDescriptor.wFunctionalitySupport & 0xF;
_ssplus_cap->minRxLaneCount = (parsedDescriptor.wFunctionalitySupport & 0x0F00) >> 8;
_ssplus_cap->minTxLaneCount = (parsedDescriptor.wFunctionalitySupport & 0xF000) >> 12;
// Check that we have enough to read all the sublink attributes
if (dev_cap->bLength < LIBUSB_BT_SSPLUS_USB_DEVICE_CAPABILITY_SIZE + _ssplus_cap->numSublinkSpeedAttributes * sizeof(uint32_t)) {
usbi_err(ctx, "short ssplus capability descriptor, unable to read sublinks: Not enough data");
return LIBUSB_ERROR_IO;
}
// Read the attributes
uint8_t* base = ((uint8_t*)dev_cap) + LIBUSB_BT_SSPLUS_USB_DEVICE_CAPABILITY_SIZE;
for(uint8_t i = 0 ; i < _ssplus_cap->numSublinkSpeedAttributes ; i++) {
uint32_t attr = READ_LE32(base + i * sizeof(uint32_t));
_ssplus_cap->sublinkSpeedAttributes[i].ssid = attr & 0x0f;
_ssplus_cap->sublinkSpeedAttributes[i].mantisa = attr >> 16;
_ssplus_cap->sublinkSpeedAttributes[i].exponent = (attr >> 4) & 0x3 ;
_ssplus_cap->sublinkSpeedAttributes[i].type = attr & 0x40 ? LIBUSB_SSPLUS_ATTR_TYPE_ASYM : LIBUSB_SSPLUS_ATTR_TYPE_SYM;
_ssplus_cap->sublinkSpeedAttributes[i].direction = attr & 0x80 ? LIBUSB_SSPLUS_ATTR_DIR_TX : LIBUSB_SSPLUS_ATTR_DIR_RX;
_ssplus_cap->sublinkSpeedAttributes[i].protocol = attr & 0x4000 ? LIBUSB_SSPLUS_ATTR_PROT_SSPLUS: LIBUSB_SSPLUS_ATTR_PROT_SS;
}
*ssplus_usb_device_cap = _ssplus_cap;
return LIBUSB_SUCCESS;
}
void API_EXPORTED libusb_free_ssplus_usb_device_capability_descriptor(
struct libusb_ssplus_usb_device_capability_descriptor *ssplus_usb_device_cap)
{
free(ssplus_usb_device_cap);
}
/** \ingroup libusb_desc
* Free a SuperSpeed USB Device Capability descriptor obtained from
* libusb_get_ss_usb_device_capability_descriptor().

View File

@@ -50,6 +50,8 @@ EXPORTS
libusb_free_ss_endpoint_companion_descriptor@4 = libusb_free_ss_endpoint_companion_descriptor
libusb_free_ss_usb_device_capability_descriptor
libusb_free_ss_usb_device_capability_descriptor@4 = libusb_free_ss_usb_device_capability_descriptor
libusb_free_ssplus_usb_device_capability_descriptor
libusb_free_ssplus_usb_device_capability_descriptor@4 = libusb_free_ssplus_usb_device_capability_descriptor
libusb_free_streams
libusb_free_streams@12 = libusb_free_streams
libusb_free_transfer
@@ -108,6 +110,8 @@ EXPORTS
libusb_get_ss_endpoint_companion_descriptor@12 = libusb_get_ss_endpoint_companion_descriptor
libusb_get_ss_usb_device_capability_descriptor
libusb_get_ss_usb_device_capability_descriptor@12 = libusb_get_ss_usb_device_capability_descriptor
libusb_get_ssplus_usb_device_capability_descriptor
libusb_get_ssplus_usb_device_capability_descriptor@12 = libusb_get_ssplus_usb_device_capability_descriptor
libusb_get_string_descriptor_ascii
libusb_get_string_descriptor_ascii@16 = libusb_get_string_descriptor_ascii
libusb_get_usb_2_0_extension_descriptor

View File

@@ -339,6 +339,7 @@ enum libusb_descriptor_type {
/* BOS descriptor sizes */
#define LIBUSB_BT_USB_2_0_EXTENSION_SIZE 7
#define LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE 10
#define LIBUSB_BT_SSPLUS_USB_DEVICE_CAPABILITY_SIZE 12
#define LIBUSB_BT_CONTAINER_ID_SIZE 20
#define LIBUSB_BT_PLATFORM_DESCRIPTOR_MIN_SIZE 20
@@ -562,7 +563,10 @@ enum libusb_bos_type {
LIBUSB_BT_CONTAINER_ID = 0x04,
/** Platform descriptor */
LIBUSB_BT_PLATFORM_DESCRIPTOR = 0x05
LIBUSB_BT_PLATFORM_DESCRIPTOR = 0x05,
/* SuperSpeed+ device capability */
LIBUSB_BT_SUPERSPEED_PLUS_CAPABILITY = 0x0A,
};
/** \ingroup libusb_desc
@@ -980,6 +984,100 @@ struct libusb_ss_usb_device_capability_descriptor {
uint16_t bU2DevExitLat;
};
/** \ingroup libusb_desc
* enum used in \ref libusb_ssplus_sublink_attribute
*/
enum libusb_superspeedplus_sublink_attribute_sublink_type {
LIBUSB_SSPLUS_ATTR_TYPE_SYM = 0,
LIBUSB_SSPLUS_ATTR_TYPE_ASYM = 1,
};
/** \ingroup libusb_desc
* enum used in \ref libusb_ssplus_sublink_attribute
*/
enum libusb_superspeedplus_sublink_attribute_sublink_direction {
LIBUSB_SSPLUS_ATTR_DIR_RX = 0,
LIBUSB_SSPLUS_ATTR_DIR_TX = 1,
};
/** \ingroup libusb_desc
* enum used in \ref libusb_ssplus_sublink_attribute
* Bit = Bits per second
* Kb = Kbps
* Mb = Mbps
* Gb = Gbps
*/
enum libusb_superspeedplus_sublink_attribute_exponent {
LIBUSB_SSPLUS_ATTR_EXP_BPS = 0,
LIBUSB_SSPLUS_ATTR_EXP_KBS = 1,
LIBUSB_SSPLUS_ATTR_EXP_MBS = 2,
LIBUSB_SSPLUS_ATTR_EXP_GBS = 3,
};
/** \ingroup libusb_desc
* enum used in \ref libusb_ssplus_sublink_attribute
*/
enum libusb_superspeedplus_sublink_attribute_link_protocol {
LIBUSB_SSPLUS_ATTR_PROT_SS = 0,
LIBUSB_SSPLUS_ATTR_PROT_SSPLUS = 1,
};
/** \ingroup libusb_desc
* Expose \ref libusb_ssplus_usb_device_capability_descriptor.sublinkSpeedAttributes
*/
struct libusb_ssplus_sublink_attribute {
/** Sublink Speed Attribute ID (SSID).
This field is an ID that uniquely identifies the speed of this sublink */
uint8_t ssid;
/** This field defines the
base 10 exponent times 3, that shall be applied to the
mantissa. */
enum libusb_superspeedplus_sublink_attribute_exponent exponent;
/** This field identifies whether the
Sublink Speed Attribute defines a symmetric or
asymmetric bit rate.*/
enum libusb_superspeedplus_sublink_attribute_sublink_type type;
/** This field indicates if this
Sublink Speed Attribute defines the receive or
transmit bit rate. */
enum libusb_superspeedplus_sublink_attribute_sublink_direction direction;
/** This field identifies the protocol
supported by the link. */
enum libusb_superspeedplus_sublink_attribute_link_protocol protocol;
/** This field defines the mantissa that shall be applied to the exponent when
calculating the maximum bit rate. */
uint16_t mantisa;
};
/** \ingroup libusb_desc
* A structure representing the SuperSpeedPlus descriptor
* This descriptor is documented in section 9.6.2.5 of the USB 3.1 specification.
*/
struct libusb_ssplus_usb_device_capability_descriptor {
/** Sublink Speed Attribute Count */
uint8_t numSublinkSpeedAttributes;
/** Sublink Speed ID Count */
uint8_t numSublinkSpeedIDs;
/** Unique ID to indicates the minimum lane speed */
uint8_t ssid;
/** This field indicates the minimum receive lane count.*/
uint8_t minRxLaneCount;
/** This field indicates the minimum transmit lane count*/
uint8_t minTxLaneCount;
/** num attrtibutes= \ref libusb_ssplus_usb_device_capability_descriptor.numSublinkSpeedAttributes= */
struct libusb_ssplus_sublink_attribute sublinkSpeedAttributes[];
};
/** \ingroup libusb_desc
* A structure representing the Container ID descriptor.
* This descriptor is documented in section 9.6.2.3 of the USB 3.0 specification.
@@ -1625,6 +1723,12 @@ int LIBUSB_CALL libusb_get_ss_usb_device_capability_descriptor(
struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap);
void LIBUSB_CALL libusb_free_ss_usb_device_capability_descriptor(
struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap);
int LIBUSB_CALL libusb_get_ssplus_usb_device_capability_descriptor(
libusb_context *ctx,
struct libusb_bos_dev_capability_descriptor *dev_cap,
struct libusb_ssplus_usb_device_capability_descriptor **ssplus_usb_device_cap);
void LIBUSB_CALL libusb_free_ssplus_usb_device_capability_descriptor(
struct libusb_ssplus_usb_device_capability_descriptor *ssplus_usb_device_cap);
int LIBUSB_CALL libusb_get_container_id_descriptor(libusb_context *ctx,
struct libusb_bos_dev_capability_descriptor *dev_cap,
struct libusb_container_id_descriptor **container_id);

View File

@@ -1 +1 @@
#define LIBUSB_NANO 11896
#define LIBUSB_NANO 11897