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);
