diff --git a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/cdc_acm_host.c b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/cdc_acm_host.c index 0edcaa4791..cfdb10e6da 100644 --- a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/cdc_acm_host.c +++ b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/cdc_acm_host.c @@ -53,6 +53,7 @@ typedef struct { usb_host_client_handle_t cdc_acm_client_hdl; /*!< USB Host handle reused for all CDC-ACM devices in the system */ SemaphoreHandle_t open_close_mutex; EventGroupHandle_t event_group; + cdc_acm_new_dev_callback_t new_dev_cb; SLIST_HEAD(list_dev, cdc_dev_s) cdc_devices_list; /*!< List of open pseudo devices */ } cdc_acm_obj_t; @@ -66,7 +67,8 @@ static cdc_acm_obj_t *p_cdc_acm_obj = NULL; static const cdc_acm_host_driver_config_t cdc_acm_driver_config_default = { .driver_task_stack_size = 4096, .driver_task_priority = 10, - .xCoreID = 0 + .xCoreID = 0, + .new_dev_cb = NULL, }; /** @@ -429,6 +431,7 @@ esp_err_t cdc_acm_host_install(const cdc_acm_host_driver_config_t *driver_config cdc_acm_obj->event_group = event_group; cdc_acm_obj->open_close_mutex = mutex; cdc_acm_obj->cdc_acm_client_hdl = usb_client; + cdc_acm_obj->new_dev_cb = driver_config->new_dev_cb; // Between 1st call of this function and following section, another task might try to install this driver: // Make sure that there is only one instance of this driver in the system @@ -1018,6 +1021,16 @@ static void usb_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg switch (event_msg->event) { case USB_HOST_CLIENT_EVENT_NEW_DEV: ESP_LOGD(TAG, "New device connected"); + if (p_cdc_acm_obj->new_dev_cb) { + usb_device_handle_t new_dev; + if (usb_host_device_open(p_cdc_acm_obj->cdc_acm_client_hdl, event_msg->new_dev.address, &new_dev) != ESP_OK) { + ESP_LOGW(TAG, "Couldn't open the new device"); + break; + } + assert(new_dev); + p_cdc_acm_obj->new_dev_cb(new_dev); + usb_host_device_close(p_cdc_acm_obj->cdc_acm_client_hdl, new_dev); + } break; case USB_HOST_CLIENT_EVENT_DEV_GONE: { ESP_LOGD(TAG, "Device suddenly disconnected"); diff --git a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/cdc_acm_host.h b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/cdc_acm_host.h index d3143fecd2..2e60bed368 100644 --- a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/cdc_acm_host.h +++ b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/include/usb/cdc_acm_host.h @@ -7,6 +7,7 @@ #pragma once #include +#include "usb/usb_host.h" #include "usb_types_cdc.h" #include "esp_err.h" @@ -69,6 +70,16 @@ typedef struct { } data; } cdc_acm_host_dev_event_data_t; +/** + * @brief New USB device callback + * + * Provides already opened usb_dev, that will be closed after this callback returns. + * This is useful for peeking device's descriptors, e.g. peeking VID/PID and loading proper driver. + * + * @attention This callback is called from USB Host context, so the CDC device can't be opened here. + */ +typedef void (*cdc_acm_new_dev_callback_t)(usb_device_handle_t usb_dev); + /** * @brief Data receive callback type */ @@ -88,6 +99,7 @@ typedef struct { size_t driver_task_stack_size; /**< Stack size of the driver's task */ unsigned driver_task_priority; /**< Priority of the driver's task */ int xCoreID; /**< Core affinity of the driver's task */ + cdc_acm_new_dev_callback_t new_dev_cb; /**< New USB device connected callback. Can be NULL. */ } cdc_acm_host_driver_config_t; /** diff --git a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/test_cdc_acm_host.c b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/test_cdc_acm_host.c index 526624e720..dafab15799 100644 --- a/examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/test_cdc_acm_host.c +++ b/examples/peripherals/usb/host/cdc/common/cdc_acm_host/test/test_cdc_acm_host.c @@ -130,6 +130,19 @@ static void notif_cb(cdc_acm_dev_hdl_t cdc_hdl, const cdc_acm_host_dev_event_dat } } +static bool new_dev_cb_called = false; +static void new_dev_cb(usb_device_handle_t usb_dev) { + new_dev_cb_called = true; + const usb_config_desc_t *config_desc; + const usb_device_desc_t *device_desc; + + // Get descriptors + TEST_ASSERT_EQUAL(ESP_OK, usb_host_get_device_descriptor(usb_dev, &device_desc)); + TEST_ASSERT_EQUAL(ESP_OK, usb_host_get_active_config_descriptor(usb_dev, &config_desc)); + + printf("New device connected. VID = 0x%04X PID = %04X\n", device_desc->idVendor, device_desc->idProduct); +} + /* Basic test to check CDC communication: * open/read/write/close device * CDC-ACM specific commands: set/get_line_coding, set_control_line_state */ @@ -398,6 +411,29 @@ TEST_CASE("custom_command", "[cdc_acm]") vTaskDelay(20); } +TEST_CASE("new_device_connection", "[cdc_acm]") +{ + // Create a task that will handle USB library events + TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(usb_lib_task, "usb_lib", 4*4096, xTaskGetCurrentTaskHandle(), 10, NULL, 0)); + ulTaskNotifyTake(false, 1000); + + printf("Installing CDC-ACM driver\n"); + const cdc_acm_host_driver_config_t driver_config = { + .driver_task_priority = 11, + .driver_task_stack_size = 2048, + .xCoreID = 0, + .new_dev_cb = new_dev_cb, + }; + TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_install(&driver_config)); + + vTaskDelay(80); + TEST_ASSERT_TRUE_MESSAGE(new_dev_cb_called, "New device callback was not called\n"); + + // Clean-up + TEST_ASSERT_EQUAL(ESP_OK, cdc_acm_host_uninstall()); + vTaskDelay(20); +} + /* 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]")