Skip to content

Commit 5d6ace9

Browse files
Merge pull request #402 from espressif/feat/usb_dual_host
feat(usb/host): Add support for USB dual host on ESP32-P4
2 parents d5b83f3 + 48b250f commit 5d6ace9

File tree

8 files changed

+243
-91
lines changed

8 files changed

+243
-91
lines changed

docs/en/usb_host.rst

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ The Host Library has the following features:
3333
:esp32p4: - Supports High Speed (HS), Full Speed (FS) and Low Speed (LS) Devices.
3434
- Supports all four transfer types: Control, Bulk, Interrupt, and Isochronous.
3535
:esp32p4: - Supports High-Bandwidth Isochronous endpoints.
36-
: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.
36+
:esp32p4: - {IDF_TARGET_NAME} has two USB 2.0 OTG controllers: one High-Speed and one Full-Speed. Each controller can operate as a USB host independently, so either one alone or both together can function as USB hosts simultaneously.
3737
- Allows multiple class drivers to run simultaneously, i.e., multiple clients of the Host Library.
3838
- A single device can be used by multiple clients simultaneously, e.g., composite devices.
3939
- 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)``.
@@ -54,7 +54,6 @@ Currently, the Host Library and the underlying Host Stack has the following limi
5454
- The External Hub Driver: Remote Wakeup feature is not supported (External Hubs are active, even if there are no devices inserted).
5555
- The External Hub Driver: Doesn't handle error cases (overcurrent handling, errors during initialization etc. are not implemented yet).
5656
- The External Hub Driver: No Interface selection. The Driver uses the first available Interface with Hub Class code (09h).
57-
- The External Port Driver: No downstream port debounce mechanism (not implemented yet).
5857
:esp32p4: - The External Hub Driver: No Transaction Translator layer (No FS/LS Devices support when a Hub is attached to HS Host).
5958

6059

@@ -536,8 +535,7 @@ CDC-ACM
536535
"""""""
537536

538537
* A host class driver for the Communication Device Class (Abstract Control Model) is distributed as a managed component via the `ESP Component Registry <https://components.espressif.com/component/espressif/usb_host_cdc_acm>`__.
539-
* `cdc_acm_host <https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb/host/cdc/cdc_acm_host>`__ demonstrates how to use the CDC-ACM Host Driver to enable communication between {IDF_TARGET_NAME} and a USB CDC-ACM device.
540-
* `cdc_acm_vcp <https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb/host/cdc/cdc_acm_vcp>`__ demonstrates how to extend the CDC-ACM driver for Virtual Communication Port (VCP) devices like CP210x, FTDI FT23x or CH34x devices, and how to control the device and send data using the CDC-ACM API.
538+
* `cdc_acm_host <https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb/host/cdc>`__ demonstrates how to use the CDC-ACM Host Driver to enable communication between {IDF_TARGET_NAME} and a USB CDC-ACM device, including vendor-specific devices such as CP210x, FTDI FT23x or CH34x.
541539
* The CDC-ACM driver is also used in `esp_modem examples <https://github.com/espressif/esp-protocols/tree/master/components/esp_modem/examples>`__, where it is used for communication with cellular modems.
542540

543541
MSC

docs/en/usb_host/usb_host_notes_arch.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ The layers of the Host Stack are described in the following table. The layers ar
2626
- The HAL (Hardware Abstraction Layer) abstracts the operating steps of the USB controller into functions according to ESP-IDF's `Hardware Abstraction API Guidelines <https://docs.espressif.com/projects/esp-idf/en/stable/{IDF_TARGET_PATH_NAME}/api-guides/hardware-abstraction.html>`__. This layer also abstracts the controller's host port and host channels, and provides APIs to operate the them.
2727
* - HCD
2828
- ``hcd.h``, ``hcd.c``
29-
- The HCD (Host Controller Driver) acts as hardware agnostic API for all USB controllers (i.e., an API that can theoretically be used with any USB controller implementation). This layer also abstracts the root port (i.e., root hub) and USB pipes.
29+
- The HCD (Host Controller Driver) acts as hardware agnostic API for all USB controllers (i.e., an API that can theoretically be used with any USB controller implementation). This layer also abstracts the root port and USB pipes.
3030
* - USBH and Hub Driver
3131
- ``usbh.h``, ``usbh.c``
3232
- The USBH (USB Host Driver) layer is equivalent to the USBD layer described in chapter 10 of the USB2.0 specification. The USBH presents an abstraction of USB devices, internally manages a list of connected devices (i.e., device pool), and also arbitrates device sharing between clients (i.e., tracks which endpoints are in use and also presents a shared endpoint 0).
3333
* - Hub Driver
34-
- ``hub.h``, ``hub.c``
35-
- The Hub Driver layer acts as a special client of the USBH that is responsible for handling device attachment/detachment, and notifying the USBH of such events. For device attachment, the Hub Driver also handles the enumeration process as well.
34+
- ``hub.h``, ``hub.c``, ``enum.h``, ``enum.c``
35+
- The Hub Driver layer implements the Root HUB and acts as a special client of the USBH that is responsible for handling device attachment/detachment, and notifying the USBH of such events. For device attachment, the Hub Driver also handles the enumeration process as well.
3636
* - USB Host Library
3737
- ``usb_host.h``, ``usb_host.c``
3838
- The USB Host Library layer is the lowest public API layer of the Host Stack and presents the concept of USB Host Clients. The abstraction of clients allows for multiple class drivers to coexist simultaneously (where each class roughly maps to a single client) and also acts as a mechanism for division of labor (where each client is responsible for its own processing and event handling).

