descriptor: Introduce interface association descriptors (IAD)

Types:
  struct libusb_interface_association_descriptor
  struct libusb_interface_association_descriptor_array

Accessor / cleanup functions:
  libusb_get_interface_association_descriptors
  libusb_get_active_interface_association_descriptors
  libusb_free_interface_association_descriptors

Signed-off-by: Ryan Metcalfe <ryan.metcalfe@novanta.com>
[Tormod: Fixed Doxygen comment]
Signed-off-by: Tormod Volden <debian.tormod@gmail.com>
This commit is contained in:
Ryan Metcalfe
2021-07-26 16:07:48 -04:00
committed by Tormod Volden
parent e263e32f20
commit 809a6df614
4 changed files with 262 additions and 1 deletions

View File

@@ -1137,3 +1137,188 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_ha
data[di] = 0;
return di;
}
static int parse_iad_array(struct libusb_context *ctx,
struct libusb_interface_association_descriptor_array *iad_array,
const uint8_t *buffer, int size)
{
uint8_t i;
struct usbi_descriptor_header header;
int consumed = 0;
const uint8_t *buf = buffer;
struct libusb_interface_association_descriptor *iad;
if (size < LIBUSB_DT_CONFIG_SIZE) {
usbi_err(ctx, "short config descriptor read %d/%d",
size, LIBUSB_DT_CONFIG_SIZE);
return LIBUSB_ERROR_IO;
}
// First pass: Iterate through desc list, count number of IADs
iad_array->length = 0;
while (consumed < size) {
parse_descriptor(buf, "bb", &header);
if (header.bDescriptorType == LIBUSB_DT_INTERFACE_ASSOCIATION)
iad_array->length++;
buf += header.bLength;
consumed += header.bLength;
}
iad_array->iad = NULL;
if (iad_array->length > 0) {
iad = calloc(iad_array->length, sizeof(*iad));
if (!iad)
return LIBUSB_ERROR_NO_MEM;
iad_array->iad = iad;
// Second pass: Iterate through desc list, fill IAD structures
consumed = 0;
i = 0;
while (consumed < size) {
parse_descriptor(buffer, "bb", &header);
if (header.bDescriptorType == LIBUSB_DT_INTERFACE_ASSOCIATION)
parse_descriptor(buffer, "bbbbbbbb", &iad[i++]);
buffer += header.bLength;
consumed += header.bLength;
}
}
return LIBUSB_SUCCESS;
}
static int raw_desc_to_iad_array(struct libusb_context *ctx, const uint8_t *buf,
int size, struct libusb_interface_association_descriptor_array **iad_array)
{
struct libusb_interface_association_descriptor_array *_iad_array
= calloc(1, sizeof(*_iad_array));
int r;
if (!_iad_array)
return LIBUSB_ERROR_NO_MEM;
r = parse_iad_array(ctx, _iad_array, buf, size);
if (r < 0) {
usbi_err(ctx, "parse_iad_array failed with error %d", r);
free(_iad_array);
return r;
}
*iad_array = _iad_array;
return LIBUSB_SUCCESS;
}
/** \ingroup libusb_desc
* Get an array of interface association descriptors (IAD) for a given
* configuration.
* This is a non-blocking function which does not involve any requests being
* sent to the device.
*
* \param dev a device
* \param config_index the index of the configuration you wish to retrieve the
* IADs for.
* \param iad_array output location for the array of IADs. Only valid if 0 was
* returned. Must be freed with libusb_free_interface_association_descriptors()
* after use. It's possible that a given configuration contains no IADs. In this
* case the iad_array is still output, but will have 'length' field set to 0, and
* iad field set to NULL.
* \returns 0 on success
* \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist
* \returns another LIBUSB_ERROR code on error
* \see libusb_get_active_interface_association_descriptors()
*/
int API_EXPORTED libusb_get_interface_association_descriptors(libusb_device *dev,
uint8_t config_index, struct libusb_interface_association_descriptor_array **iad_array)
{
union usbi_config_desc_buf _config;
uint16_t config_len;
uint8_t *buf;
int r;
if (!iad_array)
return LIBUSB_ERROR_INVALID_PARAM;
usbi_dbg(DEVICE_CTX(dev), "IADs for config index %u", config_index);
if (config_index >= dev->device_descriptor.bNumConfigurations)
return LIBUSB_ERROR_NOT_FOUND;
r = get_config_descriptor(dev, config_index, _config.buf, sizeof(_config.buf));
if (r < 0)
return r;
config_len = libusb_le16_to_cpu(_config.desc.wTotalLength);
buf = malloc(config_len);
if (!buf)
return LIBUSB_ERROR_NO_MEM;
r = get_config_descriptor(dev, config_index, buf, config_len);
if (r >= 0)
r = raw_desc_to_iad_array(DEVICE_CTX(dev), buf, r, iad_array);
free(buf);
return r;
}
/** \ingroup libusb_desc
* Get an array of interface association descriptors (IAD) for the currently
* active configuration.
* This is a non-blocking function which does not involve any requests being
* sent to the device.
*
* \param dev a device
* \param iad_array output location for the array of IADs. Only valid if 0 was
* returned. Must be freed with libusb_free_interface_association_descriptors()
* after use. It's possible that a given configuration contains no IADs. In this
* case the iad_array is still output, but will have 'length' field set to 0, and
* iad field set to NULL.
* \returns 0 on success
* \returns LIBUSB_ERROR_NOT_FOUND if the device is in unconfigured state
* \returns another LIBUSB_ERROR code on error
* \see libusb_get_interface_association_descriptors
*/
int API_EXPORTED libusb_get_active_interface_association_descriptors(libusb_device *dev,
struct libusb_interface_association_descriptor_array **iad_array)
{
union usbi_config_desc_buf _config;
uint16_t config_len;
uint8_t *buf;
int r;
if (!iad_array)
return LIBUSB_ERROR_INVALID_PARAM;
r = get_active_config_descriptor(dev, _config.buf, sizeof(_config.buf));
if (r < 0)
return r;
config_len = libusb_le16_to_cpu(_config.desc.wTotalLength);
buf = malloc(config_len);
if (!buf)
return LIBUSB_ERROR_NO_MEM;
r = get_active_config_descriptor(dev, buf, config_len);
if (r >= 0)
r = raw_desc_to_iad_array(DEVICE_CTX(dev), buf, r, iad_array);
free(buf);
return r;
}
/** \ingroup libusb_desc
* Free an array of interface association descriptors (IADs) obtained from
* libusb_get_interface_association_descriptors() or
* libusb_get_active_interface_association_descriptors().
* It is safe to call this function with a NULL iad_array parameter, in which
* case the function simply returns.
*
* \param iad_array the IAD array to free
*/
void API_EXPORTED libusb_free_interface_association_descriptors(
struct libusb_interface_association_descriptor_array *iad_array)
{
if (!iad_array)
return;
if (iad_array->iad)
free((void*)iad_array->iad);
free(iad_array);
}

