Initial project setup
This commit is contained in:
@@ -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_cdc)
|
||||
@@ -0,0 +1,4 @@
|
||||
idf_component_register(SRC_DIRS .
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES unity
|
||||
WHOLE_ARCHIVE)
|
||||
@@ -0,0 +1,5 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_tinyusb:
|
||||
version: "*"
|
||||
override_path: "../../../"
|
||||
@@ -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();
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-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 "esp_system.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#include "unity.h"
|
||||
#include "tinyusb.h"
|
||||
#include "tusb_cdc_acm.h"
|
||||
#include "vfs_tinyusb.h"
|
||||
|
||||
#define VFS_PATH "/dev/usb-cdc1"
|
||||
|
||||
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, 4, 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)),
|
||||
TUD_CDC_DESCRIPTOR(2, 4, 0x83, 8, 0x04, 0x84, (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)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TinyUSB CDC testcase
|
||||
*
|
||||
* This is not a 'standard' testcase, as it never exits. The testcase runs in a loop where it echoes back all the data received.
|
||||
*
|
||||
* - Init TinyUSB with standard CDC device and configuration descriptors
|
||||
* - Init 2 CDC-ACM interfaces
|
||||
* - Map CDC1 to Virtual File System
|
||||
* - In a loop: Read data from CDC0 and CDC1. Echo received data back
|
||||
*
|
||||
* Note: CDC0 appends 'novfs' to echoed data, so the host (test runner) can easily determine which port is which.
|
||||
*/
|
||||
TEST_CASE("tinyusb_cdc", "[esp_tinyusb][cdc]")
|
||||
{
|
||||
// Install TinyUSB driver
|
||||
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
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));
|
||||
|
||||
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 = NULL,
|
||||
.callback_line_coding_changed = NULL
|
||||
};
|
||||
|
||||
// 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));
|
||||
|
||||
// Init CDC 1
|
||||
acm_cfg.cdc_port = TINYUSB_CDC_ACM_1;
|
||||
acm_cfg.callback_rx = NULL;
|
||||
TEST_ASSERT_FALSE(tusb_cdc_acm_initialized(TINYUSB_CDC_ACM_1));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, tusb_cdc_acm_init(&acm_cfg));
|
||||
TEST_ASSERT_TRUE(tusb_cdc_acm_initialized(TINYUSB_CDC_ACM_1));
|
||||
|
||||
// Install VFS to CDC 1
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_tusb_cdc_register(TINYUSB_CDC_ACM_1, VFS_PATH));
|
||||
esp_vfs_tusb_cdc_set_rx_line_endings(ESP_LINE_ENDINGS_CRLF);
|
||||
esp_vfs_tusb_cdc_set_tx_line_endings(ESP_LINE_ENDINGS_LF);
|
||||
FILE *cdc = fopen(VFS_PATH, "r+");
|
||||
TEST_ASSERT_NOT_NULL(cdc);
|
||||
|
||||
uint8_t buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1];
|
||||
while (true) {
|
||||
size_t b = fread(buf, 1, sizeof(buf), cdc);
|
||||
if (b > 0) {
|
||||
printf("Intf VFS, RX %d bytes\n", b);
|
||||
//ESP_LOG_BUFFER_HEXDUMP("test", buf, b, ESP_LOG_INFO);
|
||||
fwrite(buf, 1, b, cdc);
|
||||
}
|
||||
vTaskDelay(1);
|
||||
|
||||
size_t rx_size = 0;
|
||||
int itf = 0;
|
||||
ESP_ERROR_CHECK(tinyusb_cdcacm_read(itf, buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size));
|
||||
if (rx_size > 0) {
|
||||
printf("Intf %d, RX %d bytes\n", itf, rx_size);
|
||||
|
||||
// Add 'novfs' to reply so the host can identify the port
|
||||
strcpy((char *)&buf[rx_size - 2], "novfs\r\n");
|
||||
tinyusb_cdcacm_write_queue(itf, buf, rx_size + sizeof("novfs") - 1);
|
||||
|
||||
tinyusb_cdcacm_write_flush(itf, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import pytest
|
||||
from pytest_embedded_idf.dut import IdfDut
|
||||
from time import sleep
|
||||
from serial import Serial, SerialException
|
||||
from serial.tools.list_ports import comports
|
||||
|
||||
|
||||
@pytest.mark.esp32s2
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.esp32p4
|
||||
@pytest.mark.usb_device
|
||||
def test_usb_device_cdc(dut) -> None:
|
||||
'''
|
||||
Running the test locally:
|
||||
1. Build the testa app for your DUT (ESP32-S2 or S3)
|
||||
2. Connect you DUT to your test runner (local machine) with USB port and flashing port
|
||||
3. Run `pytest --target esp32s3`
|
||||
|
||||
Test procedure:
|
||||
1. Run the test on the DUT
|
||||
2. Expect 2 Virtual COM Ports in the system
|
||||
3. Open both comports and send some data. Expect echoed data
|
||||
'''
|
||||
dut.expect_exact('Press ENTER to see the list of tests.')
|
||||
dut.write('[cdc]')
|
||||
dut.expect_exact('TinyUSB: TinyUSB Driver installed')
|
||||
sleep(2) # Some time for the OS to enumerate our USB device
|
||||
|
||||
# Find devices with Espressif TinyUSB VID/PID
|
||||
s = []
|
||||
ports = comports()
|
||||
|
||||
for port, _, hwid in ports:
|
||||
if '303A:4002' in hwid:
|
||||
s.append(port)
|
||||
|
||||
if len(s) != 2:
|
||||
raise Exception('TinyUSB COM port not found')
|
||||
|
||||
try:
|
||||
with Serial(s[0]) as cdc0:
|
||||
with Serial(s[1]) as cdc1:
|
||||
# Write dummy string and check for echo
|
||||
cdc0.write('text\r\n'.encode())
|
||||
res = cdc0.readline()
|
||||
assert b'text' in res
|
||||
if b'novfs' in res:
|
||||
novfs_cdc = cdc0
|
||||
vfs_cdc = cdc1
|
||||
|
||||
cdc1.write('text\r\n'.encode())
|
||||
res = cdc1.readline()
|
||||
assert b'text' in res
|
||||
if b'novfs' in res:
|
||||
novfs_cdc = cdc1
|
||||
vfs_cdc = cdc0
|
||||
|
||||
# Write more than MPS, check that the transfer is not divided
|
||||
novfs_cdc.write(bytes(100))
|
||||
dut.expect_exact("Intf 0, RX 100 bytes")
|
||||
|
||||
# Write more than RX buffer, check correct reception
|
||||
novfs_cdc.write(bytes(600))
|
||||
transfer_len1 = int(dut.expect(r'Intf 0, RX (\d+) bytes')[1].decode())
|
||||
transfer_len2 = int(dut.expect(r'Intf 0, RX (\d+) bytes')[1].decode())
|
||||
assert transfer_len1 + transfer_len2 == 600
|
||||
|
||||
# The VFS is setup for CRLF RX and LF TX
|
||||
vfs_cdc.write('text\r\n'.encode())
|
||||
res = vfs_cdc.readline()
|
||||
assert b'text\n' in res
|
||||
|
||||
return
|
||||
|
||||
except SerialException as e:
|
||||
print(f"SerialException occurred: {e}")
|
||||
raise
|
||||
|
||||
except Exception as e:
|
||||
print(f"An unexpected error occurred: {e}")
|
||||
raise
|
||||
@@ -0,0 +1,18 @@
|
||||
# Configure TinyUSB, it will be used to mock USB devices
|
||||
CONFIG_TINYUSB_MSC_ENABLED=n
|
||||
CONFIG_TINYUSB_CDC_ENABLED=y
|
||||
CONFIG_TINYUSB_CDC_COUNT=2
|
||||
CONFIG_TINYUSB_HID_COUNT=0
|
||||
|
||||
# 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
|
||||
Reference in New Issue
Block a user