Skip to content

Commit

Permalink
Merge pull request #11 from espressif/feature/esp_tinyusb_hs_fs_confi…
Browse files Browse the repository at this point in the history
…gurations

esp_tinyusb v1.4.4: Adding FS configuration descriptor for HS device

Espressif's additions to TinyUSB v1.4.4 release
  • Loading branch information
roma-jam authored Mar 15, 2024
2 parents 8bbbe24 + 8ea55b6 commit 4b6a798
Show file tree
Hide file tree
Showing 17 changed files with 759 additions and 114 deletions.
29 changes: 15 additions & 14 deletions device/esp_tinyusb/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,44 @@
## 1.4.4 (Unreleased)
## 1.4.4

- CDC-ACM: Remove MIN() definition if already defined
- MSC: Set EP size in configuration descriptor based on speed
- esp_tinyusb: Added HighSpeed and Qualifier device descriptors in tinyusb configuration
- CDC-ACM: Removed MIN() definition if already defined
- MSC: Fixed EP size selecting in default configuration descriptor

## 1.4.3

- esp_tinyusb: ESP32P4 HS only support
- esp_tinyusb: Added ESP32P4 support (HS only)

## 1.4.2

- MSC: Fix maximum files open
- Add uninstall function
- MSC: Fixed maximum files open
- Added uninstall function

## 1.4.0

- MSC: Fix integer overflows
- CDC-ACM: Remove intermediate RX ringbuffer
- CDC-ACM: Increase default FIFO size to 512 bytes
- CDC-ACM: Fix Virtual File System binding
- MSC: Fixed integer overflows
- CDC-ACM: Removed intermediate RX ringbuffer
- CDC-ACM: Increased default FIFO size to 512 bytes
- CDC-ACM: Fixed Virtual File System binding

## 1.3.0

- Add NCM extension
- Added NCM extension

## 1.2.1 - 1.2.2

- Minor bugfixes

## 1.2.0

- Add MSC extension for accessing SPI Flash on memory card https://github.com/espressif/idf-extra-components/commit/a8c00d7707ba4ceeb0970c023d702c7768dba3dc
- Added MSC extension for accessing SPI Flash on memory card https://github.com/espressif/idf-extra-components/commit/a8c00d7707ba4ceeb0970c023d702c7768dba3dc

## 1.1.0

- Add support for NCM, ECM/RNDIS, DFU and Bluetooth TinyUSB drivers https://github.com/espressif/idf-extra-components/commit/79f35c9b047b583080f93a63310e2ee7d82ef17b
- Added support for NCM, ECM/RNDIS, DFU and Bluetooth TinyUSB drivers https://github.com/espressif/idf-extra-components/commit/79f35c9b047b583080f93a63310e2ee7d82ef17b

## 1.0.4

- Clean up string descriptors handling https://github.com/espressif/idf-extra-components/commit/046cc4b02f524d5c7e3e56480a473cfe844dc3d6
- Cleaned up string descriptors handling https://github.com/espressif/idf-extra-components/commit/046cc4b02f524d5c7e3e56480a473cfe844dc3d6

## 1.0.2 - 1.0.3

Expand Down
4 changes: 2 additions & 2 deletions device/esp_tinyusb/Kconfig
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
menu "TinyUSB Stack"
config TINYUSB_DEBUG_LEVEL
int "TinyUSB log level (0-3)"
default 0
default 1
range 0 3
help
Specify verbosity of TinyUSB log output.
Expand All @@ -12,7 +12,7 @@ menu "TinyUSB Stack"
default TINYUSB_RHPORT_HS
help
Allows set the USB PHY Controller for TinyUSB: HS (USB OTG2.0 PHY for HighSpeed)

