summaryrefslogtreecommitdiff
path: root/libusb/descriptor.c
diff options
context:
space:
mode:
Diffstat (limited to 'libusb/descriptor.c')
-rw-r--r--libusb/descriptor.c185
1 files changed, 185 insertions, 0 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);
+}