diff --git a/lib/libusb/libusb.h b/lib/libusb/libusb.h --- a/lib/libusb/libusb.h +++ b/lib/libusb/libusb.h @@ -77,6 +77,7 @@ 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, @@ -101,6 +102,7 @@ #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 @@ -355,6 +357,22 @@ 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 *)); + +struct libusb_interface_association_descriptor_array { + const struct libusb_interface_association_descriptor *iad; + int length; +}; + typedef struct libusb_interface_descriptor { uint8_t bLength; uint8_t bDescriptorType; @@ -567,6 +585,9 @@ 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/libusb.3 b/lib/libusb/libusb.3 --- a/lib/libusb/libusb.3 +++ b/lib/libusb/libusb.3 @@ -559,6 +559,31 @@ .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 . +.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/libusb10_desc.c b/lib/libusb/libusb10_desc.c --- a/lib/libusb/libusb10_desc.c +++ b/lib/libusb/libusb10_desc.c @@ -763,3 +763,93 @@ { 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(sizeof( + struct libusb_interface_association_descriptor_array), + 1); + 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.h b/lib/libusb/libusb20_desc.h --- a/lib/libusb/libusb20_desc.h +++ b/lib/libusb/libusb20_desc.h @@ -248,6 +248,18 @@ 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 @@ /** 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 @@ 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); diff --git a/lib/libusb/libusb20_desc.c b/lib/libusb/libusb20_desc.c --- a/lib/libusb/libusb20_desc.c +++ b/lib/libusb/libusb20_desc.c @@ -44,6 +44,7 @@ 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,15 @@ struct libusb20_interface *last_if; struct libusb20_endpoint *lub_endpoint; struct libusb20_endpoint *last_ep; + struct libusb20_iad_desc *last_iad; + 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 +93,7 @@ nendpoint = 0; niface = 0; iface_no = 0xFFFF; + niad = 0; ptr = NULL; /* get "wTotalLength" and setup "pcdesc" */ @@ -109,6 +114,8 @@ iface_no = ptr[2]; niface_no_alt++; } + } else if (ptr[1] == LIBUSB20_DT_INTERFACE_ASSOCIATION) { + niad++; } } @@ -119,10 +126,12 @@ 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 +143,15 @@ 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 +166,8 @@ 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,8 +175,10 @@ ptr = NULL; lub_interface--; lub_endpoint--; + lub_iad--; last_if = NULL; last_ep = NULL; + last_iad = NULL; /* descriptor pre-scan */ while ((ptr = libusb20_desc_foreach(&pcdesc, ptr))) { @@ -223,6 +237,15 @@ last_if->num_altsetting = 0; last_if->num_endpoints = 0; last_ep = NULL; + } else if (ptr[1] == LIBUSB20_DT_INTERFACE_ASSOCIATION) { + lub_iad++; + last_iad = lub_iad; + LIBUSB20_INIT(LIBUSB20_INTERFACE_ASSOCIATION_DESC, + &last_iad->desc); + libusb20_me_decode(ptr, ptr[0], &last_iad->desc); + last_iad->extra.ptr = LIBUSB20_ADD_BYTES(ptr, ptr[0]); + last_iad->extra.len = 0; + last_iad->extra.type = LIBUSB20_ME_IS_RAW; } else { /* unknown descriptor */ if (last_if) {