Skip to content

Commit 2954367

Browse files
committed
feature(teardown_test): Added the test_app for teardowning the cdc device
1 parent 4d5dbf7 commit 2954367

File tree

7 files changed

+351
-0
lines changed

7 files changed

+351
-0
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# The following lines of boilerplate have to be in your project's
2+
# CMakeLists in this exact order for cmake to work correctly
3+
cmake_minimum_required(VERSION 3.16)
4+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
5+
6+
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
7+
set(COMPONENTS main)
8+
9+
project(test_app_teardown_device)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
idf_component_register(SRC_DIRS .
2+
INCLUDE_DIRS .
3+
REQUIRES unity
4+
WHOLE_ARCHIVE)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
## IDF Component Manager Manifest File
2+
dependencies:
3+
espressif/esp_tinyusb:
4+
version: "*"
5+
override_path: "../../../"
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <stdio.h>
8+
#include <string.h>
9+
#include "unity.h"
10+
#include "unity_test_runner.h"
11+
#include "unity_test_utils_memory.h"
12+
13+
void app_main(void)
14+
{
15+
/*
16+
_ _ _
17+
| | (_) | |
18+
___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__
19+
/ _ \/ __| '_ \| __| | '_ \| | | | | | / __| '_ \
20+
| __/\__ \ |_) | |_| | | | | |_| | |_| \__ \ |_) |
21+
\___||___/ .__/ \__|_|_| |_|\__, |\__,_|___/_.__/
22+
| |______ __/ |
23+
|_|______| |___/
24+
_____ _____ _____ _____
25+
|_ _| ___/ ___|_ _|
26+
| | | |__ \ `--. | |
27+
| | | __| `--. \ | |
28+
| | | |___/\__/ / | |
29+
\_/ \____/\____/ \_/
30+
*/
31+
32+
printf(" _ _ _ \n");
33+
printf(" | | (_) | | \n");
34+
printf(" ___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__ \n");
35+
printf(" / _ \\/ __| '_ \\| __| | '_ \\| | | | | | / __| '_ \\ \n");
36+
printf("| __/\\__ \\ |_) | |_| | | | | |_| | |_| \\__ \\ |_) |\n");
37+
printf(" \\___||___/ .__/ \\__|_|_| |_|\\__, |\\__,_|___/_.__/ \n");
38+
printf(" | |______ __/ | \n");
39+
printf(" |_|______| |___/ \n");
40+
printf(" _____ _____ _____ _____ \n");
41+
printf("|_ _| ___/ ___|_ _| \n");
42+
printf(" | | | |__ \\ `--. | | \n");
43+
printf(" | | | __| `--. \\ | | \n");
44+
printf(" | | | |___/\\__/ / | | \n");
45+
printf(" \\_/ \\____/\\____/ \\_/ \n");
46+
47+
unity_utils_setup_heap_record(80);
48+
unity_utils_set_leak_level(128);
49+
unity_run_menu();
50+
}
51+
52+
/* setUp runs before every test */
53+
void setUp(void)
54+
{
55+
unity_utils_record_free_mem();
56+
}
57+
58+
/* tearDown runs after every test */
59+
void tearDown(void)
60+
{
61+
unity_utils_evaluate_leaks();
62+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include "soc/soc_caps.h"
8+
// #if SOC_USB_OTG_SUPPORTED
9+
10+
//
11+
#include <stdio.h>
12+
#include <string.h>
13+
//
14+
#include "freertos/FreeRTOS.h"
15+
#include "freertos/task.h"
16+
//
17+
#include "esp_system.h"
18+
#include "esp_log.h"
19+
#include "esp_err.h"
20+
//
21+
#include "unity.h"
22+
#include "tinyusb.h"
23+
#include "tusb_cdc_acm.h"
24+
25+
static const char *TEARDOWN_CMD = "teardown";
26+
// Delay between device connection, required for the Host to handle device disconnection/connection without errors
27+
#define TEARDOWN_DELAY_MS 2000
28+
#define TEARDOWN_AMOUNT 10
29+
30+
static const tusb_desc_device_t cdc_device_descriptor = {
31+
.bLength = sizeof(cdc_device_descriptor),
32+
.bDescriptorType = TUSB_DESC_DEVICE,
33+
.bcdUSB = 0x0200,
34+
.bDeviceClass = TUSB_CLASS_MISC,
35+
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
36+
.bDeviceProtocol = MISC_PROTOCOL_IAD,
37+
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
38+
.idVendor = USB_ESPRESSIF_VID,
39+
.idProduct = 0x4002,
40+
.bcdDevice = 0x0100,
41+
.iManufacturer = 0x01,
42+
.iProduct = 0x02,
43+
.iSerialNumber = 0x03,
44+
.bNumConfigurations = 0x01
45+
};
46+
47+
static const uint16_t cdc_desc_config_len = TUD_CONFIG_DESC_LEN + CFG_TUD_CDC * TUD_CDC_DESC_LEN;
48+
static const uint8_t cdc_desc_configuration[] = {
49+
TUD_CONFIG_DESCRIPTOR(1, 4, 0, cdc_desc_config_len, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
50+
TUD_CDC_DESCRIPTOR(0, 4, 0x81, 8, 0x02, 0x82, (TUD_OPT_HIGH_SPEED ? 512 : 64)),
51+
};
52+
53+
#if (TUD_OPT_HIGH_SPEED)
54+
static const tusb_desc_device_qualifier_t device_qualifier = {
55+
.bLength = sizeof(tusb_desc_device_qualifier_t),
56+
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
57+
.bcdUSB = 0x0200,
58+
.bDeviceClass = TUSB_CLASS_MISC,
59+
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
60+
.bDeviceProtocol = MISC_PROTOCOL_IAD,
61+
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
62+
.bNumConfigurations = 0x01,
63+
.bReserved = 0
64+
};
65+
#endif // TUD_OPT_HIGH_SPEED
66+
67+
static QueueHandle_t rx_queue;
68+
static uint8_t rx_buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1];
69+
70+
typedef struct {
71+
int itf; // Interface number
72+
uint8_t data[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1]; // Data buffer
73+
size_t len; // Number of bytes received
74+
} rx_pkt_t;
75+
76+
static void test_rx_enqueue_pkt(int itf, uint8_t *buf, size_t size)
77+
{
78+
rx_pkt_t rx_pkt = {
79+
.itf = itf,
80+
.len = size,
81+
};
82+
memcpy(rx_pkt.data, buf, size);
83+
xQueueSend(rx_queue, &rx_pkt, 0);
84+
}
85+
86+
static void tinyusb_cdc_rx_callback(int itf, cdcacm_event_t *event)
87+
{
88+
size_t rx_size = 0;
89+
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_cdcacm_read(itf, rx_buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size));
90+
test_rx_enqueue_pkt(itf, rx_buf, rx_size);
91+
}
92+
93+
/**
94+
* @brief TinyUSB Teardown specific testcase
95+
*/
96+
TEST_CASE("tinyusb_teardown", "[esp_tinyusb][teardown]")
97+
{
98+
rx_pkt_t rx_pkt;
99+
// Create FreeRTOS primitives
100+
rx_queue = xQueueCreate(5, sizeof(rx_pkt_t));
101+
TEST_ASSERT(rx_queue);
102+
103+
// TinyUSB driver configuration
104+
const tinyusb_config_t tusb_cfg = {
105+
.device_descriptor = &cdc_device_descriptor,
106+
.string_descriptor = NULL,
107+
.string_descriptor_count = 0,
108+
.external_phy = false,
109+
#if (TUD_OPT_HIGH_SPEED)
110+
.fs_configuration_descriptor = cdc_desc_configuration,
111+
.hs_configuration_descriptor = cdc_desc_configuration,
112+
.qualifier_descriptor = &device_qualifier,
113+
#else
114+
.configuration_descriptor = cdc_desc_configuration,
115+
#endif // TUD_OPT_HIGH_SPEED
116+
};
117+
118+
// TinyUSB ACM Driver configuration
119+
tinyusb_config_cdcacm_t acm_cfg = {
120+
.usb_dev = TINYUSB_USBDEV_0,
121+
.cdc_port = TINYUSB_CDC_ACM_0,
122+
.rx_unread_buf_sz = 64,
123+
.callback_rx = &tinyusb_cdc_rx_callback,
124+
.callback_rx_wanted_char = NULL,
125+
.callback_line_state_changed = NULL,
126+
.callback_line_coding_changed = NULL
127+
};
128+
int attempts = TEARDOWN_AMOUNT;
129+
while (1) {
130+
vTaskDelay(pdMS_TO_TICKS(TEARDOWN_DELAY_MS));
131+
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));
132+
// Init CDC 0
133+
TEST_ASSERT_FALSE(tusb_cdc_acm_initialized(TINYUSB_CDC_ACM_0));
134+
TEST_ASSERT_EQUAL(ESP_OK, tusb_cdc_acm_init(&acm_cfg));
135+
TEST_ASSERT_TRUE(tusb_cdc_acm_initialized(TINYUSB_CDC_ACM_0));
136+
137+
// Wait for the pytest poke
138+
while (1) {
139+
if (xQueueReceive(rx_queue, &rx_pkt, portMAX_DELAY)) {
140+
if (rx_pkt.len) {
141+
/* echoed back */
142+
TEST_ASSERT_EQUAL(rx_pkt.len, tinyusb_cdcacm_write_queue(rx_pkt.itf, rx_pkt.data, rx_pkt.len));
143+
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_cdcacm_write_flush(rx_pkt.itf, pdMS_TO_TICKS(100)));
144+
if (strncmp(TEARDOWN_CMD, (const char *) rx_pkt.data, rx_pkt.len) == 0) {
145+
// Wait for the host
146+
attempts--;
147+
break;
148+
}
149+
}
150+
}
151+
}
152+
vTaskDelay(pdMS_TO_TICKS(TEARDOWN_DELAY_MS));
153+
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_cdcacm_unregister_callback(TINYUSB_CDC_ACM_0, CDC_EVENT_RX));
154+
TEST_ASSERT_EQUAL(ESP_OK, tusb_cdc_acm_deinit(TINYUSB_CDC_ACM_0));
155+
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall());
156+
}
157+
// Remove primitives
158+
vQueueDelete(rx_queue);
159+
// All attempts should be completed
160+
TEST_ASSERT_EQUAL(0, attempts);
161+
}
162+
163+
// #endif
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
import pytest
5+
from pytest_embedded_idf.dut import IdfDut
6+
import serial
7+
# import serial.tools.list_ports
8+
from serial.tools.list_ports import comports
9+
# import time
10+
from time import sleep
11+
from time import time
12+
13+
14+
@pytest.mark.esp32s2
15+
@pytest.mark.esp32s3
16+
@pytest.mark.esp32p4
17+
@pytest.mark.usb_device
18+
19+
def find_tusb_cdc(vid, pid):
20+
ports = comports()
21+
for cdc in ports:
22+
if cdc.vid == vid and cdc.pid == pid:
23+
return cdc.device
24+
return None
25+
26+
def wait_for_tusb_cdc(vid, pid, timeout=30):
27+
start_time = time()
28+
while time() - start_time < timeout:
29+
tusb_cdc = find_tusb_cdc(vid, pid)
30+
if tusb_cdc:
31+
return tusb_cdc
32+
sleep(0.5) # Check every 0.5 seconds
33+
return None
34+
35+
def teardown_device(amount):
36+
TUSB_VID = 0x303A # Espressif TinyUSB VID
37+
TUSB_PID = 0x4002 # Espressif TinyUSB VID
38+
39+
# Command to send and expected response
40+
COMMAND = "teardown"
41+
EXPECTED_RESPONSE = "teardown"
42+
43+
# Number of iterations, must be equal to ITERATIONS in the test application
44+
ITERATIONS = amount
45+
46+
for i in range(ITERATIONS):
47+
print(f"Iteration {i+1} of {ITERATIONS}")
48+
49+
# Wait for the device to appear
50+
print("Waiting for the device to connect...")
51+
tusb_cdc = wait_for_tusb_cdc(TUSB_VID, TUSB_PID)
52+
if not tusb_cdc:
53+
print("Error: Device did not appear within the timeout period.")
54+
break
55+
56+
try:
57+
# Open the serial port
58+
with serial.Serial(port=tusb_cdc, baudrate=9600, timeout=1) as cdc:
59+
print(f"Opened port: {tusb_cdc}")
60+
61+
# Send the 'teardown' command
62+
cdc.write(COMMAND.encode('utf-8'))
63+
print(f"Sent command: {COMMAND.strip()}")
64+
65+
# Wait for the response
66+
res = cdc.readline().decode('utf-8').strip()
67+
print(f"Received response: {res}")
68+
69+
# Check if the response matches the expected response
70+
if res == EXPECTED_RESPONSE:
71+
print("Response matches expected value.")
72+
else:
73+
print("Error: Response does not match expected value.")
74+
75+
except tusb_cdc.SerialException as e:
76+
print(f"Error communicating with the serial port: {e}")
77+
break
78+
79+
# Wait for the device to disconnect
80+
print("Waiting for the device to disconnect...")
81+
while find_tusb_cdc(TUSB_VID, TUSB_PID):
82+
sleep(0.5) # Check every 0.5 seconds
83+
84+
print("Finished all iterations.")
85+
86+
def test_usb_teardown_device(dut) -> None:
87+
dut.expect_exact('Press ENTER to see the list of tests.')
88+
dut.write('[teardown]')
89+
dut.expect_exact('TinyUSB: TinyUSB Driver installed')
90+
sleep(2) # Some time for the OS to enumerate our USB device
91+
teardown_device(10) # Teardown tusb device 10 times
92+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Configure TinyUSB, it will be used to mock USB devices
2+
CONFIG_TINYUSB_CDC_ENABLED=y
3+
CONFIG_TINYUSB_CDC_COUNT=1
4+
5+
# Disable watchdogs, they'd get triggered during unity interactive menu
6+
CONFIG_ESP_INT_WDT=n
7+
CONFIG_ESP_TASK_WDT=n
8+
9+
# Run-time checks of Heap and Stack
10+
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
11+
CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y
12+
CONFIG_COMPILER_STACK_CHECK=y
13+
14+
CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y
15+
16+
CONFIG_COMPILER_CXX_EXCEPTIONS=y

0 commit comments

Comments
 (0)