Skip to content

Commit

Permalink
Merge pull request #62 from espressif/feat/tinyusb_vendor
Browse files Browse the repository at this point in the history
fix(esp_tinyusb): Provide default descriptors for Vendor specific class
  • Loading branch information
tore-espressif authored Sep 27, 2024
2 parents 98078c3 + cd6674f commit dfcb08a
Show file tree
Hide file tree
Showing 28 changed files with 315 additions and 26 deletions.
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
#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: "../../../"
File renamed without changes.
File renamed without changes.
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

0 comments on commit dfcb08a

Please sign in to comment.