Merge branch 'feature/usb_dual_host_2' into 'master'

feat(usb/host): Add option to choose peripherals for USB host library

Closes IDF-11705, IDF-9052, and DOC-10991

See merge request espressif/esp-idf!35401
This commit is contained in:
Tomas Rezucha
2025-05-15 16:18:05 +08:00
18 changed files with 169 additions and 226 deletions

View File

@ -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
};

View File

@ -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
};

View File

@ -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 <stdint.h>
#include "soc/periph_defs.h"
#if SOC_USB_OTG_SUPPORTED
/*
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

View File

@ -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"

View File

@ -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;
}

View File

@ -7,6 +7,7 @@
#include <stdio.h>
#include <catch2/catch_test_macros.hpp>
#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

View File

@ -11,6 +11,7 @@
#include <sys/queue.h>
#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;

View File

@ -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;
/**

View File

@ -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

View File

@ -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 */

View File

@ -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;
}

View File

@ -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,

View File

@ -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

View File

@ -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");

View File

@ -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,

View File

@ -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).

View File

@ -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) 的第一个可用接口。

View File

@ -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);