Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(esp_tinyusb): Provide default descriptors for Vendor specific class #62

Merged
merged 2 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build_and_run_test_app_usb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,6 @@ jobs:
- name: Install Python packages
env:
PIP_EXTRA_INDEX_URL: "https://dl.espressif.com/pypi/"
run: pip install --only-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pyserial
run: pip install --only-binary cryptography pytest-embedded pytest-embedded-serial-esp pytest-embedded-idf pyserial pyusb
- name: Run USB Test App on target
run: pytest --target=${{ matrix.idf_target }} -m usb_host --build-dir=build_${{ matrix.idf_target }}
3 changes: 2 additions & 1 deletion device/esp_tinyusb/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## Unreleased
## 1.4.5

- CDC-ACM: Fixed memory leak at VFS unregister
- Vendor specific: Provided default configuration

## 1.4.4

Expand Down
9 changes: 9 additions & 0 deletions device/esp_tinyusb/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -269,4 +269,13 @@ menu "TinyUSB Stack"
bool "None"
endchoice
endmenu # "Network driver (ECM/NCM/RNDIS)"

menu "Vendor Specific Interface"
config TINYUSB_VENDOR_COUNT
int "TinyUSB Vendor specific interfaces count"
default 0
range 0 2
help
Setting value greater than 0 will enable TinyUSB Vendor specific feature.
endmenu # "Vendor Specific Interface"
endmenu # "TinyUSB Stack"
8 changes: 4 additions & 4 deletions device/esp_tinyusb/descriptors_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ esp_err_t tinyusb_set_descriptors(const tinyusb_config_t *config)

