diff --git a/device/esp_tinyusb/CHANGELOG.md b/device/esp_tinyusb/CHANGELOG.md index b7805b1e..b9534e8f 100644 --- a/device/esp_tinyusb/CHANGELOG.md +++ b/device/esp_tinyusb/CHANGELOG.md @@ -1,3 +1,6 @@ +## (Unreleased) +- esp_tinyusb: Added option to select TinyUSB peripheral on esp32p4 via menuconfig + ## 1.6.0 - CDC-ACM: Fixed memory leak on deinit diff --git a/device/esp_tinyusb/CMakeLists.txt b/device/esp_tinyusb/CMakeLists.txt index 98fc960e..6ced87e8 100644 --- a/device/esp_tinyusb/CMakeLists.txt +++ b/device/esp_tinyusb/CMakeLists.txt @@ -4,9 +4,9 @@ set(srcs "usb_descriptors.c" ) -if(NOT CONFIG_TINYUSB_NO_DEFAULT_TASK) +if(CONFIG_TINYUSB_DEFAULT_TASK) list(APPEND srcs "tusb_tasks.c") -endif() # CONFIG_TINYUSB_NO_DEFAULT_TASK +endif() # CONFIG_TINYUSB_DEFAULT_TASK if(CONFIG_TINYUSB_CDC_ENABLED) list(APPEND srcs diff --git a/device/esp_tinyusb/Kconfig b/device/esp_tinyusb/Kconfig index 0247e50d..49cd94df 100644 --- a/device/esp_tinyusb/Kconfig +++ b/device/esp_tinyusb/Kconfig @@ -1,4 +1,4 @@ -menu "TinyUSB Stack" +menu "ESP TinyUSB" config TINYUSB_DEBUG_LEVEL int "TinyUSB log level (0-3)" default 1 @@ -6,15 +6,32 @@ menu "TinyUSB Stack" help Specify verbosity of TinyUSB log output. - choice TINYUSB_RHPORT + config TINYUSB_DYNAMIC_RHPORT depends on IDF_TARGET_ESP32P4 - prompt "TinyUSB PHY" - default TINYUSB_RHPORT_HS + bool "TinyUSB Peripheral dynamic selection" + default n + help + Allows to select and configure the Peripheral for TinyUSB in run-time. + + - Disable to select peripheral via CFG_TUSB_RHPORTx_MODE (legacy way). + - Enable to use run-time port selection via TinyUSB configuration parameter. + + choice TINYUSB_RHPORT + depends on !TINYUSB_DYNAMIC_RHPORT + prompt "USB Peripheral" + default TINYUSB_RHPORT_HS if IDF_TARGET_ESP32P4 + default TINYUSB_RHPORT_FS help - Allows set the USB PHY Controller for TinyUSB: HS (USB OTG2.0 PHY for HighSpeed) + Allows set the USB Peripheral Controller for TinyUSB. + + - High-speed (USB OTG2.0 Peripheral for High-, Full- and Low-speed) + - Full-speed (USB OTG1.1 Peripheral for Full- and Low-speed) config TINYUSB_RHPORT_HS - bool "HS" + bool "OTG2.0" + depends on IDF_TARGET_ESP32P4 + config TINYUSB_RHPORT_FS + bool "OTG1.1" endchoice menu "TinyUSB DCD" @@ -31,39 +48,31 @@ menu "TinyUSB Stack" endchoice endmenu # "TinyUSB DCD" - menu "TinyUSB task configuration" - config TINYUSB_NO_DEFAULT_TASK - bool "Do not create a TinyUSB task" - default n - help - This option allows to not create the FreeRTOS task during the driver initialization. - User will have to handle TinyUSB events manually. + config TINYUSB_DEFAULT_TASK + bool "Create TinyUSB task" + default y + help + To handle USB event, TinyUSB task tud_task() should be implemented. This option allows create the FreeRTOS task during the driver initialization with default parameters. - config TINYUSB_TASK_PRIORITY - int "TinyUSB task priority" - default 5 - depends on !TINYUSB_NO_DEFAULT_TASK - help - Set the priority of the default TinyUSB main task. + Enable - FreeRTOS TinyUSB task (tud_task()) is implemented by the current component. + Disable - FreeRTOS TinyUSB task (tud_task()) must be implemented outside of the current component. - config TINYUSB_TASK_STACK_SIZE - int "TinyUSB task stack size (bytes)" - default 4096 - depends on !TINYUSB_NO_DEFAULT_TASK - help - Set the stack size of the default TinyUSB main task. + Enabled by default. + menu "Task configuration" + depends on TINYUSB_DEFAULT_TASK choice TINYUSB_TASK_AFFINITY - prompt "TinyUSB task affinity" + prompt "CPU affinity" default TINYUSB_TASK_AFFINITY_CPU1 if !FREERTOS_UNICORE default TINYUSB_TASK_AFFINITY_NO_AFFINITY - depends on !TINYUSB_NO_DEFAULT_TASK help Allows setting TinyUSB tasks affinity, i.e. whether the task is pinned to CPU0, pinned to CPU1, or allowed to run on any CPU. + Default value: No affinity for unicore systems, pinned to CPU1 for multicore. + config TINYUSB_TASK_AFFINITY_NO_AFFINITY - bool "No affinity" + bool "None" config TINYUSB_TASK_AFFINITY_CPU0 bool "CPU0" config TINYUSB_TASK_AFFINITY_CPU1 @@ -71,21 +80,42 @@ menu "TinyUSB Stack" depends on !FREERTOS_UNICORE endchoice + config TINYUSB_INIT_IN_TASK + bool "Init in task" + default n if TINYUSB_DEFAULT_TASK + default y + depends on !TINYUSB_TASK_AFFINITY_NO_AFFINITY + help + Run TinyUSB stack initialization just after starting the default TinyUSB task. + This is especially useful in multicore scenarios, when we need to pin the task + to a specific core and, at the same time initialize TinyUSB stack + (i.e. install interrupts) on the same core. + + Disabled by default, when TINYUSB_DEFAULT_TASK is enabled. + Enabled by default, when TINYUSB_DEFAULT_TASK is disabled. + + config TINYUSB_TASK_PRIORITY + int "Priority" + default 5 + help + Set the priority of the default TinyUSB main task. + + Default value: 5. + + config TINYUSB_TASK_STACK_SIZE + int "Stack size (bytes)" + default 4096 + help + Set the stack size of the default TinyUSB main task. + + Default value: 4096 bytes. + config TINYUSB_TASK_AFFINITY hex default FREERTOS_NO_AFFINITY if TINYUSB_TASK_AFFINITY_NO_AFFINITY default 0x0 if TINYUSB_TASK_AFFINITY_CPU0 default 0x1 if TINYUSB_TASK_AFFINITY_CPU1 - config TINYUSB_INIT_IN_DEFAULT_TASK - bool "Initialize TinyUSB stack within the default TinyUSB task" - default n - depends on !TINYUSB_NO_DEFAULT_TASK - help - Run TinyUSB stack initialization just after starting the default TinyUSB task. - This is especially useful in multicore scenarios, when we need to pin the task - to a specific core and, at the same time initialize TinyUSB stack - (i.e. install interrupts) on the same core. endmenu # "TinyUSB task configuration" menu "Descriptor configuration" diff --git a/device/esp_tinyusb/test_apps/default_task/CMakeLists.txt b/device/esp_tinyusb/test_apps/default_task/CMakeLists.txt new file mode 100644 index 00000000..0dbc7e39 --- /dev/null +++ b/device/esp_tinyusb/test_apps/default_task/CMakeLists.txt @@ -0,0 +1,9 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +set(COMPONENTS main) + +project(test_app_default_task_without_init) diff --git a/device/esp_tinyusb/test_apps/default_task/main/CMakeLists.txt b/device/esp_tinyusb/test_apps/default_task/main/CMakeLists.txt new file mode 100644 index 00000000..e81e6278 --- /dev/null +++ b/device/esp_tinyusb/test_apps/default_task/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRC_DIRS . + INCLUDE_DIRS . + REQUIRES unity + WHOLE_ARCHIVE) diff --git a/device/esp_tinyusb/test_apps/default_task/main/idf_component.yml b/device/esp_tinyusb/test_apps/default_task/main/idf_component.yml new file mode 100644 index 00000000..b1cb5b54 --- /dev/null +++ b/device/esp_tinyusb/test_apps/default_task/main/idf_component.yml @@ -0,0 +1,5 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/esp_tinyusb: + version: "*" + override_path: "../../../" diff --git a/device/esp_tinyusb/test_apps/default_task/main/test_app_main.c b/device/esp_tinyusb/test_apps/default_task/main/test_app_main.c new file mode 100644 index 00000000..0d265c1f --- /dev/null +++ b/device/esp_tinyusb/test_apps/default_task/main/test_app_main.c @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" + +void app_main(void) +{ + /* + _ _ _ + | | (_) | | + ___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__ + / _ \/ __| '_ \| __| | '_ \| | | | | | / __| '_ \ + | __/\__ \ |_) | |_| | | | | |_| | |_| \__ \ |_) | + \___||___/ .__/ \__|_|_| |_|\__, |\__,_|___/_.__/ + | |______ __/ | + |_|______| |___/ + _____ _____ _____ _____ + |_ _| ___/ ___|_ _| + | | | |__ \ `--. | | + | | | __| `--. \ | | + | | | |___/\__/ / | | + \_/ \____/\____/ \_/ + */ + + printf(" _ _ _ \n"); + printf(" | | (_) | | \n"); + printf(" ___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__ \n"); + printf(" / _ \\/ __| '_ \\| __| | '_ \\| | | | | | / __| '_ \\ \n"); + printf("| __/\\__ \\ |_) | |_| | | | | |_| | |_| \\__ \\ |_) |\n"); + printf(" \\___||___/ .__/ \\__|_|_| |_|\\__, |\\__,_|___/_.__/ \n"); + printf(" | |______ __/ | \n"); + printf(" |_|______| |___/ \n"); + printf(" _____ _____ _____ _____ \n"); + printf("|_ _| ___/ ___|_ _| \n"); + printf(" | | | |__ \\ `--. | | \n"); + printf(" | | | __| `--. \\ | | \n"); + printf(" | | | |___/\\__/ / | | \n"); + printf(" \\_/ \\____/\\____/ \\_/ \n"); + + unity_utils_setup_heap_record(80); + unity_utils_set_leak_level(128); + unity_run_menu(); +} + +/* setUp runs before every test */ +void setUp(void) +{ + unity_utils_record_free_mem(); +} + +/* tearDown runs after every test */ +void tearDown(void) +{ + unity_utils_evaluate_leaks(); +} diff --git a/device/esp_tinyusb/test_apps/default_task/main/test_tusb.c b/device/esp_tinyusb/test_apps/default_task/main/test_tusb.c new file mode 100644 index 00000000..34711da5 --- /dev/null +++ b/device/esp_tinyusb/test_apps/default_task/main/test_tusb.c @@ -0,0 +1,112 @@ +/* + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc_caps.h" +#if SOC_USB_OTG_SUPPORTED + +// +#include +#include +// +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +// +#include "esp_system.h" +#include "esp_log.h" +#include "esp_err.h" +// +#include "unity.h" +#include "tinyusb.h" + +static const char *TAG = "default_task"; + +SemaphoreHandle_t wait_mount = NULL; + +#define TEARDOWN_DEVICE_DELAY_MS 1000 + +#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN) +static uint8_t const test_configuration_descriptor[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, 0, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_SELF_POWERED | TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), +}; + +static const tusb_desc_device_t test_device_descriptor = { + .bLength = sizeof(test_device_descriptor), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .idVendor = 0x303A, // This is Espressif VID. This needs to be changed according to Users / Customers + .idProduct = 0x4002, + .bcdDevice = 0x100, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + .bNumConfigurations = 0x01 +}; + +#if (TUD_OPT_HIGH_SPEED) +static const tusb_desc_device_qualifier_t device_qualifier = { + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, + .bcdUSB = 0x0200, + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0 +}; +#endif // TUD_OPT_HIGH_SPEED + +// Invoked when device is mounted +void tud_mount_cb(void) +{ + xSemaphoreGive(wait_mount); +} + +/** + * @brief TinyUSB Teardown specific testcase + * + * Scenario: + */ +TEST_CASE("tinyusb_default_task_with_init", "[esp_tinyusb][tusb_task]") +{ + wait_mount = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_EQUAL(NULL, wait_mount); + + // TinyUSB driver configuration + const tinyusb_config_t tusb_cfg = { + .device_descriptor = &test_device_descriptor, + .string_descriptor = NULL, + .string_descriptor_count = 0, + .external_phy = false, +#if (TUD_OPT_HIGH_SPEED) + .fs_configuration_descriptor = test_configuration_descriptor, + .hs_configuration_descriptor = test_configuration_descriptor, + .qualifier_descriptor = &device_qualifier, +#else + .configuration_descriptor = test_configuration_descriptor, +#endif // TUD_OPT_HIGH_SPEED + }; + + TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg)); + // Wait for the usb event + ESP_LOGD(TAG, "wait mount..."); + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(wait_mount, pdMS_TO_TICKS(TEARDOWN_DEVICE_DELAY_MS))); + ESP_LOGD(TAG, "mounted"); + + // Teardown + vTaskDelay(pdMS_TO_TICKS(TEARDOWN_DEVICE_DELAY_MS)); + TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall()); + // Remove primitives + vSemaphoreDelete(wait_mount); +} + +#endif diff --git a/device/esp_tinyusb/test_apps/default_task/pytest_default_task.py b/device/esp_tinyusb/test_apps/default_task/pytest_default_task.py new file mode 100644 index 00000000..03905e71 --- /dev/null +++ b/device/esp_tinyusb/test_apps/default_task/pytest_default_task.py @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +import pytest +from pytest_embedded_idf.dut import IdfDut + +@pytest.mark.esp32s2 +@pytest.mark.esp32s3 +@pytest.mark.esp32p4 +@pytest.mark.usb_device +def test_usb_default_task_with_init(dut: IdfDut) -> None: + dut.run_all_single_board_cases(group='tusb_task') diff --git a/device/esp_tinyusb/test_apps/default_task/sdkconfig.defaults b/device/esp_tinyusb/test_apps/default_task/sdkconfig.defaults new file mode 100644 index 00000000..d98be092 --- /dev/null +++ b/device/esp_tinyusb/test_apps/default_task/sdkconfig.defaults @@ -0,0 +1,16 @@ +# Configure TinyUSB, it will be used to mock USB devices +CONFIG_TINYUSB_DEFAULT_TASK=y +CONFIG_TINYUSB_INIT_IN_TASK=n + +# Disable watchdogs, they'd get triggered during unity interactive menu +CONFIG_ESP_INT_WDT=n +CONFIG_ESP_TASK_WDT=n + +# Run-time checks of Heap and Stack +CONFIG_HEAP_POISONING_COMPREHENSIVE=y +CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y +CONFIG_COMPILER_STACK_CHECK=y + +CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y + +CONFIG_COMPILER_CXX_EXCEPTIONS=y diff --git a/device/esp_tinyusb/test_apps/default_task_with_init/CMakeLists.txt b/device/esp_tinyusb/test_apps/default_task_with_init/CMakeLists.txt new file mode 100644 index 00000000..ce86ccca --- /dev/null +++ b/device/esp_tinyusb/test_apps/default_task_with_init/CMakeLists.txt @@ -0,0 +1,9 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +set(COMPONENTS main) + +project(test_app_default_task_with_init) diff --git a/device/esp_tinyusb/test_apps/default_task_with_init/main/CMakeLists.txt b/device/esp_tinyusb/test_apps/default_task_with_init/main/CMakeLists.txt new file mode 100644 index 00000000..e81e6278 --- /dev/null +++ b/device/esp_tinyusb/test_apps/default_task_with_init/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRC_DIRS . + INCLUDE_DIRS . + REQUIRES unity + WHOLE_ARCHIVE) diff --git a/device/esp_tinyusb/test_apps/default_task_with_init/main/idf_component.yml b/device/esp_tinyusb/test_apps/default_task_with_init/main/idf_component.yml new file mode 100644 index 00000000..b1cb5b54 --- /dev/null +++ b/device/esp_tinyusb/test_apps/default_task_with_init/main/idf_component.yml @@ -0,0 +1,5 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/esp_tinyusb: + version: "*" + override_path: "../../../" diff --git a/device/esp_tinyusb/test_apps/default_task_with_init/main/test_app_main.c b/device/esp_tinyusb/test_apps/default_task_with_init/main/test_app_main.c new file mode 100644 index 00000000..0d265c1f --- /dev/null +++ b/device/esp_tinyusb/test_apps/default_task_with_init/main/test_app_main.c @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" + +void app_main(void) +{ + /* + _ _ _ + | | (_) | | + ___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__ + / _ \/ __| '_ \| __| | '_ \| | | | | | / __| '_ \ + | __/\__ \ |_) | |_| | | | | |_| | |_| \__ \ |_) | + \___||___/ .__/ \__|_|_| |_|\__, |\__,_|___/_.__/ + | |______ __/ | + |_|______| |___/ + _____ _____ _____ _____ + |_ _| ___/ ___|_ _| + | | | |__ \ `--. | | + | | | __| `--. \ | | + | | | |___/\__/ / | | + \_/ \____/\____/ \_/ + */ + + printf(" _ _ _ \n"); + printf(" | | (_) | | \n"); + printf(" ___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__ \n"); + printf(" / _ \\/ __| '_ \\| __| | '_ \\| | | | | | / __| '_ \\ \n"); + printf("| __/\\__ \\ |_) | |_| | | | | |_| | |_| \\__ \\ |_) |\n"); + printf(" \\___||___/ .__/ \\__|_|_| |_|\\__, |\\__,_|___/_.__/ \n"); + printf(" | |______ __/ | \n"); + printf(" |_|______| |___/ \n"); + printf(" _____ _____ _____ _____ \n"); + printf("|_ _| ___/ ___|_ _| \n"); + printf(" | | | |__ \\ `--. | | \n"); + printf(" | | | __| `--. \\ | | \n"); + printf(" | | | |___/\\__/ / | | \n"); + printf(" \\_/ \\____/\\____/ \\_/ \n"); + + unity_utils_setup_heap_record(80); + unity_utils_set_leak_level(128); + unity_run_menu(); +} + +/* setUp runs before every test */ +void setUp(void) +{ + unity_utils_record_free_mem(); +} + +/* tearDown runs after every test */ +void tearDown(void) +{ + unity_utils_evaluate_leaks(); +} diff --git a/device/esp_tinyusb/test_apps/default_task_with_init/main/test_tusb.c b/device/esp_tinyusb/test_apps/default_task_with_init/main/test_tusb.c new file mode 100644 index 00000000..8b1837fc --- /dev/null +++ b/device/esp_tinyusb/test_apps/default_task_with_init/main/test_tusb.c @@ -0,0 +1,112 @@ +/* + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc_caps.h" +#if SOC_USB_OTG_SUPPORTED + +// +#include +#include +// +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +// +#include "esp_system.h" +#include "esp_log.h" +#include "esp_err.h" +// +#include "unity.h" +#include "tinyusb.h" + +static const char *TAG = "default_task_with_init"; + +SemaphoreHandle_t wait_mount = NULL; + +#define TEARDOWN_DEVICE_DELAY_MS 1000 + +#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN) +static uint8_t const test_configuration_descriptor[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, 0, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_SELF_POWERED | TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), +}; + +static const tusb_desc_device_t test_device_descriptor = { + .bLength = sizeof(test_device_descriptor), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .idVendor = 0x303A, // This is Espressif VID. This needs to be changed according to Users / Customers + .idProduct = 0x4002, + .bcdDevice = 0x100, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + .bNumConfigurations = 0x01 +}; + +#if (TUD_OPT_HIGH_SPEED) +static const tusb_desc_device_qualifier_t device_qualifier = { + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, + .bcdUSB = 0x0200, + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0 +}; +#endif // TUD_OPT_HIGH_SPEED + +// Invoked when device is mounted +void tud_mount_cb(void) +{ + xSemaphoreGive(wait_mount); +} + +/** + * @brief TinyUSB Teardown specific testcase + * + * Scenario: + */ +TEST_CASE("tinyusb_default_task_with_init", "[esp_tinyusb][tusb_task]") +{ + wait_mount = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_EQUAL(NULL, wait_mount); + + // TinyUSB driver configuration + const tinyusb_config_t tusb_cfg = { + .device_descriptor = &test_device_descriptor, + .string_descriptor = NULL, + .string_descriptor_count = 0, + .external_phy = false, +#if (TUD_OPT_HIGH_SPEED) + .fs_configuration_descriptor = test_configuration_descriptor, + .hs_configuration_descriptor = test_configuration_descriptor, + .qualifier_descriptor = &device_qualifier, +#else + .configuration_descriptor = test_configuration_descriptor, +#endif // TUD_OPT_HIGH_SPEED + }; + + TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg)); + // Wait for the usb event + ESP_LOGD(TAG, "wait mount..."); + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(wait_mount, pdMS_TO_TICKS(TEARDOWN_DEVICE_DELAY_MS))); + ESP_LOGD(TAG, "mounted"); + + // Teardown + vTaskDelay(pdMS_TO_TICKS(TEARDOWN_DEVICE_DELAY_MS)); + TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall()); + // Remove primitives + vSemaphoreDelete(wait_mount); +} + +#endif diff --git a/device/esp_tinyusb/test_apps/default_task_with_init/pytest_default_task_with_init.py b/device/esp_tinyusb/test_apps/default_task_with_init/pytest_default_task_with_init.py new file mode 100644 index 00000000..03905e71 --- /dev/null +++ b/device/esp_tinyusb/test_apps/default_task_with_init/pytest_default_task_with_init.py @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +import pytest +from pytest_embedded_idf.dut import IdfDut + +@pytest.mark.esp32s2 +@pytest.mark.esp32s3 +@pytest.mark.esp32p4 +@pytest.mark.usb_device +def test_usb_default_task_with_init(dut: IdfDut) -> None: + dut.run_all_single_board_cases(group='tusb_task') diff --git a/device/esp_tinyusb/test_apps/default_task_with_init/sdkconfig.defaults b/device/esp_tinyusb/test_apps/default_task_with_init/sdkconfig.defaults new file mode 100644 index 00000000..69b01a19 --- /dev/null +++ b/device/esp_tinyusb/test_apps/default_task_with_init/sdkconfig.defaults @@ -0,0 +1,16 @@ +# Configure TinyUSB, it will be used to mock USB devices +CONFIG_TINYUSB_DEFAULT_TASK=y +CONFIG_TINYUSB_INIT_IN_TASK=y + +# Disable watchdogs, they'd get triggered during unity interactive menu +CONFIG_ESP_INT_WDT=n +CONFIG_ESP_TASK_WDT=n + +# Run-time checks of Heap and Stack +CONFIG_HEAP_POISONING_COMPREHENSIVE=y +CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y +CONFIG_COMPILER_STACK_CHECK=y + +CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y + +CONFIG_COMPILER_CXX_EXCEPTIONS=y diff --git a/device/esp_tinyusb/test_apps/external_task/CMakeLists.txt b/device/esp_tinyusb/test_apps/external_task/CMakeLists.txt new file mode 100644 index 00000000..ca98d70c --- /dev/null +++ b/device/esp_tinyusb/test_apps/external_task/CMakeLists.txt @@ -0,0 +1,9 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +set(COMPONENTS main) + +project(test_app_external_task_without_init) diff --git a/device/esp_tinyusb/test_apps/external_task/main/CMakeLists.txt b/device/esp_tinyusb/test_apps/external_task/main/CMakeLists.txt new file mode 100644 index 00000000..e81e6278 --- /dev/null +++ b/device/esp_tinyusb/test_apps/external_task/main/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRC_DIRS . + INCLUDE_DIRS . + REQUIRES unity + WHOLE_ARCHIVE) diff --git a/device/esp_tinyusb/test_apps/external_task/main/idf_component.yml b/device/esp_tinyusb/test_apps/external_task/main/idf_component.yml new file mode 100644 index 00000000..b1cb5b54 --- /dev/null +++ b/device/esp_tinyusb/test_apps/external_task/main/idf_component.yml @@ -0,0 +1,5 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/esp_tinyusb: + version: "*" + override_path: "../../../" diff --git a/device/esp_tinyusb/test_apps/external_task/main/test_app_main.c b/device/esp_tinyusb/test_apps/external_task/main/test_app_main.c new file mode 100644 index 00000000..0d265c1f --- /dev/null +++ b/device/esp_tinyusb/test_apps/external_task/main/test_app_main.c @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "unity.h" +#include "unity_test_runner.h" +#include "unity_test_utils_memory.h" + +void app_main(void) +{ + /* + _ _ _ + | | (_) | | + ___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__ + / _ \/ __| '_ \| __| | '_ \| | | | | | / __| '_ \ + | __/\__ \ |_) | |_| | | | | |_| | |_| \__ \ |_) | + \___||___/ .__/ \__|_|_| |_|\__, |\__,_|___/_.__/ + | |______ __/ | + |_|______| |___/ + _____ _____ _____ _____ + |_ _| ___/ ___|_ _| + | | | |__ \ `--. | | + | | | __| `--. \ | | + | | | |___/\__/ / | | + \_/ \____/\____/ \_/ + */ + + printf(" _ _ _ \n"); + printf(" | | (_) | | \n"); + printf(" ___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__ \n"); + printf(" / _ \\/ __| '_ \\| __| | '_ \\| | | | | | / __| '_ \\ \n"); + printf("| __/\\__ \\ |_) | |_| | | | | |_| | |_| \\__ \\ |_) |\n"); + printf(" \\___||___/ .__/ \\__|_|_| |_|\\__, |\\__,_|___/_.__/ \n"); + printf(" | |______ __/ | \n"); + printf(" |_|______| |___/ \n"); + printf(" _____ _____ _____ _____ \n"); + printf("|_ _| ___/ ___|_ _| \n"); + printf(" | | | |__ \\ `--. | | \n"); + printf(" | | | __| `--. \\ | | \n"); + printf(" | | | |___/\\__/ / | | \n"); + printf(" \\_/ \\____/\\____/ \\_/ \n"); + + unity_utils_setup_heap_record(80); + unity_utils_set_leak_level(128); + unity_run_menu(); +} + +/* setUp runs before every test */ +void setUp(void) +{ + unity_utils_record_free_mem(); +} + +/* tearDown runs after every test */ +void tearDown(void) +{ + unity_utils_evaluate_leaks(); +} diff --git a/device/esp_tinyusb/test_apps/external_task/main/test_tusb.c b/device/esp_tinyusb/test_apps/external_task/main/test_tusb.c new file mode 100644 index 00000000..4551f934 --- /dev/null +++ b/device/esp_tinyusb/test_apps/external_task/main/test_tusb.c @@ -0,0 +1,144 @@ +/* + * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc_caps.h" +#if SOC_USB_OTG_SUPPORTED + +// +#include +#include +// +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +// +#include "esp_system.h" +#include "esp_log.h" +#include "esp_err.h" +// +#include "unity.h" +#include "tinyusb.h" + +static const char *TAG = "external_task"; + +static SemaphoreHandle_t wait_mount = NULL; +static TaskHandle_t s_test_tusb_tskh; + +#define TUSB_DEVICE_DELAY_MS 1000 +#define TUSB_EXTERNAL_TASK_SIZE 4096 +#define TUSB_EXTERNAL_TASK_PRIO 5 +#define TUSB_EXTERNAL_TASK_AFFINITY 0x7FFFFFFF /* FREERTOS_NO_AFFINITY */ + +#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN) +static uint8_t const test_configuration_descriptor[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, 0, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_SELF_POWERED | TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), +}; + +static const tusb_desc_device_t test_device_descriptor = { + .bLength = sizeof(test_device_descriptor), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .idVendor = 0x303A, // This is Espressif VID. This needs to be changed according to Users / Customers + .idProduct = 0x4002, + .bcdDevice = 0x100, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + .bNumConfigurations = 0x01 +}; + +#if (TUD_OPT_HIGH_SPEED) +static const tusb_desc_device_qualifier_t device_qualifier = { + .bLength = sizeof(tusb_desc_device_qualifier_t), + .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER, + .bcdUSB = 0x0200, + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .bNumConfigurations = 0x01, + .bReserved = 0 +}; +#endif // TUD_OPT_HIGH_SPEED + +// Invoked when device is mounted +void tud_mount_cb(void) +{ + xSemaphoreGive(wait_mount); +} + +/** + * @brief This top level thread processes all usb events and invokes callbacks + */ +static void test_tusb_external_task(void *arg) +{ + ESP_LOGD(TAG, "External TinyUSB task started"); + while (1) { // RTOS forever loop + tud_task(); + } +} + +/** + * @brief TinyUSB Teardown specific testcase + * + * Scenario: + * 1. Install TinyUSB driver + * 2. Create external TinyUSB task for tud_task() + * 3. Wait tud_mount_cb() for TUSB_DEVICE_DELAY_MS + * 4. Wait TUSB_DEVICE_DELAY_MS + * 5. Teardwon TinyUSB + * 6. Release resources + * + * @note If run the task before installing the tinyusb driver, the external task will lead to cpu starvation. + */ +TEST_CASE("tinyusb_external_task", "[esp_tinyusb][tusb_task]") +{ + wait_mount = xSemaphoreCreateBinary(); + TEST_ASSERT_NOT_EQUAL(NULL, wait_mount); + + // TinyUSB driver configuration + const tinyusb_config_t tusb_cfg = { + .device_descriptor = &test_device_descriptor, + .string_descriptor = NULL, + .string_descriptor_count = 0, + .external_phy = false, +#if (TUD_OPT_HIGH_SPEED) + .fs_configuration_descriptor = test_configuration_descriptor, + .hs_configuration_descriptor = test_configuration_descriptor, + .qualifier_descriptor = &device_qualifier, +#else + .configuration_descriptor = test_configuration_descriptor, +#endif // TUD_OPT_HIGH_SPEED + }; + + TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg)); + // Create an external task for tinyusb device stack + xTaskCreate(test_tusb_external_task, + "TinyUSB", + TUSB_EXTERNAL_TASK_SIZE, + NULL, + TUSB_EXTERNAL_TASK_PRIO, + &s_test_tusb_tskh); + TEST_ASSERT_NOT_NULL(s_test_tusb_tskh); + + // Wait for the usb event + ESP_LOGD(TAG, "wait mount..."); + TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(wait_mount, pdMS_TO_TICKS(TUSB_DEVICE_DELAY_MS))); + ESP_LOGD(TAG, "mounted"); + // Teardown + vTaskDelay(pdMS_TO_TICKS(TUSB_DEVICE_DELAY_MS)); + TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall()); + // Remove primitives + vTaskDelete(s_test_tusb_tskh); + vSemaphoreDelete(wait_mount); +} + +#endif diff --git a/device/esp_tinyusb/test_apps/external_task/pytest_external_task_without_init.py b/device/esp_tinyusb/test_apps/external_task/pytest_external_task_without_init.py new file mode 100644 index 00000000..c69b7f53 --- /dev/null +++ b/device/esp_tinyusb/test_apps/external_task/pytest_external_task_without_init.py @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +import pytest +from pytest_embedded_idf.dut import IdfDut + +@pytest.mark.esp32s2 +@pytest.mark.esp32s3 +@pytest.mark.esp32p4 +@pytest.mark.usb_device +def test_usb_external_task_internal_init(dut: IdfDut) -> None: + dut.run_all_single_board_cases(group='tusb_task') diff --git a/device/esp_tinyusb/test_apps/external_task/sdkconfig.defaults b/device/esp_tinyusb/test_apps/external_task/sdkconfig.defaults new file mode 100644 index 00000000..d98be092 --- /dev/null +++ b/device/esp_tinyusb/test_apps/external_task/sdkconfig.defaults @@ -0,0 +1,16 @@ +# Configure TinyUSB, it will be used to mock USB devices +CONFIG_TINYUSB_DEFAULT_TASK=y +CONFIG_TINYUSB_INIT_IN_TASK=n + +# Disable watchdogs, they'd get triggered during unity interactive menu +CONFIG_ESP_INT_WDT=n +CONFIG_ESP_TASK_WDT=n + +# Run-time checks of Heap and Stack +CONFIG_HEAP_POISONING_COMPREHENSIVE=y +CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y +CONFIG_COMPILER_STACK_CHECK=y + +CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y + +CONFIG_COMPILER_CXX_EXCEPTIONS=y diff --git a/device/esp_tinyusb/tinyusb.c b/device/esp_tinyusb/tinyusb.c index 2315ab50..deb9633f 100644 --- a/device/esp_tinyusb/tinyusb.c +++ b/device/esp_tinyusb/tinyusb.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -32,6 +32,9 @@ esp_err_t tinyusb_driver_install(const tinyusb_config_t *config) usb_phy_config_t phy_conf = { .controller = USB_PHY_CTRL_OTG, .otg_mode = USB_OTG_MODE_DEVICE, +#if (SOC_USB_OTG_PERIPH_NUM > 1) + .otg_speed = (TUD_OPT_HIGH_SPEED) ? USB_PHY_SPEED_HIGH : USB_PHY_SPEED_FULL, +#endif // SOC_USB_OTG_PERIPH_NUM > 1 }; // External PHY IOs config @@ -61,10 +64,10 @@ esp_err_t tinyusb_driver_install(const tinyusb_config_t *config) ESP_RETURN_ON_ERROR(tinyusb_set_descriptors(config), TAG, "Descriptors config failed"); // Init -#if !CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK +#if !CONFIG_TINYUSB_INIT_IN_TASK ESP_RETURN_ON_FALSE(tusb_init(), ESP_FAIL, TAG, "Init TinyUSB stack failed"); #endif -#if !CONFIG_TINYUSB_NO_DEFAULT_TASK +#if CONFIG_TINYUSB_DEFAULT_TASK ESP_RETURN_ON_ERROR(tusb_run_task(), TAG, "Run TinyUSB task failed"); #endif ESP_LOGI(TAG, "TinyUSB Driver installed"); @@ -73,16 +76,11 @@ esp_err_t tinyusb_driver_install(const tinyusb_config_t *config) esp_err_t tinyusb_driver_uninstall(void) { - esp_err_t ret = tusb_stop_task(); - - if (ret != ESP_OK) { - return ret; - } - - if (!tusb_teardown()) { - return ESP_ERR_NOT_FINISHED; - } - +#if CONFIG_TINYUSB_DEFAULT_TASK + ESP_RETURN_ON_ERROR(tusb_stop_task(), TAG, "Unable to stop TinyUSB task"); +#endif // CONFIG_TINYUSB_DEFAULT_TASK + ESP_RETURN_ON_FALSE(tusb_teardown(), ESP_ERR_NOT_FINISHED, TAG, "Unable to teardown TinyUSB"); tinyusb_free_descriptors(); - return usb_del_phy(phy_hdl); + ESP_RETURN_ON_ERROR(usb_del_phy(phy_hdl), TAG, "Unable to delete PHY"); + return ESP_OK; } diff --git a/device/esp_tinyusb/tusb_tasks.c b/device/esp_tinyusb/tusb_tasks.c index cd4add4e..f9c89585 100644 --- a/device/esp_tinyusb/tusb_tasks.c +++ b/device/esp_tinyusb/tusb_tasks.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -16,10 +16,10 @@ const static char *TAG = "tusb_tsk"; static TaskHandle_t s_tusb_tskh; -#if CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK +#if CONFIG_TINYUSB_INIT_IN_TASK const static int INIT_OK = BIT0; const static int INIT_FAILED = BIT1; -#endif +#endif // CONFIG_TINYUSB_INIT_IN_TASK /** * @brief This top level thread processes all usb events and invokes callbacks @@ -27,7 +27,7 @@ const static int INIT_FAILED = BIT1; static void tusb_device_task(void *arg) { ESP_LOGD(TAG, "tinyusb task started"); -#if CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK +#if CONFIG_TINYUSB_INIT_IN_TASK EventGroupHandle_t *init_flags = arg; if (!tusb_init()) { ESP_LOGI(TAG, "Init TinyUSB stack failed"); @@ -36,7 +36,7 @@ static void tusb_device_task(void *arg) } ESP_LOGD(TAG, "tinyusb task has been initialized"); xEventGroupSetBits(*init_flags, INIT_OK); -#endif // CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK +#endif // CONFIG_TINYUSB_INIT_IN_TASK while (1) { // RTOS forever loop tud_task(); } @@ -49,21 +49,21 @@ esp_err_t tusb_run_task(void) ESP_RETURN_ON_FALSE(!s_tusb_tskh, ESP_ERR_INVALID_STATE, TAG, "TinyUSB main task already started"); void *task_arg = NULL; -#if CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK +#if CONFIG_TINYUSB_INIT_IN_TASK // need to synchronize to potentially report issue if init failed EventGroupHandle_t init_flags = xEventGroupCreate(); ESP_RETURN_ON_FALSE(init_flags, ESP_ERR_NO_MEM, TAG, "Failed to allocate task sync flags"); task_arg = &init_flags; -#endif +#endif // CONFIG_TINYUSB_INIT_IN_TASK // Create a task for tinyusb device stack: xTaskCreatePinnedToCore(tusb_device_task, "TinyUSB", CONFIG_TINYUSB_TASK_STACK_SIZE, task_arg, CONFIG_TINYUSB_TASK_PRIORITY, &s_tusb_tskh, CONFIG_TINYUSB_TASK_AFFINITY); ESP_RETURN_ON_FALSE(s_tusb_tskh, ESP_FAIL, TAG, "create TinyUSB main task failed"); -#if CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK +#if CONFIG_TINYUSB_INIT_IN_TASK // wait until tusb initialization has completed EventBits_t bits = xEventGroupWaitBits(init_flags, INIT_OK | INIT_FAILED, pdFALSE, pdFALSE, portMAX_DELAY); vEventGroupDelete(init_flags); ESP_RETURN_ON_FALSE(bits & INIT_OK, ESP_FAIL, TAG, "Init TinyUSB stack failed"); -#endif +#endif // CONFIG_TINYUSB_INIT_IN_TASK return ESP_OK; }