From 7a5614eeb259feb19b563ec22600045a59989dca Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Fri, 9 Feb 2024 10:21:35 +0100 Subject: [PATCH] feat(cdc_acm): Open device with any VID/PID --- host/class/cdc/usb_host_cdc_acm/CHANGELOG.md | 1 + host/class/cdc/usb_host_cdc_acm/README.md | 2 + .../class/cdc/usb_host_cdc_acm/cdc_acm_host.c | 26 +++++++------ .../include/usb/cdc_acm_host.h | 11 +++++- .../test_app/main/test_cdc_acm_host.c | 37 +++++++++++++++++++ 5 files changed, 63 insertions(+), 14 deletions(-) diff --git a/host/class/cdc/usb_host_cdc_acm/CHANGELOG.md b/host/class/cdc/usb_host_cdc_acm/CHANGELOG.md index 42daf0b6..5e764708 100644 --- a/host/class/cdc/usb_host_cdc_acm/CHANGELOG.md +++ b/host/class/cdc/usb_host_cdc_acm/CHANGELOG.md @@ -1,6 +1,7 @@ ## [Unreleased] - Fixed CDC descriptor parsing logic, when some CDC devices could not be opened +- Added an option to open a CDC device with any VID/PID ## 2.0.4 diff --git a/host/class/cdc/usb_host_cdc_acm/README.md b/host/class/cdc/usb_host_cdc_acm/README.md index b28344d4..1a110018 100644 --- a/host/class/cdc/usb_host_cdc_acm/README.md +++ b/host/class/cdc/usb_host_cdc_acm/README.md @@ -43,6 +43,8 @@ The following steps outline the typical API call pattern of the CDC-ACM Class Dr 6. An opened device can be closed via `cdc_acm_host_close()` 7. The CDC-ACM driver can be uninstalled via `cdc_acm_host_uninstall()` +Use `CDC_HOST_ANY_*` macros to signal to `cdc_acm_host_open()` function that you don't care about the device's VID and PID. In this case, first USB device will be opened. It is recommended to use this feature if only one device can ever be in the system (there is no USB HUB connected). + ## Examples - For an example with a CDC-ACM device, refer to [cdc_acm_host](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb/host/cdc/cdc_acm_host) diff --git a/host/class/cdc/usb_host_cdc_acm/cdc_acm_host.c b/host/class/cdc/usb_host_cdc_acm/cdc_acm_host.c index 50d575c7..b60e8488 100644 --- a/host/class/cdc/usb_host_cdc_acm/cdc_acm_host.c +++ b/host/class/cdc/usb_host_cdc_acm/cdc_acm_host.c @@ -255,7 +255,7 @@ static esp_err_t cdc_acm_start(cdc_dev_t *cdc_dev, cdc_acm_host_dev_callback_t e cdc_dev->data.intf_desc->bAlternateSetting), err, TAG, "Could not claim interface"); if (cdc_dev->data.in_xfer) { - ESP_LOGD("CDC_ACM", "Submitting poll for BULK IN transfer"); + ESP_LOGD(TAG, "Submitting poll for BULK IN transfer"); ESP_ERROR_CHECK(usb_host_transfer_submit(cdc_dev->data.in_xfer)); } @@ -270,7 +270,7 @@ static esp_err_t cdc_acm_start(cdc_dev_t *cdc_dev, cdc_acm_host_dev_callback_t e cdc_dev->notif.intf_desc->bAlternateSetting), err, TAG, "Could not claim interface"); } - ESP_LOGD("CDC_ACM", "Submitting poll for INTR IN transfer"); + ESP_LOGD(TAG, "Submitting poll for INTR IN transfer"); ESP_ERROR_CHECK(usb_host_transfer_submit(cdc_dev->notif.xfer)); } @@ -337,7 +337,8 @@ static esp_err_t cdc_acm_find_and_open_usb_device(uint16_t vid, uint16_t pid, in SLIST_FOREACH(cdc_dev, &p_cdc_acm_obj->cdc_devices_list, list_entry) { const usb_device_desc_t *device_desc; ESP_ERROR_CHECK(usb_host_get_device_descriptor(cdc_dev->dev_hdl, &device_desc)); - if (device_desc->idVendor == vid && device_desc->idProduct == pid) { + if ((vid == device_desc->idVendor || vid == CDC_HOST_ANY_VID) && + (pid == device_desc->idProduct || pid == CDC_HOST_ANY_PID)) { // Return path 1: (*dev)->dev_hdl = cdc_dev->dev_hdl; return ESP_OK; @@ -365,7 +366,8 @@ static esp_err_t cdc_acm_find_and_open_usb_device(uint16_t vid, uint16_t pid, in assert(current_device); const usb_device_desc_t *device_desc; ESP_ERROR_CHECK(usb_host_get_device_descriptor(current_device, &device_desc)); - if (device_desc->idVendor == vid && device_desc->idProduct == pid) { + if ((vid == device_desc->idVendor || vid == CDC_HOST_ANY_VID) && + (pid == device_desc->idProduct || pid == CDC_HOST_ANY_PID)) { // Return path 2: (*dev)->dev_hdl = current_device; return ESP_OK; @@ -788,7 +790,7 @@ static bool cdc_acm_is_transfer_completed(usb_transfer_t *transfer) static void in_xfer_cb(usb_transfer_t *transfer) { - ESP_LOGD("CDC_ACM", "in xfer cb"); + ESP_LOGD(TAG, "in xfer cb"); cdc_dev_t *cdc_dev = (cdc_dev_t *)transfer->context; if (!cdc_acm_is_transfer_completed(transfer)) { @@ -832,13 +834,13 @@ static void in_xfer_cb(usb_transfer_t *transfer) } } - ESP_LOGD("CDC_ACM", "Submitting poll for BULK IN transfer"); + ESP_LOGD(TAG, "Submitting poll for BULK IN transfer"); usb_host_transfer_submit(cdc_dev->data.in_xfer); } static void notif_xfer_cb(usb_transfer_t *transfer) { - ESP_LOGD("CDC_ACM", "notif xfer cb"); + ESP_LOGD(TAG, "notif xfer cb"); cdc_dev_t *cdc_dev = (cdc_dev_t *)transfer->context; if (cdc_acm_is_transfer_completed(transfer)) { @@ -867,20 +869,20 @@ static void notif_xfer_cb(usb_transfer_t *transfer) } case USB_CDC_NOTIF_RESPONSE_AVAILABLE: // Encapsulated commands not implemented - fallthrough default: - ESP_LOGW("CDC_ACM", "Unsupported notification type 0x%02X", notif->bNotificationCode); - ESP_LOG_BUFFER_HEX("CDC_ACM", transfer->data_buffer, transfer->actual_num_bytes); + ESP_LOGW(TAG, "Unsupported notification type 0x%02X", notif->bNotificationCode); + ESP_LOG_BUFFER_HEX(TAG, transfer->data_buffer, transfer->actual_num_bytes); break; } // Start polling for new data again - ESP_LOGD("CDC_ACM", "Submitting poll for INTR IN transfer"); + ESP_LOGD(TAG, "Submitting poll for INTR IN transfer"); usb_host_transfer_submit(cdc_dev->notif.xfer); } } static void out_xfer_cb(usb_transfer_t *transfer) { - ESP_LOGD("CDC_ACM", "out/ctrl xfer cb"); + ESP_LOGD(TAG, "out/ctrl xfer cb"); assert(transfer->context); xSemaphoreGive((SemaphoreHandle_t)transfer->context); } @@ -945,7 +947,7 @@ esp_err_t cdc_acm_host_data_tx_blocking(cdc_acm_dev_hdl_t cdc_hdl, const uint8_t return ESP_ERR_TIMEOUT; } - ESP_LOGD("CDC_ACM", "Submitting BULK OUT transfer"); + ESP_LOGD(TAG, "Submitting BULK OUT transfer"); SemaphoreHandle_t transfer_finished_semaphore = (SemaphoreHandle_t)cdc_dev->data.out_xfer->context; xSemaphoreTake(transfer_finished_semaphore, 0); // Make sure the semaphore is taken before we submit new transfer diff --git a/host/class/cdc/usb_host_cdc_acm/include/usb/cdc_acm_host.h b/host/class/cdc/usb_host_cdc_acm/include/usb/cdc_acm_host.h index b80db0a2..a575f1b6 100644 --- a/host/class/cdc/usb_host_cdc_acm/include/usb/cdc_acm_host.h +++ b/host/class/cdc/usb_host_cdc_acm/include/usb/cdc_acm_host.h @@ -11,6 +11,10 @@ #include "usb/usb_types_cdc.h" #include "esp_err.h" +// Pass these to cdc_acm_host_open() to signal that you don't care about VID/PID of the opened device +#define CDC_HOST_ANY_VID (0) +#define CDC_HOST_ANY_PID (0) + #ifdef __cplusplus extern "C" { #endif @@ -166,8 +170,11 @@ esp_err_t cdc_acm_host_register_new_dev_callback(cdc_acm_new_dev_callback_t new_ * * The driver first looks for CDC compliant descriptor, if it is not found the driver checks if the interface has 2 Bulk endpoints that can be used for data * - * @param[in] vid Device's Vendor ID - * @param[in] pid Device's Product ID + * Use CDC_HOST_ANY_* macros to signal that you don't care about the device's VID and PID. In this case, first USB device will be opened. + * It is recommended to use this feature if only one device can ever be in the system (there is no USB HUB connected). + * + * @param[in] vid Device's Vendor ID, set to CDC_HOST_ANY_VID for any + * @param[in] pid Device's Product ID, set to CDC_HOST_ANY_PID for any * @param[in] interface_idx Index of device's interface used for CDC-ACM communication * @param[in] dev_config Configuration structure of the device * @param[out] cdc_hdl_ret CDC device handle diff --git a/host/class/cdc/usb_host_cdc_acm/test_app/main/test_cdc_acm_host.c b/host/class/cdc/usb_host_cdc_acm/test_app/main/test_cdc_acm_host.c index c2dc5de0..54f5cf3c 100644 --- a/host/class/cdc/usb_host_cdc_acm/test_app/main/test_cdc_acm_host.c +++ b/host/class/cdc/usb_host_cdc_acm/test_app/main/test_cdc_acm_host.c @@ -674,6 +674,43 @@ TEST_CASE("tx_timeout", "[cdc_acm]") vTaskDelay(20); //Short delay to allow task to be cleaned up } +/** + * @brief Test: Opening with any VID/PID + * + * #. Try to open a device with all combinations of any VID/PID + * #. Try to open a non-existing device with all combinations of any VID/PID + */ +TEST_CASE("any_vid_pid", "[cdc_acm]") +{ + cdc_acm_dev_hdl_t cdc_dev = NULL; + test_install_cdc_driver(); + + const cdc_acm_host_device_config_t dev_config = { + .connection_timeout_ms = 500, + .out_buffer_size = 64, + .event_cb = notif_cb, + .data_cb = handle_rx, + .user_arg = tx_buf, + }; + + printf("Opening existing CDC-ACM devices with any VID/PID\n"); + TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(CDC_HOST_ANY_VID, CDC_HOST_ANY_PID, 0, &dev_config, &cdc_dev)); + TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev)); + + TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(0x303A, CDC_HOST_ANY_PID, 0, &dev_config, &cdc_dev)); + TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev)); + + TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_open(CDC_HOST_ANY_VID, 0x4002, 0, &dev_config, &cdc_dev)); + TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_close(cdc_dev)); + + printf("Opening non-existing CDC-ACM devices with any VID/PID\n"); + TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, cdc_acm_host_open(0x1234, CDC_HOST_ANY_PID, 0, &dev_config, &cdc_dev)); + TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, cdc_acm_host_open(CDC_HOST_ANY_VID, 0x1234, 0, &dev_config, &cdc_dev)); + + TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_uninstall()); + vTaskDelay(20); //Short delay to allow task to be cleaned up +} + /* Following test case implements dual CDC-ACM USB device that can be used as mock device for CDC-ACM Host tests */ void run_usb_dual_cdc_device(void); TEST_CASE("mock_device_app", "[cdc_acm_device][ignore]")