summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Metcalfe <ryan.metcalfe@novanta.com>2021-07-26 16:07:48 -0400
committerTormod Volden <debian.tormod@gmail.com>2023-01-19 15:01:22 +0100
commit809a6df614b87be780df9b0c97ba6c0bb6172aab (patch)
treed5d3a0bd4bc399d12b931dbd4f67fa6b7a0c2736
parente263e32f20b903ed1d2c68fe3d80b55d7cfae52e (diff)
downloadlibusb-809a6df614b87be780df9b0c97ba6c0bb6172aab.tar.gz
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>
-rw-r--r--libusb/descriptor.c185
-rw-r--r--libusb/libusb-1.0.def6
-rw-r--r--libusb/libusb.h70
-rw-r--r--libusb/version_nano.h2
4 files changed, 262 insertions, 1 deletions
diff --git a/libusb/descriptor.c b/libusb/descriptor.c
index 5cdacbb..727cf98 100644
--- a/libusb/descriptor.c
+++ b/libusb/descriptor.c
@@ -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);
+}
diff --git a/libusb/libusb-1.0.def b/libusb/libusb-1.0.def
index c8d1eb2..01276aa 100644
--- a/libusb/libusb-1.0.def
+++ b/libusb/libusb-1.0.def
@@ -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
diff --git a/libusb/libusb.h b/libusb/libusb.h
index bc7ef16..0946a55 100644
--- a/libusb/libusb.h
+++ b/libusb/libusb.h
@@ -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,
@@ -633,6 +637,65 @@ struct libusb_endpoint_descriptor {
};
/** \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.
* All multiple-byte fields are represented in host-endian format.
@@ -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);
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index 8454b80..04b0954 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 11763
+#define LIBUSB_NANO 11764