config TINYUSB_RHPORT_HS
bool "HS"
endchoice
Expand Down
204 changes: 179 additions & 25 deletions device/esp_tinyusb/descriptors_control.c
Original file line number Diff line number Diff line change
@@ -1,20 +1,44 @@
/*
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <string.h>
#include "esp_log.h"
#include "esp_check.h"
#include "esp_err.h"
#include "descriptors_control.h"
#include "usb_descriptors.h"

#define USB_STRING_DESCRIPTOR_ARRAY_SIZE 8 // Max 8 string descriptors for a device. LANGID, Manufacturer, Product, Serial number + 4 user defined
#define MAX_DESC_BUF_SIZE 32 // Max length of string descriptor (can be extended, USB supports lengths up to 255 bytes)

static const char *TAG = "tusb_desc";
static const tusb_desc_device_t *s_device_descriptor;
static const uint8_t *s_configuration_descriptor;
static const char *s_str_descriptor[USB_STRING_DESCRIPTOR_ARRAY_SIZE]; // Array of pointers (strings). Statically allocated, can be made dynamic in the future.

// =============================================================================
// STRUCTS
// =============================================================================

/**
* @brief Descriptor pointers for tinyusb descriptor requests callbacks
*
*/
typedef struct {
const tusb_desc_device_t *dev; /*!< Pointer to device descriptor */
union {
const uint8_t *cfg; /*!< Pointer to FullSpeed configuration descriptor when device one-speed only */
const uint8_t *fs_cfg; /*!< Pointer to FullSpeed configuration descriptor when device support HighSpeed */
};
#if (TUD_OPT_HIGH_SPEED)
const uint8_t *hs_cfg; /*!< Pointer to HighSpeed configuration descriptor */
const tusb_desc_device_qualifier_t *qualifier; /*!< Pointer to Qualifier descriptor */
uint8_t *other_speed; /*!< Pointer for other speed configuration descriptor */
#endif // TUD_OPT_HIGH_SPEED
const char *str[USB_STRING_DESCRIPTOR_ARRAY_SIZE]; /*!< Pointer to array of UTF-8 strings */
int str_count; /*!< Number of descriptors in str */
} tinyusb_descriptor_config_t;

static tinyusb_descriptor_config_t s_desc_cfg;

// =============================================================================
// CALLBACKS
Expand All @@ -28,7 +52,8 @@ static const char *s_str_descriptor[USB_STRING_DESCRIPTOR_ARRAY_SIZE]; // Array
*/
uint8_t const *tud_descriptor_device_cb(void)
{
return (uint8_t const *)s_device_descriptor;
assert(s_desc_cfg.dev);
return (uint8_t const *)s_desc_cfg.dev;
}

/**
Expand All @@ -41,8 +66,53 @@ uint8_t const *tud_descriptor_device_cb(void)
uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
{
(void)index; // Unused, this driver supports only 1 configuration
return s_configuration_descriptor;
assert(s_desc_cfg.cfg);

#if (TUD_OPT_HIGH_SPEED)
// HINT: cfg and fs_cfg are union, no need to assert(fs_cfg)
assert(s_desc_cfg.hs_cfg);
// Return configuration descriptor based on Host speed
return (TUSB_SPEED_HIGH == tud_speed_get())
? s_desc_cfg.hs_cfg
: s_desc_cfg.fs_cfg;
#else
return s_desc_cfg.cfg;
#endif // TUD_OPT_HIGH_SPEED
}

#if (TUD_OPT_HIGH_SPEED)
/**
* @brief Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
* Descriptor contents must exist long enough for transfer to complete
* If not highspeed capable stall this request
*/
uint8_t const *tud_descriptor_device_qualifier_cb(void)
{
assert(s_desc_cfg.qualifier);
return (uint8_t const *)s_desc_cfg.qualifier;
}

/**
* @brief Invoked when received GET OTHER SPEED CONFIGURATION DESCRIPTOR request
* Descriptor contents must exist long enough for transfer to complete
* Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
*/
uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index)
{
assert(s_desc_cfg.other_speed);

const uint8_t *other_speed = (TUSB_SPEED_HIGH == tud_speed_get())
? s_desc_cfg.fs_cfg
: s_desc_cfg.hs_cfg;

memcpy(s_desc_cfg.other_speed,
other_speed,
((tusb_desc_configuration_t *)other_speed)->wTotalLength);

((tusb_desc_configuration_t *)s_desc_cfg.other_speed)->bDescriptorType = TUSB_DESC_OTHER_SPEED_CONFIG;
return s_desc_cfg.other_speed;
}
#endif // TUD_OPT_HIGH_SPEED

