-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature(teardown_test): Added the test_app for teardowning the cdc de…
…vice
- Loading branch information
Showing
7 changed files
with
404 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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_teardown_device) |
4 changes: 4 additions & 0 deletions
4
device/esp_tinyusb/test_apps/teardown_device/main/CMakeLists.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
idf_component_register(SRC_DIRS . | ||
INCLUDE_DIRS . | ||
REQUIRES unity | ||
WHOLE_ARCHIVE) |
5 changes: 5 additions & 0 deletions
5
device/esp_tinyusb/test_apps/teardown_device/main/idf_component.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
## IDF Component Manager Manifest File | ||
dependencies: | ||
espressif/esp_tinyusb: | ||
version: "*" | ||
override_path: "../../../" |
62 changes: 62 additions & 0 deletions
62
device/esp_tinyusb/test_apps/teardown_device/main/test_app_main.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include <stdio.h> | ||
#include <string.h> | ||
#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(); | ||
} |
214 changes: 214 additions & 0 deletions
214
device/esp_tinyusb/test_apps/teardown_device/main/test_teardown.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
/* | ||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#include "soc/soc_caps.h" | ||
#if SOC_USB_OTG_SUPPORTED | ||
|
||
// | ||
#include <stdio.h> | ||
#include <string.h> | ||
// | ||
#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" | ||
#include "tusb_cdc_acm.h" | ||
|
||
static const char *TAG = "teardown"; | ||
|
||
SemaphoreHandle_t wait_mount = NULL; | ||
SemaphoreHandle_t wait_terminal = NULL; | ||
SemaphoreHandle_t wait_command = NULL; | ||
|
||
static uint8_t rx_buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1]; | ||
static uint8_t tx_buf[CONFIG_TINYUSB_CDC_TX_BUFSIZE + 1] = { 0 }; | ||
|
||
#define TEARDOWN_CMD_KEY 0xAA | ||
#define TEARDOWN_RPL_KEY 0x55 | ||
#define TEARDOWN_CMD_RPL_SIZE ((TUD_OPT_HIGH_SPEED ? 512 : 64)) | ||
#define TEARDOWN_ATTACH_TIMEOUT_MS 2000 | ||
#define TEARDOWN_COMMAND_TIMEOUT_MS 3000 | ||
#define TEARDOWN_AMOUNT 4 | ||
|
||
static const tusb_desc_device_t cdc_device_descriptor = { | ||
.bLength = sizeof(cdc_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 = USB_ESPRESSIF_VID, | ||
.idProduct = 0x4002, | ||
.bcdDevice = 0x0100, | ||
.iManufacturer = 0x01, | ||
.iProduct = 0x02, | ||
.iSerialNumber = 0x03, | ||
.bNumConfigurations = 0x01 | ||
}; | ||
|
||
static const uint16_t cdc_desc_config_len = TUD_CONFIG_DESC_LEN + CFG_TUD_CDC * TUD_CDC_DESC_LEN; | ||
static const uint8_t cdc_desc_configuration[] = { | ||
TUD_CONFIG_DESCRIPTOR(1, 2, 0, cdc_desc_config_len, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), | ||
TUD_CDC_DESCRIPTOR(0, 4, 0x81, 8, 0x02, 0x82, (TUD_OPT_HIGH_SPEED ? 512 : 64)), | ||
}; | ||
|
||
#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 | ||
|
||
static void tinyusb_cdc_rx_callback(int itf, cdcacm_event_t *event) | ||
{ | ||
// Something was received | ||
xSemaphoreGive(wait_command); | ||
} | ||
|
||
/** | ||
* @brief CDC device line change callback | ||
* | ||
* CDC device signals, that the DTR, RTS states changed | ||
* | ||
* @param[in] itf CDC device index | ||
* @param[in] event CDC event type | ||
*/ | ||
void tinyusb_cdc_line_state_changed_callback(int itf, cdcacm_event_t *event) | ||
{ | ||
int dtr = event->line_state_changed_data.dtr; | ||
int rts = event->line_state_changed_data.rts; | ||
ESP_LOGD(TAG, "Line state changed on channel %d: DTR:%d, RTS:%d", itf, dtr, rts); | ||
|
||
// Terminal: | ||
// dtr==1 && rts==1 - connected | ||
// dtr==0 && rts==0 - disconnected | ||
xSemaphoreGive(wait_terminal); | ||
} | ||
|
||
// Invoked when device is mounted | ||
void tud_mount_cb(void) | ||
{ | ||
xSemaphoreGive(wait_mount); | ||
} | ||
|
||
/** | ||
* @brief TinyUSB Teardown specific testcase | ||
* | ||
* Scenario: | ||
* - Installs the tinyUSB driver via esp-tinyusb wrapper with 1xCDC class device | ||
* - Awaits device configuration be the Host (TEARDOWN_ATTACH_TIMEOUT_MS) | ||
* - Awaits the terminal connection (TEARDOWN_ATTACH_TIMEOUT_MS) | ||
* - Expects the command sequence from the Host (TEARDOWN_COMMAND_TIMEOUT_MS) | ||
* - Replies with the response sequence to the Host | ||
* - Awaits terminal disconnection (TEARDOWN_ATTACH_TIMEOUT_MS) | ||
* - Teardowns the tinyUSB driver via esp-tinyusb wrapper | ||
* - Repeats the steps from the step.1 N times (where N = TEARDOWN_AMOUNT) | ||
* - Verifies amount of attempts and memory leakage (attempts should be 0) | ||
* | ||
* command sequence[] = ep_size * 0xAA | ||
* response sequence[] = ep_size * 0x55 | ||
* | ||
* Hint: Values 0xAA and 0x55 were selected to verify the buffer memory integrity, | ||
* as the 0xAA and 0x55 are the inversion of each other and the data bits in the same position changes from 1 to 0 in every transaction. | ||
*/ | ||
TEST_CASE("tinyusb_teardown", "[esp_tinyusb][teardown]") | ||
{ | ||
size_t rx_size = 0; | ||
|
||
wait_mount = xSemaphoreCreateBinary(); | ||
TEST_ASSERT_NOT_EQUAL(NULL, wait_mount); | ||
wait_command = xSemaphoreCreateBinary(); | ||
TEST_ASSERT_NOT_EQUAL(NULL, wait_command); | ||
wait_terminal = xSemaphoreCreateBinary(); | ||
TEST_ASSERT_NOT_EQUAL(NULL, wait_terminal); | ||
|
||
// Prep reply | ||
for (int i = 0; i < TEARDOWN_CMD_RPL_SIZE; i++) { | ||
tx_buf[i] = TEARDOWN_RPL_KEY; | ||
} | ||
|
||
// TinyUSB driver configuration | ||
const tinyusb_config_t tusb_cfg = { | ||
.device_descriptor = &cdc_device_descriptor, | ||
.string_descriptor = NULL, | ||
.string_descriptor_count = 0, | ||
.external_phy = false, | ||
#if (TUD_OPT_HIGH_SPEED) | ||
.fs_configuration_descriptor = cdc_desc_configuration, | ||
.hs_configuration_descriptor = cdc_desc_configuration, | ||
.qualifier_descriptor = &device_qualifier, | ||
#else | ||
.configuration_descriptor = cdc_desc_configuration, | ||
#endif // TUD_OPT_HIGH_SPEED | ||
}; | ||
|
||
// TinyUSB ACM Driver configuration | ||
const tinyusb_config_cdcacm_t acm_cfg = { | ||
.usb_dev = TINYUSB_USBDEV_0, | ||
.cdc_port = TINYUSB_CDC_ACM_0, | ||
.rx_unread_buf_sz = 64, | ||
.callback_rx = &tinyusb_cdc_rx_callback, | ||
.callback_rx_wanted_char = NULL, | ||
.callback_line_state_changed = &tinyusb_cdc_line_state_changed_callback, | ||
.callback_line_coding_changed = NULL | ||
}; | ||
|
||
int attempts = TEARDOWN_AMOUNT; | ||
// TODO: Run the test while attempts not 0 or the timeout occurred | ||
while (attempts) { | ||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg)); | ||
// Init CDC 0 | ||
TEST_ASSERT_FALSE(tusb_cdc_acm_initialized(TINYUSB_CDC_ACM_0)); | ||
TEST_ASSERT_EQUAL(ESP_OK, tusb_cdc_acm_init(&acm_cfg)); | ||
TEST_ASSERT_TRUE(tusb_cdc_acm_initialized(TINYUSB_CDC_ACM_0)); | ||
// Wait for the usb event | ||
ESP_LOGD(TAG, "wait dev mounted..."); | ||
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(wait_mount, pdMS_TO_TICKS(TEARDOWN_ATTACH_TIMEOUT_MS))); | ||
ESP_LOGD(TAG, "wait terminal connection..."); | ||
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(wait_terminal, pdMS_TO_TICKS(TEARDOWN_ATTACH_TIMEOUT_MS))); | ||
// Wait for the command | ||
ESP_LOGD(TAG, "wait command..."); | ||
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(wait_command, pdMS_TO_TICKS(TEARDOWN_COMMAND_TIMEOUT_MS))); | ||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_cdcacm_read(TINYUSB_CDC_ACM_0, rx_buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size)); | ||
for (int i = 0; i < TEARDOWN_CMD_RPL_SIZE; i++) { | ||
TEST_ASSERT_EQUAL(TEARDOWN_CMD_KEY, rx_buf[i]); | ||
} | ||
ESP_LOGD(TAG, "command received"); | ||
// Reply the response sequence | ||
ESP_LOGD(TAG, "send response..."); | ||
TEST_ASSERT_EQUAL(TEARDOWN_CMD_RPL_SIZE, tinyusb_cdcacm_write_queue(TINYUSB_CDC_ACM_0, tx_buf, TEARDOWN_CMD_RPL_SIZE)); | ||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_cdcacm_write_flush(TINYUSB_CDC_ACM_0, pdMS_TO_TICKS(1000))); | ||
ESP_LOGD(TAG, "wait for terminal disconnection"); | ||
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(wait_terminal, pdMS_TO_TICKS(TEARDOWN_ATTACH_TIMEOUT_MS))); | ||
// Teardown | ||
attempts--; | ||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_cdcacm_unregister_callback(TINYUSB_CDC_ACM_0, CDC_EVENT_RX)); | ||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_cdcacm_unregister_callback(TINYUSB_CDC_ACM_0, CDC_EVENT_LINE_STATE_CHANGED)); | ||
TEST_ASSERT_EQUAL(ESP_OK, tusb_cdc_acm_deinit(TINYUSB_CDC_ACM_0)); | ||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall()); | ||
} | ||
// Remove primitives | ||
vSemaphoreDelete(wait_mount); | ||
vSemaphoreDelete(wait_command); | ||
// All attempts should be completed | ||
TEST_ASSERT_EQUAL(0, attempts); | ||
} | ||
|
||
#endif |
94 changes: 94 additions & 0 deletions
94
device/esp_tinyusb/test_apps/teardown_device/pytest_teardown_device.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
import pytest | ||
from pytest_embedded_idf.dut import IdfDut | ||
import serial | ||
from serial.tools.list_ports import comports | ||
from time import sleep, time | ||
|
||
def get_tusb_cdc_device(vid, pid): | ||
ports = comports() | ||
for cdc in ports: | ||
if cdc.vid == vid and cdc.pid == pid: | ||
return cdc.device | ||
return None | ||
|
||
def wait_for_tusb_cdc(vid, pid, timeout=30): | ||
start_time = time() | ||
while time() - start_time < timeout: | ||
sleep(0.5) # Check every 0.5 seconds | ||
tusb_cdc = get_tusb_cdc_device(vid, pid) | ||
if tusb_cdc: | ||
return tusb_cdc | ||
return None | ||
|
||
def teardown_device(key_len, amount): | ||
TUSB_VID = 0x303A # Espressif TinyUSB VID | ||
TUSB_PID = 0x4002 # Espressif TinyUSB VID | ||
|
||
# Command to send and expected response | ||
COMMAND = b'\xAA' * key_len | ||
EXPECTED_RESPONSE = b'\x55' * key_len | ||
|
||
# Number of iterations, must be equal to ITERATIONS in the test application | ||
ITERATIONS = amount | ||
|
||
for i in range(ITERATIONS): | ||
print(f"Iteration {i+1} of {ITERATIONS}") | ||
|
||
# Wait for the device to appear | ||
print("Waiting for the device to connect...") | ||
tusb_cdc = wait_for_tusb_cdc(TUSB_VID, TUSB_PID) | ||
if not tusb_cdc: | ||
print("Error: Device did not appear within the timeout period.") | ||
assert True | ||
|
||
try: | ||
# Open the serial port | ||
with serial.Serial(port=tusb_cdc, baudrate=9600, timeout=1) as cdc: | ||
print(f"Opened port: {tusb_cdc}") | ||
# Send the key command | ||
res = cdc.write(COMMAND) | ||
assert res == key_len | ||
# Get the response | ||
res = cdc.readline() | ||
assert len(res) == key_len | ||
# Explicitly close the cdc | ||
cdc.close() | ||
# Check if the response matches the expected response | ||
if res == EXPECTED_RESPONSE: | ||
print("Response matches expected value.") | ||
else: | ||
print(f"Sent {len(COMMAND)}: {COMMAND.hex().upper()}") | ||
print(f"Received {len(res)}: {res.hex().upper()}") | ||
raise Exception("Error: Response does not match expected value.") | ||
|
||
except serial.SerialException as e: | ||
print(f"Error communicating with the serial port: {e}") | ||
raise | ||
|
||
# Wait for the device to disconnect | ||
print("Waiting for the device to disconnect...") | ||
while get_tusb_cdc_device(TUSB_VID, TUSB_PID): | ||
sleep(0.1) # Poll every 0.1 second while tinyusb cdc dev still in list | ||
|
||
print("Finished all iterations.") | ||
|
||
@pytest.mark.esp32s2 | ||
@pytest.mark.esp32s3 | ||
@pytest.mark.esp32p4 | ||
@pytest.mark.usb_device | ||
def test_usb_teardown_device(dut) -> None: | ||
dut.expect_exact('Press ENTER to see the list of tests.') | ||
dut.write('[teardown]') | ||
dut.expect_exact('TinyUSB: TinyUSB Driver installed') | ||
sleep(2) # Some time for the OS to enumerate our USB device | ||
if dut.target == 'esp32p4': | ||
MPS = 512 | ||
else: | ||
MPS = 64 | ||
# On Linux, the serial port kept opened, while len==MPS, https://github.com/pyserial/pyserial/issues/753 | ||
# that can be seen via Beagle: CDC OUT transaction appears only when terminal is closed. | ||
teardown_device(MPS, 4) # Teardown tusb device | ||
|
Oops, something went wrong.