// Select FullSpeed configuration descriptor
if (config->configuration_descriptor == NULL) {
// Default configuration descriptor is provided only for CDC, MSC and NCM classes
#if (CFG_TUD_HID > 0 || CFG_TUD_MIDI > 0 || CFG_TUD_CUSTOM_CLASS > 0 || CFG_TUD_ECM_RNDIS > 0 || CFG_TUD_DFU > 0 || CFG_TUD_DFU_RUNTIME > 0 || CFG_TUD_BTH > 0)
// Default configuration descriptor must be provided for the following classes
tore-espressif marked this conversation as resolved.
Show resolved Hide resolved
#if (CFG_TUD_HID > 0 || CFG_TUD_MIDI > 0 || CFG_TUD_ECM_RNDIS > 0 || CFG_TUD_DFU > 0 || CFG_TUD_DFU_RUNTIME > 0 || CFG_TUD_BTH > 0)
ESP_GOTO_ON_FALSE(config->configuration_descriptor, ESP_ERR_INVALID_ARG, fail, TAG, "Configuration descriptor must be provided for this device");
#else
ESP_LOGW(TAG, "No FullSpeed configuration descriptor provided, using default.");
Expand All @@ -192,8 +192,8 @@ esp_err_t tinyusb_set_descriptors(const tinyusb_config_t *config)
#if (TUD_OPT_HIGH_SPEED)
// High Speed
if (config->hs_configuration_descriptor == NULL) {
// Default configuration descriptor is provided only for CDC, MSC and NCM classes
#if (CFG_TUD_HID > 0 || CFG_TUD_MIDI > 0 || CFG_TUD_CUSTOM_CLASS > 0 || CFG_TUD_ECM_RNDIS > 0 || CFG_TUD_DFU > 0 || CFG_TUD_DFU_RUNTIME > 0 || CFG_TUD_BTH > 0)
// Default configuration descriptor must be provided for the following classes
#if (CFG_TUD_HID > 0 || CFG_TUD_MIDI > 0 || CFG_TUD_ECM_RNDIS > 0 || CFG_TUD_DFU > 0 || CFG_TUD_DFU_RUNTIME > 0 || CFG_TUD_BTH > 0)
ESP_GOTO_ON_FALSE(config->hs_configuration_descriptor, ESP_ERR_INVALID_ARG, fail, TAG, "HighSpeed configuration descriptor must be provided for this device");
#else
ESP_LOGW(TAG, "No HighSpeed configuration descriptor provided, using default.");
Expand Down
2 changes: 1 addition & 1 deletion device/esp_tinyusb/idf_component.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
## IDF Component Manager Manifest File
description: Espressif's additions to TinyUSB
documentation: "https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/usb_device.html"
version: "1.4.4"
version: "1.4.5"
url: https://github.com/espressif/esp-usb/tree/master/device/esp_tinyusb
dependencies:
idf: '>=5.0' # IDF 4.x contains TinyUSB as submodule
Expand Down
13 changes: 6 additions & 7 deletions device/esp_tinyusb/include/tusb_config.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* SPDX-FileCopyrightText: 2019 Ha Thach (tinyusb.org),
* SPDX-FileContributor: 2020 Espressif Systems (Shanghai) CO LTD
* SPDX-FileContributor: 2020-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-License-Identifier: MIT
*
* Copyright (c) 2019 Ha Thach (tinyusb.org),
Expand Down Expand Up @@ -55,8 +55,8 @@ extern "C" {
# define CONFIG_TINYUSB_MIDI_COUNT 0
#endif

#ifndef CONFIG_TINYUSB_CUSTOM_CLASS_ENABLED
# define CONFIG_TINYUSB_CUSTOM_CLASS_ENABLED 0
#ifndef CONFIG_TINYUSB_VENDOR_COUNT
# define CONFIG_TINYUSB_VENDOR_COUNT 0
#endif

#ifndef CONFIG_TINYUSB_NET_MODE_ECM_RNDIS
Expand Down Expand Up @@ -128,9 +128,8 @@ extern "C" {
#define CFG_TUD_MIDI_TX_BUFSIZE 64

// Vendor FIFO size of TX and RX
// If not configured vendor endpoints will not be buffered
#define CFG_TUD_VENDOR_RX_BUFSIZE 64
#define CFG_TUD_VENDOR_TX_BUFSIZE 64
#define CFG_TUD_VENDOR_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
#define CFG_TUD_VENDOR_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)

// DFU macros
#define CFG_TUD_DFU_XFER_BUFSIZE CONFIG_TINYUSB_DFU_BUFSIZE
Expand All @@ -143,7 +142,7 @@ extern "C" {
#define CFG_TUD_MSC CONFIG_TINYUSB_MSC_ENABLED
#define CFG_TUD_HID CONFIG_TINYUSB_HID_COUNT
#define CFG_TUD_MIDI CONFIG_TINYUSB_MIDI_COUNT
#define CFG_TUD_CUSTOM_CLASS CONFIG_TINYUSB_CUSTOM_CLASS_ENABLED
#define CFG_TUD_VENDOR CONFIG_TINYUSB_VENDOR_COUNT
#define CFG_TUD_ECM_RNDIS CONFIG_TINYUSB_NET_MODE_ECM_RNDIS
#define CFG_TUD_NCM CONFIG_TINYUSB_NET_MODE_NCM
#define CFG_TUD_DFU CONFIG_TINYUSB_DFU_MODE_DFU
Expand Down
4 changes: 0 additions & 4 deletions device/esp_tinyusb/test_app/README.md

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ 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_usb_device_esp_tinyusb)
project(test_app_cdc_and_usb_device)
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
dependencies:
espressif/esp_tinyusb:
version: "*"
override_path: "../../"
override_path: "../../../"
9 changes: 9 additions & 0 deletions device/esp_tinyusb/test_apps/vendor/CMakeLists.txt
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_vendor_specific)
4 changes: 4 additions & 0 deletions device/esp_tinyusb/test_apps/vendor/main/CMakeLists.txt
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 device/esp_tinyusb/test_apps/vendor/main/idf_component.yml
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: "../../../"
48 changes: 48 additions & 0 deletions device/esp_tinyusb/test_apps/vendor/main/test_app_main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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"

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");

// We don't check memory leaks here because we cannot uninstall TinyUSB yet
unity_run_menu();
}
67 changes: 67 additions & 0 deletions device/esp_tinyusb/test_apps/vendor/main/test_vendor.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* 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 "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"

static const char *TAG = "vendor_test";

char buffer_in[64];
void tud_vendor_rx_cb(uint8_t itf)
{
ESP_LOGI(TAG, "tud_vendor_rx_cb(itf=%d)", itf);
int available = tud_vendor_n_available(itf);
int read = tud_vendor_n_read(itf, buffer_in, available);
ESP_LOGI(TAG, "actual read: %d. buffer message: %s", read, buffer_in);
}

// Invoked when a control transfer occurred on an interface of this class
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
{
// nothing to with DATA & ACK stage
if (stage != CONTROL_STAGE_SETUP) {
return true;
}
// stall unknown request
return false;
}

/**
* @brief TinyUSB Vendor specific testcase
*/
TEST_CASE("tinyusb_vendor", "[esp_tinyusb][vendor]")
{
// Install TinyUSB driver
const tinyusb_config_t tusb_cfg = {
.external_phy = false,
.device_descriptor = NULL,
#if (TUD_OPT_HIGH_SPEED)
.fs_configuration_descriptor = NULL,
.hs_configuration_descriptor = NULL,
.qualifier_descriptor = NULL,
#else
.configuration_descriptor = NULL,
#endif // TUD_OPT_HIGH_SPEED
};
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));


}