/**
* @brief Invoked when received GET STRING DESCRIPTOR request
Expand All @@ -54,24 +124,25 @@ uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid)
{
(void) langid; // Unused, this driver supports only one language in string descriptors
assert(s_desc_cfg.str);
uint8_t chr_count;
static uint16_t _desc_str[MAX_DESC_BUF_SIZE];

if (index == 0) {
memcpy(&_desc_str[1], s_str_descriptor[0], 2);
memcpy(&_desc_str[1], s_desc_cfg.str[0], 2);
chr_count = 1;
} else {
if (index >= USB_STRING_DESCRIPTOR_ARRAY_SIZE) {
ESP_LOGW(TAG, "String index (%u) is out of bounds, check your string descriptor", index);
return NULL;
}

if (s_str_descriptor[index] == NULL) {
if (s_desc_cfg.str[index] == NULL) {
ESP_LOGW(TAG, "String index (%u) points to NULL, check your string descriptor", index);
return NULL;
}

const char *str = s_str_descriptor[index];
const char *str = s_desc_cfg.str[index];
chr_count = strnlen(str, MAX_DESC_BUF_SIZE - 1); // Buffer len - header

// Convert ASCII string into UTF-16
Expand All @@ -89,11 +160,83 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid)
// =============================================================================
// Driver functions
// =============================================================================

void tinyusb_set_descriptor(const tusb_desc_device_t *dev_desc, const char **str_desc, int str_desc_count, const uint8_t *cfg_desc)
esp_err_t tinyusb_set_descriptors(const tinyusb_config_t *config)
{
assert(dev_desc && str_desc && cfg_desc);
assert(str_desc_count <= USB_STRING_DESCRIPTOR_ARRAY_SIZE);
esp_err_t ret = ESP_FAIL;
assert(config);
const char **pstr_desc;
// Flush descriptors control struct
memset(&s_desc_cfg, 0x00, sizeof(tinyusb_descriptor_config_t));
// Parse configuration and save descriptors's pointer
// Select Device Descriptor
if (config->device_descriptor == NULL) {
ESP_LOGW(TAG, "No Device descriptor provided, using default.");
s_desc_cfg.dev = &descriptor_dev_default;
} else {
s_desc_cfg.dev = config->device_descriptor;
}

// 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)
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.");
s_desc_cfg.cfg = descriptor_fs_cfg_default;
#endif
} else {
s_desc_cfg.cfg = config->configuration_descriptor;
}

#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)
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.");
s_desc_cfg.hs_cfg = descriptor_hs_cfg_default;
#endif
} else {
s_desc_cfg.hs_cfg = config->hs_configuration_descriptor;
}

// HS and FS cfg desc should be equal length
ESP_GOTO_ON_FALSE(((tusb_desc_configuration_t *)s_desc_cfg.hs_cfg)->wTotalLength ==
((tusb_desc_configuration_t *)s_desc_cfg.fs_cfg)->wTotalLength,
ESP_ERR_INVALID_ARG, fail, TAG, "HighSpeed and FullSpeed configuration descriptors must be same length");

// Qualifier Descriptor
if (config->qualifier_descriptor == NULL) {
ESP_GOTO_ON_FALSE((s_desc_cfg.dev == &descriptor_dev_default), ESP_ERR_INVALID_ARG, fail, TAG, "Qualifier descriptor must be present (Device Descriptor not default).");
// Get default qualifier if device descriptor is default
ESP_LOGW(TAG, "No Qulifier descriptor provided, using default.");
s_desc_cfg.qualifier = &descriptor_qualifier_default;
} else {
s_desc_cfg.qualifier = config->qualifier_descriptor;
}

// Other Speed buffer allocate
s_desc_cfg.other_speed = calloc(1, ((tusb_desc_configuration_t *)s_desc_cfg.hs_cfg)->wTotalLength);
ESP_GOTO_ON_FALSE(s_desc_cfg.other_speed, ESP_ERR_NO_MEM, fail, TAG, "Other speed memory allocation error");
#endif // TUD_OPT_HIGH_SPEED

// Select String Descriptors and count them
if (config->string_descriptor == NULL) {
ESP_LOGW(TAG, "No String descriptors provided, using default.");
pstr_desc = descriptor_str_default;
while (descriptor_str_default[++s_desc_cfg.str_count] != NULL);
} else {
pstr_desc = config->string_descriptor;
s_desc_cfg.str_count = (config->string_descriptor_count != 0)
? config->string_descriptor_count
: 8; // '8' is for backward compatibility with esp_tinyusb v1.0.0. Do NOT remove!
}

ESP_GOTO_ON_FALSE(s_desc_cfg.str_count <= USB_STRING_DESCRIPTOR_ARRAY_SIZE, ESP_ERR_NOT_SUPPORTED, fail, TAG, "String descriptors exceed limit");
memcpy(s_desc_cfg.str, pstr_desc, s_desc_cfg.str_count * sizeof(pstr_desc[0]));

ESP_LOGI(TAG, "\n"
"┌─────────────────────────────────┐\n"
Expand Down Expand Up @@ -121,20 +264,31 @@ void tinyusb_set_descriptor(const tusb_desc_device_t *dev_desc, const char **str
"├───────────────────┼─────────────┤\n"
"│bNumConfigurations │ %-#10x │\n"
"└───────────────────┴─────────────┘",
dev_desc->bDeviceClass, dev_desc->bDeviceSubClass,
dev_desc->bDeviceProtocol, dev_desc->bMaxPacketSize0,
dev_desc->idVendor, dev_desc->idProduct, dev_desc->bcdDevice,
dev_desc->iManufacturer, dev_desc->iProduct, dev_desc->iSerialNumber,
dev_desc->bNumConfigurations);

// Save passed descriptors
s_device_descriptor = dev_desc;
s_configuration_descriptor = cfg_desc;
memcpy(s_str_descriptor, str_desc, str_desc_count * sizeof(str_desc[0]));
s_desc_cfg.dev->bDeviceClass, s_desc_cfg.dev->bDeviceSubClass,
s_desc_cfg.dev->bDeviceProtocol, s_desc_cfg.dev->bMaxPacketSize0,
s_desc_cfg.dev->idVendor, s_desc_cfg.dev->idProduct, s_desc_cfg.dev->bcdDevice,
s_desc_cfg.dev->iManufacturer, s_desc_cfg.dev->iProduct, s_desc_cfg.dev->iSerialNumber,
s_desc_cfg.dev->bNumConfigurations);

return ESP_OK;

fail:
#if (TUD_OPT_HIGH_SPEED)
free(s_desc_cfg.other_speed);
#endif // TUD_OPT_HIGH_SPEED
return ret;
}

void tinyusb_set_str_descriptor(const char *str, int str_idx)
{
assert(str_idx < USB_STRING_DESCRIPTOR_ARRAY_SIZE);
s_str_descriptor[str_idx] = str;
s_desc_cfg.str[str_idx] = str;
}

void tinyusb_free_descriptors(void)
{
#if (TUD_OPT_HIGH_SPEED)
assert(s_desc_cfg.other_speed);
free(s_desc_cfg.other_speed);
#endif // TUD_OPT_HIGH_SPEED
}
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.3"
version: "1.4.4"
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
17 changes: 15 additions & 2 deletions device/esp_tinyusb/include/tinyusb.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -31,7 +31,20 @@ typedef struct {
const char **string_descriptor; /*!< Pointer to array of string descriptors. If set to NULL, TinyUSB device will use a default string descriptors whose values are set in Kconfig */
int string_descriptor_count; /*!< Number of descriptors in above array */
bool external_phy; /*!< Should USB use an external PHY */
const uint8_t *configuration_descriptor; /*!< Pointer to a configuration descriptor. If set to NULL, TinyUSB device will use a default configuration descriptor whose values are set in Kconfig */
union {
struct {
const uint8_t *configuration_descriptor; /*!< Pointer to a configuration descriptor. If set to NULL, TinyUSB device will use a default configuration descriptor whose values are set in Kconfig */
};
#if (TUD_OPT_HIGH_SPEED)
struct {
const uint8_t *fs_configuration_descriptor; /*!< Pointer to a FullSpeed configuration descriptor. If set to NULL, TinyUSB device will use a default configuration descriptor whose values are set in Kconfig */
};
};
const uint8_t *hs_configuration_descriptor; /*!< Pointer to a HighSpeed configuration descriptor. If set to NULL, TinyUSB device will use a default configuration descriptor whose values are set in Kconfig */
const tusb_desc_device_qualifier_t *qualifier_descriptor; /*!< Pointer to a qualifier descriptor */
#else
};
#endif // TUD_OPT_HIGH_SPEED
bool self_powered; /*!< This is a self-powered USB device. USB VBUS must be monitored. */
int vbus_monitor_io; /*!< GPIO for VBUS monitoring. Ignored if not self_powered. */
} tinyusb_config_t;
Expand Down
Loading

0 comments on commit 4b6a798

Please sign in to comment.