The branch main has been updated by aokblast:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=ae489991d1c0b550ead309abc1eade3b185e7499

commit ae489991d1c0b550ead309abc1eade3b185e7499
Author:     ShengYi Hung <[email protected]>
AuthorDate: 2025-06-11 17:24:04 +0000
Commit:     ShengYi Hung <[email protected]>
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);

Reply via email to