docs/zh_CN/usb_host.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -507,8 +507,7 @@ CDC-ACM
507507
"""""""
508508

509509
* 通信设备 Class(抽象控制模型)的主机 Class 驱动程序通过 `乐鑫组件注册表 <https://components.espressif.com/component/espressif/usb_host_cdc_acm>`__ 作为受管理的组件分发。
510-
* 示例 `cdc_acm_host <https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb/host/cdc/cdc_acm_host>`__ 演示了使用 CDC-ACM 主机驱动程序组件,实现 {IDF_TARGET_NAME} 与 USB CDC-ACM 设备的通信。
511-
* 示例 `cdc_acm_vcp <https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb/host/cdc/cdc_acm_vcp>`__ 演示了如何扩展 CDC-ACM 的主机驱动程序,以支持 VCP 设备(即虚拟通信端口设备,如 CP210x、FTDI FT23x 或 CH34x),以及如何使用 CDC-ACM API 控制设备并发送数据。
510+
* 示例 `cdc_acm_host <https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb/host/cdc>`__ 演示了如何使用 CDC-ACM 主机驱动程序,让 {IDF_TARGET_NAME} 与 USB CDC-ACM 设备通信,包括 CP210x、FTDI FT23x 或 CH34x 等厂商设备。
512511
* 示例 `esp_modem <https://github.com/espressif/esp-protocols/tree/master/components/esp_modem/examples>`__ 中也使用了 CDC-ACM 驱动程序,该程序在这些示例中与蜂窝模块通信。
513512

514513
MSC

host/usb/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this component will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [Unreleased]
8+
9+
### Added
10+
11+
- USB Dual Host support on ESP32-P4
12+
713
## [1.2.0] - 2026-02-05
814

915
### Added

