From b961f42e8b07f46c9bee62ef1eb46dc297d85b84 Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Tue, 3 Dec 2024 08:52:48 +0100 Subject: [PATCH 1/3] feat(usb/host): Add option to choose peripheral for USB host library Starting with ESP32-P4 we can have targets that have more than 1 USB-OTG peripheral. This commit adds an option to choose which peripherals will be used by USB Host lib. Internally, we will still have only 1 Root HUB but with multiple Root ports. --- components/soc/include/soc/usb_dwc_periph.h | 2 +- components/usb/hcd_dwc.c | 123 +++++++++++------- .../main/usb_host_install_unit_test.cpp | 2 + components/usb/hub.c | 13 +- components/usb/include/usb/usb_host.h | 7 + components/usb/private_include/hcd.h | 10 +- components/usb/private_include/hub.h | 1 + components/usb/test_apps/common/hcd_common.c | 22 +++- components/usb/test_apps/common/phy_common.c | 8 +- components/usb/test_apps/common/phy_common.h | 7 +- .../test_apps/usb_host/main/test_app_main.c | 21 ++- components/usb/usb_host.c | 24 +++- .../en/api-reference/peripherals/usb_host.rst | 2 +- .../usb_host_lib/main/usb_host_lib_main.c | 2 + 14 files changed, 169 insertions(+), 75 deletions(-) diff --git a/components/soc/include/soc/usb_dwc_periph.h b/components/soc/include/soc/usb_dwc_periph.h index 185a6bdb37..f35664d0c9 100644 --- a/components/soc/include/soc/usb_dwc_periph.h +++ b/components/soc/include/soc/usb_dwc_periph.h @@ -106,9 +106,9 @@ extern const usb_dwc_info_t usb_dwc_info; /* ------------------------------- Deprecated ------------------------------- */ /* Todo: Remove in ESP-IDF v6.0 (IDF-9052) */ #include -#include "soc/periph_defs.h" #if SOC_USB_OTG_SUPPORTED +#include "soc/periph_defs.h" /* Stores a bunch of USB-peripheral data. diff --git a/components/usb/hcd_dwc.c b/components/usb/hcd_dwc.c index ea7b96cad8..ecc4711d60 100644 --- a/components/usb/hcd_dwc.c +++ b/components/usb/hcd_dwc.c @@ -43,7 +43,7 @@ #define CTRL_EP_MAX_MPS_LS 8 // Largest Maximum Packet Size for Low Speed control endpoints #define CTRL_EP_MAX_MPS_HSFS 64 // Largest Maximum Packet Size for High & Full Speed control endpoints -#define NUM_PORTS 1 // The controller only has one port. +#define NUM_PORTS SOC_USB_OTG_PERIPH_NUM // Each peripheral is a root port // ----------------------- Configs ------------------------- @@ -108,7 +108,6 @@ DEFINE_CRIT_SECTION_LOCK_STATIC(hcd_lock); * For other SOCs this is no-operation */ #if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE -#define ALIGN_UP_BY(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) #define CACHE_SYNC_FRAME_LIST(frame_list) cache_sync_frame_list(frame_list) #define CACHE_SYNC_XFER_DESCRIPTOR_LIST_M2C(buffer) cache_sync_xfer_descriptor_list(buffer, true) #define CACHE_SYNC_XFER_DESCRIPTOR_LIST_C2M(buffer) cache_sync_xfer_descriptor_list(buffer, false) @@ -257,15 +256,15 @@ struct port_obj { void *callback_arg; SemaphoreHandle_t port_mux; void *context; + intr_handle_t isr_hdl; // Interrupt handle for this root port (USB-OTG peripheral) }; /** * @brief Object representing the HCD */ typedef struct { - // Ports (Hardware only has one) - port_t *port_obj; - intr_handle_t isr_hdl; + // Ports: Each peripheral is a root port + port_t *port_obj[NUM_PORTS]; } hcd_obj_t; static hcd_obj_t *s_hcd_obj = NULL; // Note: "s_" is for the static pointer @@ -1041,52 +1040,66 @@ esp_err_t hcd_install(const hcd_config_t *config) HCD_CHECK_FROM_CRIT(s_hcd_obj == NULL, ESP_ERR_INVALID_STATE); HCD_EXIT_CRITICAL(); + // Check if peripheral_map does not have bits set outside of valid range. Valid bits are BIT0 - BIT(NUM_PORTS - 1) + HCD_CHECK((config->peripheral_map != 0) && (config->peripheral_map < BIT(NUM_PORTS)), ESP_ERR_INVALID_ARG); + esp_err_t err_ret; + // Allocate memory for the driver object hcd_obj_t *p_hcd_obj_dmy = calloc(1, sizeof(hcd_obj_t)); if (p_hcd_obj_dmy == NULL) { return ESP_ERR_NO_MEM; } - // Allocate each port object (the hardware currently only has one port) - p_hcd_obj_dmy->port_obj = port_obj_alloc(); - if (p_hcd_obj_dmy->port_obj == NULL) { - err_ret = ESP_ERR_NO_MEM; - goto port_alloc_err; - } - // Allocate interrupt - err_ret = esp_intr_alloc(usb_dwc_info.controllers[0].irq, - config->intr_flags | ESP_INTR_FLAG_INTRDISABLED, // The interrupt must be disabled until the port is initialized - intr_hdlr_main, - (void *)p_hcd_obj_dmy->port_obj, - &p_hcd_obj_dmy->isr_hdl); - if (err_ret != ESP_OK) { - ESP_LOGE(HCD_DWC_TAG, "Interrupt alloc error: %s", esp_err_to_name(err_ret)); - goto intr_alloc_err; - } - // Apply custom FIFO config if provided, otherwise mark as default (all zeros) - memset(&p_hcd_obj_dmy->port_obj->fifo_config, 0, sizeof(p_hcd_obj_dmy->port_obj->fifo_config)); - if (config->fifo_config != NULL) { - // Convert and validate user-provided config - err_ret = convert_fifo_config_to_hal_config(config->fifo_config, &p_hcd_obj_dmy->port_obj->fifo_config); - if (err_ret != ESP_OK) { - goto assign_err; + + // Allocate each port object + for (int i = 0; i < NUM_PORTS; i++) { + if (BIT(i) & config->peripheral_map) { + p_hcd_obj_dmy->port_obj[i] = port_obj_alloc(); + if (p_hcd_obj_dmy->port_obj[i] == NULL) { + err_ret = ESP_ERR_NO_MEM; + goto clean_up; + } + // Allocate interrupt + const int irq_index = usb_dwc_info.controllers[i].irq; + err_ret = esp_intr_alloc(irq_index, + config->intr_flags | ESP_INTR_FLAG_INTRDISABLED, // The interrupt must be disabled until the port is initialized + intr_hdlr_main, + (void *)p_hcd_obj_dmy->port_obj[i], + &p_hcd_obj_dmy->port_obj[i]->isr_hdl); + if (err_ret != ESP_OK) { + ESP_LOGE(HCD_DWC_TAG, "Interrupt alloc error: %s", esp_err_to_name(err_ret)); + goto clean_up; + } + + // Apply custom FIFO config if provided + if (config->fifo_config != NULL) { + // Convert and validate user-provided config + err_ret = convert_fifo_config_to_hal_config(config->fifo_config, &p_hcd_obj_dmy->port_obj[i]->fifo_config); + if (err_ret != ESP_OK) { + goto clean_up; + } + } } } + HCD_ENTER_CRITICAL(); if (s_hcd_obj != NULL) { HCD_EXIT_CRITICAL(); err_ret = ESP_ERR_INVALID_STATE; - goto assign_err; + goto clean_up; } s_hcd_obj = p_hcd_obj_dmy; HCD_EXIT_CRITICAL(); return ESP_OK; -assign_err: - esp_intr_free(p_hcd_obj_dmy->isr_hdl); -intr_alloc_err: - port_obj_free(p_hcd_obj_dmy->port_obj); -port_alloc_err: +clean_up: + // Free resources + for (int i = 0; i < NUM_PORTS; i++) { + if (p_hcd_obj_dmy->port_obj[i]) { + esp_intr_free(p_hcd_obj_dmy->port_obj[i]->isr_hdl); + port_obj_free(p_hcd_obj_dmy->port_obj[i]); + } + } free(p_hcd_obj_dmy); return err_ret; } @@ -1094,8 +1107,14 @@ port_alloc_err: esp_err_t hcd_uninstall(void) { HCD_ENTER_CRITICAL(); - // Check that all ports have been disabled (there's only one port) - if (s_hcd_obj == NULL || s_hcd_obj->port_obj->initialized) { + // Check that all ports have been disabled + bool all_ports_disabled = true; + for (int i = 0; i < NUM_PORTS; i++) { + if (s_hcd_obj->port_obj[i]) { + all_ports_disabled = all_ports_disabled && !(s_hcd_obj->port_obj[i]->initialized); + } + } + if (s_hcd_obj == NULL || !all_ports_disabled) { HCD_EXIT_CRITICAL(); return ESP_ERR_INVALID_STATE; } @@ -1104,8 +1123,12 @@ esp_err_t hcd_uninstall(void) HCD_EXIT_CRITICAL(); // Free resources - port_obj_free(p_hcd_obj_dmy->port_obj); - esp_intr_free(p_hcd_obj_dmy->isr_hdl); + for (int i = 0; i < NUM_PORTS; i++) { + if (p_hcd_obj_dmy->port_obj[i]) { + esp_intr_free(p_hcd_obj_dmy->port_obj[i]->isr_hdl); + port_obj_free(p_hcd_obj_dmy->port_obj[i]); + } + } free(p_hcd_obj_dmy); return ESP_OK; } @@ -1366,13 +1389,13 @@ exit: esp_err_t hcd_port_init(int port_number, const hcd_port_config_t *port_config, hcd_port_handle_t *port_hdl) { - HCD_CHECK(port_number > 0 && port_config != NULL && port_hdl != NULL, ESP_ERR_INVALID_ARG); - HCD_CHECK(port_number <= NUM_PORTS, ESP_ERR_NOT_FOUND); + HCD_CHECK(port_number >= 0 && port_config != NULL && port_hdl != NULL, ESP_ERR_INVALID_ARG); + HCD_CHECK(port_number < NUM_PORTS, ESP_ERR_NOT_FOUND); HCD_ENTER_CRITICAL(); - HCD_CHECK_FROM_CRIT(s_hcd_obj != NULL && !s_hcd_obj->port_obj->initialized, ESP_ERR_INVALID_STATE); + HCD_CHECK_FROM_CRIT(s_hcd_obj != NULL && !s_hcd_obj->port_obj[port_number]->initialized, ESP_ERR_INVALID_STATE); // Port object memory and resources (such as the mutex) already be allocated. Just need to initialize necessary fields only - port_t *port_obj = s_hcd_obj->port_obj; + port_t *port_obj = s_hcd_obj->port_obj[port_number]; TAILQ_INIT(&port_obj->pipes_idle_tailq); TAILQ_INIT(&port_obj->pipes_active_tailq); port_obj->state = HCD_PORT_STATE_NOT_POWERED; @@ -1380,18 +1403,22 @@ esp_err_t hcd_port_init(int port_number, const hcd_port_config_t *port_config, h port_obj->callback = port_config->callback; port_obj->callback_arg = port_config->callback_arg; port_obj->context = port_config->context; - usb_dwc_hal_init(port_obj->hal, 0); + + // USB-HAL's size is dependent on its configuration, namely on number of channels in the configuration + // We must first initialize the HAL, to get the number of channels and then allocate memory for the channels + usb_dwc_hal_init(port_obj->hal, port_number); port_obj->hal->channels.hdls = calloc(port_obj->hal->constant_config.chan_num_total, sizeof(usb_dwc_hal_chan_t*)); HCD_CHECK_FROM_CRIT(port_obj->hal->channels.hdls != NULL, ESP_ERR_NO_MEM); + port_obj->initialized = true; - // Clear the frame list. We set the frame list register and enable periodic scheduling after a successful reset + // Clear the frame list. We will set the frame list register and enable periodic scheduling after a successful reset memset(port_obj->frame_list, 0, FRAME_LIST_LEN * sizeof(uint32_t)); // If FIFO config is zeroed -> calculate from bias if (_is_fifo_config_by_bias(&port_obj->fifo_config)) { // Calculate default FIFO sizes based on Kconfig bias settings _calculate_fifo_from_bias(port_obj, port_obj->hal); } - esp_intr_enable(s_hcd_obj->isr_hdl); + esp_intr_enable(port_obj->isr_hdl); *port_hdl = (hcd_port_handle_t)port_obj; HCD_EXIT_CRITICAL(); ESP_LOGD(HCD_DWC_TAG, "FIFO config lines: RX=%u, PTX=%u, NPTX=%u", @@ -1413,7 +1440,7 @@ esp_err_t hcd_port_deinit(hcd_port_handle_t port_hdl) && port->task_waiting_port_notif == NULL, ESP_ERR_INVALID_STATE); port->initialized = false; - esp_intr_disable(s_hcd_obj->isr_hdl); + esp_intr_disable(port->isr_hdl); free(port->hal->channels.hdls); usb_dwc_hal_deinit(port->hal); HCD_EXIT_CRITICAL(); @@ -1529,7 +1556,7 @@ esp_err_t hcd_port_recover(hcd_port_handle_t port_hdl) ESP_ERR_INVALID_STATE); // We are about to do a soft reset on the peripheral. Disable the peripheral throughout - esp_intr_disable(s_hcd_obj->isr_hdl); + esp_intr_disable(port->isr_hdl); usb_dwc_hal_core_soft_reset(port->hal); port->state = HCD_PORT_STATE_NOT_POWERED; port->last_event = HCD_PORT_EVENT_NONE; @@ -1537,7 +1564,7 @@ esp_err_t hcd_port_recover(hcd_port_handle_t port_hdl) // Clear the frame list. We set the frame list register and enable periodic scheduling after a successful reset memset(port->frame_list, 0, FRAME_LIST_LEN * sizeof(uint32_t)); - esp_intr_enable(s_hcd_obj->isr_hdl); + esp_intr_enable(port->isr_hdl); HCD_EXIT_CRITICAL(); return ESP_OK; } diff --git a/components/usb/host_test/usb_host_layer_test/main/usb_host_install_unit_test.cpp b/components/usb/host_test/usb_host_layer_test/main/usb_host_install_unit_test.cpp index a5943f684a..7cc25c7954 100644 --- a/components/usb/host_test/usb_host_layer_test/main/usb_host_install_unit_test.cpp +++ b/components/usb/host_test/usb_host_layer_test/main/usb_host_install_unit_test.cpp @@ -7,6 +7,7 @@ #include #include +#include "esp_bit_defs.h" #include "usb_host.h" // Real implementation of usb_host.h // Test all the mocked headers defined for this mock @@ -52,6 +53,7 @@ SCENARIO("USB Host install") .intr_flags = 1, .enum_filter_cb = nullptr, .fifo_settings_custom = {}, + .peripheral_map = BIT0, }; // USB host config is valid, USB Host driver is not installed from previous test case diff --git a/components/usb/hub.c b/components/usb/hub.c index 609f9d64f2..7afb5873c8 100644 --- a/components/usb/hub.c +++ b/components/usb/hub.c @@ -11,6 +11,7 @@ #include #include "esp_err.h" #include "esp_heap_caps.h" +#include "esp_bit_defs.h" #include "esp_private/critical_section.h" #include "esp_log.h" #include "usb_private.h" @@ -27,8 +28,6 @@ Implementation of the HUB driver that only supports the Root Hub with a single p implement the bare minimum to control the root HCD port. */ -#define HUB_ROOT_PORT_NUM 1 // HCD only supports one port - #ifdef CONFIG_USB_HOST_HW_BUFFER_BIAS_IN #define HUB_ROOT_HCD_PORT_FIFO_BIAS HCD_PORT_FIFO_BIAS_RX #elif CONFIG_USB_HOST_HW_BUFFER_BIAS_PERIODIC_OUT @@ -60,7 +59,6 @@ typedef enum { ROOT_PORT_STATE_POWERED, /**< Root port is powered, device is not connected */ ROOT_PORT_STATE_DISABLED, /**< A device is connected but is disabled (i.e., not reset, no SOFs are sent) */ ROOT_PORT_STATE_ENABLED, /**< A device is connected, port has been reset, SOFs are sent */ - ROOT_PORT_STATE_RECOVERY, /**< Root port encountered an error and needs to be recovered */ } root_port_state_t; /** @@ -566,7 +564,14 @@ esp_err_t hub_install(hub_config_t *hub_config, void **client_ret) .context = NULL, }; hcd_port_handle_t root_port_hdl; - ret = hcd_port_init(HUB_ROOT_PORT_NUM, &port_config, &root_port_hdl); + + // Right now we support only one root port, can be extended in future + int root_port_index = 0; + if (hub_config->port_map & BIT1) { + root_port_index = 1; + } + + ret = hcd_port_init(root_port_index, &port_config, &root_port_hdl); if (ret != ESP_OK) { ESP_LOGE(HUB_DRIVER_TAG, "HCD Port init error: %s", esp_err_to_name(ret)); goto err; diff --git a/components/usb/include/usb/usb_host.h b/components/usb/include/usb/usb_host.h index 001327d809..992d783dea 100644 --- a/components/usb/include/usb/usb_host.h +++ b/components/usb/include/usb/usb_host.h @@ -121,6 +121,13 @@ typedef struct { } fifo_settings_custom; /**< Optional custom FIFO configuration (advanced use). RX and NPTX must be > 0. If all fields are zero, a default configuration will be selected based on Kconfig bias. */ + unsigned peripheral_map; /**< Selects the USB peripheral(s) to use. + - On targets with multiple USB peripherals, this field can be used to specify which ones to enable. + - Set to 0 to use the default peripheral. + - On High-Speed capable targets, the default is the High-Speed peripheral. + - On Full-Speed only targets, the default is the Full-Speed peripheral. + - Example: peripheral_map = BIT1; installs USB host on peripheral 1. + - The mapping of bits to specific peripherals is defined in the USB_DWC_LL_GET_HW() macro. */ } usb_host_config_t; /** diff --git a/components/usb/private_include/hcd.h b/components/usb/private_include/hcd.h index 2920a11b99..eeb1f22709 100644 --- a/components/usb/private_include/hcd.h +++ b/components/usb/private_include/hcd.h @@ -154,9 +154,10 @@ typedef struct { * @brief HCD configuration structure */ typedef struct { - int intr_flags; /**< Interrupt flags for HCD interrupt */ - const hcd_fifo_settings_t *fifo_config;/**< Optional pointer to custom FIFO config. - If NULL, default configuration is used. */ + int intr_flags; /**< Interrupt flags for HCD interrupt */ + const hcd_fifo_settings_t *fifo_config; /**< Optional pointer to custom FIFO config. + If NULL, default configuration is used. */ + unsigned peripheral_map; /**< Bit map of USB-OTG peripherals that belong to HCD. Set to zero to use default */ } hcd_config_t; /** @@ -166,6 +167,7 @@ typedef struct { hcd_port_callback_t callback; /**< HCD port event callback */ void *callback_arg; /**< User argument for HCD port callback */ void *context; /**< Context variable used to associate the port with upper layer object */ + unsigned peripheral_idx; /**< Index of USB peripheral this port belongs to. Index numbers are defined in USB_DWC_LL_GET_HW */ } hcd_port_config_t; /** @@ -225,8 +227,6 @@ esp_err_t hcd_uninstall(void); * * After a port is initialized, it will be put into the HCD_PORT_STATE_NOT_POWERED state. * - * @note The host controller only has one port, thus the only valid port_number is 1 - * * @param[in] port_number Port number * @param[in] port_config Port configuration * @param[out] port_hdl Port handle diff --git a/components/usb/private_include/hub.h b/components/usb/private_include/hub.h index f563a56f28..60ee825917 100644 --- a/components/usb/private_include/hub.h +++ b/components/usb/private_include/hub.h @@ -60,6 +60,7 @@ typedef void (*hub_event_cb_t)(hub_event_data_t *event_data, void *arg); * @brief Hub driver configuration */ typedef struct { + unsigned port_map; /**< Bitmap of root ports to enable */ usb_proc_req_cb_t proc_req_cb; /**< Processing request callback */ void *proc_req_cb_arg; /**< Processing request callback argument */ hub_event_cb_t event_cb; /**< Hub event callback */ diff --git a/components/usb/test_apps/common/hcd_common.c b/components/usb/test_apps/common/hcd_common.c index 8a50f05a6d..91caa3a4f3 100644 --- a/components/usb/test_apps/common/hcd_common.c +++ b/components/usb/test_apps/common/hcd_common.c @@ -25,9 +25,24 @@ // ----------------------------------------------------- Macros -------------------------------------------------------- +#define TEST_P4_OTG11 0 // Change this to 1 to test on OTG1.1 peripheral - only for ESP32-P4 + // --------------------- Constants ------------------------- -#define PORT_NUM 1 +#if TEST_P4_OTG11 +#define TEST_PHY USB_PHY_TARGET_INT +#define TEST_PERIPHERAL_MAP BIT1 +#define TEST_PORT_NUM 1 +#else +#if CONFIG_IDF_TARGET_ESP32P4 +#define TEST_PHY USB_PHY_TARGET_UTMI +#else +#define TEST_PHY USB_PHY_TARGET_INT +#endif +#define TEST_PERIPHERAL_MAP BIT0 +#define TEST_PORT_NUM 0 +#endif // TEST_P4_OTG11 + #define EVENT_QUEUE_LEN 5 #define ENUM_ADDR 1 // Device address to use for tests that enumerate the device #define ENUM_CONFIG 1 // Device configuration number to use for tests that enumerate the device @@ -149,7 +164,7 @@ int test_hcd_get_num_pipe_events(hcd_pipe_handle_t pipe_hdl) hcd_port_handle_t test_hcd_setup(void) { - test_setup_usb_phy(); + test_setup_usb_phy(TEST_PHY); // Create a queue for port callback to queue up port events QueueHandle_t port_evt_queue = xQueueCreate(EVENT_QUEUE_LEN, sizeof(port_event_msg_t)); @@ -157,6 +172,7 @@ hcd_port_handle_t test_hcd_setup(void) // Install HCD hcd_config_t hcd_config = { .intr_flags = ESP_INTR_FLAG_LEVEL1, + .peripheral_map = TEST_PERIPHERAL_MAP, }; TEST_ASSERT_EQUAL(ESP_OK, hcd_install(&hcd_config)); // Initialize a port @@ -166,7 +182,7 @@ hcd_port_handle_t test_hcd_setup(void) .context = (void *)port_evt_queue, }; hcd_port_handle_t port_hdl; - TEST_ASSERT_EQUAL(ESP_OK, hcd_port_init(PORT_NUM, &port_config, &port_hdl)); + TEST_ASSERT_EQUAL(ESP_OK, hcd_port_init(TEST_PORT_NUM, &port_config, &port_hdl)); TEST_ASSERT_NOT_NULL(port_hdl); return port_hdl; } diff --git a/components/usb/test_apps/common/phy_common.c b/components/usb/test_apps/common/phy_common.c index acd78b4e30..7bb7394c97 100644 --- a/components/usb/test_apps/common/phy_common.c +++ b/components/usb/test_apps/common/phy_common.c @@ -20,7 +20,7 @@ static usb_phy_handle_t phy_hdl = NULL; -void test_setup_usb_phy(void) +void test_setup_usb_phy(usb_phy_target_t phy_target) { // Deinitialize PHY from previous failed test if (phy_hdl != NULL) { @@ -31,11 +31,7 @@ void test_setup_usb_phy(void) // Initialize the internal USB PHY to connect to the USB OTG peripheral usb_phy_config_t phy_config = { .controller = USB_PHY_CTRL_OTG, -#if CONFIG_IDF_TARGET_ESP32P4 // ESP32-P4 has 2 USB-DWC peripherals, each with its dedicated PHY. We support HS+UTMI only ATM. - .target = USB_PHY_TARGET_UTMI, -#else - .target = USB_PHY_TARGET_INT, -#endif + .target = phy_target, .otg_mode = USB_OTG_MODE_HOST, .otg_speed = USB_PHY_SPEED_UNDEFINED, // In Host mode, the speed is determined by the connected device .ext_io_conf = NULL, diff --git a/components/usb/test_apps/common/phy_common.h b/components/usb/test_apps/common/phy_common.h index 7cb109112e..2714c7b2ea 100644 --- a/components/usb/test_apps/common/phy_common.h +++ b/components/usb/test_apps/common/phy_common.h @@ -4,10 +4,15 @@ * SPDX-License-Identifier: Apache-2.0 */ +#pragma once +#include "hal/usb_phy_types.h" + /** * @brief Install USB PHY separately from the usb_host_install() + * + * @param[in] phy_target USB PHY target */ -void test_setup_usb_phy(void); +void test_setup_usb_phy(usb_phy_target_t phy_target); /** * @brief Uninstall PHY diff --git a/components/usb/test_apps/usb_host/main/test_app_main.c b/components/usb/test_apps/usb_host/main/test_app_main.c index 517b76abb0..11a8702d32 100644 --- a/components/usb/test_apps/usb_host/main/test_app_main.c +++ b/components/usb/test_apps/usb_host/main/test_app_main.c @@ -13,17 +13,36 @@ #include "phy_common.h" #include "usb/usb_host.h" +// ----------------------------------------------------- Macros -------------------------------------------------------- + +#define TEST_P4_OTG11 0 // Change this to 1 to test on OTG1.1 peripheral - only for ESP32-P4 + +// --------------------- Constants ------------------------- + +#if TEST_P4_OTG11 +#define TEST_PHY USB_PHY_TARGET_INT +#define TEST_PERIPHERAL_MAP BIT1 +#else +#if CONFIG_IDF_TARGET_ESP32P4 +#define TEST_PHY USB_PHY_TARGET_UTMI +#else +#define TEST_PHY USB_PHY_TARGET_INT +#endif +#define TEST_PERIPHERAL_MAP BIT0 +#endif // TEST_P4_OTG11 + void setUp(void) { unity_utils_record_free_mem(); dev_msc_init(); // Install PHY separately - test_setup_usb_phy(); + test_setup_usb_phy(TEST_PHY); // Install USB Host usb_host_config_t host_config = { .skip_phy_setup = true, .root_port_unpowered = false, .intr_flags = ESP_INTR_FLAG_LEVEL1, + .peripheral_map = TEST_PERIPHERAL_MAP, }; ESP_ERROR_CHECK(usb_host_install(&host_config)); printf("USB Host installed\n"); diff --git a/components/usb/usb_host.c b/components/usb/usb_host.c index 8216d3cfdf..1c7e619d83 100644 --- a/components/usb/usb_host.c +++ b/components/usb/usb_host.c @@ -17,9 +17,11 @@ Warning: The USB Host Library API is still a beta version and may be subject to #include "freertos/queue.h" #include "freertos/semphr.h" #include "esp_private/critical_section.h" +#include "soc/usb_dwc_periph.h" #include "esp_err.h" #include "esp_log.h" #include "esp_heap_caps.h" +#include "esp_bit_defs.h" #include "hub.h" #include "enum.h" #include "usbh.h" @@ -464,16 +466,26 @@ esp_err_t usb_host_install(const usb_host_config_t *config) - Hub */ + // For backward compatibility accept 0 too + const unsigned peripheral_map = config->peripheral_map == 0 ? BIT0 : config->peripheral_map; + // Install USB PHY (if necessary). USB PHY driver will also enable the underlying Host Controller if (!config->skip_phy_setup) { + bool init_utmi_phy = false; // Default value for Linux simulation + +#if SOC_USB_OTG_SUPPORTED // In case we run on a real target, select the PHY from usb_dwc_info description structure + // Right now we support only one peripheral, can be extended in future + int peripheral_index = 0; + if (peripheral_map & BIT1) { + peripheral_index = 1; + } + init_utmi_phy = (usb_dwc_info.controllers[peripheral_index].supported_phys == USB_PHY_INST_UTMI_0); +#endif // SOC_USB_OTG_SUPPORTED + // Host Library defaults to internal PHY usb_phy_config_t phy_config = { .controller = USB_PHY_CTRL_OTG, -#if CONFIG_IDF_TARGET_ESP32P4 // ESP32-P4 has 2 USB-DWC peripherals, each with its dedicated PHY. We support HS+UTMI only ATM. - .target = USB_PHY_TARGET_UTMI, -#else - .target = USB_PHY_TARGET_INT, -#endif + .target = init_utmi_phy ? USB_PHY_TARGET_UTMI : USB_PHY_TARGET_INT, .otg_mode = USB_OTG_MODE_HOST, .otg_speed = USB_PHY_SPEED_UNDEFINED, // In Host mode, the speed is determined by the connected device .ext_io_conf = NULL, @@ -491,6 +503,7 @@ esp_err_t usb_host_install(const usb_host_config_t *config) hcd_fifo_settings_t user_fifo_config; hcd_config_t hcd_config = { .intr_flags = config->intr_flags, + .peripheral_map = peripheral_map, .fifo_config = NULL, // Default: use bias strategy from Kconfig }; @@ -545,6 +558,7 @@ esp_err_t usb_host_install(const usb_host_config_t *config) // Install Hub hub_config_t hub_config = { + .port_map = peripheral_map, // Each USB-OTG peripheral maps to a root port .proc_req_cb = proc_req_callback, .proc_req_cb_arg = NULL, .event_cb = hub_event_callback, diff --git a/docs/en/api-reference/peripherals/usb_host.rst b/docs/en/api-reference/peripherals/usb_host.rst index 295bd6ea50..00aeadfc53 100644 --- a/docs/en/api-reference/peripherals/usb_host.rst +++ b/docs/en/api-reference/peripherals/usb_host.rst @@ -33,6 +33,7 @@ The Host Library has the following features: :esp32p4: - Supports High Speed (HS), Full Speed (FS) and Low Speed (LS) Devices. - Supports all four transfer types: Control, Bulk, Interrupt, and Isochronous. :esp32p4: - Supports High-Bandwidth Isochronous endpoints. + :esp32p4: - {IDF_TARGET_NAME} includes two USB 2.0 OTG peripherals: one High-Speed and one Full-Speed. Both support USB Host functionality; however, due to a current software limitation, only one can operate as a USB Host at a time. Support for dual USB Host operation is planned for a future update. - Allows multiple class drivers to run simultaneously, i.e., multiple clients of the Host Library. - A single device can be used by multiple clients simultaneously, e.g., composite devices. - The Host Library itself and the underlying Host Stack does not internally instantiate any OS tasks. The number of tasks is entirely controlled by how the Host Library interface is used. However, a general rule of thumb regarding the number of tasks is ``(the number of host class drivers running + 1)``. @@ -46,7 +47,6 @@ Currently, the Host Library and the underlying Host Stack has the following limi - Only supports Asynchronous transfers. - Only supports using one configuration. Changing to other configurations after enumeration is not supported yet. - Transfer timeouts are not supported yet. - :esp32p4: - {IDF_TARGET_NAME} contains two USB-OTG peripherals USB 2.0 OTG High-Speed and USB 2.0 OTG Full-Speed. Only the High-Speed instance is supported now. - The External Hub Driver: Remote Wakeup feature is not supported (External Hubs are active, even if there are no devices inserted). - The External Hub Driver: Doesn't handle error cases (overcurrent handling, errors during initialization etc. are not implemented yet). - The External Hub Driver: No Interface selection. The Driver uses the first available Interface with Hub Class code (09h). diff --git a/examples/peripherals/usb/host/usb_host_lib/main/usb_host_lib_main.c b/examples/peripherals/usb/host/usb_host_lib/main/usb_host_lib_main.c index d70c92db72..8c8043440d 100644 --- a/examples/peripherals/usb/host/usb_host_lib/main/usb_host_lib_main.c +++ b/examples/peripherals/usb/host/usb_host_lib/main/usb_host_lib_main.c @@ -113,8 +113,10 @@ static void usb_host_lib_task(void *arg) # ifdef ENABLE_ENUM_FILTER_CALLBACK .enum_filter_cb = set_config_cb, # endif // ENABLE_ENUM_FILTER_CALLBACK + .peripheral_map = BIT0, }; ESP_ERROR_CHECK(usb_host_install(&host_config)); + ESP_LOGI(TAG, "USB Host installed with peripheral map 0x%x", host_config.peripheral_map); //Signalize the app_main, the USB host library has been installed xTaskNotifyGive(arg); From cf18fe55682303a99b152f14a98d421e3868dff2 Mon Sep 17 00:00:00 2001 From: Tomas Rezucha Date: Tue, 6 May 2025 10:41:09 +0200 Subject: [PATCH 2/3] refactor(usb/host): Remove deprecated usb_otg_periph.h --- components/soc/esp32s2/usb_dwc_periph.c | 51 --------------------- components/soc/esp32s3/usb_dwc_periph.c | 51 --------------------- components/soc/include/soc/usb_dwc_periph.h | 37 --------------- components/soc/include/soc/usb_otg_periph.h | 12 ----- 4 files changed, 151 deletions(-) delete mode 100644 components/soc/include/soc/usb_otg_periph.h diff --git a/components/soc/esp32s2/usb_dwc_periph.c b/components/soc/esp32s2/usb_dwc_periph.c index 1d77e297fa..b243d84c65 100644 --- a/components/soc/esp32s2/usb_dwc_periph.c +++ b/components/soc/esp32s2/usb_dwc_periph.c @@ -58,54 +58,3 @@ const usb_dwc_info_t usb_dwc_info = { }, }, }; - -/* ------------------------------- Deprecated ------------------------------- */ - -#include "soc/usb_periph.h" -/* -Note: These IO pins are deprecated. When connecting USB OTG to an external FSLS -PHY, the FSLS Serial Interface signals can be routed to any GPIO via the GPIO -matrix. Thus, this mapping of signals to IO pins is meaningless. - -Todo: Remove in IDF v6.0 (IDF-9029) -*/ -const usb_iopin_dsc_t usb_periph_iopins[] = { - {USBPHY_VP_NUM, USB_EXTPHY_VP_IDX, 0, 1}, - {USBPHY_VM_NUM, USB_EXTPHY_VM_IDX, 0, 1}, - {USBPHY_RCV_NUM, USB_EXTPHY_RCV_IDX, 0, 1}, - {USBPHY_OEN_NUM, USB_EXTPHY_OEN_IDX, 1, 1}, - {USBPHY_VPO_NUM, USB_EXTPHY_VPO_IDX, 1, 1}, - {USBPHY_VMO_NUM, USB_EXTPHY_VMO_IDX, 1, 1}, - {GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_IDDIG_IN_IDX, 0, 0}, //connected connector is mini-B - //connected connector is mini-B - {GPIO_MATRIX_CONST_ONE_INPUT, USB_SRP_BVALID_IN_IDX, 0, 0}, //HIGH to force USB device mode - {GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_VBUSVALID_IN_IDX, 0, 0}, //receiving a valid Vbus from host - {GPIO_MATRIX_CONST_ZERO_INPUT, USB_OTG_AVALID_IN_IDX, 0, 0}, - {-1, -1, 0, 0} -}; - -/* - Bunch of constants for USB peripheral: GPIO signals -*/ -const usb_phy_signal_conn_t usb_otg_periph_signal = { - .extphy_vp_in = USB_EXTPHY_VP_IDX, - .extphy_vm_in = USB_EXTPHY_VM_IDX, - .extphy_rcv_in = USB_EXTPHY_RCV_IDX, - .extphy_oen_out = USB_EXTPHY_OEN_IDX, - .extphy_vpo_out = USB_EXTPHY_VPO_IDX, - .extphy_vmo_out = USB_EXTPHY_VMO_IDX, - .extphy_suspend_in = USB_EXTPHY_SUSPND_IDX, - .extphy_speed_in = USB_EXTPHY_SPEED_IDX, - .srp_bvalid_in = USB_SRP_BVALID_IN_IDX, - .srp_sessend_in = USB_SRP_SESSEND_IN_IDX, - .srp_chrgvbus_out = USB_SRP_CHRGVBUS_IDX, - .srp_dischrgvbus_out = USB_SRP_DISCHRGVBUS_IDX, - .otg_iddig_in = USB_OTG_IDDIG_IN_IDX, - .otg_avalid_in = USB_OTG_AVALID_IN_IDX, - .otg_vbusvalid_in = USB_OTG_VBUSVALID_IN_IDX, - .otg_idpullup_out = USB_OTG_IDPULLUP_IDX, - .otg_dppulldown_out = USB_OTG_DPPULLDOWN_IDX, - .otg_dmpulldown_out = USB_OTG_DMPULLDOWN_IDX, - .otg_drvvbus_out = USB_OTG_DRVVBUS_IDX, - .module = PERIPH_USB_MODULE -}; diff --git a/components/soc/esp32s3/usb_dwc_periph.c b/components/soc/esp32s3/usb_dwc_periph.c index 1d77e297fa..b243d84c65 100644 --- a/components/soc/esp32s3/usb_dwc_periph.c +++ b/components/soc/esp32s3/usb_dwc_periph.c @@ -58,54 +58,3 @@ const usb_dwc_info_t usb_dwc_info = { }, }, }; - -/* ------------------------------- Deprecated ------------------------------- */ - -#include "soc/usb_periph.h" -/* -Note: These IO pins are deprecated. When connecting USB OTG to an external FSLS -PHY, the FSLS Serial Interface signals can be routed to any GPIO via the GPIO -matrix. Thus, this mapping of signals to IO pins is meaningless. - -Todo: Remove in IDF v6.0 (IDF-9029) -*/ -const usb_iopin_dsc_t usb_periph_iopins[] = { - {USBPHY_VP_NUM, USB_EXTPHY_VP_IDX, 0, 1}, - {USBPHY_VM_NUM, USB_EXTPHY_VM_IDX, 0, 1}, - {USBPHY_RCV_NUM, USB_EXTPHY_RCV_IDX, 0, 1}, - {USBPHY_OEN_NUM, USB_EXTPHY_OEN_IDX, 1, 1}, - {USBPHY_VPO_NUM, USB_EXTPHY_VPO_IDX, 1, 1}, - {USBPHY_VMO_NUM, USB_EXTPHY_VMO_IDX, 1, 1}, - {GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_IDDIG_IN_IDX, 0, 0}, //connected connector is mini-B - //connected connector is mini-B - {GPIO_MATRIX_CONST_ONE_INPUT, USB_SRP_BVALID_IN_IDX, 0, 0}, //HIGH to force USB device mode - {GPIO_MATRIX_CONST_ONE_INPUT, USB_OTG_VBUSVALID_IN_IDX, 0, 0}, //receiving a valid Vbus from host - {GPIO_MATRIX_CONST_ZERO_INPUT, USB_OTG_AVALID_IN_IDX, 0, 0}, - {-1, -1, 0, 0} -}; - -/* - Bunch of constants for USB peripheral: GPIO signals -*/ -const usb_phy_signal_conn_t usb_otg_periph_signal = { - .extphy_vp_in = USB_EXTPHY_VP_IDX, - .extphy_vm_in = USB_EXTPHY_VM_IDX, - .extphy_rcv_in = USB_EXTPHY_RCV_IDX, - .extphy_oen_out = USB_EXTPHY_OEN_IDX, - .extphy_vpo_out = USB_EXTPHY_VPO_IDX, - .extphy_vmo_out = USB_EXTPHY_VMO_IDX, - .extphy_suspend_in = USB_EXTPHY_SUSPND_IDX, - .extphy_speed_in = USB_EXTPHY_SPEED_IDX, - .srp_bvalid_in = USB_SRP_BVALID_IN_IDX, - .srp_sessend_in = USB_SRP_SESSEND_IN_IDX, - .srp_chrgvbus_out = USB_SRP_CHRGVBUS_IDX, - .srp_dischrgvbus_out = USB_SRP_DISCHRGVBUS_IDX, - .otg_iddig_in = USB_OTG_IDDIG_IN_IDX, - .otg_avalid_in = USB_OTG_AVALID_IN_IDX, - .otg_vbusvalid_in = USB_OTG_VBUSVALID_IN_IDX, - .otg_idpullup_out = USB_OTG_IDPULLUP_IDX, - .otg_dppulldown_out = USB_OTG_DPPULLDOWN_IDX, - .otg_dmpulldown_out = USB_OTG_DMPULLDOWN_IDX, - .otg_drvvbus_out = USB_OTG_DRVVBUS_IDX, - .module = PERIPH_USB_MODULE -}; diff --git a/components/soc/include/soc/usb_dwc_periph.h b/components/soc/include/soc/usb_dwc_periph.h index f35664d0c9..8d1c5350db 100644 --- a/components/soc/include/soc/usb_dwc_periph.h +++ b/components/soc/include/soc/usb_dwc_periph.h @@ -103,43 +103,6 @@ extern const usb_dwc_info_t usb_dwc_info; #endif // SOC_USB_OTG_SUPPORTED -/* ------------------------------- Deprecated ------------------------------- */ -/* Todo: Remove in ESP-IDF v6.0 (IDF-9052) */ -#include - -#if SOC_USB_OTG_SUPPORTED -#include "soc/periph_defs.h" - -/* - Stores a bunch of USB-peripheral data. -*/ -typedef struct { - const uint8_t extphy_vp_in; - const uint8_t extphy_vm_in; - const uint8_t extphy_rcv_in; - const uint8_t extphy_oen_out; - const uint8_t extphy_vpo_out; - const uint8_t extphy_vmo_out; - const uint8_t extphy_suspend_in; - const uint8_t extphy_speed_in; - const uint8_t srp_bvalid_in; - const uint8_t srp_sessend_in; - const uint8_t srp_chrgvbus_out; - const uint8_t srp_dischrgvbus_out; - const uint8_t otg_iddig_in; - const uint8_t otg_avalid_in; - const uint8_t otg_vbusvalid_in; - const uint8_t otg_idpullup_out; - const uint8_t otg_dppulldown_out; - const uint8_t otg_dmpulldown_out; - const uint8_t otg_drvvbus_out; - const periph_module_t module; -} usb_phy_signal_conn_t; - -extern const usb_phy_signal_conn_t usb_otg_periph_signal; - -#endif // SOC_USB_OTG_SUPPORTED - #ifdef __cplusplus } #endif diff --git a/components/soc/include/soc/usb_otg_periph.h b/components/soc/include/soc/usb_otg_periph.h deleted file mode 100644 index be92ac4eb1..0000000000 --- a/components/soc/include/soc/usb_otg_periph.h +++ /dev/null @@ -1,12 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -/* Todo: Remove in ESP-IDF v6.0 (IDF-9052) */ -#warning "This header is deprecated, please use usb_dwc_periph.h instead" - -#include "soc/usb_dwc_periph.h" From f201fd08dafa7baeaca3f771e954537423642fbf Mon Sep 17 00:00:00 2001 From: Zhang Shuxian Date: Thu, 8 May 2025 14:21:46 +0800 Subject: [PATCH 3/3] docs: Update CN for usb_host.rst --- docs/en/api-reference/peripherals/usb_host.rst | 2 +- docs/zh_CN/api-reference/peripherals/usb_host.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/api-reference/peripherals/usb_host.rst b/docs/en/api-reference/peripherals/usb_host.rst index 00aeadfc53..a389cb604d 100644 --- a/docs/en/api-reference/peripherals/usb_host.rst +++ b/docs/en/api-reference/peripherals/usb_host.rst @@ -33,7 +33,7 @@ The Host Library has the following features: :esp32p4: - Supports High Speed (HS), Full Speed (FS) and Low Speed (LS) Devices. - Supports all four transfer types: Control, Bulk, Interrupt, and Isochronous. :esp32p4: - Supports High-Bandwidth Isochronous endpoints. - :esp32p4: - {IDF_TARGET_NAME} includes two USB 2.0 OTG peripherals: one High-Speed and one Full-Speed. Both support USB Host functionality; however, due to a current software limitation, only one can operate as a USB Host at a time. Support for dual USB Host operation is planned for a future update. + :esp32p4: - {IDF_TARGET_NAME} includes two USB 2.0 OTG peripherals: one High-Speed and one Full-Speed. Both support USB Host functionality. However, due to a current software limitation, only one can operate as a USB Host at a time. Support for dual USB Host operation is planned for a future update. - Allows multiple class drivers to run simultaneously, i.e., multiple clients of the Host Library. - A single device can be used by multiple clients simultaneously, e.g., composite devices. - The Host Library itself and the underlying Host Stack does not internally instantiate any OS tasks. The number of tasks is entirely controlled by how the Host Library interface is used. However, a general rule of thumb regarding the number of tasks is ``(the number of host class drivers running + 1)``. diff --git a/docs/zh_CN/api-reference/peripherals/usb_host.rst b/docs/zh_CN/api-reference/peripherals/usb_host.rst index 02b43622e7..b006f36ebe 100644 --- a/docs/zh_CN/api-reference/peripherals/usb_host.rst +++ b/docs/zh_CN/api-reference/peripherals/usb_host.rst @@ -33,6 +33,7 @@ USB 主机库(以下简称主机库)是 USB 主机栈的最底层,提供 :esp32p4: - 支持高速 (HS)、全速 (FS) 和低速 (LS) 设备。 - 支持四种传输类型,即控制传输、块传输、中断传输和同步传输。 :esp32p4: - 支持高带宽等时性端点。 + :esp32p4: - {IDF_TARGET_NAME} 包含两个 USB 2.0 OTG 外设:USB 2.0 OTG 高速和 USB 2.0 OTG 全速,二者均支持 USB 主机功能。但由于当前软件的限制,同一时间仅能有一个作为 USB 主机工作。未来版本计划支持两个 USB 主机同时运行。 - 支持多个 Class 驱动程序同时运行,即主机的多个客户端同时运行。 - 单个设备可以由多个客户端同时使用,如复合设备。 - 主机库及其底层主机栈不会在内部自动创建操作系统任务,任务数量完全由主机库接口的使用方式决定。一般来说,任务数量为 ``(运行中的主机 Class 驱动程序数量 + 1)``。 @@ -46,7 +47,6 @@ USB 主机库(以下简称主机库)是 USB 主机栈的最底层,提供 - 仅支持异步传输。 - 仅支持使用发现的首个配置,尚不支持变更为其他配置。 - 尚不支持传输超时。 - :esp32p4: - {IDF_TARGET_NAME} 包含两个 USB-OTG 外设:USB 2.0 OTG 高速和 USB 2.0 OTG 全速。目前仅支持高速实例。 - 外部 Hub 驱动:不支持远程唤醒功能(即使没有设备插入,外部 Hub 也处于工作状态)。 - 外部 Hub 驱动:不处理错误用例(尚未实现过流处理、初始化错误等功能)。 - 外部 Hub 驱动:不支持接口选择。驱动程序使用具有 Hub 类代码 (09h) 的第一个可用接口。