View File

@@ -40,6 +40,8 @@ EXPORTS
libusb_free_container_id_descriptor@4 = libusb_free_container_id_descriptor
libusb_free_device_list
libusb_free_device_list@8 = libusb_free_device_list
libusb_free_interface_association_descriptors
libusb_free_interface_association_descriptors@4 = libusb_free_interface_association_descriptors
libusb_free_pollfds
libusb_free_pollfds@4 = libusb_free_pollfds
libusb_free_ss_endpoint_companion_descriptor
@@ -54,6 +56,8 @@ EXPORTS
libusb_free_usb_2_0_extension_descriptor@4 = libusb_free_usb_2_0_extension_descriptor
libusb_get_active_config_descriptor
libusb_get_active_config_descriptor@8 = libusb_get_active_config_descriptor
libusb_get_active_interface_association_descriptors
libusb_get_active_interface_association_descriptors@8 = libusb_get_active_interface_association_descriptors
libusb_get_bos_descriptor
libusb_get_bos_descriptor@8 = libusb_get_bos_descriptor
libusb_get_bus_number
@@ -76,6 +80,8 @@ EXPORTS
libusb_get_device_list@8 = libusb_get_device_list
libusb_get_device_speed
libusb_get_device_speed@4 = libusb_get_device_speed
libusb_get_interface_association_descriptors
libusb_get_interface_association_descriptors@12 = libusb_get_interface_association_descriptors
libusb_get_max_iso_packet_size
libusb_get_max_iso_packet_size@8 = libusb_get_max_iso_packet_size
libusb_get_max_packet_size

