git: ae489991d1c0 - main - libusb: implement IAD parser
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 22 Jun 2026 13:36:58 UTC
The branch main has been updated by aokblast:
URL: https://cgit.FreeBSD.org/src/commit/?id=ae489991d1c0b550ead309abc1eade3b185e7499
commit ae489991d1c0b550ead309abc1eade3b185e7499
Author: ShengYi Hung <aokblast@FreeBSD.org>
AuthorDate: 2025-06-11 17:24:04 +0000
Commit: ShengYi Hung <aokblast@FreeBSD.org>
CommitDate: 2026-06-22 13:36:47 +0000
libusb: implement IAD parser
libusb provide functions to parse interface association descriptor. This
descriptor indicates that a function is composed by multiple interface
and which interfaces is associate to the target function. This
descriptor is not a separate USB require, instead, it comes with the
config descriptor.
Reviewed by: adrian
Sponsored by: The FreeBSD Foundation
Differential Revision: https://reviews.freebsd.org/D50958
---
lib/libusb/libusb.3 | 27 ++++++++++++++
lib/libusb/libusb.h | 21 +++++++++++
lib/libusb/libusb10_desc.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++
lib/libusb/libusb20_desc.c | 30 +++++++++++++---
lib/libusb/libusb20_desc.h | 23 ++++++++++++
5 files changed, 185 insertions(+), 5 deletions(-)
diff --git a/lib/libusb/libusb.3 b/lib/libusb/libusb.3
index 90d9afdb5609..9b46470ed13d 100644
--- a/lib/libusb/libusb.3
+++ b/lib/libusb/libusb.3
@@ -561,6 +561,33 @@ libusb_free_platform_descriptor function.
.Fn libusb_free_platform_descriptor "struct libusb_platform_descriptor *platform_descriptor"
This function is NULL safe and frees a parsed platform descriptor given by
.Fa platform_descriptor .
+.Pp
+.Ft int
+.Fn libusb_get_interface_association_descriptors "libusb_device *dev" "uint8_t config_index" "struct libusb_interface_association_descriptor_array **iad_arr"
+This function parses the interface association descriptor from the descriptor given by
+.Fa dev
+and
+.Fa config_index
+then stores a pointer to the parsed descriptor array into
+.Fa iad_arr .
+Return zero on success and a LIBUSB_ERROR code on failure.
+On success the parsed interface association descriptor must be freed using the
+libusb_free_interface_association_descriptors function.
+.Pp
+.Ft int
+.Fn libusb_get_active_interface_association_descriptors "libusb_device *dev" "struct libusb_interface_association_descriptor_array **iad_arr"
+This function parses the interface association descriptor from the descriptor given by
+.Fa dev
+and active config then stores a pointer to the parsed descriptor array into
+.Fa iad_arr .
+Return zero on success and a LIBUSB_ERROR code on failure.
+On success the parsed interface association descriptor must be freed using the
+libusb_free_interface_association_descriptors function.
+.Pp
+.Ft void
+.Fn libusb_free_interface_association_descriptors "struct libusb_interface_association_descriptor_array *iad_arr"
+This function is NULL safe and frees a parsed interface association descriptor array given by
+.Fa iad_arr .
.Sh USB ASYNCHRONOUS I/O
.Ft struct libusb_transfer *
.Fn libusb_alloc_transfer "int iso_packets"
diff --git a/lib/libusb/libusb.h b/lib/libusb/libusb.h
index 55982be2ead3..5a33dd39d85e 100644
--- a/lib/libusb/libusb.h
+++ b/lib/libusb/libusb.h
@@ -82,6 +82,7 @@ enum libusb_descriptor_type {
LIBUSB_DT_STRING = 0x03,
LIBUSB_DT_INTERFACE = 0x04,
LIBUSB_DT_ENDPOINT = 0x05,
+ LIBUSB_DT_INTERFACE_ASSOCIATION = 0x0b,
LIBUSB_DT_HID = 0x21,
LIBUSB_DT_REPORT = 0x22,
LIBUSB_DT_PHYSICAL = 0x23,
@@ -106,6 +107,7 @@ enum libusb_device_capability_type {
#define LIBUSB_DT_HUB_NONVAR_SIZE 7
#define LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE 6
#define LIBUSB_DT_BOS_SIZE 5
+#define LIBUSB_DT_INTERFACE_ASSOCIATION_SIZE 8
#define LIBUSB_USB_2_0_EXTENSION_DEVICE_CAPABILITY_SIZE 7
#define LIBUSB_SS_USB_DEVICE_CAPABILITY_SIZE 10
@@ -367,6 +369,22 @@ typedef struct libusb_ss_endpoint_companion_descriptor {
uint16_t wBytesPerInterval;
} libusb_ss_endpoint_companion_descriptor __aligned(sizeof(void *));
+typedef struct libusb_interface_association_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bFirstInterface;
+ uint8_t bInterfaceCount;
+ uint8_t bFunctionClass;
+ uint8_t bFunctionSubClass;
+ uint8_t bFunctionProtocol;
+ uint8_t iFunction;
+} libusb_interface_association_descriptor __aligned(sizeof(void *));
+
+typedef struct libusb_interface_association_descriptor_array {
+ const struct libusb_interface_association_descriptor *iad;
+ int length;
+} libusb_interface_association_descriptor_array;
+
typedef struct libusb_interface_descriptor {
uint8_t bLength;
uint8_t bDescriptorType;
@@ -579,6 +597,9 @@ int libusb_get_container_id_descriptor(struct libusb_context *ctx, struct libusb
void libusb_free_container_id_descriptor(struct libusb_container_id_descriptor *container_id);
int libusb_get_platform_descriptor(libusb_context *ctx, struct libusb_bos_dev_capability_descriptor *dev_cap, struct libusb_platform_descriptor **platform_descriptor);
void libusb_free_platform_descriptor(struct libusb_platform_descriptor *platform_descriptor);
+int libusb_get_interface_association_descriptors(libusb_device *dev, uint8_t config_index, struct libusb_interface_association_descriptor_array **iad_arr);
+int libusb_get_active_interface_association_descriptors(libusb_device *dev, struct libusb_interface_association_descriptor_array **iad_arr);
+void libusb_free_interface_association_descriptors(struct libusb_interface_association_descriptor_array *iad_arr);
/* Asynchronous device I/O */
diff --git a/lib/libusb/libusb10_desc.c b/lib/libusb/libusb10_desc.c
index 7da5c84f4ad2..592b0cb7a4cd 100644
--- a/lib/libusb/libusb10_desc.c
+++ b/lib/libusb/libusb10_desc.c
@@ -763,3 +763,92 @@ libusb_free_platform_descriptor(
{
free(platform_descriptor);
}
+
+int
+libusb_get_interface_association_descriptors(libusb_device *dev,
+ uint8_t config_index,
+ struct libusb_interface_association_descriptor_array **iad_arr)
+{
+ struct libusb_interface_association_descriptor_array *iads;
+ struct libusb_interface_association_descriptor *iad;
+ struct libusb20_config *config;
+ struct LIBUSB20_INTERFACE_ASSOCIATION_DESC_DECODED *iad_desc;
+
+ int idx;
+ int niad;
+
+ if (!iad_arr)
+ return (LIBUSB_ERROR_INVALID_PARAM);
+
+ if (config_index >= dev->os_priv->ddesc.bNumConfigurations)
+ return (LIBUSB_ERROR_NOT_FOUND);
+
+ config = libusb20_dev_alloc_config(dev->os_priv, config_index);
+ if (config == NULL)
+ return (LIBUSB_ERROR_NO_MEM);
+ niad = config->niad;
+ iads = calloc(1,
+ sizeof(struct libusb_interface_association_descriptor_array));
+ if (iads == NULL) {
+ free(config);
+ return (LIBUSB_ERROR_NO_MEM);
+ }
+ *iad_arr = iads;
+ if (niad == 0) {
+ iads->iad = NULL;
+ iads->length = 0;
+ free(config);
+ return (LIBUSB_SUCCESS);
+ }
+
+ iad = malloc(
+ sizeof(struct libusb_interface_association_descriptor) * niad);
+ if (iad == NULL) {
+ *iad_arr = NULL;
+ free(iads);
+ free(config);
+ return (LIBUSB_ERROR_NO_MEM);
+ }
+
+ for (idx = 0; idx < niad; ++idx) {
+ iad_desc = &config->iad_desc[idx].desc;
+ iad[idx].bLength = iad_desc->bLength;
+ iad[idx].bDescriptorType = iad_desc->bDescriptorType;
+ iad[idx].bFirstInterface = iad_desc->bFirstInterface;
+ iad[idx].bInterfaceCount = iad_desc->bInterfaceCount;
+ iad[idx].bFunctionClass = iad_desc->bFunctionClass;
+ iad[idx].bFunctionSubClass = iad_desc->bFunctionSubClass;
+ iad[idx].bFunctionProtocol = iad_desc->bFunctionProtocol;
+ iad[idx].iFunction = iad_desc->iFunction;
+ }
+ iads->length = niad;
+ iads->iad = iad;
+
+ free(config);
+ return (LIBUSB_SUCCESS);
+}
+
+int
+libusb_get_active_interface_association_descriptors(libusb_device *dev,
+ struct libusb_interface_association_descriptor_array **iad_arr)
+{
+ struct libusb20_device *pdev;
+ uint8_t config_index;
+
+ pdev = dev->os_priv;
+ config_index = libusb20_dev_get_config_index(pdev);
+
+ return (libusb_get_interface_association_descriptors(dev, config_index,
+ iad_arr));
+}
+
+void
+libusb_free_interface_association_descriptors(
+ struct libusb_interface_association_descriptor_array *iad_arr)
+{
+ if (iad_arr == NULL)
+ return;
+ if (iad_arr->iad)
+ free((void *)iad_arr->iad);
+ free(iad_arr);
+}
diff --git a/lib/libusb/libusb20_desc.c b/lib/libusb/libusb20_desc.c
index 89c01d5afeff..a0e3561ebb1b 100644
--- a/lib/libusb/libusb20_desc.c
+++ b/lib/libusb/libusb20_desc.c
@@ -44,6 +44,7 @@ static const uint32_t libusb20_me_encode_empty[2]; /* dummy */
LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_DEVICE_DESC);
LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_ENDPOINT_DESC);
LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_INTERFACE_DESC);
+LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_INTERFACE_ASSOCIATION_DESC);
LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONFIG_DESC);
LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_CONTROL_SETUP);
LIBUSB20_MAKE_STRUCT_FORMAT(LIBUSB20_SS_ENDPT_COMP_DESC);
@@ -67,12 +68,14 @@ libusb20_parse_config_desc(const void *config_desc)
struct libusb20_interface *last_if;
struct libusb20_endpoint *lub_endpoint;
struct libusb20_endpoint *last_ep;
+ struct libusb20_iad_desc *lub_iad;
struct libusb20_me_struct pcdesc;
const uint8_t *ptr;
uint32_t size;
uint16_t niface_no_alt;
uint16_t niface;
+ uint16_t niad;
uint16_t nendpoint;
uint16_t iface_no;
@@ -89,6 +92,7 @@ libusb20_parse_config_desc(const void *config_desc)
nendpoint = 0;
niface = 0;
iface_no = 0xFFFF;
+ niad = 0;
ptr = NULL;
/* get "wTotalLength" and setup "pcdesc" */
@@ -109,6 +113,8 @@ libusb20_parse_config_desc(const void *config_desc)
iface_no = ptr[2];
niface_no_alt++;
}
+ } else if (ptr[1] == LIBUSB20_DT_INTERFACE_ASSOCIATION) {
+ niad++;
}
}
@@ -119,10 +125,12 @@ libusb20_parse_config_desc(const void *config_desc)
if (nendpoint >= 256) {
return (NULL); /* corrupt */
}
- size = sizeof(*lub_config) +
- (niface * sizeof(*lub_interface)) +
+ if (niad >= 256)
+ return (NULL);
+
+ size = sizeof(*lub_config) + (niface * sizeof(*lub_interface)) +
(nendpoint * sizeof(*lub_endpoint)) +
- pcdesc.len;
+ (niad * sizeof(*lub_iad)) + pcdesc.len;
lub_config = malloc(size);
if (lub_config == NULL) {
@@ -134,14 +142,15 @@ libusb20_parse_config_desc(const void *config_desc)
lub_interface = (void *)(lub_config + 1);
lub_alt_interface = (void *)(lub_interface + niface_no_alt);
lub_endpoint = (void *)(lub_interface + niface);
+ lub_iad = (void *)(lub_endpoint + nendpoint);
/*
* Make a copy of the config descriptor, so that the caller can free
* the initial config descriptor pointer!
*/
- memcpy((void *)(lub_endpoint + nendpoint), config_desc, pcdesc.len);
+ memcpy((void *)(lub_iad + niad), config_desc, pcdesc.len);
- ptr = (const void *)(lub_endpoint + nendpoint);
+ ptr = (const void *)(lub_iad + niad);
pcdesc.ptr = LIBUSB20_ADD_BYTES(ptr, 0);
/* init config structure */
@@ -156,6 +165,8 @@ libusb20_parse_config_desc(const void *config_desc)
lub_config->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
lub_config->extra.len = -ptr[0];
lub_config->extra.type = LIBUSB20_ME_IS_RAW;
+ lub_config->niad = niad;
+ lub_config->iad_desc = lub_iad;
/* reset states */
niface = 0;
@@ -163,6 +174,7 @@ libusb20_parse_config_desc(const void *config_desc)
ptr = NULL;
lub_interface--;
lub_endpoint--;
+ lub_iad--;
last_if = NULL;
last_ep = NULL;
@@ -223,6 +235,14 @@ libusb20_parse_config_desc(const void *config_desc)
last_if->num_altsetting = 0;
last_if->num_endpoints = 0;
last_ep = NULL;
+ } else if (ptr[1] == LIBUSB20_DT_INTERFACE_ASSOCIATION) {
+ lub_iad++;
+ LIBUSB20_INIT(LIBUSB20_INTERFACE_ASSOCIATION_DESC,
+ &lub_iad->desc);
+ libusb20_me_decode(ptr, ptr[0], &lub_iad->desc);
+ lub_iad->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]);
+ lub_iad->extra.len = 0;
+ lub_iad->extra.type = LIBUSB20_ME_IS_RAW;
} else {
/* unknown descriptor */
if (last_if) {
diff --git a/lib/libusb/libusb20_desc.h b/lib/libusb/libusb20_desc.h
index 0f7c9294ebc8..f19a9dc00a24 100644
--- a/lib/libusb/libusb20_desc.h
+++ b/lib/libusb/libusb20_desc.h
@@ -248,6 +248,18 @@ LIBUSB20_MAKE_STRUCT(LIBUSB20_ENDPOINT_DESC);
LIBUSB20_MAKE_STRUCT(LIBUSB20_INTERFACE_DESC);
+#define LIBUSB20_INTERFACE_ASSOCIATION_DESC(m,n) \
+ m(n, UINT8_T, bLength, ) \
+ m(n, UINT8_T, bDescriptorType, ) \
+ m(n, UINT8_T, bFirstInterface, ) \
+ m(n, UINT8_T, bInterfaceCount, ) \
+ m(n, UINT8_T, bFunctionClass, ) \
+ m(n, UINT8_T, bFunctionSubClass, ) \
+ m(n, UINT8_T, bFunctionProtocol, ) \
+ m(n, UINT8_T, iFunction, ) \
+
+LIBUSB20_MAKE_STRUCT(LIBUSB20_INTERFACE_ASSOCIATION_DESC);
+
#define LIBUSB20_CONFIG_DESC(m,n) \
m(n, UINT8_T, bLength, ) \
m(n, UINT8_T, bDescriptorType, ) \
@@ -368,6 +380,10 @@ enum libusb20_descriptor_type {
/** Endpoint descriptor. See LIBUSB20_ENDPOINT_DESC. */
LIBUSB20_DT_ENDPOINT = 0x05,
+ /** Interface Association descriptor. See
+ LIBUSB20_INTERFACE_ASSOCIATION_DESC. */
+ LIBUSB20_DT_INTERFACE_ASSOCIATION = 0x0b,
+
/** HID descriptor */
LIBUSB20_DT_HID = 0x21,
@@ -583,11 +599,18 @@ struct libusb20_interface {
uint8_t num_endpoints;
} __aligned(sizeof(void *));
+struct libusb20_iad_desc {
+ struct LIBUSB20_INTERFACE_ASSOCIATION_DESC_DECODED desc;
+ struct libusb20_me_struct extra;
+} __aligned(sizeof(void *));
+
struct libusb20_config {
struct LIBUSB20_CONFIG_DESC_DECODED desc;
struct libusb20_me_struct extra;
struct libusb20_interface *interface;
uint8_t num_interface;
+ struct libusb20_iad_desc *iad_desc;
+ int niad;
} __aligned(sizeof(void *));
uint8_t libusb20_me_get_1(const struct libusb20_me_struct *ie, uint16_t offset);