From a04eaab135b02224a46d0d1417c4e41ab06be880 Mon Sep 17 00:00:00 2001 From: Li Bo Date: Mon, 8 Jul 2024 20:47:16 +0800 Subject: [PATCH] feat(host_uac): support open device with known vid pid --- host/class/uac/usb_host_uac/CHANGELOG.md | 9 ++ host/class/uac/usb_host_uac/CMakeLists.txt | 3 + host/class/uac/usb_host_uac/Kconfig | 6 ++ host/class/uac/usb_host_uac/idf_component.yml | 3 +- .../uac/usb_host_uac/include/usb/uac_host.h | 25 ++++- .../test_app/main/test_host_uac.c | 89 +++++++++++++++++ host/class/uac/usb_host_uac/uac_host.c | 97 ++++++++++++++++++- 7 files changed, 227 insertions(+), 5 deletions(-) diff --git a/host/class/uac/usb_host_uac/CHANGELOG.md b/host/class/uac/usb_host_uac/CHANGELOG.md index 532bcd2c..f654ac03 100644 --- a/host/class/uac/usb_host_uac/CHANGELOG.md +++ b/host/class/uac/usb_host_uac/CHANGELOG.md @@ -1,3 +1,12 @@ +# Changelog for USB Host UAC + +## 1.1.0 + +### Improvements + +- Added add `uac_host_device_open_with_vid_pid` to open connected audio devices with known VID and PID +- Print component version message `uac-host: Install Succeed, Version: 1.1.0` in `uac_host_install` function + ## 1.0.0 - Initial version diff --git a/host/class/uac/usb_host_uac/CMakeLists.txt b/host/class/uac/usb_host_uac/CMakeLists.txt index 6f198ee3..e1d023ba 100644 --- a/host/class/uac/usb_host_uac/CMakeLists.txt +++ b/host/class/uac/usb_host_uac/CMakeLists.txt @@ -1,3 +1,6 @@ idf_component_register( SRCS "uac_descriptors.c" "uac_host.c" INCLUDE_DIRS "include" PRIV_REQUIRES usb esp_ringbuf) + +include(package_manager) +cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR}) diff --git a/host/class/uac/usb_host_uac/Kconfig b/host/class/uac/usb_host_uac/Kconfig index 011de6aa..b8771283 100644 --- a/host/class/uac/usb_host_uac/Kconfig +++ b/host/class/uac/usb_host_uac/Kconfig @@ -4,6 +4,12 @@ menu "USB Host UAC" default n help Print UAC Configuration Descriptor to console. + config UAC_DEV_ADDR_LIST_MAX + int "Max USB Device Address List" + default 6 + help + Max USB Device Address List to find the UAC device. If more devices are connected. + The driver will only use the first UAC_DEV_ADDR_LIST_MAX devices. config UAC_FREQ_NUM_MAX int "Max Number of Frequencies each Alt-interface supports" default 4 diff --git a/host/class/uac/usb_host_uac/idf_component.yml b/host/class/uac/usb_host_uac/idf_component.yml index f732b7fa..454f7059 100644 --- a/host/class/uac/usb_host_uac/idf_component.yml +++ b/host/class/uac/usb_host_uac/idf_component.yml @@ -1,9 +1,10 @@ ## IDF Component Manager Manifest File -version: "1.0.0" +version: "1.1.0" description: USB Host UAC driver url: https://github.com/espressif/esp-usb/tree/master/host/class/uac/usb_host_uac dependencies: idf: ">=4.4" + cmake_utilities: "0.5.*" targets: - esp32s2 - esp32s3 diff --git a/host/class/uac/usb_host_uac/include/usb/uac_host.h b/host/class/uac/usb_host_uac/include/usb/uac_host.h index f9733f9b..18c2bdbc 100644 --- a/host/class/uac/usb_host_uac/include/usb/uac_host.h +++ b/host/class/uac/usb_host_uac/include/usb/uac_host.h @@ -197,8 +197,10 @@ esp_err_t uac_host_install(const uac_host_driver_config_t *config); esp_err_t uac_host_uninstall(void); /** - * @brief Open a UAC logic device/interface + * @brief Open a UAC logic device/interface when new UAC device is connected * + * @note The config->addr and config->iface_num should be retrieved from the UAC driver event callback + * after UAC_HOST_DRIVER_EVENT_RX_CONNECTED or UAC_HOST_DRIVER_EVENT_TX_CONNECTED event * @param[in] config Pointer to UAC device configuration structure * @param[out] uac_dev_handle Pointer to UAC device handle * @return esp_err_t @@ -210,6 +212,27 @@ esp_err_t uac_host_uninstall(void); */ esp_err_t uac_host_device_open(const uac_host_device_config_t *config, uac_host_device_handle_t *uac_dev_handle); +/** + * @brief Open a UAC logic device/interface with specific VID, PID and interface number + * + * @note The config->addr is not used in this function, the VID, PID and config->iface_num should be provided by user. + * If multiple hardware devices with the same VID and PID are connected through USB hub, the first one will be opened. + * + * @param[in] vid Vendor ID + * @param[in] pid Product ID + * @param[in] config Pointer to UAC device configuration structure + * @param[out] uac_dev_handle Pointer to UAC device handle + * @return esp_err_t + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if UAC driver is not installed + * - ESP_ERR_INVALID_ARG if the configuration is invalid + * - ESP_ERR_NO_MEM if memory allocation failed + * - ESP_ERR_NOT_SUPPORTED if the UAC version is not supported + * - ESP_ERR_NOT_FOUND if the device is not found + */ +esp_err_t uac_host_device_open_with_vid_pid(uint16_t vid, uint16_t pid, const uac_host_device_config_t *config, + uac_host_device_handle_t *uac_dev_handle); + /** * @brief Close a UAC logic device/interface * diff --git a/host/class/uac/usb_host_uac/test_app/main/test_host_uac.c b/host/class/uac/usb_host_uac/test_app/main/test_host_uac.c index 9f9629ee..26047073 100644 --- a/host/class/uac/usb_host_uac/test_app/main/test_host_uac.c +++ b/host/class/uac/usb_host_uac/test_app/main/test_host_uac.c @@ -370,6 +370,95 @@ TEST_CASE("test uac device handling", "[uac_host][known_device]") } } +/** + * @brief Test with known UAC device, check if the device's parameters are parsed correctly + * @note please modify the known device parameters if the device is changed + */ +TEST_CASE("test uac device handling with known pid vid", "[uac_host][known_device]") +{ + uint8_t mic_iface_num = UAC_DEV_MIC_IFACE_NUM; + uint8_t spk_iface_num = UAC_DEV_SPK_IFACE_NUM; + uint8_t test_counter = 0; + + while (++test_counter < 5) { + // check if mic device params as expected + uac_host_device_handle_t mic_device_handle = NULL; + uac_host_dev_info_t dev_info; + // check if device params as expected + uac_host_device_config_t dev_config = { + .iface_num = mic_iface_num, + .buffer_size = 16000, + .buffer_threshold = 4000, + .callback = uac_device_callback, + .callback_arg = NULL, + }; + + do { + esp_err_t ret = uac_host_device_open_with_vid_pid(UAC_DEV_VID, UAC_DEV_PID, &dev_config, &mic_device_handle); + if (ret == ESP_ERR_NOT_FOUND) { + ESP_LOGI(TAG, "Device not found, please connect the device"); + vTaskDelay(1000); + } + } while (mic_device_handle == NULL); + + TEST_ASSERT_EQUAL(ESP_OK, uac_host_device_open_with_vid_pid(UAC_DEV_VID, UAC_DEV_PID, &dev_config, &mic_device_handle)); + TEST_ASSERT_EQUAL(ESP_OK, uac_host_get_device_info(mic_device_handle, &dev_info)); + TEST_ASSERT_EQUAL(UAC_STREAM_RX, dev_info.type); + ESP_LOGI(TAG, "UAC Device opened: MIC"); + TEST_ASSERT_EQUAL(ESP_OK, uac_host_printf_device_param(mic_device_handle)); + TEST_ASSERT_EQUAL(UAC_DEV_PID, dev_info.PID); + TEST_ASSERT_EQUAL(UAC_DEV_VID, dev_info.VID); + TEST_ASSERT_EQUAL(UAC_DEV_MIC_IFACE_NUM, dev_info.iface_num); + TEST_ASSERT_EQUAL(UAC_DEV_MIC_IFACE_ALT_NUM, dev_info.iface_alt_num); + printf("iManufacturer: %ls\n", dev_info.iManufacturer); + printf("iProduct: %ls\n", dev_info.iProduct); + printf("iSerialNumber: %ls\n", dev_info.iSerialNumber); + uac_host_dev_alt_param_t iface_alt_params; + for (int i = 0; i < dev_info.iface_alt_num; i++) { + TEST_ASSERT_EQUAL(ESP_OK, uac_host_get_device_alt_param(mic_device_handle, i + 1, &iface_alt_params)); + TEST_ASSERT_EQUAL(UAC_DEV_MIC_IFACE_CHANNELS_ALT[i], iface_alt_params.channels); + TEST_ASSERT_EQUAL(UAC_DEV_MIC_IFACE_BIT_RESOLUTION_ALT[i], iface_alt_params.bit_resolution); + TEST_ASSERT_EQUAL(UAC_DEV_MIC_IFACE_SAMPLE_FREQ_TPYE_ALT[i], iface_alt_params.sample_freq_type); + // check frequency one by one + for (size_t j = 0; j < iface_alt_params.sample_freq_type; j++) { + TEST_ASSERT_EQUAL(UAC_DEV_MIC_IFACE_SAMPLE_FREQ_ALT[i][j], iface_alt_params.sample_freq[j]); + } + } + + // check if spk device params as expected + uac_host_device_handle_t spk_device_handle = NULL; + dev_config.iface_num = spk_iface_num; + TEST_ASSERT_EQUAL(ESP_OK, uac_host_device_open_with_vid_pid(UAC_DEV_VID, UAC_DEV_PID, &dev_config, &spk_device_handle)); + TEST_ASSERT_EQUAL(ESP_OK, uac_host_get_device_info(spk_device_handle, &dev_info)); + TEST_ASSERT_EQUAL(UAC_STREAM_TX, dev_info.type); + ESP_LOGI(TAG, "UAC Device opened: SPK"); + TEST_ASSERT_EQUAL(ESP_OK, uac_host_printf_device_param(spk_device_handle)); + TEST_ASSERT_EQUAL(UAC_DEV_PID, dev_info.PID); + TEST_ASSERT_EQUAL(UAC_DEV_VID, dev_info.VID); + TEST_ASSERT_EQUAL(UAC_DEV_SPK_IFACE_NUM, dev_info.iface_num); + TEST_ASSERT_EQUAL(UAC_DEV_SPK_IFACE_ALT_NUM, dev_info.iface_alt_num); + printf("iManufacturer: %ls\n", dev_info.iManufacturer); + printf("iProduct: %ls\n", dev_info.iProduct); + printf("iSerialNumber: %ls\n", dev_info.iSerialNumber); + + for (int i = 0; i < dev_info.iface_alt_num; i++) { + TEST_ASSERT_EQUAL(ESP_OK, uac_host_get_device_alt_param(spk_device_handle, i + 1, &iface_alt_params)); + TEST_ASSERT_EQUAL(UAC_DEV_SPK_IFACE_CHANNELS_ALT[i], iface_alt_params.channels); + TEST_ASSERT_EQUAL(UAC_DEV_SPK_IFACE_BIT_RESOLUTION_ALT[i], iface_alt_params.bit_resolution); + TEST_ASSERT_EQUAL(UAC_DEV_SPK_IFACE_SAMPLE_FREQ_TPYE_ALT[i], iface_alt_params.sample_freq_type); + for (size_t j = 0; j < iface_alt_params.sample_freq_type; j++) { + TEST_ASSERT_EQUAL(UAC_DEV_SPK_IFACE_SAMPLE_FREQ_ALT[i][j], iface_alt_params.sample_freq[j]); + } + } + + // close the device + test_close_device(mic_device_handle); + test_close_device(spk_device_handle); + // reset the queue + test_uac_queue_reset(); + } +} + /** * @brief record the rx stream data from microphone */ diff --git a/host/class/uac/usb_host_uac/uac_host.c b/host/class/uac/usb_host_uac/uac_host.c index 5cb951d4..eb3d49a6 100644 --- a/host/class/uac/usb_host_uac/uac_host.c +++ b/host/class/uac/usb_host_uac/uac_host.c @@ -321,6 +321,26 @@ static uac_device_t *get_uac_device_by_addr(uint8_t addr) return NULL; } +/** + * @brief Return UAC Device from USB device VID and PID + * @param[in] vid USB device VID + * @param[in] pid USB device PID + * @return uac_device_t Pointer to UAC Device + */ +static uac_device_t *get_uac_device_by_vid_pid(uint16_t vid, uint16_t pid) +{ + uac_iface_t *interface = NULL; + + UAC_ENTER_CRITICAL(); + STAILQ_FOREACH(interface, &s_uac_driver->uac_ifaces_tailq, tailq_entry) { + if (vid == interface->dev_info.VID && pid == interface->dev_info.PID) { + UAC_EXIT_CRITICAL(); + return interface->parent; + } + } + UAC_EXIT_CRITICAL(); + return NULL; +} /** * @brief Get UAC Interface pointer by Interface number @@ -345,6 +365,29 @@ static uac_iface_t *get_interface_by_addr(uint8_t addr, uint8_t iface_num) return NULL; } +/** + * @brief Get UAC Interface pointer by vid, pid and Interface number + * @param[in] vid USB device VID + * @param[in] pid USB device PID + * @param[in] iface_num Interface number + * @return uac_iface_t Pointer to UAC Interface configuration structure + */ +static uac_iface_t *get_interface_by_vid_pid(uint16_t vid, uint16_t pid, uint8_t iface_num) +{ + uac_iface_t *interface = NULL; + + UAC_ENTER_CRITICAL(); + STAILQ_FOREACH(interface, &s_uac_driver->uac_ifaces_tailq, tailq_entry) { + if (vid == interface->dev_info.VID && pid == interface->dev_info.PID && iface_num == interface->dev_info.iface_num) { + UAC_EXIT_CRITICAL(); + return interface; + } + } + + UAC_EXIT_CRITICAL(); + return NULL; +} + /** * @brief Verify presence of Interface in the RAM list * @@ -1331,7 +1374,7 @@ static esp_err_t _uac_host_device_add(uint8_t addr, usb_device_handle_t dev_hdl, case UAC_AC_HEADER: { const uac_ac_header_desc_t *header_desc = (const uac_ac_header_desc_t *)uac_cs_desc; if (header_desc->bcdADC != 0x0100) { - ESP_LOGW(TAG, "UAC version %04X not supported", header_desc->bcdADC); + ESP_LOGW(TAG, "UAC version 0x%04X not supported", header_desc->bcdADC); free(uac_device); return ESP_ERR_NOT_SUPPORTED; } @@ -1339,7 +1382,7 @@ static esp_err_t _uac_host_device_add(uint8_t addr, usb_device_handle_t dev_hdl, UAC_GOTO_ON_FALSE(cs_ac_desc, ESP_ERR_NO_MEM, "Unable to allocate memory for UAC Control CS descriptor"); memcpy(cs_ac_desc, uac_cs_desc, header_desc->wTotalLength); uac_device->cs_ac_desc = cs_ac_desc; - ESP_LOGD(TAG, "UAC version %04X", header_desc->bcdADC); + ESP_LOGD(TAG, "UAC version 0x%04X", header_desc->bcdADC); break; } default: @@ -1678,7 +1721,7 @@ esp_err_t uac_host_install(const uac_host_driver_config_t *config) NULL, config->task_priority, NULL, config->core_id); UAC_GOTO_ON_FALSE(task_created, ESP_ERR_NO_MEM, "Unable to create USB UAC Host task"); } - + ESP_LOGI(TAG, "Install Succeed, Version: %d.%d.%d", USB_HOST_UAC_VER_MAJOR, USB_HOST_UAC_VER_MINOR, USB_HOST_UAC_VER_PATCH); return ESP_OK; fail: @@ -1790,6 +1833,54 @@ esp_err_t uac_host_device_open(const uac_host_device_config_t *config, uac_host_ return ret; } +esp_err_t uac_host_device_open_with_vid_pid(uint16_t vid, uint16_t pid, const uac_host_device_config_t *config, uac_host_device_handle_t *uac_dev_handle) +{ + UAC_RETURN_ON_INVALID_ARG(uac_dev_handle); + UAC_RETURN_ON_INVALID_ARG(config); + UAC_RETURN_ON_FALSE(config->iface_num, ESP_ERR_INVALID_ARG, "Invalid interface number"); + UAC_RETURN_ON_FALSE(s_uac_driver, ESP_ERR_INVALID_STATE, "UAC Driver is not installed"); + UAC_RETURN_ON_FALSE(vid, ESP_ERR_INVALID_ARG, "Invalid vendor ID"); + UAC_RETURN_ON_FALSE(pid, ESP_ERR_INVALID_ARG, "Invalid product ID"); + + // check if the device is already opened + *uac_dev_handle = get_interface_by_vid_pid(vid, pid, config->iface_num); + if (*uac_dev_handle) { + ESP_LOGD(TAG, "Device VID 0x%04X, PID 0x%04X, Interface %d already opened", vid, pid, config->iface_num); + return ESP_OK; + } + + // check all connected devices + int actual_count = 0; + uint8_t dev_addr_list[CONFIG_UAC_DEV_ADDR_LIST_MAX] = {0}; + usb_host_device_addr_list_fill(sizeof(dev_addr_list), dev_addr_list, &actual_count); + if (actual_count == 0) { + return ESP_ERR_NOT_FOUND; + } + + for (int i = 0; i < actual_count; i++) { + const usb_device_desc_t *dev_desc = NULL; + usb_device_handle_t dev_hdl = NULL; + uac_device_t *uac_dev = get_uac_device_by_vid_pid(vid, pid); + uac_host_device_config_t config_copy = *config; + config_copy.addr = dev_addr_list[i]; + if (uac_dev) { + ESP_LOGD(TAG, "Found Device VID 0x%04X, PID 0x%04X", vid, pid); + return uac_host_device_open(&config_copy, uac_dev_handle); + } else { + UAC_RETURN_ON_ERROR(usb_host_device_open(s_uac_driver->client_handle, dev_addr_list[i], &dev_hdl), "Unable to open USB device"); + UAC_RETURN_ON_ERROR(usb_host_get_device_descriptor(dev_hdl, &dev_desc), "Unable to get device descriptor"); + ESP_LOGD(TAG, "Found Device VID 0x%04X, PID 0x%04X", dev_desc->idVendor, dev_desc->idProduct); + if (dev_desc->idVendor == vid && dev_desc->idProduct == pid) { + usb_host_device_close(s_uac_driver->client_handle, dev_hdl); + return uac_host_device_open(&config_copy, uac_dev_handle); + } + usb_host_device_close(s_uac_driver->client_handle, dev_hdl); + } + } + + return ESP_ERR_NOT_FOUND; +} + esp_err_t uac_host_device_close(uac_host_device_handle_t uac_dev_handle) { uac_iface_t *uac_iface = get_iface_by_handle(uac_dev_handle);