Skip to content

Commit

Permalink
Merge branch 'feat/usb_disconnect_api_backport_v5.3' into 'release/v5.3'
Browse files Browse the repository at this point in the history
refactor(usb): Update HCD tests to use port power off for disconnections backport v5.3

See merge request espressif/esp-idf!33527
  • Loading branch information
suda-morris committed Sep 28, 2024
2 parents 6209663 + e009a4f commit a5ed03e
Show file tree
Hide file tree
Showing 21 changed files with 148 additions and 152 deletions.
2 changes: 1 addition & 1 deletion components/usb/hcd_dwc.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
// FS: Must be 2-64. HS: Must be 8-256. See USB-OTG databook Table 5-47
#define XFER_LIST_LEN_INTR FRAME_LIST_LEN
#define XFER_LIST_LEN_ISOC 64 // Implement longer ISOC transfer list to give us enough space for additional timing margin
#define XFER_LIST_ISOC_MARGIN 2 // The 1st ISOC transfer is scheduled 2 (micro)frames later so we have enough timing margin
#define XFER_LIST_ISOC_MARGIN 3 // The 1st ISOC transfer is scheduled 3 (micro)frames later so we have enough timing margin

// ------------------------ Flags --------------------------

Expand Down
34 changes: 23 additions & 11 deletions components/usb/hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -369,8 +369,8 @@ static void root_port_handle_events(hcd_port_handle_t root_port_hdl)
p_hub_driver_obj->dynamic.port_reqs |= PORT_REQ_RECOVER;
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_ACTION_ROOT_REQ;
break;
case ROOT_PORT_STATE_ENABLED:
// There is an enabled (active) device. We need to indicate to USBH that the device is gone
case ROOT_PORT_STATE_NOT_POWERED: // The user turned off ports' power. Indicate to USBH that the device is gone
case ROOT_PORT_STATE_ENABLED: // There is an enabled (active) device. Indicate to USBH that the device is gone
port_has_device = true;
break;
default:
Expand Down Expand Up @@ -408,10 +408,19 @@ static void root_port_req(hcd_port_handle_t root_port_hdl)
if (port_reqs & PORT_REQ_RECOVER) {
ESP_LOGD(HUB_DRIVER_TAG, "Recovering root port");
ESP_ERROR_CHECK(hcd_port_recover(p_hub_driver_obj->constant.root_port_hdl));
ESP_ERROR_CHECK(hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_POWER_ON));

// In case the port's power was turned off with usb_host_lib_set_root_port_power(false)
// we will not turn on the power during port recovery
HUB_DRIVER_ENTER_CRITICAL();
p_hub_driver_obj->dynamic.root_port_state = ROOT_PORT_STATE_POWERED;
const root_port_state_t root_state = p_hub_driver_obj->dynamic.root_port_state;
HUB_DRIVER_EXIT_CRITICAL();

if (root_state != ROOT_PORT_STATE_NOT_POWERED) {
ESP_ERROR_CHECK(hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_POWER_ON));
HUB_DRIVER_ENTER_CRITICAL();
p_hub_driver_obj->dynamic.root_port_state = ROOT_PORT_STATE_POWERED;
HUB_DRIVER_EXIT_CRITICAL();
}
}
}

Expand Down Expand Up @@ -570,15 +579,18 @@ esp_err_t hub_root_stop(void)
{
HUB_DRIVER_ENTER_CRITICAL();
HUB_DRIVER_CHECK_FROM_CRIT(p_hub_driver_obj != NULL, ESP_ERR_INVALID_STATE);
HUB_DRIVER_CHECK_FROM_CRIT(p_hub_driver_obj->dynamic.root_port_state != ROOT_PORT_STATE_NOT_POWERED, ESP_ERR_INVALID_STATE);
HUB_DRIVER_EXIT_CRITICAL();
esp_err_t ret;
ret = hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_POWER_OFF);
if (ret == ESP_OK) {
HUB_DRIVER_ENTER_CRITICAL();
p_hub_driver_obj->dynamic.root_port_state = ROOT_PORT_STATE_NOT_POWERED;
if (p_hub_driver_obj->dynamic.root_port_state == ROOT_PORT_STATE_NOT_POWERED) {
// The HUB was already stopped by usb_host_lib_set_root_port_power(false)
HUB_DRIVER_EXIT_CRITICAL();
return ESP_OK;
}
p_hub_driver_obj->dynamic.root_port_state = ROOT_PORT_STATE_NOT_POWERED;
HUB_DRIVER_EXIT_CRITICAL();

// HCD_PORT_CMD_POWER_OFF will only fail if the port is already powered_off
// This should never happen, so we assert ret == ESP_OK
const esp_err_t ret = hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_POWER_OFF);
assert(ret == ESP_OK);
return ret;
}

