From de1a18b1c068854ac8f98149b31f632e0f7aaf6a Mon Sep 17 00:00:00 2001 From: Roman Leonov Date: Mon, 6 Jan 2025 10:49:42 +0100 Subject: [PATCH] refactor(config_test): Added test for different tinyusb configurations --- .../configuration_desc/CMakeLists.txt | 9 + .../configuration_desc/main/CMakeLists.txt | 4 + .../configuration_desc/main/idf_component.yml | 5 + .../configuration_desc/main/test_app_main.c | 62 +++++ .../main/test_config_desc.c | 227 ++++++++++++++++++ .../configuration_desc/pytest_config_desc.py | 13 + .../configuration_desc/sdkconfig.defaults | 16 ++ 7 files changed, 336 insertions(+) create mode 100644 device/esp_tinyusb/test_apps/configuration_desc/CMakeLists.txt create mode 100644 device/esp_tinyusb/test_apps/configuration_desc/main/CMakeLists.txt create mode 100644 device/esp_tinyusb/test_apps/configuration_desc/main/idf_component.yml create mode 100644 device/esp_tinyusb/test_apps/configuration_desc/main/test_app_main.c create mode 100644 device/esp_tinyusb/test_apps/configuration_desc/main/test_config_desc.c create mode 100644 device/esp_tinyusb/test_apps/configuration_desc/pytest_config_desc.py create mode 100644 device/esp_tinyusb/test_apps/configuration_desc/sdkconfig.defaults diff --git a/device/esp_tinyusb/test_apps/configuration_desc/CMakeLists.txt b/device/esp_tinyusb/test_apps/configuration_desc/CMakeLists.txt new file mode 100644 index 00000000..192f72f2 --- /dev/null +++ b/device/esp_tinyusb/test_apps/configuration_desc/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_configuration_descriptor) diff --git a/device/esp_tinyusb/test_apps/configuration_desc/main/CMakeLists.txt b/device/esp_tinyusb/test_apps/configuration_desc/main/CMakeLists.txt new file mode 100644 index 00000000..e81e6278 --- /dev/null +++ b/device/esp_tinyusb/test_apps/configuration_desc/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/configuration_desc/main/idf_component.yml b/device/esp_tinyusb/test_apps/configuration_desc/main/idf_component.yml new file mode 100644 index 00000000..b1cb5b54 --- /dev/null +++ b/device/esp_tinyusb/test_apps/configuration_desc/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/configuration_desc/main/test_app_main.c b/device/esp_tinyusb/test_apps/configuration_desc/main/test_app_main.c new file mode 100644 index 00000000..0d265c1f --- /dev/null +++ b/device/esp_tinyusb/test_apps/configuration_desc/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/configuration_desc/main/test_config_desc.c b/device/esp_tinyusb/test_apps/configuration_desc/main/test_config_desc.c new file mode 100644 index 00000000..1ab3ea19 --- /dev/null +++ b/device/esp_tinyusb/test_apps/configuration_desc/main/test_config_desc.c @@ -0,0 +1,227 @@ +/* + * 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 "esp_system.h" +#include "esp_err.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_log.h" +#include "esp_err.h" +#include "unity.h" +#include "tinyusb.h" +#include "tusb_tasks.h" + +static const char *TAG = "teardown"; + +#define TEARDOWN_DEVICE_ATTACH_TIMEOUT_MS 1000 +#define TEARDOWN_DEVICE_DETACH_DELAY_MS 1000 + +// ========================= TinyUSB descriptors =============================== + +// Here we need to create dual CDC device, to match the CONFIG_TINYUSB_CDC_COUNT from sdkconfig.defaults +static const uint16_t cdc_desc_config_len = TUD_CONFIG_DESC_LEN + CFG_TUD_CDC * TUD_CDC_DESC_LEN; +static const uint8_t test_fs_configuration_descriptor[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, 0, 0, cdc_desc_config_len, TUSB_DESC_CONFIG_ATT_SELF_POWERED | TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + TUD_CDC_DESCRIPTOR(0, 4, 0x81, 8, 0x02, 0x82, 64), + TUD_CDC_DESCRIPTOR(2, 4, 0x83, 8, 0x04, 0x84, 64), +}; + +#if (TUD_OPT_HIGH_SPEED) +static const uint8_t test_hs_configuration_descriptor[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, 4, 0, cdc_desc_config_len, TUSB_DESC_CONFIG_ATT_SELF_POWERED | TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + TUD_CDC_DESCRIPTOR(0, 4, 0x81, 8, 0x02, 0x82, 512), + TUD_CDC_DESCRIPTOR(2, 4, 0x83, 8, 0x04, 0x84, 512), +}; + +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 + +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 +}; + +// ========================== Private logic ==================================== +SemaphoreHandle_t wait_mount = NULL; + +static bool __test_init(void) +{ + wait_mount = xSemaphoreCreateBinary(); + return (wait_mount != NULL); +} + +static void __test_conn(void) +{ + if (wait_mount) { + xSemaphoreGive(wait_mount); + } +} + +static esp_err_t __test_wait_conn(void) +{ + if (!wait_mount) { + return ESP_ERR_INVALID_STATE; + } + + return ( xSemaphoreTake(wait_mount, pdMS_TO_TICKS(TEARDOWN_DEVICE_ATTACH_TIMEOUT_MS)) + ? ESP_OK + : ESP_ERR_TIMEOUT ); +} + +static void __test_release(void) +{ + if (wait_mount) { + vSemaphoreDelete(wait_mount); + } +} + +static void __test_tinyusb_set_config(const tinyusb_config_t *tusb_cfg) +{ + // Install + TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(tusb_cfg)); + // Wait for mounted callback + TEST_ASSERT_EQUAL(ESP_OK, __test_wait_conn()); + // Give Host some timer to init the device + vTaskDelay(pdMS_TO_TICKS(TEARDOWN_DEVICE_DETACH_DELAY_MS)); + // Cleanup + TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall()); + // Release resources + __test_release(); +} + +// ========================== Callbacks ======================================== +// Invoked when device is mounted +void tud_mount_cb(void) +{ + ESP_LOGD(TAG, "%s", __FUNCTION__); + __test_conn(); +} + +// ============================= Tests ========================================= + +TEST_CASE("descriptors_config_all_default", "[esp_tinyusb][usb_device][config]") +{ + TEST_ASSERT_EQUAL(true, __test_init()); + // Install TinyUSB driver + const tinyusb_config_t tusb_cfg = { + .external_phy = false, + .device_descriptor = NULL, + .configuration_descriptor = NULL, +#if (TUD_OPT_HIGH_SPEED) + .hs_configuration_descriptor = NULL, +#endif // TUD_OPT_HIGH_SPEED + }; + __test_tinyusb_set_config(&tusb_cfg); +} + +TEST_CASE("descriptors_config_device", "[esp_tinyusb][usb_device][config]") +{ + TEST_ASSERT_EQUAL(true, __test_init()); + // Install TinyUSB driver + const tinyusb_config_t tusb_cfg = { + .external_phy = false, + .device_descriptor = &test_device_descriptor, + .configuration_descriptor = NULL, +#if (TUD_OPT_HIGH_SPEED) + .hs_configuration_descriptor = NULL, + .qualifier_descriptor = &device_qualifier, +#endif // TUD_OPT_HIGH_SPEED + }; + __test_tinyusb_set_config(&tusb_cfg); +} + +TEST_CASE("descriptors_config_device_and_config", "[esp_tinyusb][usb_device][config]") +{ + TEST_ASSERT_EQUAL(true, __test_init()); + // Install TinyUSB driver + const tinyusb_config_t tusb_cfg = { + .external_phy = false, + .device_descriptor = &test_device_descriptor, + .configuration_descriptor = test_fs_configuration_descriptor, +#if (TUD_OPT_HIGH_SPEED) + .hs_configuration_descriptor = NULL, + .qualifier_descriptor = &device_qualifier, +#endif // TUD_OPT_HIGH_SPEED + }; + __test_tinyusb_set_config(&tusb_cfg); +} + +#if (TUD_OPT_HIGH_SPEED) +TEST_CASE("descriptors_config_device_and_fs_config_only", "[esp_tinyusb][usb_device][config]") +{ + TEST_ASSERT_EQUAL(true, __test_init()); + // Install TinyUSB driver + const tinyusb_config_t tusb_cfg = { + .external_phy = false, + .device_descriptor = &test_device_descriptor, + .configuration_descriptor = test_fs_configuration_descriptor, + .hs_configuration_descriptor = NULL, + .qualifier_descriptor = &device_qualifier, + }; + __test_tinyusb_set_config(&tusb_cfg); +} + +TEST_CASE("descriptors_config_device_and_hs_config_only", "[esp_tinyusb][usb_device][config]") +{ + TEST_ASSERT_EQUAL(true, __test_init()); + // Install TinyUSB driver + const tinyusb_config_t tusb_cfg = { + .external_phy = false, + .device_descriptor = &test_device_descriptor, + .configuration_descriptor = NULL, + .hs_configuration_descriptor = test_hs_configuration_descriptor, + .qualifier_descriptor = &device_qualifier, + }; + __test_tinyusb_set_config(&tusb_cfg); +} + +TEST_CASE("descriptors_config_all_configured", "[esp_tinyusb][usb_device][config]") +{ + TEST_ASSERT_EQUAL(true, __test_init()); + // Install TinyUSB driver + const tinyusb_config_t tusb_cfg = { + .external_phy = false, + .device_descriptor = &test_device_descriptor, + .fs_configuration_descriptor = test_fs_configuration_descriptor, + .hs_configuration_descriptor = test_hs_configuration_descriptor, + .qualifier_descriptor = &device_qualifier, + }; + __test_tinyusb_set_config(&tusb_cfg); +} +#endif // TUD_OPT_HIGH_SPEED + +#endif // SOC_USB_OTG_SUPPORTED diff --git a/device/esp_tinyusb/test_apps/configuration_desc/pytest_config_desc.py b/device/esp_tinyusb/test_apps/configuration_desc/pytest_config_desc.py new file mode 100644 index 00000000..70b8743a --- /dev/null +++ b/device/esp_tinyusb/test_apps/configuration_desc/pytest_config_desc.py @@ -0,0 +1,13 @@ +# 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_device_configuration(dut: IdfDut) -> None: + dut.run_all_single_board_cases(group='config') diff --git a/device/esp_tinyusb/test_apps/configuration_desc/sdkconfig.defaults b/device/esp_tinyusb/test_apps/configuration_desc/sdkconfig.defaults new file mode 100644 index 00000000..6034177a --- /dev/null +++ b/device/esp_tinyusb/test_apps/configuration_desc/sdkconfig.defaults @@ -0,0 +1,16 @@ +# Configure TinyUSB, it will be used to mock USB devices +CONFIG_TINYUSB_CDC_ENABLED=y +CONFIG_TINYUSB_CDC_COUNT=2 + +# 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