View File

@@ -269,6 +269,10 @@ enum libusb_descriptor_type {
/** Endpoint descriptor. See libusb_endpoint_descriptor. */
LIBUSB_DT_ENDPOINT = 0x05,
/** Interface Association Descriptor.
* See libusb_interface_association_descriptor */
LIBUSB_DT_INTERFACE_ASSOCIATION = 0x0b,
/** BOS descriptor */
LIBUSB_DT_BOS = 0x0f,
@@ -632,6 +636,65 @@ struct libusb_endpoint_descriptor {
int extra_length;
};
/** \ingroup libusb_desc
* A structure representing the standard USB interface association descriptor.
* This descriptor is documented in section 9.6.4 of the USB 3.0 specification.
* All multiple-byte fields are represented in host-endian format.
*/
struct libusb_interface_association_descriptor {
/** Size of this descriptor (in bytes) */
uint8_t bLength;
/** Descriptor type. Will have value
* \ref libusb_descriptor_type::LIBUSB_DT_INTERFACE_ASSOCIATION
* LIBUSB_DT_INTERFACE_ASSOCIATION in this context. */
uint8_t bDescriptorType;
/** Interface number of the first interface that is associated
* with this function */
uint8_t bFirstInterface;
/** Number of contiguous interfaces that are associated with
* this function */
uint8_t bInterfaceCount;
/** USB-IF class code for this function.
* A value of zero is not allowed in this descriptor.
* If this field is 0xff, the function class is vendor-specific.
* All other values are reserved for assignment by the USB-IF.
*/
uint8_t bFunctionClass;
/** USB-IF subclass code for this function.
* If this field is not set to 0xff, all values are reserved
* for assignment by the USB-IF
*/
uint8_t bFunctionSubClass;
/** USB-IF protocol code for this function.
* These codes are qualified by the values of the bFunctionClass
* and bFunctionSubClass fields.
*/
uint8_t bFunctionProtocol;
/** Index of string descriptor describing this function */
uint8_t iFunction;
};
/** \ingroup libusb_desc
* Structure containing an array of 0 or more interface association
* descriptors
*/
struct libusb_interface_association_descriptor_array {
/** Array of interface association descriptors. The size of this array
* is determined by the length field.
*/
const struct libusb_interface_association_descriptor *iad;
/** Number of interface association descriptors contained. Read-only. */
int length;
};
/** \ingroup libusb_desc
* A structure representing the standard USB interface descriptor. This
* descriptor is documented in section 9.6.5 of the USB 3.0 specification.
@@ -1433,6 +1496,13 @@ int LIBUSB_CALL libusb_get_max_packet_size(libusb_device *dev,
int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev,
unsigned char endpoint);
int LIBUSB_CALL libusb_get_interface_association_descriptors(libusb_device *dev,
uint8_t config_index, struct libusb_interface_association_descriptor_array **iad_array);
int LIBUSB_CALL libusb_get_active_interface_association_descriptors(libusb_device *dev,
struct libusb_interface_association_descriptor_array **iad_array);
void LIBUSB_CALL libusb_free_interface_association_descriptors(
struct libusb_interface_association_descriptor_array *iad_array);
int LIBUSB_CALL libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, libusb_device_handle **dev_handle);
int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **dev_handle);
void LIBUSB_CALL libusb_close(libusb_device_handle *dev_handle);

View File

@@ -1 +1 @@
#define LIBUSB_NANO 11763
#define LIBUSB_NANO 11764