Expand Down
34 changes: 27 additions & 7 deletions components/usb/include/usb/usb_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,16 @@ typedef void (*usb_host_client_event_cb_t)(const usb_host_client_event_msg_t *ev
* Configuration structure of the USB Host Library. Provided in the usb_host_install() function
*/
typedef struct {
bool skip_phy_setup; /**< If set, the USB Host Library will not configure the USB PHY thus allowing the user
to manually configure the USB PHY before calling usb_host_install(). Users should
set this if they want to use an external USB PHY. Otherwise, the USB Host Library
will automatically configure the internal USB PHY */
int intr_flags; /**< Interrupt flags for the underlying ISR used by the USB Host stack */
usb_host_enum_filter_cb_t enum_filter_cb; /**< Enumeration filter callback. Enable CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
to use this feature. Set to NULL otherwise. */
bool skip_phy_setup; /**< If set, the USB Host Library will not configure the USB PHY thus allowing
the user to manually configure the USB PHY before calling usb_host_install().
Users should set this if they want to use an external USB PHY. Otherwise,
the USB Host Library will automatically configure the internal USB PHY */
bool root_port_unpowered; /**< If set, the USB Host Library will not power on the root port on installation.
This allows users to power on the root port manually by calling
usb_host_lib_set_root_port_power(). */
int intr_flags; /**< Interrupt flags for the underlying ISR used by the USB Host stack */
usb_host_enum_filter_cb_t enum_filter_cb; /**< Enumeration filter callback. Enable CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
to use this feature. Set to NULL otherwise. */
} usb_host_config_t;

/**
Expand Down Expand Up @@ -187,6 +190,23 @@ esp_err_t usb_host_lib_unblock(void);
*/
esp_err_t usb_host_lib_info(usb_host_lib_info_t *info_ret);

/**
* @brief Power the root port ON or OFF
*
* - Powering ON the root port will allow device connections to occur
* - Powering OFF the root port will disconnect all devices downstream off the root port and prevent
* any further device connections.
*
* @note If 'usb_host_config_t.root_port_unpowered' was set on USB Host Library installation, users must call this
* function to power ON the root port before any device connections can occur.
*
* @param[in] enable True to power the root port ON, false to power OFF
* @return
* - ESP_OK: Root port power enabled/disabled
* - ESP_ERR_INVALID_STATE: Root port already powered or HUB driver not installed
*/
esp_err_t usb_host_lib_set_root_port_power(bool enable);

// ------------------------------------------------ Client Functions ---------------------------------------------------

/**
Expand Down
1 change: 0 additions & 1 deletion components/usb/test_apps/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ idf_component_register(SRCS "dev_hid.c"
"dev_isoc.c"
"dev_msc.c"
"mock_msc.c"
"test_usb_common.c"
INCLUDE_DIRS "."
REQUIRES usb unity)
46 changes: 0 additions & 46 deletions components/usb/test_apps/common/test_usb_common.c

This file was deleted.

26 changes: 0 additions & 26 deletions components/usb/test_apps/common/test_usb_common.h

This file was deleted.

2 changes: 1 addition & 1 deletion components/usb/test_apps/hcd/main/test_hcd_bulk.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Test HCD bulk pipe URBs
#define TEST_NUM_SECTORS_TOTAL 10
#define TEST_NUM_SECTORS_PER_XFER 2

TEST_CASE("Test HCD bulk pipe URBs", "[bulk][full_speed]")
TEST_CASE("Test HCD bulk pipe URBs", "[bulk][full_speed][high_speed]")
{
usb_speed_t port_speed = test_hcd_wait_for_conn(port_hdl); // Trigger a connection
vTaskDelay(pdMS_TO_TICKS(100)); // Short delay send of SOF (for FS) or EOPs (for LS)
Expand Down
33 changes: 24 additions & 9 deletions components/usb/test_apps/hcd/main/test_hcd_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
#include "hcd.h"
#include "usb_private.h"
#include "usb/usb_types_ch9.h"
#include "esp_private/usb_phy.h"
#include "test_hcd_common.h"
#include "test_usb_common.h"
#include "mock_msc.h"
#include "unity.h"

Expand All @@ -38,6 +38,7 @@ typedef struct {
} pipe_event_msg_t;

hcd_port_handle_t port_hdl = NULL;
static usb_phy_handle_t phy_hdl = NULL;

// ---------------------------------------------------- Private --------------------------------------------------------

Expand Down Expand Up @@ -144,7 +145,21 @@ int test_hcd_get_num_pipe_events(hcd_pipe_handle_t pipe_hdl)

hcd_port_handle_t test_hcd_setup(void)
{
test_usb_init_phy(); // Initialize the internal USB PHY and USB Controller for testing
// Deinitialize PHY from previous failed test
if (phy_hdl != NULL) {
TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, usb_del_phy(phy_hdl), "Failed to delete PHY");
phy_hdl = NULL;
}
// Initialize the internal USB PHY to connect to the USB OTG peripheral
usb_phy_config_t phy_config = {
.controller = USB_PHY_CTRL_OTG,
.target = 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,
.otg_io_conf = NULL,
};
TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, usb_new_phy(&phy_config, &phy_hdl), "Failed to init USB 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));
TEST_ASSERT_NOT_NULL(port_evt_queue);
Expand All @@ -163,8 +178,6 @@ hcd_port_handle_t test_hcd_setup(void)
hcd_port_handle_t port_hdl;
TEST_ASSERT_EQUAL(ESP_OK, hcd_port_init(PORT_NUM, &port_config, &port_hdl));
TEST_ASSERT_NOT_NULL(port_hdl);
TEST_ASSERT_EQUAL(HCD_PORT_STATE_NOT_POWERED, hcd_port_get_state(port_hdl));
test_usb_set_phy_state(false, 0); // Force disconnected state on PHY
return port_hdl;
}

Expand All @@ -181,17 +194,18 @@ void test_hcd_teardown(hcd_port_handle_t port_hdl)
// Uninstall the HCD
TEST_ASSERT_EQUAL(ESP_OK, hcd_uninstall());
vQueueDelete(port_evt_queue);
test_usb_deinit_phy(); // Deinitialize the internal USB PHY after testing
// Deinitialize the internal USB PHY
TEST_ASSERT_EQUAL_MESSAGE(ESP_OK, usb_del_phy(phy_hdl), "Failed to delete PHY");
phy_hdl = NULL;
}

usb_speed_t test_hcd_wait_for_conn(hcd_port_handle_t port_hdl)
{
// Power ON the port
// Power ON the port. This should allow for connections to occur
TEST_ASSERT_EQUAL(ESP_OK, hcd_port_command(port_hdl, HCD_PORT_CMD_POWER_ON));
TEST_ASSERT_EQUAL(HCD_PORT_STATE_DISCONNECTED, hcd_port_get_state(port_hdl));
// Wait for connection event
printf("Waiting for connection\n");
test_usb_set_phy_state(true, pdMS_TO_TICKS(100)); // Allow for connected state on PHY
test_hcd_expect_port_event(port_hdl, HCD_PORT_EVENT_CONNECTION);
TEST_ASSERT_EQUAL(HCD_PORT_EVENT_CONNECTION, hcd_port_handle_event(port_hdl));
TEST_ASSERT_EQUAL(HCD_PORT_STATE_DISABLED, hcd_port_get_state(port_hdl));
Expand All @@ -217,9 +231,10 @@ void test_hcd_wait_for_disconn(hcd_port_handle_t port_hdl, bool already_disabled
TEST_ASSERT_EQUAL(ESP_OK, hcd_port_command(port_hdl, HCD_PORT_CMD_DISABLE));
TEST_ASSERT_EQUAL(HCD_PORT_STATE_DISABLED, hcd_port_get_state(port_hdl));
}
// Wait for a safe disconnect
printf("Waiting for disconnection\n");
test_usb_set_phy_state(false, pdMS_TO_TICKS(100)); // Force disconnected state on PHY
// Power-off the port to trigger a disconnection
TEST_ASSERT_EQUAL(ESP_OK, hcd_port_command(port_hdl, HCD_PORT_CMD_POWER_OFF));
// Wait for the port disconnection event
test_hcd_expect_port_event(port_hdl, HCD_PORT_EVENT_DISCONNECTION);
TEST_ASSERT_EQUAL(HCD_PORT_EVENT_DISCONNECTION, hcd_port_handle_event(port_hdl));
TEST_ASSERT_EQUAL(HCD_PORT_STATE_RECOVERY, hcd_port_get_state(port_hdl));
Expand Down
6 changes: 4 additions & 2 deletions components/usb/test_apps/hcd/main/test_hcd_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ int test_hcd_get_num_pipe_events(hcd_pipe_handle_t pipe_hdl);
/**
* @brief Sets up the HCD and initializes an HCD port.
*
* The HCD port is left in the HCD_PORT_STATE_NOT_POWERED state
*
* @return hcd_port_handle_t Port handle
*/
hcd_port_handle_t test_hcd_setup(void);
Expand All @@ -67,7 +69,7 @@ void test_hcd_teardown(hcd_port_handle_t port_hdl);
/**
* @brief Wait for a connection on an HCD port
*
* @note This function will internally call test_usb_set_phy_state() to allow for a connection
* @note HCD_PORT_CMD_POWER_ON is called internally to allow connections
*
* @param port_hdl Port handle
* @return usb_speed_t Speed of the connected device
Expand All @@ -77,7 +79,7 @@ usb_speed_t test_hcd_wait_for_conn(hcd_port_handle_t port_hdl);
/**
* @brief Wait for a disconnection on an HCD port
*
* @note This fucntion will internally call test_usb_set_phy_state() to force a disconnection
* @note HCD_PORT_CMD_POWER_OFF is called internally to force a disconnection
*
* @param port_hdl Port handle
* @param already_disabled Whether the HCD port is already in the disabled state
Expand Down
10 changes: 6 additions & 4 deletions components/usb/test_apps/hcd/main/test_hcd_ctrl.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -33,7 +33,7 @@ Test HCD control pipe URBs (normal completion and early abort)
- Expect URB to be USB_TRANSFER_STATUS_CANCELED or USB_TRANSFER_STATUS_COMPLETED
- Teardown
*/
TEST_CASE("Test HCD control pipe URBs", "[ctrl][low_speed][full_speed]")
TEST_CASE("Test HCD control pipe URBs", "[ctrl][low_speed][full_speed][high_speed]")
{
usb_speed_t port_speed = test_hcd_wait_for_conn(port_hdl); // Trigger a connection
vTaskDelay(pdMS_TO_TICKS(100)); // Short delay send of SOF (for FS) or EOPs (for LS)
Expand Down Expand Up @@ -110,7 +110,7 @@ TEST_CASE("Test HCD control pipe URBs", "[ctrl][low_speed][full_speed]")
/*
Test HCD control pipe STALL condition, abort, and clear
@todo this test is not passing with low-speed: test with bus analyzer
@todo this test is not passing with low-speed: test with bus analyzer IDF-10995
Purpose:
- Test that a control pipe can react to a STALL (i.e., a HCD_PIPE_EVENT_ERROR_STALL event)
Expand All @@ -128,7 +128,7 @@ Test HCD control pipe STALL condition, abort, and clear
- Dequeue URBs
- Teardown
*/
TEST_CASE("Test HCD control pipe STALL", "[ctrl][full_speed]")
TEST_CASE("Test HCD control pipe STALL", "[ctrl][full_speed][high_speed]")
{
usb_speed_t port_speed = test_hcd_wait_for_conn(port_hdl); // Trigger a connection
vTaskDelay(pdMS_TO_TICKS(100)); // Short delay send of SOF (for FS) or EOPs (for LS)
Expand Down Expand Up @@ -217,6 +217,8 @@ TEST_CASE("Test HCD control pipe STALL", "[ctrl][full_speed]")
/*
Test control pipe run-time halt and clear
@todo this test is not passing on P4: test with bus analyzer IDF-10996
Purpose:
- Test that a control pipe can be halted with HCD_PIPE_CMD_HALT whilst there are ongoing URBs
- Test that a control pipe can be un-halted with a HCD_PIPE_CMD_CLEAR
Expand Down
Loading

0 comments on commit a5ed03e

Please sign in to comment.