host/usb/include/usb/usb_host.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -123,7 +123,7 @@ typedef struct {
123123
bool root_port_unpowered; /**< If set, the USB Host Library will not power on the root port on installation.
124124
This allows users to power on the root port manually by calling
125125
usb_host_lib_set_root_port_power(). */
126-
int intr_flags; /**< Interrupt flags for the underlying ISR used by the USB Host stack */
126+
int intr_flags; /**< Interrupt flags for the underlying ISR used by the USB Host stack. In dual host applications, this applies to all ports.*/
127127
usb_host_enum_filter_cb_t enum_filter_cb; /**< Enumeration filter callback. Enable CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
128128
to use this feature. Set to NULL otherwise. */
129129
struct {
@@ -133,7 +133,7 @@ typedef struct {
133133
Can be 0 if periodic TX endpoints are not used. */
134134
uint32_t rx_fifo_lines; /**< Required: Number of RX FIFO lines.
135135
Must be > 0 to enable custom configuration. */
136-
} fifo_settings_custom; /**< Optional custom FIFO configuration (advanced use).
136+
} fifo_settings_custom; /**< Optional custom FIFO configuration (advanced use), ignored for dual port applications.
137137
RX and NPTX must be > 0. If all fields are zero,
138138
a default configuration will be selected based on Kconfig bias. */
139139
unsigned peripheral_map; /**< Selects the USB peripheral(s) to use.
@@ -172,8 +172,10 @@ typedef struct {
172172
* @note If skip_phy_setup is set in the install configuration, the user is responsible for ensuring that the underlying
173173
* Host Controller is enabled and the USB PHY (internal or external) is already setup before this function is
174174
* called.
175-
* @param[in] config USB Host Library configuration
176175
*
176+
* @note In dual host configuration, fifo_settings_custom field is ignored and intr_flags field is used for all ports.
177+
*
178+
* @param[in] config USB Host Library configuration
177179
* @return
178180
* - ESP_OK: USB Host installed successfully
179181
* - ESP_ERR_INVALID_ARG: Invalid argument

host/usb/src/usb_host.c

Lines changed: 59 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,10 @@ typedef struct {
161161
struct {
162162
SemaphoreHandle_t event_sem;
163163
SemaphoreHandle_t mux_lock;
164-
TimerHandle_t auto_suspend_timer; // Freertos timer used for automatic suspend of the root port
165-
usb_phy_handle_t phy_handle; // Will be NULL if host library is installed with skip_phy_setup
166-
void *enum_client; // Pointer to Enum driver (acting as a client). Used to reroute completed USBH control transfers
167-
void *hub_client; // Pointer to External Hub driver (acting as a client). Used to reroute completed USBH control transfers. NULL, when External Hub Driver not available.
164+
TimerHandle_t auto_suspend_timer; // Freertos timer used for automatic suspend of the root port
165+
usb_phy_handle_t phy_handles[HCD_NUM_PORTS]; // One per port; NULL if skip_phy_setup or port not enabled
166+
void *enum_client; // Pointer to Enum driver (acting as a client). Used to reroute completed USBH control transfers
167+
void *hub_client; // Pointer to External Hub driver (acting as a client). Used to reroute completed USBH control transfers. NULL, when External Hub Driver not available.
168168
} constant;
169169
} host_lib_t;
170170

@@ -536,6 +536,23 @@ esp_err_t usb_host_install(const usb_host_config_t *config)
536536
HOST_CHECK_FROM_CRIT(p_host_lib_obj == NULL, ESP_ERR_INVALID_STATE);
537537
HOST_EXIT_CRITICAL();
538538

539+
// Validate configuration before any allocation (enables early return on invalid config)
540+
const unsigned peripheral_map = config->peripheral_map == 0 ? (unsigned)BIT0 : config->peripheral_map;
541+
const unsigned peripheral_map_max = (1U << SOC_USB_OTG_PERIPH_NUM) - 1;
542+
if (peripheral_map > peripheral_map_max) {
543+
ESP_LOGE(USB_HOST_TAG, "peripheral_map 0x%x exceeds maximum 0x%x", peripheral_map, peripheral_map_max);
544+
return ESP_ERR_INVALID_ARG;
545+
}
546+
547+
const bool multi_port = (peripheral_map & (peripheral_map - 1)) != 0;
548+
if (multi_port) {
549+
if (config->fifo_settings_custom.rx_fifo_lines != 0 ||
550+
config->fifo_settings_custom.nptx_fifo_lines != 0 ||
551+
config->fifo_settings_custom.ptx_fifo_lines != 0) {
552+
ESP_LOGW(USB_HOST_TAG, "Custom FIFO configuration is not supported for dual port applications; default FIFO will be used");
553+
}
554+
}
555+
539556
esp_err_t ret;
540557
host_lib_t *host_lib_obj = heap_caps_calloc(1, sizeof(host_lib_t), MALLOC_CAP_DEFAULT);
541558
SemaphoreHandle_t event_sem = xSemaphoreCreateBinary();
@@ -560,35 +577,34 @@ esp_err_t usb_host_install(const usb_host_config_t *config)
560577
- Hub
561578
*/
562579

563-
// For backward compatibility accept 0 too
564-
const unsigned peripheral_map = config->peripheral_map == 0 ? BIT0 : config->peripheral_map;
580+
// Initialize PHY handles to NULL
581+
memset(host_lib_obj->constant.phy_handles, 0, sizeof(host_lib_obj->constant.phy_handles));
565582

566-
// Install USB PHY (if necessary). USB PHY driver will also enable the underlying Host Controller
583+
// Install USB PHY for each enabled port (if not skip_phy_setup)
567584
if (!config->skip_phy_setup) {
568-
bool init_utmi_phy = false; // Default value for Linux simulation
585+
for (int i = 0; i < SOC_USB_OTG_PERIPH_NUM; i++) {
586+
if (!(peripheral_map & BIT(i))) {
587+
continue;
588+
}
589+
bool init_utmi_phy = false; // Default for Linux simulation / non-SOC builds
569590

570-
#if SOC_USB_OTG_SUPPORTED // In case we run on a real target, select the PHY from usb_dwc_info description structure
571-
// Right now we support only one peripheral, can be extended in future
572-
int peripheral_index = 0;
573-
if (peripheral_map & BIT1) {
574-
peripheral_index = 1;
575-
}
576-
init_utmi_phy = (usb_dwc_info.controllers[peripheral_index].supported_phys == USB_PHY_INST_UTMI_0);
591+
#if SOC_USB_OTG_SUPPORTED
592+
init_utmi_phy = (usb_dwc_info.controllers[i].supported_phys == USB_PHY_INST_UTMI_0);
577593
#endif // SOC_USB_OTG_SUPPORTED
578594

579-
// Host Library defaults to internal PHY
580-
usb_phy_config_t phy_config = {
581-
.controller = USB_PHY_CTRL_OTG,
582-
.target = init_utmi_phy ? USB_PHY_TARGET_UTMI : USB_PHY_TARGET_INT,
583-
.otg_mode = USB_OTG_MODE_HOST,
584-
.otg_speed = USB_PHY_SPEED_UNDEFINED, // In Host mode, the speed is determined by the connected device
585-
.ext_io_conf = NULL,
586-
.otg_io_conf = NULL,
587-
};
588-
ret = usb_new_phy(&phy_config, &host_lib_obj->constant.phy_handle);
589-
if (ret != ESP_OK) {
590-
ESP_LOGE(USB_HOST_TAG, "PHY install error: %s", esp_err_to_name(ret));
591-
goto phy_err;
595+
usb_phy_config_t phy_config = {
596+
.controller = USB_PHY_CTRL_OTG,
597+
.target = init_utmi_phy ? USB_PHY_TARGET_UTMI : USB_PHY_TARGET_INT,
598+
.otg_mode = USB_OTG_MODE_HOST,
599+
.otg_speed = USB_PHY_SPEED_UNDEFINED,
600+
.ext_io_conf = NULL,
601+
.otg_io_conf = NULL,
602+
};
603+
ret = usb_new_phy(&phy_config, &host_lib_obj->constant.phy_handles[i]);
604+
if (ret != ESP_OK) {
605+
ESP_LOGE(USB_HOST_TAG, "PHY install error for port %d: %s", i, esp_err_to_name(ret));
606+
goto phy_err;
607+
}
592608
}
593609
}
594610

@@ -633,11 +649,12 @@ esp_err_t usb_host_install(const usb_host_config_t *config)
633649
.fifo_config = NULL,
634650
};
635651

636-
// Check if user has provided a custom FIFO configuration
652+
// Check if user has provided a custom FIFO configuration (not used for multi-port)
637653
hcd_fifo_settings_t user_fifo_config;
638-
if (config->fifo_settings_custom.rx_fifo_lines != 0 ||
639-
config->fifo_settings_custom.nptx_fifo_lines != 0 ||
640-
config->fifo_settings_custom.ptx_fifo_lines != 0) {
654+
if (!multi_port &&
655+
(config->fifo_settings_custom.rx_fifo_lines != 0 ||
656+
config->fifo_settings_custom.nptx_fifo_lines != 0 ||
657+
config->fifo_settings_custom.ptx_fifo_lines != 0)) {
641658

642659
// Populate user FIFO configuration with provided values
643660
user_fifo_config.rx_fifo_lines = config->fifo_settings_custom.rx_fifo_lines;
@@ -678,11 +695,13 @@ esp_err_t usb_host_install(const usb_host_config_t *config)
678695
ESP_ERROR_CHECK(enum_uninstall());
679696
enum_err:
680697
ESP_ERROR_CHECK(usbh_uninstall());
698+
phy_err:
681699
usbh_err:
682-
if (host_lib_obj->constant.phy_handle) {
683-
ESP_ERROR_CHECK(usb_del_phy(host_lib_obj->constant.phy_handle));
700+
for (int i = 0; i < HCD_NUM_PORTS; i++) {
701+
if (host_lib_obj->constant.phy_handles[i] != NULL) {
702+
ESP_ERROR_CHECK(usb_del_phy(host_lib_obj->constant.phy_handles[i]));
703+
}
684704
}
685-
phy_err:
686705
alloc_err:
687706
if (mux_lock) {
688707
vSemaphoreDelete(mux_lock);
@@ -728,9 +747,11 @@ esp_err_t usb_host_uninstall(void)
728747
ESP_ERROR_CHECK(hub_uninstall());
729748
ESP_ERROR_CHECK(enum_uninstall());
730749
ESP_ERROR_CHECK(usbh_uninstall());
731-
// If the USB PHY was setup, then delete it
732-
if (host_lib_obj->constant.phy_handle) {
733-
ESP_ERROR_CHECK(usb_del_phy(host_lib_obj->constant.phy_handle));
750+
// Delete all PHYs that were created
751+
for (int i = 0; i < HCD_NUM_PORTS; i++) {
752+
if (host_lib_obj->constant.phy_handles[i] != NULL) {
753+
ESP_ERROR_CHECK(usb_del_phy(host_lib_obj->constant.phy_handles[i]));
754+
}
734755
}
735756

736757
// Free memory objects

0 commit comments

Comments
 (0)