forked from espressif/esp-idf
Merge branch 'feature/usb_host_ext_port_testing' into 'master'
feature(ci): The External Port Driver test app Closes IDF-10473 See merge request espressif/esp-idf!37275
This commit is contained in:
@@ -313,6 +313,10 @@ uint8_t test_hcd_enum_device(hcd_pipe_handle_t default_pipe)
|
|||||||
// Update address of default pipe
|
// Update address of default pipe
|
||||||
TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_update_dev_addr(default_pipe, ENUM_ADDR));
|
TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_update_dev_addr(default_pipe, ENUM_ADDR));
|
||||||
|
|
||||||
|
// Some high-speed Hubs need some time before being able to process SetConfiguration request
|
||||||
|
// Full-speed devices doesn't require that time
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
|
|
||||||
// Send a set configuration request
|
// Send a set configuration request
|
||||||
USB_SETUP_PACKET_INIT_SET_CONFIG(setup_pkt, ENUM_CONFIG);
|
USB_SETUP_PACKET_INIT_SET_CONFIG(setup_pkt, ENUM_CONFIG);
|
||||||
urb->transfer.num_bytes = sizeof(usb_setup_packet_t);
|
urb->transfer.num_bytes = sizeof(usb_setup_packet_t);
|
||||||
|
11
components/usb/test_apps/ext_port/CMakeLists.txt
Normal file
11
components/usb/test_apps/ext_port/CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# This is the project CMakeLists.txt file for the test subproject
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
|
||||||
|
set(EXTRA_COMPONENT_DIRS "../common")
|
||||||
|
|
||||||
|
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||||
|
idf_build_set_property(MINIMAL_BUILD ON)
|
||||||
|
|
||||||
|
project(test_app_hub)
|
12
components/usb/test_apps/ext_port/README.md
Normal file
12
components/usb/test_apps/ext_port/README.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
| Supported Targets | ESP32-P4 | ESP32-S2 | ESP32-S3 |
|
||||||
|
| ----------------- | -------- | -------- | -------- |
|
||||||
|
|
||||||
|
# USB: External Port test application
|
||||||
|
|
||||||
|
Testing the External Port Driver logic.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
USB external Hub should be attached to the root port (USB OTG peripheral).
|
||||||
|
|
||||||
|
Ports could be configured via menuconfig.
|
16
components/usb/test_apps/ext_port/main/CMakeLists.txt
Normal file
16
components/usb/test_apps/ext_port/main/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
|
||||||
|
# the component can be registered as WHOLE_ARCHIVE
|
||||||
|
set(srcs)
|
||||||
|
|
||||||
|
list(APPEND srcs "test_app_main.c"
|
||||||
|
"test_ext_port.c"
|
||||||
|
# Internal common test files
|
||||||
|
"hub_common.c"
|
||||||
|
"ext_port_common.c"
|
||||||
|
)
|
||||||
|
|
||||||
|
idf_component_register(SRCS ${srcs}
|
||||||
|
INCLUDE_DIRS "."
|
||||||
|
PRIV_INCLUDE_DIRS "."
|
||||||
|
REQUIRES usb unity common
|
||||||
|
WHOLE_ARCHIVE)
|
23
components/usb/test_apps/ext_port/main/Kconfig.projbuild
Normal file
23
components/usb/test_apps/ext_port/main/Kconfig.projbuild
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
source "$IDF_PATH/components/usb/test_apps/common/Kconfig.common"
|
||||||
|
|
||||||
|
menu "USB Host External Port test"
|
||||||
|
|
||||||
|
config USB_HOST_TEST_HUB_PORT_NUM_DEVICE_FSHS
|
||||||
|
int "Port number with Full-/High-speed device"
|
||||||
|
default 1
|
||||||
|
help
|
||||||
|
Configure the number of the port, with a Full-/High-speed USB device.
|
||||||
|
|
||||||
|
config USB_HOST_TEST_HUB_PORT_NUM_DEVICE_LS
|
||||||
|
int "Port number with Low-speed device"
|
||||||
|
default 2
|
||||||
|
help
|
||||||
|
Configure the number of the port, with a Low-speed USB device.
|
||||||
|
|
||||||
|
config USB_HOST_TEST_HUB_PORT_NUM_EMPTY
|
||||||
|
int "Empty Port number"
|
||||||
|
default 3
|
||||||
|
help
|
||||||
|
Configure the number of the port, without device.
|
||||||
|
|
||||||
|
endmenu
|
525
components/usb/test_apps/ext_port/main/ext_port_common.c
Normal file
525
components/usb/test_apps/ext_port/main/ext_port_common.c
Normal file
@@ -0,0 +1,525 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: CC0-1.0
|
||||||
|
*/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
// USB Host Lib
|
||||||
|
#include "ext_hub.h" // For port driver API, remove when IDF-12562 is closed
|
||||||
|
#include "ext_port.h"
|
||||||
|
#include "usb_private.h"
|
||||||
|
#include "usb/usb_types_ch9.h"
|
||||||
|
#include "usb/usb_types_ch11.h"
|
||||||
|
#include "unity.h"
|
||||||
|
#include "hub_common.h"
|
||||||
|
#include "ext_port_common.h"
|
||||||
|
|
||||||
|
#define TEST_EXT_PORT_QUEUE_LEN 3
|
||||||
|
#define EXT_PORT_PROC_CB_TIMEOUT_MS 50
|
||||||
|
#define EXT_PORT_HUB_REQUEST_TIMEOUT_MS 100
|
||||||
|
#define EXT_PORT_EVENT_TIMEOUT_MS 100
|
||||||
|
|
||||||
|
#define USB_B_REQUEST_ANY 0xFF
|
||||||
|
|
||||||
|
const char *const hub_request_string[] = {
|
||||||
|
"Get Port Status",
|
||||||
|
"Clear Port Feature",
|
||||||
|
"Get Hub State",
|
||||||
|
"Set Feature",
|
||||||
|
"RESERVED",
|
||||||
|
"RESERVED",
|
||||||
|
"Get Hub Descriptor",
|
||||||
|
"Set Hub Descriptor",
|
||||||
|
"Clear TT Buffer",
|
||||||
|
"Reset TT",
|
||||||
|
"Get TT State",
|
||||||
|
"Stop TT",
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *const hub_feature_string[] = {
|
||||||
|
"Port Connection",
|
||||||
|
"Port Enable",
|
||||||
|
"Port Suspend",
|
||||||
|
"Port Over Current",
|
||||||
|
"Port Reset",
|
||||||
|
"Feature N/A",
|
||||||
|
"Feature N/A",
|
||||||
|
"Feature N/A",
|
||||||
|
"Port Power",
|
||||||
|
"Port Low Speed",
|
||||||
|
"Feature N/A",
|
||||||
|
"Feature N/A",
|
||||||
|
"Feature N/A",
|
||||||
|
"Feature N/A",
|
||||||
|
"Feature N/A",
|
||||||
|
"Feature N/A",
|
||||||
|
"C Port Connection",
|
||||||
|
"C Port Enable",
|
||||||
|
"C Port Suspend",
|
||||||
|
"C Port Over Current",
|
||||||
|
"C Port Reset",
|
||||||
|
"Port Test",
|
||||||
|
"Port Indicator",
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *const ext_port_event_string[] = {
|
||||||
|
"N/A",
|
||||||
|
"<- Connected",
|
||||||
|
"<> Reset Completed",
|
||||||
|
"-> Disconnected",
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ext_hub_handle_t hub_hdl;
|
||||||
|
ext_hub_request_data_t data;
|
||||||
|
} ext_port_hub_request_msg_t;
|
||||||
|
|
||||||
|
static const ext_hub_port_driver_t* port_api;
|
||||||
|
SemaphoreHandle_t _process_cd_req = NULL;
|
||||||
|
QueueHandle_t _ext_port_hub_req_queue = NULL;
|
||||||
|
QueueHandle_t _ext_port_event_queue = NULL;
|
||||||
|
|
||||||
|
static void test_ext_port_callback(void *user_arg)
|
||||||
|
{
|
||||||
|
// We can call ext_port_process() right here, but use semaphore instead to have more linear logic in the test
|
||||||
|
SemaphoreHandle_t process_req_cb = (SemaphoreHandle_t)user_arg;
|
||||||
|
xSemaphoreGive(process_req_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_ext_port_event_callback(ext_port_event_data_t *event_data, void *arg)
|
||||||
|
{
|
||||||
|
QueueHandle_t ext_port_event_queue = (QueueHandle_t)arg;
|
||||||
|
ext_port_event_data_t msg = *event_data;
|
||||||
|
xQueueSend(ext_port_event_queue, &msg, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t test_ext_port_hub_class_request(ext_hub_handle_t ext_hub_hdl, ext_hub_request_data_t *request_data, void *user_arg)
|
||||||
|
{
|
||||||
|
QueueHandle_t hub_req_queue = (QueueHandle_t)user_arg;
|
||||||
|
ext_port_hub_request_msg_t msg = {
|
||||||
|
.hub_hdl = ext_hub_hdl,
|
||||||
|
.data = {
|
||||||
|
.request = request_data->request,
|
||||||
|
.port_num = request_data->port_num,
|
||||||
|
.feature = request_data->feature,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
xQueueSend(hub_req_queue, &msg, portMAX_DELAY);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_wait_ext_port_process_request(void)
|
||||||
|
{
|
||||||
|
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(_process_cd_req, pdMS_TO_TICKS(EXT_PORT_PROC_CB_TIMEOUT_MS)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_wait_ext_port_event(ext_port_event_t event)
|
||||||
|
{
|
||||||
|
// Get the port event queue from the port's context variable
|
||||||
|
QueueHandle_t ext_port_evt_queue = _ext_port_event_queue;
|
||||||
|
TEST_ASSERT_NOT_NULL(ext_port_evt_queue);
|
||||||
|
// Wait for port callback to send an event message
|
||||||
|
ext_port_event_data_t msg;
|
||||||
|
BaseType_t ret = xQueueReceive(ext_port_evt_queue, &msg, pdMS_TO_TICKS(EXT_PORT_EVENT_TIMEOUT_MS));
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(pdPASS, ret, "External Hub port event not generated on time");
|
||||||
|
// Check the contents of that event message
|
||||||
|
printf("\tHub port event: %s\n", ext_port_event_string[msg.event]);
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(event, msg.event, "Unexpected External Hub port event");
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t test_wait_ext_port_hub_request(void *ext_hub_hdl, uint8_t port_num, usb_hub_class_request_t request)
|
||||||
|
{
|
||||||
|
// Get the hub request queue
|
||||||
|
TEST_ASSERT_NOT_NULL(_ext_port_hub_req_queue);
|
||||||
|
// Wait for port callback to send an event message
|
||||||
|
ext_port_hub_request_msg_t msg;
|
||||||
|
BaseType_t ret = xQueueReceive(_ext_port_hub_req_queue, &msg, pdMS_TO_TICKS(EXT_PORT_HUB_REQUEST_TIMEOUT_MS));
|
||||||
|
if (ret != pdPASS) {
|
||||||
|
printf("Hub request callback not triggered on time \n");
|
||||||
|
return ESP_ERR_TIMEOUT;
|
||||||
|
}
|
||||||
|
// Check the contents of that event message
|
||||||
|
// TEST_ASSERT_EQUAL(ext_hub_hdl, msg.hub_hdl); // TODO: Enable verification after closing IDF-10023
|
||||||
|
TEST_ASSERT_EQUAL(port_num, msg.data.port_num);
|
||||||
|
printf("\tCallback for class request: %s %s\n", hub_request_string[msg.data.request],
|
||||||
|
(msg.data.request == USB_B_REQUEST_HUB_SET_PORT_FEATURE ||
|
||||||
|
msg.data.request == USB_B_REQUEST_HUB_CLEAR_FEATURE)
|
||||||
|
? hub_feature_string[msg.data.feature]
|
||||||
|
: " ");
|
||||||
|
// Verify the request if it is not USB_B_REQUEST_ANY
|
||||||
|
if (msg.data.request != USB_B_REQUEST_ANY) {
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(request, msg.data.request, "Unexpected request");
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ext_port_setup(void)
|
||||||
|
{
|
||||||
|
// Create a semaphore to wait for the process request
|
||||||
|
_process_cd_req = xSemaphoreCreateBinary();
|
||||||
|
TEST_ASSERT_NOT_NULL(_process_cd_req);
|
||||||
|
// Create a queue for ext port hub request
|
||||||
|
_ext_port_hub_req_queue = xQueueCreate(TEST_EXT_PORT_QUEUE_LEN, sizeof(ext_port_hub_request_msg_t));
|
||||||
|
TEST_ASSERT_NOT_NULL(_ext_port_hub_req_queue);
|
||||||
|
// Create a queue for ext port event
|
||||||
|
_ext_port_event_queue = xQueueCreate(TEST_EXT_PORT_QUEUE_LEN, sizeof(ext_port_event_data_t));
|
||||||
|
TEST_ASSERT_NOT_NULL(_ext_port_event_queue);
|
||||||
|
// Install External Port driver
|
||||||
|
ext_port_driver_config_t ext_port_config = {
|
||||||
|
.proc_req_cb = test_ext_port_callback,
|
||||||
|
.proc_req_cb_arg = (void*)_process_cd_req,
|
||||||
|
.event_cb = test_ext_port_event_callback,
|
||||||
|
.event_cb_arg = (void*)_ext_port_event_queue,
|
||||||
|
.hub_request_cb = test_ext_port_hub_class_request,
|
||||||
|
.hub_request_cb_arg = (void*)_ext_port_hub_req_queue,
|
||||||
|
};
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_install(&ext_port_config));
|
||||||
|
port_api = ext_port_get_driver();
|
||||||
|
TEST_ASSERT_NOT_NULL(port_api);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ext_port_teardown(void)
|
||||||
|
{
|
||||||
|
// Uninstall External Port driver
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_uninstall());
|
||||||
|
vSemaphoreDelete(_process_cd_req);
|
||||||
|
vQueueDelete(_ext_port_hub_req_queue);
|
||||||
|
vQueueDelete(_ext_port_event_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
ext_port_hdl_t test_ext_port_new(ext_port_config_t *config)
|
||||||
|
{
|
||||||
|
ext_port_hdl_t port = NULL;
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->new (config, (void**) &port));
|
||||||
|
TEST_ASSERT_NOT_NULL(port);
|
||||||
|
// Adding the port should trigger the process request callback
|
||||||
|
test_wait_ext_port_process_request();
|
||||||
|
// Process the port
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_process());
|
||||||
|
// Newly added port should trigger the Get Port Status request on the parent hub
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, test_wait_ext_port_hub_request(NULL,
|
||||||
|
config->parent_port_num,
|
||||||
|
USB_B_REQUEST_HUB_GET_PORT_STATUS)); // Process the port
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ext_port_power_on(uint8_t port1, ext_port_hdl_t port_hdl)
|
||||||
|
{
|
||||||
|
usb_port_status_t port_status;
|
||||||
|
hub_get_port_status(port1, &port_status);
|
||||||
|
// Verify actual port status
|
||||||
|
TEST_ASSERT_EQUAL(0, port_status.wPortStatus.val); // Port should be not powered
|
||||||
|
TEST_ASSERT_EQUAL(0, port_status.wPortChange.val); // No port Change
|
||||||
|
|
||||||
|
// Set port status to the External Port Driver
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->set_status(port_hdl, &port_status));
|
||||||
|
// Setting the port status should trigger the process request callback
|
||||||
|
test_wait_ext_port_process_request();
|
||||||
|
// Process the port
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_process());
|
||||||
|
// Wait Set Feature - Port Power
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, test_wait_ext_port_hub_request(NULL,
|
||||||
|
port1,
|
||||||
|
USB_B_REQUEST_HUB_SET_PORT_FEATURE));
|
||||||
|
// Power on the port
|
||||||
|
hub_port_power_on(port1);
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(30)); // TODO: change to power power on delay
|
||||||
|
// After powering the port, trigger the port processing
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->req_process(port_hdl));
|
||||||
|
test_wait_ext_port_process_request();
|
||||||
|
// Process the port
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_process());
|
||||||
|
// Wait Get Port Status
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, test_wait_ext_port_hub_request(NULL,
|
||||||
|
port1,
|
||||||
|
USB_B_REQUEST_HUB_GET_PORT_STATUS));
|
||||||
|
// Get Port Status
|
||||||
|
hub_get_port_status(port1, &port_status);
|
||||||
|
// Verify the actual port status
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortStatus.PORT_POWER); // Port should be powered
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ext_port_disconnected(uint8_t port1, ext_port_hdl_t port_hdl)
|
||||||
|
{
|
||||||
|
usb_port_status_t port_status;
|
||||||
|
|
||||||
|
// Get Actual Port Status
|
||||||
|
hub_get_port_status(port1, &port_status);
|
||||||
|
// Port should be powered, has no connection and no port change
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortStatus.PORT_POWER);
|
||||||
|
TEST_ASSERT_EQUAL(0, port_status.wPortStatus.PORT_CONNECTION);
|
||||||
|
TEST_ASSERT_EQUAL(0, port_status.wPortChange.val);
|
||||||
|
|
||||||
|
// Set port status to the External Port Driver
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->set_status(port_hdl, &port_status));
|
||||||
|
// Setting the port status should trigger the process request callback
|
||||||
|
test_wait_ext_port_process_request();
|
||||||
|
// Process the port
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_process());
|
||||||
|
// Disconnected port doesn't require any processing, so there should be no process request
|
||||||
|
TEST_ASSERT_EQUAL(ESP_ERR_TIMEOUT, test_wait_ext_port_hub_request(NULL,
|
||||||
|
port1,
|
||||||
|
USB_B_REQUEST_ANY));
|
||||||
|
// Port doesn't require any processing, so there should debug message "No more ports to handle"
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_process()); // TODO: IDF-12562 replace with Enable INTR EP event await
|
||||||
|
// Port is idle
|
||||||
|
}
|
||||||
|
|
||||||
|
usb_speed_t test_ext_port_connected(uint8_t port1, ext_port_hdl_t port_hdl)
|
||||||
|
{
|
||||||
|
usb_port_status_t port_status;
|
||||||
|
// Get the port status
|
||||||
|
hub_get_port_status(port1, &port_status);
|
||||||
|
// Port should be powered, has a connection and a port change
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortStatus.PORT_POWER);
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortStatus.PORT_CONNECTION);
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortChange.C_PORT_CONNECTION);
|
||||||
|
|
||||||
|
// Set port status to the External Port Driver
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->set_status(port_hdl, &port_status));
|
||||||
|
// Setting the port status should trigger the process request callback
|
||||||
|
test_wait_ext_port_process_request();
|
||||||
|
// Process the port
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_process());
|
||||||
|
// Setting the status of the port with C_CONNECTION should trigger the Clear Port Connection
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, test_wait_ext_port_hub_request(NULL,
|
||||||
|
port1,
|
||||||
|
USB_B_REQUEST_HUB_CLEAR_FEATURE));
|
||||||
|
|
||||||
|
// Clear Port Connection
|
||||||
|
hub_port_clear_connection(port1);
|
||||||
|
// After clearing connection, trigger the port processing
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->req_process(port_hdl));
|
||||||
|
test_wait_ext_port_process_request();
|
||||||
|
// Process the port
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_process());
|
||||||
|
// Powered port with connection should trigger the status update
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, test_wait_ext_port_hub_request(NULL,
|
||||||
|
port1,
|
||||||
|
USB_B_REQUEST_HUB_GET_PORT_STATUS));
|
||||||
|
// Get Port Status
|
||||||
|
hub_get_port_status(port1, &port_status);
|
||||||
|
// Port should be powered, has a connection and no port change
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortStatus.PORT_POWER); // Powered
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortStatus.PORT_CONNECTION); // Connection detected
|
||||||
|
TEST_ASSERT_EQUAL(0, port_status.wPortChange.C_PORT_CONNECTION); // Port does not need to clear connection
|
||||||
|
// Set port status to the External Port Driver
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->set_status(port_hdl, &port_status));
|
||||||
|
// Wait for the process request callback
|
||||||
|
test_wait_ext_port_process_request();
|
||||||
|
// Setting the port status should trigger the process request callback
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_process());
|
||||||
|
// Powered port with connection should trigger the Port Reset request on the parent hub
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, test_wait_ext_port_hub_request(NULL,
|
||||||
|
port1,
|
||||||
|
USB_B_REQUEST_HUB_SET_PORT_FEATURE));
|
||||||
|
// Port Reset
|
||||||
|
hub_port_reset(port1);
|
||||||
|
// Give port some time to process the reset
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(30)); // TODO: Port Reset recovery
|
||||||
|
// After resetting the port, trigger the port processing
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->req_process(port_hdl));
|
||||||
|
test_wait_ext_port_process_request();
|
||||||
|
// Process the port
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_process());
|
||||||
|
// Powered port with connection should trigger the Get Port Status request on the parent hub
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, test_wait_ext_port_hub_request(NULL,
|
||||||
|
port1,
|
||||||
|
USB_B_REQUEST_HUB_GET_PORT_STATUS));
|
||||||
|
// Get Port Status
|
||||||
|
hub_get_port_status(port1, &port_status);
|
||||||
|
// Port should be powered, has a connection and requires clear a port reset
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortStatus.PORT_POWER); // Powered
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortStatus.PORT_CONNECTION); // Connection detected
|
||||||
|
TEST_ASSERT_EQUAL(0, port_status.wPortChange.C_PORT_CONNECTION); // Port does not need to clear connection
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortChange.C_PORT_RESET); // Port does need to clear reset
|
||||||
|
// Set port status
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->set_status(port_hdl, &port_status));
|
||||||
|
// Wait for the process request callback
|
||||||
|
test_wait_ext_port_process_request();
|
||||||
|
// Process the port
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_process());
|
||||||
|
// Powered port with connection after reset should trigger the Clear Port Reset request on the parent hub
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, test_wait_ext_port_hub_request(NULL,
|
||||||
|
port1,
|
||||||
|
USB_B_REQUEST_HUB_CLEAR_FEATURE));
|
||||||
|
// Clear Port reset request
|
||||||
|
hub_port_clear_reset(port1);
|
||||||
|
// After clearing reset, trigger the port processing
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->req_process(port_hdl));
|
||||||
|
// Wait for the process request callback
|
||||||
|
test_wait_ext_port_process_request();
|
||||||
|
// Process the port
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_process());
|
||||||
|
// Powered port with connection after reset should trigger the Get Port Status request on the parent hub
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, test_wait_ext_port_hub_request(NULL,
|
||||||
|
port1,
|
||||||
|
USB_B_REQUEST_HUB_GET_PORT_STATUS));
|
||||||
|
|
||||||
|
// Get Port Status
|
||||||
|
hub_get_port_status(port1, &port_status);
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortStatus.PORT_POWER); // Powered
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortStatus.PORT_CONNECTION); // Connection detected
|
||||||
|
TEST_ASSERT_EQUAL(0, port_status.wPortChange.C_PORT_CONNECTION); // Port does not need to clear connection
|
||||||
|
TEST_ASSERT_EQUAL(0, port_status.wPortChange.C_PORT_RESET); // Port does not need to clear reset
|
||||||
|
// Set port status to the External Port Driver
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->set_status(port_hdl, &port_status));
|
||||||
|
// Wait for the process request callback
|
||||||
|
test_wait_ext_port_process_request();
|
||||||
|
// Process the port
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_process());
|
||||||
|
// Processing the port after clear reset should generate the connection event
|
||||||
|
test_wait_ext_port_event(EXT_PORT_CONNECTED);
|
||||||
|
// Get device speed
|
||||||
|
usb_speed_t dev_speed;
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->get_speed(port_hdl, &dev_speed));
|
||||||
|
return dev_speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ext_port_imitate_disconnection(uint8_t port1, ext_port_hdl_t port_hdl)
|
||||||
|
{
|
||||||
|
usb_port_status_t port_status;
|
||||||
|
// Get Port Status
|
||||||
|
hub_get_port_status(port1, &port_status);
|
||||||
|
// Imitate disconnection possible only on the port, that has device and enabled
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortStatus.PORT_POWER); // Port powered
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortStatus.PORT_ENABLE); // Port enabled
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortStatus.PORT_CONNECTION); // Port has connection
|
||||||
|
TEST_ASSERT_EQUAL(0, port_status.wPortChange.val); // No change to handle
|
||||||
|
|
||||||
|
//
|
||||||
|
// EMULATE DISCONNECTION VIA PORT STATUS
|
||||||
|
//
|
||||||
|
// Change the port status: reset enable, reset the connection and set the clear connection bit
|
||||||
|
port_status.wPortStatus.PORT_ENABLE = 0;
|
||||||
|
port_status.wPortStatus.PORT_CONNECTION = 0;
|
||||||
|
port_status.wPortChange.C_PORT_CONNECTION = 1;
|
||||||
|
|
||||||
|
// Set new port status
|
||||||
|
printf("set status %04X.%04X*\n", port_status.wPortStatus.val, port_status.wPortChange.val);
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->set_status(port_hdl, &port_status));
|
||||||
|
// Setting the port status should trigger the process request callback
|
||||||
|
test_wait_ext_port_process_request();
|
||||||
|
// Process the port
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_process());
|
||||||
|
// Setting the status of the port with connection should trigger the Clear Port Connection
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, test_wait_ext_port_hub_request(NULL,
|
||||||
|
port1,
|
||||||
|
USB_B_REQUEST_HUB_CLEAR_FEATURE));
|
||||||
|
// Assume that clear connection done successfully, but we do not need actually change the port status
|
||||||
|
// Clear Port Feature: C_CONNECTION - assume we done it, ESP_OK
|
||||||
|
port_status.wPortChange.C_PORT_CONNECTION = 0;
|
||||||
|
// Request process
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->req_process(port_hdl));
|
||||||
|
// Wait for the process request callback
|
||||||
|
test_wait_ext_port_process_request();
|
||||||
|
// Process the port
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_process());
|
||||||
|
// Cleared port connection should trigger the Get Port Status request on the parent hub
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, test_wait_ext_port_hub_request(NULL,
|
||||||
|
port1,
|
||||||
|
USB_B_REQUEST_HUB_GET_PORT_STATUS));
|
||||||
|
|
||||||
|
// We don't need to request actual port status, proceed with cached status
|
||||||
|
|
||||||
|
// Set new port status
|
||||||
|
printf("set status %04X.%04X*\n", port_status.wPortStatus.val, port_status.wPortChange.val);
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->set_status(port_hdl, &port_status));
|
||||||
|
// Setting the port status should trigger the process request callback
|
||||||
|
test_wait_ext_port_process_request();
|
||||||
|
// Process the port
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_process());
|
||||||
|
// The External Port driver should indicate that device has been disconnected and port disconnected
|
||||||
|
test_wait_ext_port_event(EXT_PORT_DISCONNECTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ext_port_disable(uint8_t port1, ext_port_hdl_t port_hdl)
|
||||||
|
{
|
||||||
|
usb_port_status_t port_status;
|
||||||
|
hub_get_port_status(port1, &port_status);
|
||||||
|
|
||||||
|
// Disable port is possible only when it is enabled, powered and has a connection
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortStatus.PORT_POWER); // Port powered
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortStatus.PORT_ENABLE); // Port enabled
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortStatus.PORT_CONNECTION); // Port has connection
|
||||||
|
TEST_ASSERT_EQUAL(0, port_status.wPortChange.val); // No change to handle
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->disable(port_hdl));
|
||||||
|
// Wait for the process request callback
|
||||||
|
test_wait_ext_port_process_request();
|
||||||
|
// Process the port
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_process());
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, test_wait_ext_port_hub_request(NULL,
|
||||||
|
port1,
|
||||||
|
USB_B_REQUEST_HUB_CLEAR_FEATURE));
|
||||||
|
// When port is not gone, it should create a DISCONNECTION event
|
||||||
|
test_wait_ext_port_event(EXT_PORT_DISCONNECTED);
|
||||||
|
// Disable the port
|
||||||
|
hub_port_disable(port1);
|
||||||
|
// After completion, trigger the port processing
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->req_process(port_hdl));
|
||||||
|
// Wait for the process request callback
|
||||||
|
test_wait_ext_port_process_request();
|
||||||
|
// Process the port
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_process());
|
||||||
|
// Cleared port enable should trigger the Get Port Status request on the parent hub
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, test_wait_ext_port_hub_request(NULL,
|
||||||
|
port1,
|
||||||
|
USB_B_REQUEST_HUB_GET_PORT_STATUS));
|
||||||
|
// Get Port Status
|
||||||
|
hub_get_port_status(port1, &port_status);
|
||||||
|
// Port should be powered, disabled and have a connection
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortStatus.PORT_POWER); // Powered
|
||||||
|
TEST_ASSERT_EQUAL(0, port_status.wPortStatus.PORT_ENABLE); // Disabled
|
||||||
|
TEST_ASSERT_EQUAL(1, port_status.wPortStatus.PORT_CONNECTION); // Connection detected
|
||||||
|
// Set port status
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->set_status(port_hdl, &port_status));
|
||||||
|
// Wait for the process request callback
|
||||||
|
test_wait_ext_port_process_request();
|
||||||
|
// Port doesn't require any processing, so there should debug message "No more ports to handle"
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_process()); // TODO: IDF-12562 replace with Enable INTR EP event await
|
||||||
|
// Disabled port doesn't require any processing, so there should be no process request
|
||||||
|
TEST_ASSERT_EQUAL(ESP_ERR_TIMEOUT, test_wait_ext_port_hub_request(NULL,
|
||||||
|
port1,
|
||||||
|
USB_B_REQUEST_HUB_GET_PORT_STATUS));
|
||||||
|
// Port is disabled and wait device to be detached.
|
||||||
|
// Terminal state
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ext_port_gone(ext_port_hdl_t port_hdl, bool has_device)
|
||||||
|
{
|
||||||
|
if (has_device) {
|
||||||
|
// Port is enabled, port gone returns ESP_ERR_NOT_FINISHED
|
||||||
|
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FINISHED, port_api->gone(port_hdl));
|
||||||
|
// Wait for the process request callback
|
||||||
|
// test_ext_port_wait_process_request();
|
||||||
|
// Port doesn't require any processing, so there should debug message "No more ports to handle"
|
||||||
|
// TEST_ASSERT_EQUAL(ESP_OK, ext_port_process()); // TODO: IDF-12562 replace with Enable INTR EP event await
|
||||||
|
// Get Disconnect event
|
||||||
|
test_wait_ext_port_event(EXT_PORT_DISCONNECTED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Port doesn't have a device, port gone returns ESP_OK
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->gone(port_hdl));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ext_port_delete(ext_port_hdl_t port_hdl)
|
||||||
|
{
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->del(port_hdl));
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ext_port_recycle(ext_port_hdl_t port_hdl)
|
||||||
|
{
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, port_api->recycle(port_hdl));
|
||||||
|
// Recycling the port for the process request callback
|
||||||
|
test_wait_ext_port_process_request();
|
||||||
|
// Process the port
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, ext_port_process());
|
||||||
|
// Port gone
|
||||||
|
// ? Port delete
|
||||||
|
// : Port is IDLE
|
||||||
|
}
|
107
components/usb/test_apps/ext_port/main/ext_port_common.h
Normal file
107
components/usb/test_apps/ext_port/main/ext_port_common.h
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ext_port.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief External Port setup test
|
||||||
|
* Create queues for events/callbacks and install External Port driver
|
||||||
|
*/
|
||||||
|
void test_ext_port_setup(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief External Port teardown test
|
||||||
|
* Uninstall External Port driver and delete queues
|
||||||
|
*/
|
||||||
|
void test_ext_port_teardown(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create External Port
|
||||||
|
*
|
||||||
|
* Creates the port.
|
||||||
|
* Verifies that creating new port triggers the process request callback.
|
||||||
|
* Verifies that processing newly added port requires the Get Port Status request on the parent hub.
|
||||||
|
*
|
||||||
|
* @param[in] config External Port configuration
|
||||||
|
* @return
|
||||||
|
* - External Port handle if port is created successfully
|
||||||
|
*/
|
||||||
|
ext_port_hdl_t test_ext_port_new(ext_port_config_t *config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test power on the Port
|
||||||
|
*
|
||||||
|
* Requests actual port status from parent hub.
|
||||||
|
* Set status to the External Port Driver.
|
||||||
|
* Verifies that setting the port status triggers the process request callback.
|
||||||
|
* Verifies that processing the port after setting the status triggers the Get Port Status request on the parent hub.
|
||||||
|
*
|
||||||
|
* @param[in] port1 Port number
|
||||||
|
* @param[in] port_hdl External Port handle
|
||||||
|
*/
|
||||||
|
void test_ext_port_power_on(uint8_t port1, ext_port_hdl_t port_hdl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test Port disconnected
|
||||||
|
*
|
||||||
|
* Handles the port that doesn't have a connection.
|
||||||
|
*
|
||||||
|
* @param[in] port1 Port number
|
||||||
|
* @param[in] port_hdl External Port handle
|
||||||
|
*/
|
||||||
|
void test_ext_port_disconnected(uint8_t port1, ext_port_hdl_t port_hdl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test External Port connected
|
||||||
|
*
|
||||||
|
* Handles the port that has a connection
|
||||||
|
*
|
||||||
|
* @param[in] port1 Port number
|
||||||
|
* @param[in] port_hdl External Port handle
|
||||||
|
* @return
|
||||||
|
* - usb_speed_t Port speed
|
||||||
|
*/
|
||||||
|
usb_speed_t test_ext_port_connected(uint8_t port1, ext_port_hdl_t port_hdl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test External Port imitate disconnection
|
||||||
|
*
|
||||||
|
* Imitates the port disconnection via sending changed status to the External Port Driver
|
||||||
|
*
|
||||||
|
* @param[in] port1 Port number
|
||||||
|
* @param[in] port_hdl External Port handle
|
||||||
|
*/
|
||||||
|
void test_ext_port_imitate_disconnection(uint8_t port1, ext_port_hdl_t port_hdl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test External Port disable
|
||||||
|
*
|
||||||
|
* @param[in] port1 Port number
|
||||||
|
* @param[in] port_hdl External Port handle
|
||||||
|
*/
|
||||||
|
void test_ext_port_disable(uint8_t port1, ext_port_hdl_t port_hdl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test External Port recycle
|
||||||
|
*
|
||||||
|
* @param[in] port_hdl External Port handle
|
||||||
|
* @param[in] has_device Device presence
|
||||||
|
*/
|
||||||
|
void test_ext_port_gone(ext_port_hdl_t port_hdl, bool has_device);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test External Port delete
|
||||||
|
*
|
||||||
|
* @param[in] port_hdl External Port handle
|
||||||
|
*/
|
||||||
|
void test_ext_port_delete(ext_port_hdl_t port_hdl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test External Port recycle
|
||||||
|
*
|
||||||
|
* @param[in] port_hdl External Port handle
|
||||||
|
*/
|
||||||
|
void test_ext_port_recycle(ext_port_hdl_t port_hdl);
|
273
components/usb/test_apps/ext_port/main/hub_common.c
Normal file
273
components/usb/test_apps/ext_port/main/hub_common.c
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "string.h"
|
||||||
|
// Test common
|
||||||
|
#include "hcd_common.h"
|
||||||
|
#include "hub_common.h"
|
||||||
|
// USB Host Lib
|
||||||
|
#include "usb_private.h"
|
||||||
|
#include "usb/usb_types_ch9.h"
|
||||||
|
#include "usb/usb_types_ch11.h"
|
||||||
|
#include "unity.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
hcd_port_handle_t root_port_hdl;
|
||||||
|
hcd_pipe_handle_t ctrl_pipe_hdl;
|
||||||
|
urb_t *urb;
|
||||||
|
usb_speed_t speed;
|
||||||
|
uint8_t addr;
|
||||||
|
usb_hub_descriptor_t desc;
|
||||||
|
} hub_dev_t;
|
||||||
|
|
||||||
|
static hub_dev_t hub;
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// --------------------------------------- Internal logic -----------------------------------------
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* @brief Get Port status
|
||||||
|
*
|
||||||
|
* @param[in] port1 Port number
|
||||||
|
* @param[out] status Port status
|
||||||
|
*/
|
||||||
|
static void hub_class_request_get_port_status(uint8_t port1, usb_port_status_t *status)
|
||||||
|
{
|
||||||
|
usb_port_status_t *port_status = (usb_port_status_t *)(hub.urb->transfer.data_buffer + sizeof(usb_setup_packet_t));
|
||||||
|
usb_setup_packet_t *setup_pkt = (usb_setup_packet_t *)hub.urb->transfer.data_buffer;
|
||||||
|
USB_SETUP_PACKET_INIT_GET_PORT_STATUS(setup_pkt, port1);
|
||||||
|
hub.urb->transfer.num_bytes = sizeof(usb_setup_packet_t) + sizeof(usb_port_status_t);
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(hub.ctrl_pipe_hdl, hub.urb));
|
||||||
|
test_hcd_expect_pipe_event(hub.ctrl_pipe_hdl, HCD_PIPE_EVENT_URB_DONE);
|
||||||
|
TEST_ASSERT_EQUAL(hub.urb, hcd_urb_dequeue(hub.ctrl_pipe_hdl));
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, hub.urb->transfer.status, "Get Port Status: Transfer NOT completed");
|
||||||
|
if (status) {
|
||||||
|
memcpy(status, port_status, sizeof(usb_port_status_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set Port Feature
|
||||||
|
*
|
||||||
|
* @param[in] port1 Port number
|
||||||
|
* @param[in] feature Feature to set
|
||||||
|
*/
|
||||||
|
static void hub_class_request_set_port_feature(uint8_t port1, usb_hub_port_feature_t feature)
|
||||||
|
{
|
||||||
|
usb_setup_packet_t *setup_pkt = (usb_setup_packet_t *)hub.urb->transfer.data_buffer;
|
||||||
|
USB_SETUP_PACKET_INIT_SET_PORT_FEATURE(setup_pkt, port1, feature);
|
||||||
|
hub.urb->transfer.num_bytes = sizeof(usb_setup_packet_t);
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(hub.ctrl_pipe_hdl, hub.urb));
|
||||||
|
test_hcd_expect_pipe_event(hub.ctrl_pipe_hdl, HCD_PIPE_EVENT_URB_DONE);
|
||||||
|
TEST_ASSERT_EQUAL(hub.urb, hcd_urb_dequeue(hub.ctrl_pipe_hdl));
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, hub.urb->transfer.status, "Set Port Feature: Transfer NOT completed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear Port Feature
|
||||||
|
*
|
||||||
|
* @param[in] port1 Port number
|
||||||
|
* @param[in] feature Feature to clear
|
||||||
|
*/
|
||||||
|
static void hub_class_request_clear_port_feature(uint8_t port1, usb_hub_port_feature_t feature)
|
||||||
|
{
|
||||||
|
usb_setup_packet_t *setup_pkt = (usb_setup_packet_t *)hub.urb->transfer.data_buffer;
|
||||||
|
USB_SETUP_PACKET_INIT_CLEAR_PORT_FEATURE(setup_pkt, port1, feature);
|
||||||
|
hub.urb->transfer.num_bytes = sizeof(usb_setup_packet_t);
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(hub.ctrl_pipe_hdl, hub.urb));
|
||||||
|
test_hcd_expect_pipe_event(hub.ctrl_pipe_hdl, HCD_PIPE_EVENT_URB_DONE);
|
||||||
|
TEST_ASSERT_EQUAL(hub.urb, hcd_urb_dequeue(hub.ctrl_pipe_hdl));
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, hub.urb->transfer.status, "Clear Port Feature: Transfer NOT completed");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get Hub Descriptor
|
||||||
|
*
|
||||||
|
* Get Hub Descriptor and store it in the hub structure
|
||||||
|
*/
|
||||||
|
static void hub_get_descriptor(void)
|
||||||
|
{
|
||||||
|
usb_setup_packet_t *setup_pkt = (usb_setup_packet_t *)hub.urb->transfer.data_buffer;
|
||||||
|
USB_SETUP_PACKET_INIT_GET_HUB_DESCRIPTOR(setup_pkt);
|
||||||
|
hub.urb->transfer.num_bytes = sizeof(usb_setup_packet_t) + sizeof(usb_hub_descriptor_t);
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(hub.ctrl_pipe_hdl, hub.urb));
|
||||||
|
test_hcd_expect_pipe_event(hub.ctrl_pipe_hdl, HCD_PIPE_EVENT_URB_DONE);
|
||||||
|
TEST_ASSERT_EQUAL(hub.urb, hcd_urb_dequeue(hub.ctrl_pipe_hdl));
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, hub.urb->transfer.status, "Get Hub Descriptor: Transfer NOT completed");
|
||||||
|
|
||||||
|
// Extract Hub capabilities
|
||||||
|
const usb_hub_descriptor_t *hub_desc = (const usb_hub_descriptor_t *)(hub.urb->transfer.data_buffer + sizeof(usb_setup_packet_t));
|
||||||
|
memcpy(&hub.desc, hub_desc, sizeof(usb_hub_descriptor_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// ----------------------------------------- Hub Functions -----------------------------------------
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void hub_set_root_port(hcd_port_handle_t root_port_hdl)
|
||||||
|
{
|
||||||
|
hub.root_port_hdl = root_port_hdl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hub_attach(void)
|
||||||
|
{
|
||||||
|
// Attach the root port
|
||||||
|
hub.speed = test_hcd_wait_for_conn(hub.root_port_hdl);
|
||||||
|
// Short delay send of SOF (for FS) or EOPs (for LS)
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
// Create Control Pipe
|
||||||
|
hub.ctrl_pipe_hdl = test_hcd_pipe_alloc(hub.root_port_hdl, NULL, 0, hub.speed);
|
||||||
|
TEST_ASSERT_NOT_NULL(hub.ctrl_pipe_hdl);
|
||||||
|
// Enumerate Hub
|
||||||
|
hub.addr = test_hcd_enum_device(hub.ctrl_pipe_hdl);
|
||||||
|
// Hub should have an address
|
||||||
|
TEST_ASSERT_NOT_EQUAL(0, hub.addr);
|
||||||
|
// Create urb for CTRL transfers
|
||||||
|
urb_t *urb = test_hcd_alloc_urb(0, sizeof(usb_setup_packet_t) + 256);
|
||||||
|
TEST_ASSERT_NOT_NULL(urb);
|
||||||
|
hub.urb = urb;
|
||||||
|
// Get Hub Descriptor
|
||||||
|
hub_get_descriptor();
|
||||||
|
printf("\tHub has %d ports\n", hub.desc.bNbrPorts);
|
||||||
|
}
|
||||||
|
|
||||||
|
hcd_pipe_handle_t hub_child_create_pipe(usb_speed_t speed)
|
||||||
|
{
|
||||||
|
// Downstream pipe has the same root port handle
|
||||||
|
hcd_pipe_handle_t ctrl_pipe = test_hcd_pipe_alloc(hub.root_port_hdl, NULL, 0, speed);
|
||||||
|
TEST_ASSERT_NOT_NULL(ctrl_pipe);
|
||||||
|
return ctrl_pipe;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hub_child_pipe_free(hcd_pipe_handle_t pipe_hdl)
|
||||||
|
{
|
||||||
|
TEST_ASSERT_NOT_NULL(pipe_hdl);
|
||||||
|
test_hcd_pipe_free(pipe_hdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hub_child_quick_enum(hcd_pipe_handle_t ctrl_pipe, uint8_t dev_addr, uint8_t config_num)
|
||||||
|
{
|
||||||
|
// TODO: test_hcd_enum_device() could be used instead, but it requires refactoring: assign the configurable dev address
|
||||||
|
usb_setup_packet_t *setup_pkt = (usb_setup_packet_t *)hub.urb->transfer.data_buffer;
|
||||||
|
// Get the device descriptor (note that device might only return 8 bytes)
|
||||||
|
USB_SETUP_PACKET_INIT_GET_DEVICE_DESC(setup_pkt);
|
||||||
|
hub.urb->transfer.num_bytes = sizeof(usb_setup_packet_t) + sizeof(usb_device_desc_t);
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(ctrl_pipe, hub.urb));
|
||||||
|
test_hcd_expect_pipe_event(ctrl_pipe, HCD_PIPE_EVENT_URB_DONE);
|
||||||
|
TEST_ASSERT_EQUAL(hub.urb, hcd_urb_dequeue(ctrl_pipe));
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, hub.urb->transfer.status, "Transfer NOT completed");
|
||||||
|
|
||||||
|
// Update the MPS of the default pipe
|
||||||
|
usb_device_desc_t *device_desc = (usb_device_desc_t *)(hub.urb->transfer.data_buffer + sizeof(usb_setup_packet_t));
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_update_mps(ctrl_pipe, device_desc->bMaxPacketSize0));
|
||||||
|
//
|
||||||
|
printf("\t\tEnumerated device: %04x:%04x, mps: %d\n",
|
||||||
|
device_desc->idVendor,
|
||||||
|
device_desc->idProduct,
|
||||||
|
device_desc->bMaxPacketSize0);
|
||||||
|
|
||||||
|
// Send a set address request
|
||||||
|
USB_SETUP_PACKET_INIT_SET_ADDR(setup_pkt, dev_addr);
|
||||||
|
hub.urb->transfer.num_bytes = sizeof(usb_setup_packet_t);
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(ctrl_pipe, hub.urb));
|
||||||
|
test_hcd_expect_pipe_event(ctrl_pipe, HCD_PIPE_EVENT_URB_DONE);
|
||||||
|
TEST_ASSERT_EQUAL(hub.urb, hcd_urb_dequeue(ctrl_pipe));
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, hub.urb->transfer.status, "Transfer NOT completed");
|
||||||
|
|
||||||
|
// Update address of default pipe
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_update_dev_addr(ctrl_pipe, dev_addr));
|
||||||
|
|
||||||
|
// Send a set configuration request
|
||||||
|
USB_SETUP_PACKET_INIT_SET_CONFIG(setup_pkt, config_num);
|
||||||
|
hub.urb->transfer.num_bytes = sizeof(usb_setup_packet_t);
|
||||||
|
TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(ctrl_pipe, hub.urb));
|
||||||
|
test_hcd_expect_pipe_event(ctrl_pipe, HCD_PIPE_EVENT_URB_DONE);
|
||||||
|
TEST_ASSERT_EQUAL(hub.urb, hcd_urb_dequeue(ctrl_pipe));
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, hub.urb->transfer.status, "Transfer NOT completed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void hub_detach(void)
|
||||||
|
{
|
||||||
|
// Free urb
|
||||||
|
test_hcd_free_urb(hub.urb);
|
||||||
|
// Free Control Pipe
|
||||||
|
test_hcd_pipe_free(hub.ctrl_pipe_hdl);
|
||||||
|
// Detach the root port
|
||||||
|
test_hcd_wait_for_disconn(hub.root_port_hdl, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* hub_get_context(void)
|
||||||
|
{
|
||||||
|
return (void*) &hub;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t hub_get_port_num(void)
|
||||||
|
{
|
||||||
|
return hub.desc.bNbrPorts;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t hub_get_port_poweron_delay_ms(void)
|
||||||
|
{
|
||||||
|
return hub.desc.bPwrOn2PwrGood * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hub_get_port_status(uint8_t port1, usb_port_status_t *status)
|
||||||
|
{
|
||||||
|
TEST_ASSERT_NOT_NULL(status);
|
||||||
|
hub_class_request_get_port_status(port1, status);
|
||||||
|
printf("\t\tport%d %04X.%04X\n", port1,
|
||||||
|
status->wPortStatus.val,
|
||||||
|
status->wPortChange.val);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hub_get_port_connection(uint8_t port1)
|
||||||
|
{
|
||||||
|
usb_port_status_t port_status;
|
||||||
|
hub_class_request_get_port_status(port1, &port_status);
|
||||||
|
return port_status.wPortStatus.PORT_CONNECTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hub_port_power_off(uint8_t port1)
|
||||||
|
{
|
||||||
|
hub_class_request_clear_port_feature(port1, USB_FEATURE_PORT_POWER);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hub_port_power_on(uint8_t port1)
|
||||||
|
{
|
||||||
|
hub_class_request_set_port_feature(port1, USB_FEATURE_PORT_POWER);
|
||||||
|
// Wait while port powered, crucial for High-speed ports
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(hub.desc.bPwrOn2PwrGood));
|
||||||
|
}
|
||||||
|
|
||||||
|
void hub_port_reset(uint8_t port1)
|
||||||
|
{
|
||||||
|
hub_class_request_set_port_feature(port1, USB_FEATURE_PORT_RESET);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hub_port_suspend(uint8_t port1)
|
||||||
|
{
|
||||||
|
hub_class_request_set_port_feature(port1, USB_FEATURE_PORT_SUSPEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hub_port_resume(uint8_t port1)
|
||||||
|
{
|
||||||
|
hub_class_request_clear_port_feature(port1, USB_FEATURE_C_PORT_SUSPEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hub_port_disable(uint8_t port1)
|
||||||
|
{
|
||||||
|
hub_class_request_clear_port_feature(port1, USB_FEATURE_PORT_ENABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hub_port_clear_connection(uint8_t port1)
|
||||||
|
{
|
||||||
|
hub_class_request_clear_port_feature(port1, USB_FEATURE_C_PORT_CONNECTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hub_port_clear_reset(uint8_t port1)
|
||||||
|
{
|
||||||
|
hub_class_request_clear_port_feature(port1, USB_FEATURE_C_PORT_RESET);
|
||||||
|
}
|
164
components/usb/test_apps/ext_port/main/hub_common.h
Normal file
164
components/usb/test_apps/ext_port/main/hub_common.h
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "stdint.h"
|
||||||
|
#include "hcd.h"
|
||||||
|
#include "usb/usb_types_ch11.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Hub set root port handle
|
||||||
|
*
|
||||||
|
* When external hub connected to the root hub port (USB OTG peripheral), one port handle shared between all downstream pipes.
|
||||||
|
*
|
||||||
|
* @param[in] root_port_hdl Root port handle
|
||||||
|
*/
|
||||||
|
void hub_set_root_port(hcd_port_handle_t root_port_hdl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Hub attach
|
||||||
|
*
|
||||||
|
* Enables the root port, wait a connection and configure the external Hub
|
||||||
|
*/
|
||||||
|
void hub_attach(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Hub detach
|
||||||
|
*
|
||||||
|
* Disables the root port by turing the port off and disconnects the external Hub
|
||||||
|
*/
|
||||||
|
void hub_detach(void);
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// ================================== Hub Child API ================================================
|
||||||
|
// =================================================================================================
|
||||||
|
/**
|
||||||
|
* @brief Create pipe for the child device
|
||||||
|
*
|
||||||
|
* @param[in] speed Device speed
|
||||||
|
* @return hcd_pipe_handle_t Pipe handle
|
||||||
|
*/
|
||||||
|
hcd_pipe_handle_t hub_child_create_pipe(usb_speed_t speed);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free pipe for the child device
|
||||||
|
*
|
||||||
|
* @param[in] pipe_hdl Pipe handle
|
||||||
|
*/
|
||||||
|
void hub_child_pipe_free(hcd_pipe_handle_t pipe_hdl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Quick enumeration of the child device
|
||||||
|
*
|
||||||
|
* Performs quick enumeration of the device, using the ctrl_pipe handle.
|
||||||
|
* Quick enumeration includes:
|
||||||
|
* - Get Device Descriptor
|
||||||
|
* - Set Address
|
||||||
|
* - Get Configuration (1)
|
||||||
|
*
|
||||||
|
* @param[in] ctrl_pipe Control pipe handle
|
||||||
|
* @param[in] dev_addr Device address
|
||||||
|
* @param[in] config_num Configuration number
|
||||||
|
*/
|
||||||
|
void hub_child_quick_enum(hcd_pipe_handle_t ctrl_pipe, uint8_t dev_addr, uint8_t config_num);
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// =================================== Hub Getters ================================================
|
||||||
|
// =================================================================================================
|
||||||
|
/**
|
||||||
|
* @brief Get hub context
|
||||||
|
*
|
||||||
|
* @return void* pointer to a Hub context
|
||||||
|
*/
|
||||||
|
void* hub_get_context(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get number of ports
|
||||||
|
*
|
||||||
|
* @return uint8_t Number of ports
|
||||||
|
*/
|
||||||
|
uint8_t hub_get_port_num(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get port power on delay
|
||||||
|
*
|
||||||
|
* @return uint16_t Power on delay in milliseconds
|
||||||
|
*/
|
||||||
|
uint16_t hub_get_port_poweron_delay_ms(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get port status
|
||||||
|
*
|
||||||
|
* @param[in] port1 Port number
|
||||||
|
* @param[out] status Port status
|
||||||
|
*/
|
||||||
|
void hub_get_port_status(uint8_t port1, usb_port_status_t *status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get port connection status
|
||||||
|
*
|
||||||
|
* @param[in] port1 Port number
|
||||||
|
* @return bool Port connection status
|
||||||
|
*/
|
||||||
|
bool hub_get_port_connection(uint8_t port1);
|
||||||
|
|
||||||
|
// =================================================================================================
|
||||||
|
// =================================== Hub Port Control ============================================
|
||||||
|
// =================================================================================================
|
||||||
|
/**
|
||||||
|
* @brief Power off the port
|
||||||
|
*
|
||||||
|
* @param[in] port1 Port number
|
||||||
|
*/
|
||||||
|
void hub_port_power_off(uint8_t port1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Power on the port
|
||||||
|
*
|
||||||
|
* @param[in] port1 Port number
|
||||||
|
*/
|
||||||
|
void hub_port_power_on(uint8_t port1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reset the port
|
||||||
|
*
|
||||||
|
* @param[in] port1 Port number
|
||||||
|
*/
|
||||||
|
void hub_port_reset(uint8_t port1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Suspend the port
|
||||||
|
*
|
||||||
|
* @param[in] port1 Port number
|
||||||
|
*/
|
||||||
|
void hub_port_suspend(uint8_t port1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Resume the port
|
||||||
|
*
|
||||||
|
* @param[in] port1 Port number
|
||||||
|
*/
|
||||||
|
void hub_port_resume(uint8_t port1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Disable the port
|
||||||
|
*
|
||||||
|
* @param[in] port1 Port number
|
||||||
|
*/
|
||||||
|
void hub_port_disable(uint8_t port1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear the port connection
|
||||||
|
*
|
||||||
|
* @param[in] port1 Port number
|
||||||
|
*/
|
||||||
|
void hub_port_clear_connection(uint8_t port1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear the port reset
|
||||||
|
*
|
||||||
|
* @param[in] port1 Port number
|
||||||
|
*/
|
||||||
|
void hub_port_clear_reset(uint8_t port1);
|
61
components/usb/test_apps/ext_port/main/test_app_main.c
Normal file
61
components/usb/test_apps/ext_port/main/test_app_main.c
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "unity.h"
|
||||||
|
#include "unity_test_runner.h"
|
||||||
|
#include "unity_test_utils_memory.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "hcd_common.h"
|
||||||
|
#include "hub_common.h"
|
||||||
|
#include "ext_port_common.h"
|
||||||
|
|
||||||
|
static hcd_port_handle_t _root_port_hdl;
|
||||||
|
|
||||||
|
void setUp(void)
|
||||||
|
{
|
||||||
|
unity_utils_record_free_mem();
|
||||||
|
// Install HCD port
|
||||||
|
_root_port_hdl = test_hcd_setup();
|
||||||
|
// Set root port handle to the hub
|
||||||
|
hub_set_root_port(_root_port_hdl);
|
||||||
|
// Setup the External Port Driver
|
||||||
|
test_ext_port_setup();
|
||||||
|
printf("External Port Driver installed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void tearDown(void)
|
||||||
|
{
|
||||||
|
// Short delay to allow task to be cleaned up
|
||||||
|
vTaskDelay(10);
|
||||||
|
// Clean up USB Host
|
||||||
|
printf("External Port Driver uninstall\n");
|
||||||
|
test_ext_port_teardown();
|
||||||
|
test_hcd_teardown(_root_port_hdl);
|
||||||
|
// Short delay to allow task to be cleaned up after client uninstall
|
||||||
|
vTaskDelay(10);
|
||||||
|
unity_utils_evaluate_leaks();
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
// ____ ___ ___________________ __ __
|
||||||
|
// | | \/ _____/\______ \ _/ |_ ____ _______/ |_
|
||||||
|
// | | /\_____ \ | | _/ \ __\/ __ \ / ___/\ __\.
|
||||||
|
// | | / / \ | | \ | | \ ___/ \___ \ | |
|
||||||
|
// |______/ /_______ / |______ / |__| \___ >____ > |__|
|
||||||
|
// \/ \/ \/ \/
|
||||||
|
printf(" ____ ___ ___________________ __ __ \r\n");
|
||||||
|
printf("| | \\/ _____/\\______ \\ _/ |_ ____ _______/ |_ \r\n");
|
||||||
|
printf("| | /\\_____ \\ | | _/ \\ __\\/ __ \\ / ___/\\ __\\\r\n");
|
||||||
|
printf("| | / / \\ | | \\ | | \\ ___/ \\___ \\ | | \r\n");
|
||||||
|
printf("|______/ /_______ / |______ / |__| \\___ >____ > |__| \r\n");
|
||||||
|
printf(" \\/ \\/ \\/ \\/ \r\n");
|
||||||
|
|
||||||
|
unity_utils_setup_heap_record(80);
|
||||||
|
unity_utils_set_leak_level(128);
|
||||||
|
unity_run_menu();
|
||||||
|
}
|
470
components/usb/test_apps/ext_port/main/test_ext_port.c
Normal file
470
components/usb/test_apps/ext_port/main/test_ext_port.c
Normal file
@@ -0,0 +1,470 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "unity.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
// Internal common test files
|
||||||
|
#include "ext_port_common.h"
|
||||||
|
#include "hub_common.h"
|
||||||
|
// Cross-test common files
|
||||||
|
#include "hcd_common.h"
|
||||||
|
|
||||||
|
#define TEST_PORT_NUM_DEVICE_FSHS CONFIG_USB_HOST_TEST_HUB_PORT_NUM_DEVICE_FSHS
|
||||||
|
#define TEST_PORT_NUM_DEVICE_LS CONFIG_USB_HOST_TEST_HUB_PORT_NUM_DEVICE_LS
|
||||||
|
#define TEST_PORT_NUM_EMPTY CONFIG_USB_HOST_TEST_HUB_PORT_NUM_EMPTY
|
||||||
|
#define TEST_CHILD_DEVICE_ADDR 2
|
||||||
|
#define TEST_CHILD_DEVICE_CONFIG 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test the Port rconnection based on the test configuration
|
||||||
|
*
|
||||||
|
* @param port1 Port number
|
||||||
|
*/
|
||||||
|
static inline void test_port_connection(uint8_t port1)
|
||||||
|
{
|
||||||
|
switch (port1) {
|
||||||
|
case TEST_PORT_NUM_DEVICE_FSHS:
|
||||||
|
TEST_ASSERT_TRUE_MESSAGE(hub_get_port_connection(port1), "Configured port doesn't have a device. Reconfigure the port number via menuconfig and run the test again.");
|
||||||
|
break;
|
||||||
|
case TEST_PORT_NUM_EMPTY:
|
||||||
|
TEST_ASSERT_FALSE_MESSAGE(hub_get_port_connection(port1), "Configured port doesn't have a device. Reconfigure the port number via menuconfig and run the test again.");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Nothing to verify
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Test the External Port reachability by getting the number of ports and checking the port status after powering the port on.
|
||||||
|
|
||||||
|
Purpose:
|
||||||
|
- Verify the root hub port connection/disconnection
|
||||||
|
- Verify the port power on/off
|
||||||
|
- Verify the port status and print it out
|
||||||
|
|
||||||
|
Procedure:
|
||||||
|
- Attach the hub
|
||||||
|
- Get the number of ports
|
||||||
|
- Power on all ports
|
||||||
|
- Get the port status
|
||||||
|
- Power off all ports
|
||||||
|
- Detach the hub
|
||||||
|
*/
|
||||||
|
TEST_CASE("Port: all power on then off", "[ext_port][full_speed][high_speed]")
|
||||||
|
{
|
||||||
|
hub_attach();
|
||||||
|
uint8_t port_num = hub_get_port_num();
|
||||||
|
TEST_ASSERT_TRUE(TEST_PORT_NUM_DEVICE_FSHS <= port_num);
|
||||||
|
usb_port_status_t port_status;
|
||||||
|
for (uint8_t i = 1; i <= port_num; i++) {
|
||||||
|
hub_port_power_on(i);
|
||||||
|
hub_get_port_status(i, &port_status);
|
||||||
|
test_port_connection(i);
|
||||||
|
hub_port_power_off(i);
|
||||||
|
}
|
||||||
|
hub_detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Test the port in disconnected state.
|
||||||
|
|
||||||
|
Purpose:
|
||||||
|
- Verify the process how the External Port Driver handles the port without a connection
|
||||||
|
|
||||||
|
Procedure:
|
||||||
|
- Attach the hub
|
||||||
|
- Create External Port
|
||||||
|
- Handling the port: NOT_CONFIGURED -> POWERED_OFF -> POWERED_ON
|
||||||
|
- Handling the port: POWERED_ON -> DISCONNECTED -> IDLE
|
||||||
|
- Detach the hub
|
||||||
|
- Notify the port
|
||||||
|
- Delete the port
|
||||||
|
*/
|
||||||
|
TEST_CASE("Port: disconnected", "[low_speed][full_speed][high_speed]")
|
||||||
|
{
|
||||||
|
hub_attach();
|
||||||
|
uint8_t port_num = hub_get_port_num();
|
||||||
|
TEST_ASSERT_TRUE(TEST_PORT_NUM_EMPTY <= port_num);
|
||||||
|
// Create External Port
|
||||||
|
ext_port_config_t port_config = {
|
||||||
|
.ext_hub_hdl = (void*) hub_get_context() /* use any before IDF-10023 */,
|
||||||
|
.parent_dev_hdl = (void*) hub_get_context() /* use any before IDF-10023 */,
|
||||||
|
.parent_port_num = TEST_PORT_NUM_EMPTY,
|
||||||
|
.port_power_delay_ms = hub_get_port_poweron_delay_ms(),
|
||||||
|
};
|
||||||
|
ext_port_hdl_t port_hdl = test_ext_port_new(&port_config);
|
||||||
|
// After adding the port, it is in POWERED_OFF state
|
||||||
|
test_ext_port_power_on(TEST_PORT_NUM_EMPTY, port_hdl);
|
||||||
|
printf("Handle disconnected port...\n");
|
||||||
|
test_ext_port_disconnected(TEST_PORT_NUM_EMPTY, port_hdl);
|
||||||
|
// Detach parent hub
|
||||||
|
hub_detach();
|
||||||
|
// Notify port
|
||||||
|
test_ext_port_gone(port_hdl, false);
|
||||||
|
test_ext_port_delete(port_hdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Test the port with a connection and perform the enumeration of the child USB Low-speed device.
|
||||||
|
|
||||||
|
To configure the port to which the device is connected, change the TEST_PORT_NUM_DEVICE_LS value in the menuconfig.
|
||||||
|
|
||||||
|
Purpose:
|
||||||
|
- Verify the process how the External Port Driver handles the port with a connection
|
||||||
|
- Verify the connectivity with the Low-speed USB device, connected via hub
|
||||||
|
|
||||||
|
Procedure:
|
||||||
|
- Attach the hub
|
||||||
|
- Create External Port
|
||||||
|
- Handling the port: NOT_CONFIGURED -> POWERED_OFF -> POWERED_ON -> CONNECTED
|
||||||
|
- Enumerate the child device, speed: Low
|
||||||
|
- Handling the port: CONNECTED -> IDLE -> DISCONNECTED
|
||||||
|
- Recycle the port
|
||||||
|
- Detach the hub
|
||||||
|
- Notify the port
|
||||||
|
- Delete the port
|
||||||
|
*/
|
||||||
|
TEST_CASE("Port: enumerate child device Low-speed", "[ext_port][low_speed]")
|
||||||
|
{
|
||||||
|
hub_attach();
|
||||||
|
uint8_t port_num = hub_get_port_num();
|
||||||
|
TEST_ASSERT_TRUE(TEST_PORT_NUM_DEVICE_LS <= port_num);
|
||||||
|
// Create External Port
|
||||||
|
ext_port_config_t port_config = {
|
||||||
|
.ext_hub_hdl = (void*) hub_get_context() /* use any before IDF-10023 */,
|
||||||
|
.parent_dev_hdl = (void*) hub_get_context() /* use any before IDF-10023 */,
|
||||||
|
.parent_port_num = TEST_PORT_NUM_DEVICE_LS,
|
||||||
|
.port_power_delay_ms = hub_get_port_poweron_delay_ms(),
|
||||||
|
};
|
||||||
|
ext_port_hdl_t port_hdl = test_ext_port_new(&port_config);
|
||||||
|
// After adding the port, it is in POWERED_OFF state
|
||||||
|
test_ext_port_power_on(TEST_PORT_NUM_DEVICE_LS, port_hdl);
|
||||||
|
// Wait connection
|
||||||
|
usb_speed_t port_speed = test_ext_port_connected(TEST_PORT_NUM_DEVICE_LS, port_hdl);
|
||||||
|
printf("Hub port: %s speed device \n", (char*[]) {
|
||||||
|
"Low", "Full", "High"
|
||||||
|
}[port_speed]);
|
||||||
|
// Check the device speed
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(USB_SPEED_LOW, port_speed, "Invalid USB device speed");
|
||||||
|
// Enumerate device
|
||||||
|
hcd_pipe_handle_t ctrl_pipe = hub_child_create_pipe(port_speed);
|
||||||
|
hub_child_quick_enum(ctrl_pipe, TEST_CHILD_DEVICE_ADDR, TEST_CHILD_DEVICE_CONFIG);
|
||||||
|
hub_child_pipe_free(ctrl_pipe);
|
||||||
|
// Wait disconnection
|
||||||
|
test_ext_port_imitate_disconnection(TEST_PORT_NUM_DEVICE_LS, port_hdl);
|
||||||
|
// Recycle the port
|
||||||
|
test_ext_port_recycle(port_hdl);
|
||||||
|
// Detach hub
|
||||||
|
hub_detach();
|
||||||
|
// Notify port
|
||||||
|
test_ext_port_gone(port_hdl, false);
|
||||||
|
test_ext_port_delete(port_hdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Test the port with a connection and perform the enumeration of the child USB Full-speed device.
|
||||||
|
|
||||||
|
To configure the port to which the device is connected, change the TEST_PORT_NUM_DEVICE_FSHS value in the menuconfig.
|
||||||
|
|
||||||
|
Purpose:
|
||||||
|
- Verify the process how the External Port Driver handles the port with a connection
|
||||||
|
- Verify the connectivity with the Low-speed USB device, connected via hub
|
||||||
|
|
||||||
|
Procedure:
|
||||||
|
- Attach the hub
|
||||||
|
- Create External Port
|
||||||
|
- Handling the port: NOT_CONFIGURED -> POWERED_OFF -> POWERED_ON -> CONNECTED
|
||||||
|
- Enumerate the child device, speed: Full
|
||||||
|
- Handling the port: CONNECTED -> IDLE -> DISCONNECTED
|
||||||
|
- Recycle the port
|
||||||
|
- Detach the hub
|
||||||
|
- Notify the port
|
||||||
|
- Delete the port
|
||||||
|
*/
|
||||||
|
TEST_CASE("Port: enumerate child device Full-speed", "[ext_port][full_speed]")
|
||||||
|
{
|
||||||
|
hub_attach();
|
||||||
|
uint8_t port_num = hub_get_port_num();
|
||||||
|
TEST_ASSERT_TRUE(TEST_PORT_NUM_DEVICE_FSHS <= port_num);
|
||||||
|
// Create External Port
|
||||||
|
ext_port_config_t port_config = {
|
||||||
|
.ext_hub_hdl = (void*) hub_get_context() /* use any before IDF-10023 */,
|
||||||
|
.parent_dev_hdl = (void*) hub_get_context() /* use any before IDF-10023 */,
|
||||||
|
.parent_port_num = TEST_PORT_NUM_DEVICE_FSHS,
|
||||||
|
.port_power_delay_ms = hub_get_port_poweron_delay_ms(),
|
||||||
|
};
|
||||||
|
ext_port_hdl_t port_hdl = test_ext_port_new(&port_config);
|
||||||
|
// After adding the port, it is in POWERED_OFF state
|
||||||
|
test_ext_port_power_on(TEST_PORT_NUM_DEVICE_FSHS, port_hdl);
|
||||||
|
// Wait connection
|
||||||
|
usb_speed_t port_speed = test_ext_port_connected(TEST_PORT_NUM_DEVICE_FSHS, port_hdl);
|
||||||
|
printf("Hub port: %s speed device \n", (char*[]) {
|
||||||
|
"Low", "Full", "High"
|
||||||
|
}[port_speed]);
|
||||||
|
// Check the device speed
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(USB_SPEED_FULL, port_speed, "Invalid USB device speed");
|
||||||
|
// Enumerate device
|
||||||
|
hcd_pipe_handle_t ctrl_pipe = hub_child_create_pipe(port_speed);
|
||||||
|
hub_child_quick_enum(ctrl_pipe, TEST_CHILD_DEVICE_ADDR, TEST_CHILD_DEVICE_CONFIG);
|
||||||
|
hub_child_pipe_free(ctrl_pipe);
|
||||||
|
// Wait disconnection
|
||||||
|
test_ext_port_imitate_disconnection(TEST_PORT_NUM_DEVICE_FSHS, port_hdl);
|
||||||
|
// Recycle the port
|
||||||
|
test_ext_port_recycle(port_hdl);
|
||||||
|
// Detach hub
|
||||||
|
hub_detach();
|
||||||
|
// Notify port
|
||||||
|
test_ext_port_gone(port_hdl, false);
|
||||||
|
test_ext_port_delete(port_hdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Test the port with a connection and perform the enumeration of the child USB High-speed device.
|
||||||
|
|
||||||
|
To configure the port to which the device is connected, change the TEST_PORT_NUM_DEVICE_FSHS value in the menuconfig.
|
||||||
|
|
||||||
|
Purpose:
|
||||||
|
- Verify the process how the External Port Driver handles the port with a connection
|
||||||
|
- Verify the connectivity with the Low-speed USB device, connected via hub
|
||||||
|
|
||||||
|
Procedure:
|
||||||
|
- Attach the hub
|
||||||
|
- Create External Port
|
||||||
|
- Handling the port: NOT_CONFIGURED -> POWERED_OFF -> POWERED_ON -> CONNECTED
|
||||||
|
- Enumerate the child device, speed: High
|
||||||
|
- Handling the port: CONNECTED -> IDLE -> DISCONNECTED
|
||||||
|
- Recycle the port
|
||||||
|
- Detach the hub
|
||||||
|
- Notify the port
|
||||||
|
- Delete the port
|
||||||
|
*/
|
||||||
|
TEST_CASE("Port: enumerate child device High-speed", "[ext_port][high_speed]")
|
||||||
|
{
|
||||||
|
hub_attach();
|
||||||
|
uint8_t port_num = hub_get_port_num();
|
||||||
|
TEST_ASSERT_TRUE(TEST_PORT_NUM_DEVICE_FSHS <= port_num);
|
||||||
|
// Create External Port
|
||||||
|
ext_port_config_t port_config = {
|
||||||
|
.ext_hub_hdl = (void*) hub_get_context() /* use any before IDF-10023 */,
|
||||||
|
.parent_dev_hdl = (void*) hub_get_context() /* use any before IDF-10023 */,
|
||||||
|
.parent_port_num = TEST_PORT_NUM_DEVICE_FSHS,
|
||||||
|
.port_power_delay_ms = hub_get_port_poweron_delay_ms(),
|
||||||
|
};
|
||||||
|
ext_port_hdl_t port_hdl = test_ext_port_new(&port_config);
|
||||||
|
// After adding the port, it is in POWERED_OFF state
|
||||||
|
test_ext_port_power_on(TEST_PORT_NUM_DEVICE_FSHS, port_hdl);
|
||||||
|
// Wait connection
|
||||||
|
usb_speed_t port_speed = test_ext_port_connected(TEST_PORT_NUM_DEVICE_FSHS, port_hdl);
|
||||||
|
printf("Hub port: %s speed device \n", (char*[]) {
|
||||||
|
"Low", "Full", "High"
|
||||||
|
}[port_speed]);
|
||||||
|
// Check the device speed
|
||||||
|
TEST_ASSERT_EQUAL_MESSAGE(USB_SPEED_HIGH, port_speed, "Invalid USB device speed");
|
||||||
|
// Enumerate device
|
||||||
|
hcd_pipe_handle_t ctrl_pipe = hub_child_create_pipe(port_speed);
|
||||||
|
hub_child_quick_enum(ctrl_pipe, TEST_CHILD_DEVICE_ADDR, TEST_CHILD_DEVICE_CONFIG);
|
||||||
|
hub_child_pipe_free(ctrl_pipe);
|
||||||
|
// Wait disconnection
|
||||||
|
test_ext_port_imitate_disconnection(TEST_PORT_NUM_DEVICE_FSHS, port_hdl);
|
||||||
|
// Recycle the port
|
||||||
|
test_ext_port_recycle(port_hdl);
|
||||||
|
// Detach hub
|
||||||
|
hub_detach();
|
||||||
|
// Notify port
|
||||||
|
test_ext_port_gone(port_hdl, false);
|
||||||
|
test_ext_port_delete(port_hdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Test the port recycle procedure.
|
||||||
|
|
||||||
|
Purpose:
|
||||||
|
- Verify the port recycle procedure
|
||||||
|
|
||||||
|
Procedure:
|
||||||
|
- Attach the hub
|
||||||
|
- Create External Port
|
||||||
|
- Handling the port: NOT_CONFIGURED -> POWERED_OFF -> POWERED_ON -> CONNECTED
|
||||||
|
- Wait disconnection
|
||||||
|
- Recycle the port
|
||||||
|
- Repower the port
|
||||||
|
- Verify the port is still available
|
||||||
|
- Wait disconnection
|
||||||
|
- Recycle the port
|
||||||
|
- Detach the hub
|
||||||
|
- Notify the port
|
||||||
|
- Delete the port
|
||||||
|
*/
|
||||||
|
TEST_CASE("Port: recycle", "[ext_port][full_speed][high_speed]")
|
||||||
|
{
|
||||||
|
hub_attach();
|
||||||
|
uint8_t port_num = hub_get_port_num();
|
||||||
|
TEST_ASSERT_TRUE(TEST_PORT_NUM_DEVICE_FSHS <= port_num);
|
||||||
|
// Create External Port
|
||||||
|
ext_port_config_t port_config = {
|
||||||
|
.ext_hub_hdl = (void*) hub_get_context() /* use any before IDF-10023 */,
|
||||||
|
.parent_dev_hdl = (void*) hub_get_context() /* use any before IDF-10023 */,
|
||||||
|
.parent_port_num = TEST_PORT_NUM_DEVICE_FSHS,
|
||||||
|
.port_power_delay_ms = hub_get_port_poweron_delay_ms(),
|
||||||
|
};
|
||||||
|
ext_port_hdl_t port_hdl = test_ext_port_new(&port_config);
|
||||||
|
// After adding the port, it is in POWERED_OFF state
|
||||||
|
test_ext_port_power_on(TEST_PORT_NUM_DEVICE_FSHS, port_hdl);
|
||||||
|
// Wait connection
|
||||||
|
usb_speed_t port_speed = test_ext_port_connected(TEST_PORT_NUM_DEVICE_FSHS, port_hdl);
|
||||||
|
printf("Hub port: %s speed device \n", (char*[]) {
|
||||||
|
"Low", "Full", "High"
|
||||||
|
}[port_speed]);
|
||||||
|
// Wait disconnection
|
||||||
|
test_ext_port_imitate_disconnection(TEST_PORT_NUM_DEVICE_FSHS, port_hdl);
|
||||||
|
// Recylce the port
|
||||||
|
printf("Recycle the port...\n");
|
||||||
|
test_ext_port_recycle(port_hdl);
|
||||||
|
// Repower the port as we use imitation for disconnection. Port state in the External Port Driver is not changed during that.
|
||||||
|
hub_port_power_off(TEST_PORT_NUM_DEVICE_FSHS);
|
||||||
|
hub_port_power_on(TEST_PORT_NUM_DEVICE_FSHS);
|
||||||
|
// Verify that port still available
|
||||||
|
printf("Verify the port is still available...\n");
|
||||||
|
port_speed = test_ext_port_connected(TEST_PORT_NUM_DEVICE_FSHS, port_hdl);
|
||||||
|
printf("Hub port: %s speed device \n", (char*[]) {
|
||||||
|
"Low", "Full", "High"
|
||||||
|
}[port_speed]);
|
||||||
|
// Wait disconnection
|
||||||
|
test_ext_port_imitate_disconnection(TEST_PORT_NUM_DEVICE_FSHS, port_hdl);
|
||||||
|
// Recylce the port
|
||||||
|
test_ext_port_recycle(port_hdl);
|
||||||
|
// Detach hub
|
||||||
|
hub_detach();
|
||||||
|
// Notify port
|
||||||
|
test_ext_port_gone(port_hdl, false);
|
||||||
|
test_ext_port_delete(port_hdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Test the port disable procedure.
|
||||||
|
|
||||||
|
Purpose:
|
||||||
|
- Verify the port disable procedure
|
||||||
|
|
||||||
|
Procedure:
|
||||||
|
- Attach the hub
|
||||||
|
- Create External Port
|
||||||
|
- Handling the port: NOT_CONFIGURED -> POWERED_OFF -> POWERED_ON -> CONNECTED
|
||||||
|
- Disable the port
|
||||||
|
- Detach the parent hub
|
||||||
|
- Notify the port
|
||||||
|
- Delete the port
|
||||||
|
*/
|
||||||
|
TEST_CASE("Port: disable", "[ext_port][full_speed][high_speed]")
|
||||||
|
{
|
||||||
|
hub_attach();
|
||||||
|
uint8_t port_num = hub_get_port_num();
|
||||||
|
TEST_ASSERT_TRUE(TEST_PORT_NUM_DEVICE_FSHS <= port_num);
|
||||||
|
// Create External Port
|
||||||
|
ext_port_config_t port_config = {
|
||||||
|
.ext_hub_hdl = (void*) hub_get_context() /* use any before IDF-10023 */,
|
||||||
|
.parent_dev_hdl = (void*) hub_get_context() /* use any before IDF-10023 */,
|
||||||
|
.parent_port_num = TEST_PORT_NUM_DEVICE_FSHS,
|
||||||
|
.port_power_delay_ms = hub_get_port_poweron_delay_ms(),
|
||||||
|
};
|
||||||
|
ext_port_hdl_t port_hdl = test_ext_port_new(&port_config);
|
||||||
|
// After adding the port, it is in POWERED_OFF state
|
||||||
|
test_ext_port_power_on(TEST_PORT_NUM_DEVICE_FSHS, port_hdl);
|
||||||
|
// Wait connection
|
||||||
|
usb_speed_t port_speed = test_ext_port_connected(TEST_PORT_NUM_DEVICE_FSHS, port_hdl);
|
||||||
|
printf("Hub port: %s speed device \n", (char*[]) {
|
||||||
|
"Low", "Full", "High"
|
||||||
|
}[port_speed]);
|
||||||
|
printf("Disable the port ...\n");
|
||||||
|
test_ext_port_disable(TEST_PORT_NUM_DEVICE_FSHS, port_hdl);
|
||||||
|
// Detach hub
|
||||||
|
hub_detach();
|
||||||
|
// Notify port
|
||||||
|
test_ext_port_gone(port_hdl, false);
|
||||||
|
test_ext_port_delete(port_hdl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Test the port gone procedure, when the port is in POWERED_ON state.
|
||||||
|
|
||||||
|
Purpose:
|
||||||
|
- Verify the port gone procedure
|
||||||
|
|
||||||
|
Procedure:
|
||||||
|
- Attach the hub
|
||||||
|
- Create External Port
|
||||||
|
- Power on the port
|
||||||
|
- Detach the parent hub
|
||||||
|
- Notify the port
|
||||||
|
- Delete the port
|
||||||
|
*/
|
||||||
|
TEST_CASE("Port: gone in state - powered on", "[ext_port][full_speed][high_speed]")
|
||||||
|
{
|
||||||
|
hub_attach();
|
||||||
|
uint8_t port_num = hub_get_port_num();
|
||||||
|
TEST_ASSERT_TRUE(TEST_PORT_NUM_DEVICE_FSHS <= port_num);
|
||||||
|
// Create External Port
|
||||||
|
ext_port_config_t port_config = {
|
||||||
|
.ext_hub_hdl = (void*) hub_get_context() /* use any before IDF-10023 */,
|
||||||
|
.parent_dev_hdl = (void*) hub_get_context() /* use any before IDF-10023 */,
|
||||||
|
.parent_port_num = TEST_PORT_NUM_DEVICE_FSHS,
|
||||||
|
.port_power_delay_ms = hub_get_port_poweron_delay_ms(),
|
||||||
|
};
|
||||||
|
ext_port_hdl_t port_hdl = test_ext_port_new(&port_config);
|
||||||
|
// After adding the port, it is in POWERED_OFF state
|
||||||
|
test_ext_port_power_on(TEST_PORT_NUM_DEVICE_FSHS, port_hdl);
|
||||||
|
//
|
||||||
|
printf("Port Gone...\n");
|
||||||
|
hub_detach();
|
||||||
|
// Notify port
|
||||||
|
test_ext_port_gone(port_hdl, false);
|
||||||
|
test_ext_port_delete(port_hdl);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Test the port gone procedure, when the port is in ENABLED state.
|
||||||
|
|
||||||
|
Purpose:
|
||||||
|
- Verify the port gone procedure
|
||||||
|
|
||||||
|
Procedure:
|
||||||
|
- Attach the hub
|
||||||
|
- Create External Port
|
||||||
|
- Handling the port: NOT_CONFIGURED -> POWERED_OFF -> POWERED_ON -> CONNECTED
|
||||||
|
- Detach the parent hub
|
||||||
|
- Notify the port
|
||||||
|
- Delete the port
|
||||||
|
*/
|
||||||
|
TEST_CASE("Port: gone in state - enabled", "[ext_port][full_speed][high_speed]")
|
||||||
|
{
|
||||||
|
hub_attach();
|
||||||
|
uint8_t port_num = hub_get_port_num();
|
||||||
|
TEST_ASSERT_TRUE(TEST_PORT_NUM_DEVICE_FSHS <= port_num);
|
||||||
|
// Create External Port
|
||||||
|
ext_port_config_t port_config = {
|
||||||
|
.ext_hub_hdl = (void*) hub_get_context() /* use any before IDF-10023 */,
|
||||||
|
.parent_dev_hdl = (void*) hub_get_context() /* use any before IDF-10023 */,
|
||||||
|
.parent_port_num = TEST_PORT_NUM_DEVICE_FSHS,
|
||||||
|
.port_power_delay_ms = hub_get_port_poweron_delay_ms(),
|
||||||
|
};
|
||||||
|
ext_port_hdl_t port_hdl = test_ext_port_new(&port_config);
|
||||||
|
// After adding the port, it is in POWERED_OFF state
|
||||||
|
test_ext_port_power_on(TEST_PORT_NUM_DEVICE_FSHS, port_hdl);
|
||||||
|
// Wait connection
|
||||||
|
usb_speed_t port_speed = test_ext_port_connected(TEST_PORT_NUM_DEVICE_FSHS, port_hdl);
|
||||||
|
printf("Hub port: %s speed device \n", (char*[]) {
|
||||||
|
"Low", "Full", "High"
|
||||||
|
}[port_speed]);
|
||||||
|
//
|
||||||
|
printf("Port Gone...\n");
|
||||||
|
hub_detach();
|
||||||
|
// Notify port
|
||||||
|
test_ext_port_gone(port_hdl, true);
|
||||||
|
test_ext_port_delete(port_hdl);
|
||||||
|
}
|
14
components/usb/test_apps/ext_port/pytest_ext_port.py
Normal file
14
components/usb/test_apps/ext_port/pytest_ext_port.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||||
|
# SPDX-License-Identifier: CC0-1.0
|
||||||
|
import pytest
|
||||||
|
from pytest_embedded import Dut
|
||||||
|
from pytest_embedded_idf.utils import idf_parametrize
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.temp_skip_ci(targets=['esp32s2', 'esp32s3', 'esp32p4'], reason='no runner with external hub available')
|
||||||
|
@idf_parametrize('target', ['esp32s2', 'esp32s3', 'esp32p4'], indirect=['target'])
|
||||||
|
def test_usb_ext_port(dut: Dut) -> None:
|
||||||
|
if dut.target == 'esp32p4':
|
||||||
|
dut.run_all_single_board_cases(group='high_speed', reset=True)
|
||||||
|
else:
|
||||||
|
dut.run_all_single_board_cases(group='full_speed', reset=True)
|
9
components/usb/test_apps/ext_port/sdkconfig.ci
Normal file
9
components/usb/test_apps/ext_port/sdkconfig.ci
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Hub ports assignment
|
||||||
|
CONFIG_USB_HOST_TEST_HUB_PORT_NUM_DEVICE_FSHS=1
|
||||||
|
CONFIG_USB_HOST_TEST_HUB_PORT_NUM_DEVICE_LS=2
|
||||||
|
CONFIG_USB_HOST_TEST_HUB_PORT_NUM_EMPTY=3
|
||||||
|
|
||||||
|
# Use external power switch to control USB device's power
|
||||||
|
# switch is controlled by GPIO 21
|
||||||
|
CONFIG_USB_PHY_TEST_OTG_DRVVBUS_ENABLE=y
|
||||||
|
CONFIG_USB_PHY_TEST_OTG_DRVVBUS_GPIO=21
|
9
components/usb/test_apps/ext_port/sdkconfig.defaults
Normal file
9
components/usb/test_apps/ext_port/sdkconfig.defaults
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||||
|
# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration
|
||||||
|
#
|
||||||
|
# CONFIG_ESP_TASK_WDT_INIT is not set
|
||||||
|
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
|
||||||
|
# CONFIG_UNITY_ENABLE_FLOAT is not set
|
||||||
|
# CONFIG_UNITY_ENABLE_DOUBLE is not set
|
||||||
|
CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y
|
||||||
|
CONFIG_USB_HOST_HUBS_SUPPORTED=y
|
Reference in New Issue
Block a user