#endif
96 changes: 96 additions & 0 deletions device/esp_tinyusb/test_apps/vendor/pytest_vendor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0

import pytest
from pytest_embedded_idf.dut import IdfDut
import usb.core
import usb.util
from time import sleep


def find_interface_by_index(device, interface_index):
'''
Function to find the interface by index
'''
for cfg in device:
for intf in cfg:
if intf.bInterfaceNumber == interface_index:
return intf
return None


def send_data_to_intf(VID, PID, interface_index):
'''
Find a device, its interface and dual BULK endpoints
Send some data to it
'''
# Find the USB device by VID and PID
dev = usb.core.find(idVendor=VID, idProduct=PID)
if dev is None:
raise ValueError("Device not found")

# Find the interface by index
intf = find_interface_by_index(dev, interface_index)
if intf is None:
raise ValueError(f"Interface with index {interface_index} not found")

if intf:
def ep_read(len):
try:
return ep_in.read(len, 100)
except:
return None
def ep_write(buf):
try:
ep_out.write(buf, 100)
except:
pass

maximum_packet_size = 64

ep_in = usb.util.find_descriptor(intf, custom_match = \
lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_IN)

ep_out = usb.util.find_descriptor(intf, custom_match = \
lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_OUT)

#print(ep_in)
#print(ep_out)
buf = "IF{}\n".format(interface_index).encode('utf-8')
ep_write(bytes(buf))

ep_read(maximum_packet_size)
else:
print("NOT found")


@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.esp32p4
@pytest.mark.usb_device
def test_usb_device_esp_tinyusb(dut: IdfDut) -> None:
'''
Running the test locally:
1. Build the test app for your DUT
2. Connect you DUT to your test runner (local machine) with USB port and flashing port
3. Run `pytest --target esp32s3`

Important note: On Windows you must manually assign a driver the device, otherwise it will never be configured.
On Linux this is automatic

Test procedure:
1. Run the test on the DUT
2. Expect 2 Vendor specific interfaces in the system
3. Send some data to it, check log output
'''
dut.run_all_single_board_cases(group='vendor')

sleep(2) # Wait until the device is enumerated

VID = 0x303A # Replace with your device's Vendor ID
PID = 0x4040 # Replace with your device's Product ID

send_data_to_intf(VID, PID, 0)
dut.expect_exact('vendor_test: actual read: 4. buffer message: IF0')
send_data_to_intf(VID, PID, 1)
dut.expect_exact('vendor_test: actual read: 4. buffer message: IF1')
15 changes: 15 additions & 0 deletions device/esp_tinyusb/test_apps/vendor/sdkconfig.defaults
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Configure TinyUSB, it will be used to mock USB devices
CONFIG_TINYUSB_VENDOR_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
Loading
Loading