Arduino core 3.3.3

This commit is contained in:
Jason2866
2025-11-05 18:33:57 +01:00
parent 2ca7267f18
commit 0afa11add6
263 changed files with 1305 additions and 5718 deletions
@@ -1,53 +0,0 @@
## 1.4.4
- 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: Added ESP32P4 support (HS only)
## 1.4.2
- MSC: Fixed maximum files open
- Added uninstall function
## 1.4.0
- 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
- Added NCM extension
## 1.2.1 - 1.2.2
- Minor bugfixes
## 1.2.0
- Added MSC extension for accessing SPI Flash on memory card https://github.com/espressif/idf-extra-components/commit/a8c00d7707ba4ceeb0970c023d702c7768dba3dc
## 1.1.0
- Added support for NCM, ECM/RNDIS, DFU and Bluetooth TinyUSB drivers https://github.com/espressif/idf-extra-components/commit/79f35c9b047b583080f93a63310e2ee7d82ef17b
## 1.0.4
- Cleaned up string descriptors handling https://github.com/espressif/idf-extra-components/commit/046cc4b02f524d5c7e3e56480a473cfe844dc3d6
## 1.0.2 - 1.0.3
- Minor bugfixes
## 1.0.1
- CDC-ACM: Return ESP_OK if there is nothing to flush https://github.com/espressif/idf-extra-components/commit/388ff32eb09aa572d98c54cb355f1912ce42707c
## 1.0.0
- Initial version based on [esp-idf v4.4.3](https://github.com/espressif/esp-idf/tree/v4.4.3/components/tinyusb)
@@ -1,53 +0,0 @@
set(srcs
"descriptors_control.c"
"tinyusb.c"
"usb_descriptors.c"
)
if(NOT CONFIG_TINYUSB_NO_DEFAULT_TASK)
list(APPEND srcs "tusb_tasks.c")
endif() # CONFIG_TINYUSB_NO_DEFAULT_TASK
if(CONFIG_TINYUSB_CDC_ENABLED)
list(APPEND srcs
"cdc.c"
"tusb_cdc_acm.c"
)
if(CONFIG_VFS_SUPPORT_IO)
list(APPEND srcs
"tusb_console.c"
"vfs_tinyusb.c"
)
endif() # CONFIG_VFS_SUPPORT_IO
endif() # CONFIG_TINYUSB_CDC_ENABLED
if(CONFIG_TINYUSB_MSC_ENABLED)
list(APPEND srcs
tusb_msc_storage.c
)
endif() # CONFIG_TINYUSB_MSC_ENABLED
if(CONFIG_TINYUSB_NET_MODE_NCM)
list(APPEND srcs
tinyusb_net.c
)
endif() # CONFIG_TINYUSB_NET_MODE_NCM
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "include_private"
PRIV_REQUIRES usb
REQUIRES fatfs vfs
)
# Determine whether tinyusb is fetched from component registry or from local path
idf_build_get_property(build_components BUILD_COMPONENTS)
if(tinyusb IN_LIST build_components)
set(tinyusb_name tinyusb) # Local component
else()
set(tinyusb_name espressif__tinyusb) # Managed component
endif()
# Pass tusb_config.h from this component to TinyUSB
idf_component_get_property(tusb_lib ${tinyusb_name} COMPONENT_LIB)
target_include_directories(${tusb_lib} PRIVATE "include")
@@ -1,272 +0,0 @@
menu "TinyUSB Stack"
config TINYUSB_DEBUG_LEVEL
int "TinyUSB log level (0-3)"
default 1
range 0 3
help
Specify verbosity of TinyUSB log output.
choice TINYUSB_RHPORT
depends on IDF_TARGET_ESP32P4
prompt "TinyUSB PHY"
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
menu "TinyUSB task configuration"
config TINYUSB_NO_DEFAULT_TASK
bool "Do not create a TinyUSB task"
default n
help
This option allows to not create the FreeRTOS task during the driver initialization.
User will have to handle TinyUSB events manually.
config TINYUSB_TASK_PRIORITY
int "TinyUSB task priority"
default 5
depends on !TINYUSB_NO_DEFAULT_TASK
help
Set the priority of the default TinyUSB main task.
config TINYUSB_TASK_STACK_SIZE
int "TinyUSB task stack size (bytes)"
default 4096
depends on !TINYUSB_NO_DEFAULT_TASK
help
Set the stack size of the default TinyUSB main task.
choice TINYUSB_TASK_AFFINITY
prompt "TinyUSB task affinity"
default TINYUSB_TASK_AFFINITY_NO_AFFINITY
depends on !TINYUSB_NO_DEFAULT_TASK
help
Allows setting TinyUSB tasks affinity, i.e. whether the task is pinned to
CPU0, pinned to CPU1, or allowed to run on any CPU.
config TINYUSB_TASK_AFFINITY_NO_AFFINITY
bool "No affinity"
config TINYUSB_TASK_AFFINITY_CPU0
bool "CPU0"
config TINYUSB_TASK_AFFINITY_CPU1
bool "CPU1"
depends on !FREERTOS_UNICORE
endchoice
config TINYUSB_TASK_AFFINITY
hex
default FREERTOS_NO_AFFINITY if TINYUSB_TASK_AFFINITY_NO_AFFINITY
default 0x0 if TINYUSB_TASK_AFFINITY_CPU0
default 0x1 if TINYUSB_TASK_AFFINITY_CPU1
config TINYUSB_INIT_IN_DEFAULT_TASK
bool "Initialize TinyUSB stack within the default TinyUSB task"
default n
depends on !TINYUSB_NO_DEFAULT_TASK
help
Run TinyUSB stack initialization just after starting the default TinyUSB task.
This is especially useful in multicore scenarios, when we need to pin the task
to a specific core and, at the same time initialize TinyUSB stack
(i.e. install interrupts) on the same core.
endmenu
menu "Descriptor configuration"
comment "You can provide your custom descriptors via tinyusb_driver_install()"
config TINYUSB_DESC_USE_ESPRESSIF_VID
bool "VID: Use Espressif's vendor ID"
default y
help
Enable this option, USB device will use Espressif's vendor ID as its VID.
This is helpful at product develop stage.
config TINYUSB_DESC_CUSTOM_VID
hex "VID: Custom vendor ID"
default 0x1234
depends on !TINYUSB_DESC_USE_ESPRESSIF_VID
help
Custom Vendor ID.
config TINYUSB_DESC_USE_DEFAULT_PID
bool "PID: Use a default PID assigned to TinyUSB"
default y
help
Default TinyUSB PID assigning uses values 0x4000...0x4007.
config TINYUSB_DESC_CUSTOM_PID
hex "PID: Custom product ID"
default 0x5678
depends on !TINYUSB_DESC_USE_DEFAULT_PID
help
Custom Product ID.
config TINYUSB_DESC_BCD_DEVICE
hex "bcdDevice"
default 0x0100
help
Version of the firmware of the USB device.
config TINYUSB_DESC_MANUFACTURER_STRING
string "Manufacturer name"
default "Espressif Systems"
help
Name of the manufacturer of the USB device.
config TINYUSB_DESC_PRODUCT_STRING
string "Product name"
default "Espressif Device"
help
Name of the USB device.
config TINYUSB_DESC_SERIAL_STRING
string "Serial string"
default "123456"
help
Serial number of the USB device.
config TINYUSB_DESC_CDC_STRING
depends on TINYUSB_CDC_ENABLED
string "CDC Device String"
default "Espressif CDC Device"
help
Name of the CDC device.
config TINYUSB_DESC_MSC_STRING
depends on TINYUSB_MSC_ENABLED
string "MSC Device String"
default "Espressif MSC Device"
help
Name of the MSC device.
endmenu # "Descriptor configuration"
menu "Massive Storage Class (MSC)"
config TINYUSB_MSC_ENABLED
bool "Enable TinyUSB MSC feature"
default n
help
Enable TinyUSB MSC feature.
config TINYUSB_MSC_BUFSIZE
depends on TINYUSB_MSC_ENABLED
int "MSC FIFO size"
default 512
range 64 10000
help
MSC FIFO size, in bytes.
config TINYUSB_MSC_MOUNT_PATH
depends on TINYUSB_MSC_ENABLED
string "Mount Path"
default "/data"
help
MSC Mount Path of storage.
endmenu # "Massive Storage Class"
menu "Communication Device Class (CDC)"
config TINYUSB_CDC_ENABLED
bool "Enable TinyUSB CDC feature"
default n
help
Enable TinyUSB CDC feature.
config TINYUSB_CDC_COUNT
int "CDC Channel Count"
default 1
range 1 2
depends on TINYUSB_CDC_ENABLED
help
Number of independent serial ports.
config TINYUSB_CDC_RX_BUFSIZE
depends on TINYUSB_CDC_ENABLED
int "CDC FIFO size of RX channel"
default 512
range 64 10000
help
CDC FIFO size of RX channel.
config TINYUSB_CDC_TX_BUFSIZE
depends on TINYUSB_CDC_ENABLED
int "CDC FIFO size of TX channel"
default 512
help
CDC FIFO size of TX channel.
endmenu # "Communication Device Class"
menu "Musical Instrument Digital Interface (MIDI)"
config TINYUSB_MIDI_COUNT
int "TinyUSB MIDI interfaces count"
default 0
range 0 2
help
Setting value greater than 0 will enable TinyUSB MIDI feature.
endmenu # "Musical Instrument Digital Interface (MIDI)"
menu "Human Interface Device Class (HID)"
config TINYUSB_HID_COUNT
int "TinyUSB HID interfaces count"
default 0
range 0 4
help
Setting value greater than 0 will enable TinyUSB HID feature.
endmenu # "HID Device Class (HID)"
menu "Device Firmware Upgrade (DFU)"
choice TINYUSB_DFU_MODE
prompt "DFU mode"
default TINYUSB_DFU_MODE_NONE
help
Select which DFU driver you want to use.
config TINYUSB_DFU_MODE_DFU
bool "DFU"
config TINYUSB_DFU_MODE_DFU_RUNTIME
bool "DFU Runtime"
config TINYUSB_DFU_MODE_NONE
bool "None"
endchoice
config TINYUSB_DFU_BUFSIZE
depends on TINYUSB_DFU_MODE_DFU
int "DFU XFER BUFFSIZE"
default 512
help
DFU XFER BUFFSIZE.
endmenu # Device Firmware Upgrade (DFU)
menu "Bluetooth Host Class (BTH)"
config TINYUSB_BTH_ENABLED
bool "Enable TinyUSB BTH feature"
default n
help
Enable TinyUSB BTH feature.
config TINYUSB_BTH_ISO_ALT_COUNT
depends on TINYUSB_BTH_ENABLED
int "BTH ISO ALT COUNT"
default 0
help
BTH ISO ALT COUNT.
endmenu # "Bluetooth Host Device Class"
menu "Network driver (ECM/NCM/RNDIS)"
choice TINYUSB_NET_MODE
prompt "Network mode"
default TINYUSB_NET_MODE_NONE
help
Select network driver you want to use.
config TINYUSB_NET_MODE_ECM_RNDIS
bool "ECM/RNDIS"
config TINYUSB_NET_MODE_NCM
bool "NCM"
config TINYUSB_NET_MODE_NONE
bool "None"
endchoice
endmenu # "Network driver (ECM/NCM/RNDIS)"
endmenu # "TinyUSB Stack"
@@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@@ -1,32 +0,0 @@
# Espressif's additions to TinyUSB
[![Component Registry](https://components.espressif.com/components/espressif/esp_tinyusb/badge.svg)](https://components.espressif.com/components/espressif/esp_tinyusb)
This component adds features to TinyUSB that help users with integrating TinyUSB with their ESP-IDF application.
It contains:
* Configuration of USB device and string descriptors
* USB Serial Device (CDC-ACM) with optional Virtual File System support
* Input and output streams through USB Serial Device. This feature is available only when Virtual File System support is enabled.
* Other USB classes (MIDI, MSC, HID…) support directly via TinyUSB
* VBUS monitoring for self-powered devices
* SPI Flash or sd-card access via MSC USB device Class.
## Documentation and examples
You can find documentation in [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/usb_device.html).
You can find examples in [ESP-IDF on GitHub](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb/device).
## How to use?
This component is distributed via [IDF component manager](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html). Just add `idf_component.yml` file to your main component with the following content:
``` yaml
## IDF Component Manager Manifest File
dependencies:
esp_tinyusb: "~1.0.0"
```
Or simply run:
```
idf.py add-dependency esp_tinyusb~1.0.0
```
@@ -1,109 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include "esp_check.h"
#include "esp_err.h"
#include "esp_log.h"
#include "tusb.h"
#include "cdc.h"
#define CDC_INTF_NUM CFG_TUD_CDC // number of cdc blocks
static esp_tusb_cdc_t *cdc_obj[CDC_INTF_NUM] = {};
static const char *TAG = "tusb_cdc";
esp_tusb_cdc_t *tinyusb_cdc_get_intf(int itf_num)
{
if (itf_num >= CDC_INTF_NUM || itf_num < 0) {
return NULL;
}
return cdc_obj[itf_num];
}
static esp_err_t cdc_obj_check(int itf, bool expected_inited, tusb_class_code_t expected_type)
{
esp_tusb_cdc_t *this_itf = tinyusb_cdc_get_intf(itf);
bool inited = (this_itf != NULL);
ESP_RETURN_ON_FALSE(expected_inited == inited, ESP_ERR_INVALID_STATE, TAG, "Wrong state of the interface. Expected state: %s", expected_inited ? "initialized" : "not initialized");
ESP_RETURN_ON_FALSE(!(inited && (expected_type != -1) && !(this_itf->type == expected_type)), ESP_ERR_INVALID_STATE, TAG, "Wrong type of the interface. Should be : 0x%x (tusb_class_code_t)", expected_type);
return ESP_OK;
}
static esp_err_t tusb_cdc_comm_init(int itf)
{
ESP_RETURN_ON_ERROR(cdc_obj_check(itf, false, -1), TAG, "cdc_obj_check failed");
cdc_obj[itf] = calloc(1, sizeof(esp_tusb_cdc_t));
if (cdc_obj[itf] != NULL) {
cdc_obj[itf]->type = TUSB_CLASS_CDC;
ESP_LOGD(TAG, "CDC Comm class initialized");
return ESP_OK;
} else {
ESP_LOGE(TAG, "CDC Comm initialization error");
return ESP_FAIL;
}
}
static esp_err_t tusb_cdc_deinit_comm(int itf)
{
ESP_RETURN_ON_ERROR(cdc_obj_check(itf, true, TUSB_CLASS_CDC), TAG, "cdc_obj_check failed");
free(cdc_obj[itf]);
cdc_obj[itf] = NULL;
return ESP_OK;
}
static esp_err_t tusb_cdc_data_init(int itf)
{
ESP_RETURN_ON_ERROR(cdc_obj_check(itf, false, TUSB_CLASS_CDC_DATA), TAG, "cdc_obj_check failed");
cdc_obj[itf] = calloc(1, sizeof(esp_tusb_cdc_t));
if (cdc_obj[itf] != NULL) {
cdc_obj[itf]->type = TUSB_CLASS_CDC_DATA;
ESP_LOGD(TAG, "CDC Data class initialized");
return ESP_OK;
} else {
ESP_LOGE(TAG, "CDC Data initialization error");
return ESP_FAIL;
}
}
static esp_err_t tusb_cdc_deinit_data(int itf)
{
ESP_RETURN_ON_ERROR(cdc_obj_check(itf, true, TUSB_CLASS_CDC_DATA), TAG, "cdc_obj_check failed");
free(cdc_obj[itf]);
cdc_obj[itf] = NULL;
return ESP_OK;
}
esp_err_t tinyusb_cdc_init(int itf, const tinyusb_config_cdc_t *cfg)
{
ESP_RETURN_ON_ERROR(cdc_obj_check(itf, false, -1), TAG, "cdc_obj_check failed");
ESP_LOGD(TAG, "Init CDC %d", itf);
if (cfg->cdc_class == TUSB_CLASS_CDC) {
ESP_RETURN_ON_ERROR(tusb_cdc_comm_init(itf), TAG, "tusb_cdc_comm_init failed");
cdc_obj[itf]->cdc_subclass.comm_subclass = cfg->cdc_subclass.comm_subclass;
} else {
ESP_RETURN_ON_ERROR(tusb_cdc_data_init(itf), TAG, "tusb_cdc_data_init failed");
cdc_obj[itf]->cdc_subclass.data_subclass = cfg->cdc_subclass.data_subclass;
}
cdc_obj[itf]->usb_dev = cfg->usb_dev;
return ESP_OK;
}
esp_err_t tinyusb_cdc_deinit(int itf)
{
ESP_RETURN_ON_ERROR(cdc_obj_check(itf, true, -1), TAG, "cdc_obj_check failed");
ESP_LOGD(TAG, "Deinit CDC %d", itf);
if (cdc_obj[itf]->type == TUSB_CLASS_CDC) {
ESP_RETURN_ON_ERROR(tusb_cdc_deinit_comm(itf), TAG, "tusb_cdc_deinit_comm failed");
} else if (cdc_obj[itf]->type == TUSB_CLASS_CDC_DATA) {
ESP_RETURN_ON_ERROR(tusb_cdc_deinit_data(itf), TAG, "tusb_cdc_deinit_data failed");
} else {
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
@@ -1,294 +0,0 @@
/*
* 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 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";
// =============================================================================
// 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
// =============================================================================
/**
* @brief Invoked when received GET DEVICE DESCRIPTOR.
* Descriptor contents must exist long enough for transfer to complete
*
* @return Pointer to device descriptor
*/
uint8_t const *tud_descriptor_device_cb(void)
{
assert(s_desc_cfg.dev);
return (uint8_t const *)s_desc_cfg.dev;
}
/**
* @brief Invoked when received GET CONFIGURATION DESCRIPTOR.
* Descriptor contents must exist long enough for transfer to complete
*
* @param[in] index Index of required configuration
* @return Pointer to configuration descriptor
*/
uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
{
(void)index; // Unused, this driver supports only 1 configuration
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
*
* @param[in] index Index of required descriptor
* @param[in] langid Language of the descriptor
* @return Pointer to UTF-16 string descriptor
*/
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_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_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_desc_cfg.str[index];
chr_count = strnlen(str, MAX_DESC_BUF_SIZE - 1); // Buffer len - header
// Convert ASCII string into UTF-16
for (uint8_t i = 0; i < chr_count; i++) {
_desc_str[1 + i] = str[i];
}
}
// First byte is length in bytes (including header), second byte is descriptor type (TUSB_DESC_STRING)
_desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2 * chr_count + 2);
return _desc_str;
}
// =============================================================================
// Driver functions
// =============================================================================
esp_err_t tinyusb_set_descriptors(const tinyusb_config_t *config)
{
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"
"│ USB Device Descriptor Summary │\n"
"├───────────────────┬─────────────┤\n"
"│bDeviceClass │ %-4u │\n"
"├───────────────────┼─────────────┤\n"
"│bDeviceSubClass │ %-4u │\n"
"├───────────────────┼─────────────┤\n"
"│bDeviceProtocol │ %-4u │\n"
"├───────────────────┼─────────────┤\n"
"│bMaxPacketSize0 │ %-4u │\n"
"├───────────────────┼─────────────┤\n"
"│idVendor │ %-#10x │\n"
"├───────────────────┼─────────────┤\n"
"│idProduct │ %-#10x │\n"
"├───────────────────┼─────────────┤\n"
"│bcdDevice │ %-#10x │\n"
"├───────────────────┼─────────────┤\n"
"│iManufacturer │ %-#10x │\n"
"├───────────────────┼─────────────┤\n"
"│iProduct │ %-#10x │\n"
"├───────────────────┼─────────────┤\n"
"│iSerialNumber │ %-#10x │\n"
"├───────────────────┼─────────────┤\n"
"│bNumConfigurations │ %-#10x │\n"
"└───────────────────┴─────────────┘",
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_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
}
@@ -1,14 +0,0 @@
dependencies:
idf:
version: '>=5.0'
tinyusb:
public: true
version: '>=0.14.2'
description: Espressif's additions to TinyUSB
documentation: https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/usb_device.html
repository: git://github.com/espressif/esp-usb.git
repository_info:
commit_sha: 4b6a798d0bed444fff48147c8dcdbbd038e92892
path: device/esp_tinyusb
url: https://github.com/espressif/esp-usb/tree/master/device/esp_tinyusb
version: 1.4.4
@@ -1,75 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdbool.h>
#include "esp_err.h"
#include "tusb.h"
#include "tinyusb_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Configuration structure of the TinyUSB core
*
* USB specification mandates self-powered devices to monitor USB VBUS to detect connection/disconnection events.
* If you want to use this feature, connected VBUS to any free GPIO through a voltage divider or voltage comparator.
* The voltage divider output should be (0.75 * Vdd) if VBUS is 4.4V (lowest valid voltage at device port).
* The comparator thresholds should be set with hysteresis: 4.35V (falling edge) and 4.75V (raising edge).
*/
typedef struct {
union {
const tusb_desc_device_t *device_descriptor; /*!< Pointer to a device descriptor. If set to NULL, the TinyUSB device will use a default device descriptor whose values are set in Kconfig */
const tusb_desc_device_t *descriptor __attribute__((deprecated)); /*!< Alias to `device_descriptor` for backward compatibility */
};
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 */
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;
/**
* @brief This is an all-in-one helper function, including:
* 1. USB device driver initialization
* 2. Descriptors preparation
* 3. TinyUSB stack initialization
* 4. Creates and start a task to handle usb events
*
* @note Don't change Custom descriptor, but if it has to be done,
* Suggest to define as follows in order to match the Interface Association Descriptor (IAD):
* bDeviceClass = TUSB_CLASS_MISC,
* bDeviceSubClass = MISC_SUBCLASS_COMMON,
*
* @param config tinyusb stack specific configuration
* @retval ESP_ERR_INVALID_ARG Install driver and tinyusb stack failed because of invalid argument
* @retval ESP_FAIL Install driver and tinyusb stack failed because of internal error
* @retval ESP_OK Install driver and tinyusb stack successfully
*/
esp_err_t tinyusb_driver_install(const tinyusb_config_t *config);
esp_err_t tinyusb_driver_uninstall(void);
#ifdef __cplusplus
}
#endif
@@ -1,99 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "tinyusb_types.h"
#include "esp_err.h"
#include "sdkconfig.h"
#if (CONFIG_TINYUSB_NET_MODE_NONE != 1)
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief On receive callback type
*/
typedef esp_err_t (*tusb_net_rx_cb_t)(void *buffer, uint16_t len, void *ctx);
/**
* @brief Free Tx buffer callback type
*/
typedef void (*tusb_net_free_tx_cb_t)(void *buffer, void *ctx);
/**
* @brief On init callback type
*/
typedef void (*tusb_net_init_cb_t)(void *ctx);
/**
* @brief ESP TinyUSB NCM driver configuration structure
*/
typedef struct {
uint8_t mac_addr[6]; /*!< MAC address. Must be 6 bytes long. */
tusb_net_rx_cb_t on_recv_callback; /*!< TinyUSB receive data callbeck */
tusb_net_free_tx_cb_t free_tx_buffer; /*!< User function for freeing the Tx buffer.
* - could be NULL, if user app is responsible for freeing the buffer
* - must be used in asynchronous send mode
* - is only called if the used tinyusb_net_send...() function returns ESP_OK
* - in sync mode means that the packet was accepted by TinyUSB
* - in async mode means that the packet was queued to be processed in TinyUSB task
*/
tusb_net_init_cb_t on_init_callback; /*!< TinyUSB init network callback */
void *user_context; /*!< User context to be passed to any of the callback */
} tinyusb_net_config_t;
/**
* @brief Initialize TinyUSB NET driver
*
* @param[in] usb_dev USB device to use
* @param[in] cfg Configuration of the driver
* @return esp_err_t
*/
esp_err_t tinyusb_net_init(tinyusb_usbdev_t usb_dev, const tinyusb_net_config_t *cfg);
/**
* @brief TinyUSB NET driver send data synchronously
*
* @note It is possible to use sync and async send interchangeably.
* This function needs some synchronization primitives, so using sync mode (even once) uses more heap
*
* @param[in] buffer USB send data
* @param[in] len Send data len
* @param[in] buff_free_arg Pointer to be passed to the free_tx_buffer() callback
* @param[in] timeout Send data len
* @return ESP_OK on success == packet has been consumed by tusb and would be eventually freed
* by free_tx_buffer() callback (if non null)
* ESP_ERR_TIMEOUT on timeout
* ESP_ERR_INVALID_STATE if tusb not initialized, ESP_ERR_NO_MEM on alloc failure
*/
esp_err_t tinyusb_net_send_sync(void *buffer, uint16_t len, void *buff_free_arg, TickType_t timeout);
/**
* @brief TinyUSB NET driver send data asynchronously
*
* @note If using asynchronous sends, you must free the buffer using free_tx_buffer() callback.
* @note It is possible to use sync and async send interchangeably.
* @note Async flavor of the send is useful when the USB stack runs faster than the caller,
* since we have no control over the transmitted packets, if they get accepted or discarded.
*
* @param[in] buffer USB send data
* @param[in] len Send data len
* @param[in] buff_free_arg Pointer to be passed to the free_tx_buffer() callback
* @return ESP_OK on success == packet has been consumed by tusb and will be freed
* by free_tx_buffer() callback (if non null)
* ESP_ERR_INVALID_STATE if tusb not initialized
*/
esp_err_t tinyusb_net_send_async(void *buffer, uint16_t len, void *buff_free_arg);
#endif // (CONFIG_TINYUSB_NET_MODE_NONE != 1)
#ifdef __cplusplus
}
#endif
@@ -1,21 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#define USB_ESPRESSIF_VID 0x303A
typedef enum {
TINYUSB_USBDEV_0,
} tinyusb_usbdev_t;
#ifdef __cplusplus
}
#endif
@@ -1,206 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "sdkconfig.h"
#include "esp_err.h"
#include "tinyusb_types.h"
#include "class/cdc/cdc.h"
#if (CONFIG_TINYUSB_CDC_ENABLED != 1)
#error "TinyUSB CDC driver must be enabled in menuconfig"
#endif
/**
* @brief CDC ports available to setup
*/
typedef enum {
TINYUSB_CDC_ACM_0 = 0x0,
TINYUSB_CDC_ACM_1,
TINYUSB_CDC_ACM_MAX
} tinyusb_cdcacm_itf_t;
/* Callbacks and events
********************************************************************* */
/**
* @brief Data provided to the input of the `callback_rx_wanted_char` callback
*/
typedef struct {
char wanted_char; /*!< Wanted character */
} cdcacm_event_rx_wanted_char_data_t;
/**
* @brief Data provided to the input of the `callback_line_state_changed` callback
*/
typedef struct {
bool dtr; /*!< Data Terminal Ready (DTR) line state */
bool rts; /*!< Request To Send (RTS) line state */
} cdcacm_event_line_state_changed_data_t;
/**
* @brief Data provided to the input of the `line_coding_changed` callback
*/
typedef struct {
cdc_line_coding_t const *p_line_coding; /*!< New line coding value */
} cdcacm_event_line_coding_changed_data_t;
/**
* @brief Types of CDC ACM events
*/
typedef enum {
CDC_EVENT_RX,
CDC_EVENT_RX_WANTED_CHAR,
CDC_EVENT_LINE_STATE_CHANGED,
CDC_EVENT_LINE_CODING_CHANGED
} cdcacm_event_type_t;
/**
* @brief Describes an event passing to the input of a callbacks
*/
typedef struct {
cdcacm_event_type_t type; /*!< Event type */
union {
cdcacm_event_rx_wanted_char_data_t rx_wanted_char_data; /*!< Data input of the `callback_rx_wanted_char` callback */
cdcacm_event_line_state_changed_data_t line_state_changed_data; /*!< Data input of the `callback_line_state_changed` callback */
cdcacm_event_line_coding_changed_data_t line_coding_changed_data; /*!< Data input of the `line_coding_changed` callback */
};
} cdcacm_event_t;
/**
* @brief CDC-ACM callback type
*/
typedef void(*tusb_cdcacm_callback_t)(int itf, cdcacm_event_t *event);
/*********************************************************************** Callbacks and events*/
/* Other structs
********************************************************************* */
/**
* @brief Configuration structure for CDC-ACM
*/
typedef struct {
tinyusb_usbdev_t usb_dev; /*!< Usb device to set up */
tinyusb_cdcacm_itf_t cdc_port; /*!< CDC port */
size_t rx_unread_buf_sz __attribute__((deprecated("This parameter is not used any more. Configure RX buffer in menuconfig.")));
tusb_cdcacm_callback_t callback_rx; /*!< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback */
tusb_cdcacm_callback_t callback_rx_wanted_char; /*!< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback */
tusb_cdcacm_callback_t callback_line_state_changed; /*!< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback */
tusb_cdcacm_callback_t callback_line_coding_changed; /*!< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback */
} tinyusb_config_cdcacm_t;
/*********************************************************************** Other structs*/
/* Public functions
********************************************************************* */
/**
* @brief Initialize CDC ACM. Initialization will be finished with
* the `tud_cdc_line_state_cb` callback
*
* @param[in] cfg Configuration structure
* @return esp_err_t
*/
esp_err_t tusb_cdc_acm_init(const tinyusb_config_cdcacm_t *cfg);
/**
* @brief De-initialize CDC ACM.
*
* @param[in] itf Index of CDC interface
* @return esp_err_t
*/
esp_err_t tusb_cdc_acm_deinit(int itf);
/**
* @brief Register a callback invoking on CDC event. If the callback had been
* already registered, it will be overwritten
*
* @param[in] itf Index of CDC interface
* @param[in] event_type Type of registered event for a callback
* @param[in] callback Callback function
* @return esp_err_t - ESP_OK or ESP_ERR_INVALID_ARG
*/
esp_err_t tinyusb_cdcacm_register_callback(tinyusb_cdcacm_itf_t itf,
cdcacm_event_type_t event_type,
tusb_cdcacm_callback_t callback);
/**
* @brief Unregister a callback invoking on CDC event
*
* @param[in] itf Index of CDC interface
* @param[in] event_type Type of registered event for a callback
* @return esp_err_t - ESP_OK or ESP_ERR_INVALID_ARG
*/
esp_err_t tinyusb_cdcacm_unregister_callback(tinyusb_cdcacm_itf_t itf, cdcacm_event_type_t event_type);
/**
* @brief Sent one character to a write buffer
*
* @param[in] itf Index of CDC interface
* @param[in] ch Character to send
* @return size_t - amount of queued bytes
*/
size_t tinyusb_cdcacm_write_queue_char(tinyusb_cdcacm_itf_t itf, char ch);
/**
* @brief Write data to write buffer
*
* @param[in] itf Index of CDC interface
* @param[in] in_buf Data
* @param[in] in_size Data size in bytes
* @return size_t - amount of queued bytes
*/
size_t tinyusb_cdcacm_write_queue(tinyusb_cdcacm_itf_t itf, const uint8_t *in_buf, size_t in_size);
/**
* @brief Flush data in write buffer of CDC interface
*
* Use `tinyusb_cdcacm_write_queue` to add data to the buffer
*
* WARNING! TinyUSB can block output Endpoint for several RX callbacks, after will do additional flush
* after the each transfer. That can leads to the situation when you requested a flush, but it will fail until
* one of the next callbacks ends.
* SO USING OF THE FLUSH WITH TIMEOUTS IN CALLBACKS IS NOT RECOMMENDED - YOU CAN GET A LOCK FOR THE TIMEOUT
*
* @param[in] itf Index of CDC interface
* @param[in] timeout_ticks Transfer timeout. Set to zero for non-blocking mode
* @return - ESP_OK All data flushed
* - ESP_ERR_TIMEOUT Time out occurred in blocking mode
* - ESP_NOT_FINISHED The transfer is still in progress in non-blocking mode
*/
esp_err_t tinyusb_cdcacm_write_flush(tinyusb_cdcacm_itf_t itf, uint32_t timeout_ticks);
/**
* @brief Receive data from CDC interface
*
* @param[in] itf Index of CDC interface
* @param[out] out_buf Data buffer
* @param[in] out_buf_sz Data buffer size in bytes
* @param[out] rx_data_size Number of bytes written to out_buf
* @return esp_err_t ESP_OK, ESP_FAIL or ESP_ERR_INVALID_STATE
*/
esp_err_t tinyusb_cdcacm_read(tinyusb_cdcacm_itf_t itf, uint8_t *out_buf, size_t out_buf_sz, size_t *rx_data_size);
/**
* @brief Check if the CDC interface is initialized
*
* @param[in] itf Index of CDC interface
* @return - true Initialized
* - false Not Initialized
*/
bool tusb_cdc_acm_initialized(tinyusb_cdcacm_itf_t itf);
/*********************************************************************** Public functions*/
#ifdef __cplusplus
}
#endif
@@ -1,155 +0,0 @@
/*
* SPDX-FileCopyrightText: 2019 Ha Thach (tinyusb.org),
* SPDX-FileContributor: 2020 Espressif Systems (Shanghai) CO LTD
* SPDX-License-Identifier: MIT
*
* Copyright (c) 2019 Ha Thach (tinyusb.org),
* Additions Copyright (c) 2020, Espressif Systems (Shanghai) PTE LTD
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#pragma once
#include "tusb_option.h"
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef CONFIG_TINYUSB_CDC_ENABLED
# define CONFIG_TINYUSB_CDC_ENABLED 0
#endif
#ifndef CONFIG_TINYUSB_CDC_COUNT
# define CONFIG_TINYUSB_CDC_COUNT 0
#endif
#ifndef CONFIG_TINYUSB_MSC_ENABLED
# define CONFIG_TINYUSB_MSC_ENABLED 0
#endif
#ifndef CONFIG_TINYUSB_HID_COUNT
# define CONFIG_TINYUSB_HID_COUNT 0
#endif
#ifndef CONFIG_TINYUSB_MIDI_COUNT
# define CONFIG_TINYUSB_MIDI_COUNT 0
#endif
#ifndef CONFIG_TINYUSB_CUSTOM_CLASS_ENABLED
# define CONFIG_TINYUSB_CUSTOM_CLASS_ENABLED 0
#endif
#ifndef CONFIG_TINYUSB_NET_MODE_ECM_RNDIS
# define CONFIG_TINYUSB_NET_MODE_ECM_RNDIS 0
#endif
#ifndef CONFIG_TINYUSB_NET_MODE_NCM
# define CONFIG_TINYUSB_NET_MODE_NCM 0
#endif
#ifndef CONFIG_TINYUSB_DFU_MODE_DFU
# define CONFIG_TINYUSB_DFU_MODE_DFU 0
#endif
#ifndef CONFIG_TINYUSB_DFU_MODE_DFU_RUNTIME
# define CONFIG_TINYUSB_DFU_MODE_DFU_RUNTIME 0
#endif
#ifndef CONFIG_TINYUSB_BTH_ENABLED
# define CONFIG_TINYUSB_BTH_ENABLED 0
# define CONFIG_TINYUSB_BTH_ISO_ALT_COUNT 0
#endif
#ifndef CONFIG_TINYUSB_DEBUG_LEVEL
# define CONFIG_TINYUSB_DEBUG_LEVEL 0
#endif
#ifdef CONFIG_TINYUSB_RHPORT_HS
# define CFG_TUSB_RHPORT1_MODE OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED
#else
# define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED
#endif
#define CFG_TUSB_OS OPT_OS_FREERTOS
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
* e.g
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
*/
#ifndef CFG_TUSB_MEM_SECTION
# define CFG_TUSB_MEM_SECTION
#endif
#ifndef CFG_TUSB_MEM_ALIGN
# define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
#endif
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
// Debug Level
#define CFG_TUSB_DEBUG CONFIG_TINYUSB_DEBUG_LEVEL
// CDC FIFO size of TX and RX
#define CFG_TUD_CDC_RX_BUFSIZE CONFIG_TINYUSB_CDC_RX_BUFSIZE
#define CFG_TUD_CDC_TX_BUFSIZE CONFIG_TINYUSB_CDC_TX_BUFSIZE
// MSC Buffer size of Device Mass storage
#define CFG_TUD_MSC_BUFSIZE CONFIG_TINYUSB_MSC_BUFSIZE
// MIDI macros
#define CFG_TUD_MIDI_EP_BUFSIZE 64
#define CFG_TUD_MIDI_EPSIZE CFG_TUD_MIDI_EP_BUFSIZE
#define CFG_TUD_MIDI_RX_BUFSIZE 64
#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
// DFU macros
#define CFG_TUD_DFU_XFER_BUFSIZE CONFIG_TINYUSB_DFU_BUFSIZE
// Number of BTH ISO alternatives
#define CFG_TUD_BTH_ISO_ALT_COUNT CONFIG_TINYUSB_BTH_ISO_ALT_COUNT
// Enabled device class driver
#define CFG_TUD_CDC CONFIG_TINYUSB_CDC_COUNT
#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_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
#define CFG_TUD_DFU_RUNTIME CONFIG_TINYUSB_DFU_MODE_DFU_RUNTIME
#define CFG_TUD_BTH CONFIG_TINYUSB_BTH_ENABLED
#ifdef __cplusplus
}
#endif
@@ -1,33 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_err.h"
/**
* @brief Redirect output to the USB serial
* @param cdc_intf - interface number of TinyUSB's CDC
*
* @return esp_err_t - ESP_OK, ESP_FAIL or an error code
*/
esp_err_t esp_tusb_init_console(int cdc_intf);
/**
* @brief Switch log to the default output
* @param cdc_intf - interface number of TinyUSB's CDC
*
* @return esp_err_t
*/
esp_err_t esp_tusb_deinit_console(int cdc_intf);
#ifdef __cplusplus
}
#endif
@@ -1,188 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include "esp_err.h"
#include "wear_levelling.h"
#include "esp_vfs_fat.h"
#if SOC_SDMMC_HOST_SUPPORTED
#include "driver/sdmmc_host.h"
#endif
/**
* @brief Data provided to the input of the `callback_mount_changed` and `callback_premount_changed` callback
*/
typedef struct {
bool is_mounted; /*!< Flag if storage is mounted or not */
} tinyusb_msc_event_mount_changed_data_t;
/**
* @brief Types of MSC events
*/
typedef enum {
TINYUSB_MSC_EVENT_MOUNT_CHANGED, /*!< Event type AFTER mount/unmount operation is successfully finished */
TINYUSB_MSC_EVENT_PREMOUNT_CHANGED /*!< Event type BEFORE mount/unmount operation is started */
} tinyusb_msc_event_type_t;
/**
* @brief Describes an event passing to the input of a callbacks
*/
typedef struct {
tinyusb_msc_event_type_t type; /*!< Event type */
union {
tinyusb_msc_event_mount_changed_data_t mount_changed_data; /*!< Data input of the callback */
};
} tinyusb_msc_event_t;
/**
* @brief MSC callback that is delivered whenever a specific event occurs.
*/
typedef void(*tusb_msc_callback_t)(tinyusb_msc_event_t *event);
#if SOC_SDMMC_HOST_SUPPORTED
/**
* @brief Configuration structure for sdmmc initialization
*
* User configurable parameters that are used while
* initializing the sdmmc media.
*/
typedef struct {
sdmmc_card_t *card; /*!< Pointer to sdmmc card configuration structure */
tusb_msc_callback_t callback_mount_changed; /*!< Pointer to the function callback that will be delivered AFTER mount/unmount operation is successfully finished */
tusb_msc_callback_t callback_premount_changed; /*!< Pointer to the function callback that will be delivered BEFORE mount/unmount operation is started */
const esp_vfs_fat_mount_config_t mount_config; /*!< FATFS mount config */
} tinyusb_msc_sdmmc_config_t;
#endif
/**
* @brief Configuration structure for spiflash initialization
*
* User configurable parameters that are used while
* initializing the SPI Flash media.
*/
typedef struct {
wl_handle_t wl_handle; /*!< Pointer to spiflash wera-levelling handle */
tusb_msc_callback_t callback_mount_changed; /*!< Pointer to the function callback that will be delivered AFTER mount/unmount operation is successfully finished */
tusb_msc_callback_t callback_premount_changed; /*!< Pointer to the function callback that will be delivered BEFORE mount/unmount operation is started */
const esp_vfs_fat_mount_config_t mount_config; /*!< FATFS mount config */
} tinyusb_msc_spiflash_config_t;
/**
* @brief Register storage type spiflash with tinyusb driver
*
* @param config pointer to the spiflash configuration
* @return esp_err_t
* - ESP_OK, if success;
* - ESP_ERR_NO_MEM, if there was no memory to allocate storage components;
*/
esp_err_t tinyusb_msc_storage_init_spiflash(const tinyusb_msc_spiflash_config_t *config);
#if SOC_SDMMC_HOST_SUPPORTED
/**
* @brief Register storage type sd-card with tinyusb driver
*
* @param config pointer to the sd card configuration
* @return esp_err_t
* - ESP_OK, if success;
* - ESP_ERR_NO_MEM, if there was no memory to allocate storage components;
*/
esp_err_t tinyusb_msc_storage_init_sdmmc(const tinyusb_msc_sdmmc_config_t *config);
#endif
/**
* @brief Deregister storage with tinyusb driver and frees the memory
*
*/
void tinyusb_msc_storage_deinit(void);
/**
* @brief Register a callback invoking on MSC event. If the callback had been
* already registered, it will be overwritten
*
* @param event_type - type of registered event for a callback
* @param callback - callback function
* @return esp_err_t - ESP_OK or ESP_ERR_INVALID_ARG
*/
esp_err_t tinyusb_msc_register_callback(tinyusb_msc_event_type_t event_type,
tusb_msc_callback_t callback);
/**
* @brief Unregister a callback invoking on MSC event.
*
* @param event_type - type of registered event for a callback
* @return esp_err_t - ESP_OK or ESP_ERR_INVALID_ARG
*/
esp_err_t tinyusb_msc_unregister_callback(tinyusb_msc_event_type_t event_type);
/**
* @brief Mount the storage partition locally on the firmware application.
*
* Get the available drive number. Register spi flash partition.
* Connect POSIX and C standard library IO function with FATFS.
* Mounts the partition.
* This API is used by the firmware application. If the storage partition is
* mounted by this API, host (PC) can't access the storage via MSC.
* When this function is called from the tinyusb callback functions, care must be taken
* so as to make sure that user callbacks must be completed within a
* specific time. Otherwise, MSC device may re-appear again on Host.
*
* @param base_path path prefix where FATFS should be registered
* @return esp_err_t
* - ESP_OK, if success;
* - ESP_ERR_NOT_FOUND if the maximum count of volumes is already mounted
* - ESP_ERR_NO_MEM if not enough memory or too many VFSes already registered;
*/
esp_err_t tinyusb_msc_storage_mount(const char *base_path);
/**
* @brief Unmount the storage partition from the firmware application.
*
* Unmount the partition. Unregister diskio driver.
* Unregister the SPI flash partition.
* Finally, Un-register FATFS from VFS.
* After this function is called, storage device can be seen (recognized) by host (PC).
* When this function is called from the tinyusb callback functions, care must be taken
* so as to make sure that user callbacks must be completed within a specific time.
* Otherwise, MSC device may not appear on Host.
*
* @return esp_err_t
* - ESP_OK on success
* - ESP_ERR_INVALID_STATE if FATFS is not registered in VFS
*/
esp_err_t tinyusb_msc_storage_unmount(void);
/**
* @brief Get number of sectors in storage media
*
* @return usable size, in bytes
*/
uint32_t tinyusb_msc_storage_get_sector_count(void);
/**
* @brief Get sector size of storage media
*
* @return sector count
*/
uint32_t tinyusb_msc_storage_get_sector_size(void);
/**
* @brief Get status if storage media is exposed over USB to Host
*
* @return bool
* - true, if the storage media is exposed to Host
* - false, if the stoarge media is mounted on application (not exposed to Host)
*/
bool tinyusb_msc_storage_in_use_by_usb_host(void);
#ifdef __cplusplus
}
#endif
@@ -1,39 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief This helper function creates and starts a task which wraps `tud_task()`.
*
* The wrapper function basically wraps tud_task and some log.
* Default parameters: stack size and priority as configured, argument = NULL, not pinned to any core.
* If you have more requirements for this task, you can create your own task which calls tud_task as the last step.
*
* @retval ESP_OK run tinyusb main task successfully
* @retval ESP_FAIL run tinyusb main task failed of internal error or initialization within the task failed when TINYUSB_INIT_IN_DEFAULT_TASK=y
* @retval ESP_FAIL initialization within the task failed if CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK is enabled
* @retval ESP_ERR_INVALID_STATE tinyusb main task has been created before
*/
esp_err_t tusb_run_task(void);
/**
* @brief This helper function stops and destroys the task created by `tusb_run_task()`
*
* @retval ESP_OK stop and destroy tinyusb main task successfully
* @retval ESP_ERR_INVALID_STATE tinyusb main task hasn't been created yet
*/
esp_err_t tusb_stop_task(void);
#ifdef __cplusplus
}
#endif
@@ -1,69 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "esp_err.h"
#include "esp_vfs_common.h" // For esp_line_endings_t definitions
#ifdef __cplusplus
extern "C" {
#endif
#define VFS_TUSB_MAX_PATH 16
#define VFS_TUSB_PATH_DEFAULT "/dev/tusb_cdc"
/**
* @brief Register TinyUSB CDC at VFS with path
*
* Know limitation:
* In case there are multiple CDC interfaces in the system, only one of them can be registered to VFS.
*
* @param[in] cdc_intf Interface number of TinyUSB's CDC
* @param[in] path Path where the CDC will be registered, `/dev/tusb_cdc` will be used if left NULL.
* @return esp_err_t ESP_OK or ESP_FAIL
*/
esp_err_t esp_vfs_tusb_cdc_register(int cdc_intf, char const *path);
/**
* @brief Unregister TinyUSB CDC from VFS
*
* @param[in] path Path where the CDC will be unregistered if NULL will be used `/dev/tusb_cdc`
* @return esp_err_t ESP_OK or ESP_FAIL
*/
esp_err_t esp_vfs_tusb_cdc_unregister(char const *path);
/**
* @brief Set the line endings to sent
*
* This specifies the conversion between newlines ('\n', LF) on stdout and line
* endings sent:
*
* - ESP_LINE_ENDINGS_CRLF: convert LF to CRLF
* - ESP_LINE_ENDINGS_CR: convert LF to CR
* - ESP_LINE_ENDINGS_LF: no modification
*
* @param[in] mode line endings to send
*/
void esp_vfs_tusb_cdc_set_tx_line_endings(esp_line_endings_t mode);
/**
* @brief Set the line endings expected to be received
*
* This specifies the conversion between line endings received and
* newlines ('\n', LF) passed into stdin:
*
* - ESP_LINE_ENDINGS_CRLF: convert CRLF to LF
* - ESP_LINE_ENDINGS_CR: convert CR to LF
* - ESP_LINE_ENDINGS_LF: no modification
*
* @param[in] mode line endings expected
*/
void esp_vfs_tusb_cdc_set_rx_line_endings(esp_line_endings_t mode);
#ifdef __cplusplus
}
#endif
@@ -1,82 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/timers.h"
#include "tusb.h"
#include "tinyusb_types.h"
/* CDC classification
********************************************************************* */
typedef enum {
TINYUSB_CDC_DATA = 0x00,
} cdc_data_sublcass_type_t; // CDC120 specification
/* Note:other classification is represented in the file components\tinyusb\tinyusb\src\class\cdc\cdc.h */
/*********************************************************************** CDC classification*/
/* Structs
********************************************************************* */
typedef struct {
tinyusb_usbdev_t usb_dev; /*!< USB device to set up */
tusb_class_code_t cdc_class; /*!< CDC device class : Communications or Data device */
union {
cdc_comm_sublcass_type_t comm_subclass; /*!< Communications device subclasses: ACM, ECM, etc. */
cdc_data_sublcass_type_t data_subclass; /*!< Data device has only one subclass.*/
} cdc_subclass; /*!< CDC device subclass according to Class Definitions for Communications Devices the CDC v.1.20 */
} tinyusb_config_cdc_t; /*!< Main configuration structure of a CDC device */
typedef struct {
tinyusb_usbdev_t usb_dev; /*!< USB device used for the instance */
tusb_class_code_t type;
union {
cdc_comm_sublcass_type_t comm_subclass; /*!< Communications device subclasses: ACM, ECM, etc. */
cdc_data_sublcass_type_t data_subclass; /*!< Data device has only one subclass.*/
} cdc_subclass; /*!< CDC device subclass according to Class Definitions for Communications Devices the CDC v.1.20 */
void *subclass_obj; /*!< Dynamically allocated subclass specific object */
} esp_tusb_cdc_t;
/*********************************************************************** Structs*/
/* Functions
********************************************************************* */
/**
* @brief Initializing CDC basic object
* @param itf - number of a CDC object
* @param cfg - CDC configuration structure
*
* @return esp_err_t ESP_OK or ESP_FAIL
*/
esp_err_t tinyusb_cdc_init(int itf, const tinyusb_config_cdc_t *cfg);
/**
* @brief De-initializing CDC. Clean its objects
* @param itf - number of a CDC object
* @return esp_err_t ESP_OK, ESP_ERR_INVALID_ARG, ESP_ERR_INVALID_STATE
*
*/
esp_err_t tinyusb_cdc_deinit(int itf);
/**
* @brief Return interface of a CDC device
*
* @param itf_num
* @return esp_tusb_cdc_t* pointer to the interface or (NULL) on error
*/
esp_tusb_cdc_t *tinyusb_cdc_get_intf(int itf_num);
/*********************************************************************** Functions*/
#ifdef __cplusplus
}
#endif
@@ -1,47 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "tinyusb.h"
#ifdef __cplusplus
extern "C" {
#endif
#define USB_STRING_DESCRIPTOR_ARRAY_SIZE 8 // Max 8 string descriptors for a device. LANGID, Manufacturer, Product, Serial number + 4 user defined
/**
* @brief Parse tinyusb configuration and prepare the device configuration pointer list to configure tinyusb driver
*
* @attention All descriptors passed to this function must exist for the duration of USB device lifetime
*
* @param[in] config tinyusb stack specific configuration
* @retval ESP_ERR_INVALID_ARG Default configuration descriptor is provided only for CDC, MSC and NCM classes
* @retval ESP_ERR_NO_MEM Memory allocation error
* @retval ESP_OK Descriptors configured without error
*/
esp_err_t tinyusb_set_descriptors(const tinyusb_config_t *config);
/**
* @brief Set specific string descriptor
*
* @attention The descriptor passed to this function must exist for the duration of USB device lifetime
*
* @param[in] str UTF-8 string
* @param[in] str_idx String descriptor index
*/
void tinyusb_set_str_descriptor(const char *str, int str_idx);
/**
* @brief Free memory allocated during tinyusb_set_descriptors
*
*/
void tinyusb_free_descriptors(void);
#ifdef __cplusplus
}
#endif
@@ -1,62 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "tusb.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Device descriptor generated from Kconfig
*
* This descriptor is used by default.
* The user can provide their own device descriptor via tinyusb_driver_install() call
*/
extern const tusb_desc_device_t descriptor_dev_default;
#if (TUD_OPT_HIGH_SPEED)
/**
* @brief Qualifier Device descriptor generated from Kconfig
*
* This descriptor is used by default.
* The user can provide their own descriptor via tinyusb_driver_install() call
*/
extern const tusb_desc_device_qualifier_t descriptor_qualifier_default;
#endif // TUD_OPT_HIGH_SPEED
/**
* @brief Array of string descriptors generated from Kconfig
*
* This descriptor is used by default.
* The user can provide their own descriptor via tinyusb_driver_install() call
*/
extern const char *descriptor_str_default[];
/**
* @brief FullSpeed configuration descriptor generated from Kconfig
* This descriptor is used by default.
* The user can provide their own FullSpeed configuration descriptor via tinyusb_driver_install() call
*/
extern const uint8_t descriptor_fs_cfg_default[];
#if (TUD_OPT_HIGH_SPEED)
/**
* @brief HighSpeed Configuration descriptor generated from Kconfig
*
* This descriptor is used by default.
* The user can provide their own HighSpeed configuration descriptor via tinyusb_driver_install() call
*/
extern const uint8_t descriptor_hs_cfg_default[];
#endif // TUD_OPT_HIGH_SPEED
uint8_t tusb_get_mac_string_id(void);
#ifdef __cplusplus
}
#endif
@@ -1,2 +0,0 @@
supplier: 'Organization: Espressif Systems (Shanghai) CO LTD'
originator: 'Organization: Espressif Systems (Shanghai) CO LTD'
@@ -1,8 +0,0 @@
cmake_minimum_required(VERSION 3.22)
project(libusb_test
LANGUAGES C
)
add_executable(libusb_test libusb_test.c)
target_link_libraries(libusb_test -lusb-1.0)
@@ -1,121 +0,0 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <assert.h>
#include <stdio.h>
#include <libusb-1.0/libusb.h>
#define TINYUSB_VENDOR 0x303A
#define TINYUSB_PRODUCT 0x4002
#define DESC_TYPE_DEVICE_QUALIFIER 0x06
#define DESC_TYOE_OTHER_SPEED_CONFIG 0x07
// Buffer for descriptor data
unsigned char buffer[512] = { 0 };
// USB Other Speed Configuration Descriptor
typedef struct __attribute__ ((packed))
{
uint8_t bLength ; ///< Size of descriptor
uint8_t bDescriptorType ; ///< Other_speed_Configuration Type
uint16_t wTotalLength ; ///< Total length of data returned
uint8_t bNumInterfaces ; ///< Number of interfaces supported by this speed configuration
uint8_t bConfigurationValue ; ///< Value to use to select configuration
uint8_t iConfiguration ; ///< Index of string descriptor
uint8_t bmAttributes ; ///< Same as Configuration descriptor
uint8_t bMaxPower ; ///< Same as Configuration descriptor
} desc_other_speed_t;
// USB Device Qualifier Descriptor
typedef struct __attribute__ ((packed))
{
uint8_t bLength ; ///< Size of descriptor
uint8_t bDescriptorType ; ///< Device Qualifier Type
uint16_t bcdUSB ; ///< USB specification version number (e.g., 0200H for V2.00)
uint8_t bDeviceClass ; ///< Class Code
uint8_t bDeviceSubClass ; ///< SubClass Code
uint8_t bDeviceProtocol ; ///< Protocol Code
uint8_t bMaxPacketSize0 ; ///< Maximum packet size for other speed
uint8_t bNumConfigurations ; ///< Number of Other-speed Configurations
uint8_t bReserved ; ///< Reserved for future use, must be zero
} desc_device_qualifier_t;
// printf helpers
static void _print_device_qulifier_desc(unsigned char *buffer, int length);
static void _print_other_speed_desc(unsigned char *buffer, int length);
//
// MAIN
//
int main()
{
libusb_context *context = NULL;
int rc = 0;
rc = libusb_init(&context);
assert(rc == 0);
libusb_device_handle *dev_handle = libusb_open_device_with_vid_pid(context,
TINYUSB_VENDOR,
TINYUSB_PRODUCT);
if (dev_handle != NULL) {
printf("TinyUSB Device has been found\n");
// Test Qualifier Descriprtor
// 1. Get Qualifier Descriptor
// 2. print descriptor data
rc = libusb_get_descriptor(dev_handle, DESC_TYPE_DEVICE_QUALIFIER, 0, buffer, 512);
_print_device_qulifier_desc(buffer, rc);
// Test Other Speed Descriptor
// 1. Get Other Speed Descriptor
// 2. print descriptor data
rc = libusb_get_descriptor(dev_handle, DESC_TYOE_OTHER_SPEED_CONFIG, 0, buffer, 512);
_print_other_speed_desc(buffer, rc);
libusb_close(dev_handle);
} else {
printf("TinyUSB Device has NOT been found\n");
}
libusb_exit(context);
}
// =============================================================================
static void _print_device_qulifier_desc(unsigned char *buffer, int length)
{
assert(buffer);
desc_device_qualifier_t *qualifier_desc = (desc_device_qualifier_t *) buffer;
printf("========= Device Qualifier ========== \n");
printf("\t bLength: %d \n", qualifier_desc->bLength);
printf("\t bDescriptorType: %d (%#x)\n", qualifier_desc->bDescriptorType, qualifier_desc->bDescriptorType);
printf("\t bcdUSB: %d (%#x) \n", qualifier_desc->bcdUSB, qualifier_desc->bcdUSB);
printf("\t bDeviceClass: %d (%#x) \n", qualifier_desc->bDeviceClass, qualifier_desc->bDeviceClass);
printf("\t bDeviceSubClass: %d \n", qualifier_desc->bDeviceSubClass);
printf("\t bDeviceProtocol: %d \n", qualifier_desc->bDeviceProtocol);
printf("\t bMaxPacketSize0: %d \n", qualifier_desc->bMaxPacketSize0);
printf("\t bNumConfigurations: %d \n", qualifier_desc->bNumConfigurations);
}
static void _print_other_speed_desc(unsigned char *buffer, int length)
{
assert(buffer);
desc_other_speed_t *other_speed = (desc_other_speed_t *) buffer;
printf("============ Other Speed ============ \n");
printf("\t bLength: %d \n", other_speed->bLength);
printf("\t bDescriptorType: %d (%#x) \n", other_speed->bDescriptorType, other_speed->bDescriptorType);
printf("\t wTotalLength: %d \n", other_speed->wTotalLength);
printf("\t bNumInterfaces: %d \n", other_speed->bNumInterfaces);
printf("\t bConfigurationValue: %d \n", other_speed->bConfigurationValue);
printf("\t iConfiguration: %d \n", other_speed->iConfiguration);
printf("\t bmAttributes: %d (%#x) \n", other_speed->bmAttributes, other_speed->bmAttributes);
printf("\t bMaxPower: %d (%#x) \n", other_speed->bMaxPower, other_speed->bMaxPower);
}
@@ -1,11 +0,0 @@
# 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)
# Set the components to include the tests for.
set(EXTRA_COMPONENT_DIRS
../
)
project(test_app_usb_device_esp_tinyusb)
@@ -1,4 +0,0 @@
| Supported Targets | ESP32-S2 | ESP32-S3 |
| ----------------- | -------- | -------- |
# USB: esp_tinyusb test application
@@ -1,6 +0,0 @@
include($ENV{IDF_PATH}/tools/cmake/version.cmake)
idf_component_register(SRC_DIRS .
INCLUDE_DIRS .
REQUIRES unity esp_tinyusb
WHOLE_ARCHIVE)
@@ -1,7 +0,0 @@
## IDF Component Manager Manifest File
dependencies:
espressif/esp_tinyusb:
version: "*"
override_path: "../../"
rules:
- if: "idf_version >= 5.0"
@@ -1,57 +0,0 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "unity.h"
#include "esp_heap_caps.h"
static size_t before_free_8bit;
static size_t before_free_32bit;
#define TEST_MEMORY_LEAK_THRESHOLD (-530)
static void check_leak(size_t before_free, size_t after_free, const char *type)
{
ssize_t delta = after_free - before_free;
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
}
void app_main(void)
{
// ____ ___ ___________________ __ __
// | | \/ _____/\______ \ _/ |_ ____ _______/ |_
// | | /\_____ \ | | _/ \ __\/ __ \ / ___/\ __\.
// | | / / \ | | \ | | \ ___/ \___ \ | |
// |______/ /_______ / |______ / |__| \___ >____ > |__|
// \/ \/ \/ \/
printf(" ____ ___ ___________________ __ __ \r\n");
printf("| | \\/ _____/\\______ \\ _/ |_ ____ _______/ |_ \r\n");
printf("| | /\\_____ \\ | | _/ \\ __\\/ __ \\ / ___/\\ __\\\r\n");
printf("| | / / \\ | | \\ | | \\ ___/ \\___ \\ | | \r\n");
printf("|______/ /_______ / |______ / |__| \\___ >____ > |__| \r\n");
printf(" \\/ \\/ \\/ \\/ \r\n");
UNITY_BEGIN();
unity_run_menu();
UNITY_END();
}
/* setUp runs before every test */
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
/* tearDown runs after every test */
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
check_leak(before_free_8bit, after_free_8bit, "8BIT");
check_leak(before_free_32bit, after_free_32bit, "32BIT");
}
@@ -1,103 +0,0 @@
/*
* 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 "driver/gpio.h"
#include "esp_rom_gpio.h"
#include "soc/gpio_sig_map.h"
#include "unity.h"
#include "tinyusb.h"
#include "tusb_tasks.h"
#define DEVICE_DETACH_TEST_ROUNDS 10
#define DEVICE_DETACH_ROUND_DELAY_MS 1000
#if (CONFIG_IDF_TARGET_ESP32P4)
#define USB_SRP_BVALID_IN_IDX USB_SRP_BVALID_PAD_IN_IDX
#endif // CONFIG_IDF_TARGET_ESP32P4
/* TinyUSB descriptors
********************************************************************* */
#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN)
static unsigned int dev_mounted = 0;
static unsigned int dev_umounted = 0;
static uint8_t const test_configuration_descriptor[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, 0, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_SELF_POWERED | TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
};
static const tusb_desc_device_t test_device_descriptor = {
.bLength = sizeof(test_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 = 0x303A, // This is Espressif VID. This needs to be changed according to Users / Customers
.idProduct = 0x4002,
.bcdDevice = 0x100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
void test_bvalid_sig_mount_cb(void)
{
dev_mounted++;
}
void test_bvalid_sig_umount_cb(void)
{
dev_umounted++;
}
TEST_CASE("bvalid_signal", "[esp_tinyusb][usb_device]")
{
unsigned int rounds = DEVICE_DETACH_TEST_ROUNDS;
// Install TinyUSB driver
const tinyusb_config_t tusb_cfg = {
.external_phy = false,
.device_descriptor = &test_device_descriptor,
.configuration_descriptor = test_configuration_descriptor,
};
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));
dev_mounted = 0;
dev_umounted = 0;
while (rounds--) {
// LOW to emulate disconnect USB device
esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ZERO_INPUT, USB_SRP_BVALID_IN_IDX, false);
vTaskDelay(pdMS_TO_TICKS(DEVICE_DETACH_ROUND_DELAY_MS));
// HIGH to emulate connect USB device
esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, USB_SRP_BVALID_IN_IDX, false);
vTaskDelay(pdMS_TO_TICKS(DEVICE_DETACH_ROUND_DELAY_MS));
}
// Verify
TEST_ASSERT_EQUAL(dev_umounted, dev_mounted);
TEST_ASSERT_EQUAL(DEVICE_DETACH_TEST_ROUNDS, dev_mounted);
// Cleanup
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall());
TEST_ASSERT_EQUAL(ESP_OK, tusb_stop_task());
}
#endif // SOC_USB_OTG_SUPPORTED
@@ -1,18 +0,0 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void test_bvalid_sig_mount_cb(void);
void test_bvalid_sig_umount_cb(void);
#ifdef __cplusplus
}
#endif //__cplusplus
@@ -1,235 +0,0 @@
/*
* 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 "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "esp_err.h"
#include "driver/gpio.h"
#include "esp_rom_gpio.h"
#include "soc/gpio_sig_map.h"
#include "unity.h"
#include "tinyusb.h"
#include "tusb_tasks.h"
#define DEVICE_MOUNT_TIMEOUT_MS 5000
// ========================= TinyUSB descriptors ===============================
#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN)
static uint8_t const test_fs_configuration_descriptor[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, 0, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_SELF_POWERED | TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
};
#if (TUD_OPT_HIGH_SPEED)
static uint8_t const test_hs_configuration_descriptor[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, 0, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_SELF_POWERED | TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
};
#endif // TUD_OPT_HIGH_SPEED
static const tusb_desc_device_t test_device_descriptor = {
.bLength = sizeof(test_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 = 0x303A, // This is Espressif VID. This needs to be changed according to Users / Customers
.idProduct = 0x4002,
.bcdDevice = 0x100,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
// ========================== Private logic ====================================
SemaphoreHandle_t desc_config_device_mounted = NULL;
static bool __test_prep(void)
{
desc_config_device_mounted = xSemaphoreCreateBinary();
return (desc_config_device_mounted != NULL);
}
static esp_err_t __test_wait_conn(void)
{
if (!desc_config_device_mounted) {
return ESP_ERR_INVALID_STATE;
}
return ( xSemaphoreTake(desc_config_device_mounted, pdMS_TO_TICKS(DEVICE_MOUNT_TIMEOUT_MS))
? ESP_OK
: ESP_ERR_TIMEOUT );
}
static void __test_conn(void)
{
if (desc_config_device_mounted) {
xSemaphoreGive(desc_config_device_mounted);
}
}
static void __test_free(void)
{
if (desc_config_device_mounted) {
vSemaphoreDelete(desc_config_device_mounted);
}
}
// ========================== Callbacks ========================================
// Invoked when device is mounted
void test_descriptors_config_mount_cb(void)
{
__test_conn();
}
void test_descriptors_config_umount_cb(void)
{
}
TEST_CASE("descriptors_config_all_default", "[esp_tinyusb][usb_device]")
{
TEST_ASSERT_EQUAL(true, __test_prep());
// Install TinyUSB driver
const tinyusb_config_t tusb_cfg = {
.external_phy = false,
.device_descriptor = NULL,
.configuration_descriptor = NULL,
#if (TUD_OPT_HIGH_SPEED)
.hs_configuration_descriptor = NULL,
#endif // TUD_OPT_HIGH_SPEED
};
// Install
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));
// Wait for mounted callback
TEST_ASSERT_EQUAL(ESP_OK, __test_wait_conn());
// Cleanup
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall());
TEST_ASSERT_EQUAL(ESP_OK, tusb_stop_task());
__test_free();
}
TEST_CASE("descriptors_config_device", "[esp_tinyusb][usb_device]")
{
TEST_ASSERT_EQUAL(true, __test_prep());
// Install TinyUSB driver
const tinyusb_config_t tusb_cfg = {
.external_phy = false,
.device_descriptor = &test_device_descriptor,
.configuration_descriptor = NULL,
#if (TUD_OPT_HIGH_SPEED)
.hs_configuration_descriptor = NULL,
#endif // TUD_OPT_HIGH_SPEED
};
// Install
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));
// Wait for mounted callback
TEST_ASSERT_EQUAL(ESP_OK, __test_wait_conn());
// Cleanup
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall());
TEST_ASSERT_EQUAL(ESP_OK, tusb_stop_task());
__test_free();
}
TEST_CASE("descriptors_config_device_and_config", "[esp_tinyusb][usb_device]")
{
TEST_ASSERT_EQUAL(true, __test_prep());
// Install TinyUSB driver
const tinyusb_config_t tusb_cfg = {
.external_phy = false,
.device_descriptor = &test_device_descriptor,
.configuration_descriptor = test_fs_configuration_descriptor,
#if (TUD_OPT_HIGH_SPEED)
.hs_configuration_descriptor = NULL,
#endif // TUD_OPT_HIGH_SPEED
};
// Install
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));
// Wait for mounted callback
TEST_ASSERT_EQUAL(ESP_OK, __test_wait_conn());
// Cleanup
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall());
TEST_ASSERT_EQUAL(ESP_OK, tusb_stop_task());
__test_free();
}
#if (TUD_OPT_HIGH_SPEED)
TEST_CASE("descriptors_config_device_and_fs_config_only", "[esp_tinyusb][usb_device]")
{
TEST_ASSERT_EQUAL(true, __test_prep());
// Install TinyUSB driver
const tinyusb_config_t tusb_cfg = {
.external_phy = false,
.device_descriptor = &test_device_descriptor,
.configuration_descriptor = test_fs_configuration_descriptor,
.hs_configuration_descriptor = NULL,
};
// Install
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));
// Wait for mounted callback
TEST_ASSERT_EQUAL(ESP_OK, __test_wait_conn());
// Cleanup
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall());
TEST_ASSERT_EQUAL(ESP_OK, tusb_stop_task());
__test_free();
}
TEST_CASE("descriptors_config_device_and_hs_config_only", "[esp_tinyusb][usb_device]")
{
TEST_ASSERT_EQUAL(true, __test_prep());
// Install TinyUSB driver
const tinyusb_config_t tusb_cfg = {
.external_phy = false,
.device_descriptor = &test_device_descriptor,
.configuration_descriptor = NULL,
.hs_configuration_descriptor = test_hs_configuration_descriptor,
};
// Install
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));
// Wait for mounted callback
TEST_ASSERT_EQUAL(ESP_OK, __test_wait_conn());
// Cleanup
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall());
TEST_ASSERT_EQUAL(ESP_OK, tusb_stop_task());
__test_free();
}
TEST_CASE("descriptors_config_all_configured", "[esp_tinyusb][usb_device]")
{
TEST_ASSERT_EQUAL(true, __test_prep());
// Install TinyUSB driver
const tinyusb_config_t tusb_cfg = {
.external_phy = false,
.device_descriptor = &test_device_descriptor,
.fs_configuration_descriptor = test_fs_configuration_descriptor,
.hs_configuration_descriptor = test_hs_configuration_descriptor,
};
// Install
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));
// Wait for mounted callback
TEST_ASSERT_EQUAL(ESP_OK, __test_wait_conn());
// Cleanup
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall());
TEST_ASSERT_EQUAL(ESP_OK, tusb_stop_task());
__test_free();
}
#endif // TUD_OPT_HIGH_SPEED
#endif // SOC_USB_OTG_SUPPORTED
@@ -1,18 +0,0 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
void test_descriptors_config_mount_cb(void);
void test_descriptors_config_umount_cb(void);
#ifdef __cplusplus
}
#endif //__cplusplus
@@ -1,132 +0,0 @@
/*
* 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"
// idf_component_register(WHOLE_ARCHIVE) backward compatibility to IDF_v4.4
void linker_hook(void) {};
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, 64),
TUD_CDC_DESCRIPTOR(2, 4, 0x83, 8, 0x04, 0x84, 64),
};
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]")
{
// Install TinyUSB driver
const tinyusb_config_t tusb_cfg = {
.external_phy = false,
.device_descriptor = &cdc_device_descriptor,
.configuration_descriptor = cdc_desc_configuration
};
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
@@ -1,28 +0,0 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include "tinyusb.h"
#include "tusb_tasks.h"
#include "test_bvalid_sig.h"
#include "test_descriptors_config.h"
// Invoked when device is mounted
void tud_mount_cb(void)
{
printf("%s\n", __FUNCTION__);
test_bvalid_sig_mount_cb();
test_descriptors_config_mount_cb();
}
// Invoked when device is unmounted
void tud_umount_cb(void)
{
printf("%s\n", __FUNCTION__);
test_bvalid_sig_umount_cb();
test_descriptors_config_umount_cb();
}
@@ -1,73 +0,0 @@
# 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
from serial.tools.list_ports import comports
@pytest.mark.esp32s2
@pytest.mark.esp32s3
@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('[esp_tinyusb]')
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')
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
@@ -1,12 +0,0 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import pytest
from pytest_embedded_idf.dut import IdfDut
@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.usb_device
def test_usb_device_esp_tinyusb(dut: IdfDut) -> None:
dut.run_all_single_board_cases(group='usb_device')
@@ -1,19 +0,0 @@
# Configure TinyUSB, it will be used to mock USB devices
CONFIG_TINYUSB=y
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
@@ -1,73 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_err.h"
#include "esp_private/periph_ctrl.h"
#include "esp_private/usb_phy.h"
#include "soc/usb_pins.h"
#include "tinyusb.h"
#include "descriptors_control.h"
#include "tusb.h"
#include "tusb_tasks.h"
const static char *TAG = "TinyUSB";
static usb_phy_handle_t phy_hdl;
esp_err_t tinyusb_driver_install(const tinyusb_config_t *config)
{
ESP_RETURN_ON_FALSE(config, ESP_ERR_INVALID_ARG, TAG, "Config can't be NULL");
// Configure USB PHY
usb_phy_config_t phy_conf = {
.controller = USB_PHY_CTRL_OTG,
.otg_mode = USB_OTG_MODE_DEVICE,
};
// External PHY IOs config
usb_phy_ext_io_conf_t ext_io_conf = {
.vp_io_num = USBPHY_VP_NUM,
.vm_io_num = USBPHY_VM_NUM,
.rcv_io_num = USBPHY_RCV_NUM,
.oen_io_num = USBPHY_OEN_NUM,
.vpo_io_num = USBPHY_VPO_NUM,
.vmo_io_num = USBPHY_VMO_NUM,
};
if (config->external_phy) {
phy_conf.target = USB_PHY_TARGET_EXT;
phy_conf.ext_io_conf = &ext_io_conf;
} else {
phy_conf.target = USB_PHY_TARGET_INT;
}
// OTG IOs config
const usb_phy_otg_io_conf_t otg_io_conf = USB_PHY_SELF_POWERED_DEVICE(config->vbus_monitor_io);
if (config->self_powered) {
phy_conf.otg_io_conf = &otg_io_conf;
}
ESP_RETURN_ON_ERROR(usb_new_phy(&phy_conf, &phy_hdl), TAG, "Install USB PHY failed");
// Descriptors config
ESP_RETURN_ON_ERROR(tinyusb_set_descriptors(config), TAG, "Descriptors config failed");
// Init
#if !CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK
ESP_RETURN_ON_FALSE(tusb_init(), ESP_FAIL, TAG, "Init TinyUSB stack failed");
#endif
#if !CONFIG_TINYUSB_NO_DEFAULT_TASK
ESP_RETURN_ON_ERROR(tusb_run_task(), TAG, "Run TinyUSB task failed");
#endif
ESP_LOGI(TAG, "TinyUSB Driver installed");
return ESP_OK;
}
esp_err_t tinyusb_driver_uninstall()
{
tinyusb_free_descriptors();
return usb_del_phy(phy_hdl);
}
@@ -1,174 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "tinyusb_net.h"
#include "descriptors_control.h"
#include "usb_descriptors.h"
#include "device/usbd_pvt.h"
#include "esp_check.h"
#define MAC_ADDR_LEN 6
typedef struct packet {
void *buffer;
void *buff_free_arg;
uint16_t len;
esp_err_t result;
} packet_t;
struct tinyusb_net_handle {
bool initialized;
SemaphoreHandle_t buffer_sema;
EventGroupHandle_t tx_flags;
tusb_net_rx_cb_t rx_cb;
tusb_net_free_tx_cb_t tx_buff_free_cb;
tusb_net_init_cb_t init_cb;
char mac_str[2 * MAC_ADDR_LEN + 1];
void *ctx;
packet_t *packet_to_send;
};
const static int TX_FINISHED_BIT = BIT0;
static struct tinyusb_net_handle s_net_obj = { };
static const char *TAG = "tusb_net";
static void do_send_sync(void *ctx)
{
(void) ctx;
if (xSemaphoreTake(s_net_obj.buffer_sema, 0) != pdTRUE || s_net_obj.packet_to_send == NULL) {
return;
}
packet_t *packet = s_net_obj.packet_to_send;
if (tud_network_can_xmit(packet->len)) {
tud_network_xmit(packet, packet->len);
packet->result = ESP_OK;
} else {
packet->result = ESP_FAIL;
}
xSemaphoreGive(s_net_obj.buffer_sema);
xEventGroupSetBits(s_net_obj.tx_flags, TX_FINISHED_BIT);
}
static void do_send_async(void *ctx)
{
packet_t *packet = ctx;
if (tud_network_can_xmit(packet->len)) {
tud_network_xmit(packet, packet->len);
} else if (s_net_obj.tx_buff_free_cb) {
ESP_LOGW(TAG, "Packet cannot be accepted on USB interface, dropping");
s_net_obj.tx_buff_free_cb(packet->buff_free_arg, s_net_obj.ctx);
}
free(packet);
}
esp_err_t tinyusb_net_send_async(void *buffer, uint16_t len, void *buff_free_arg)
{
if (!tud_ready()) {
return ESP_ERR_INVALID_STATE;
}
packet_t *packet = calloc(1, sizeof(packet_t));
packet->len = len;
packet->buffer = buffer;
packet->buff_free_arg = buff_free_arg;
ESP_RETURN_ON_FALSE(packet, ESP_ERR_NO_MEM, TAG, "Failed to allocate packet to send");
usbd_defer_func(do_send_async, packet, false);
return ESP_OK;
}
esp_err_t tinyusb_net_send_sync(void *buffer, uint16_t len, void *buff_free_arg, TickType_t timeout)
{
if (!tud_ready()) {
return ESP_ERR_INVALID_STATE;
}
// Lazy init the flags and semaphores, as they might not be needed (if async approach is used)
if (!s_net_obj.tx_flags) {
s_net_obj.tx_flags = xEventGroupCreate();
ESP_RETURN_ON_FALSE(s_net_obj.tx_flags, ESP_ERR_NO_MEM, TAG, "Failed to allocate event flags");
}
if (!s_net_obj.buffer_sema) {
s_net_obj.buffer_sema = xSemaphoreCreateBinary();
ESP_RETURN_ON_FALSE(s_net_obj.buffer_sema, ESP_ERR_NO_MEM, TAG, "Failed to allocate buffer semaphore");
}
packet_t packet = {
.buffer = buffer,
.len = len,
.buff_free_arg = buff_free_arg
};
s_net_obj.packet_to_send = &packet;
xSemaphoreGive(s_net_obj.buffer_sema); // now the packet is ready, let's mark it available to tusb send
// to execute the send function in tinyUSB task context
usbd_defer_func(do_send_sync, NULL, false); // arg=NULL -> sync send, we keep the packet inside the object
// wait wor completion with defined timeout
EventBits_t bits = xEventGroupWaitBits(s_net_obj.tx_flags, TX_FINISHED_BIT, pdTRUE, pdTRUE, timeout);
xSemaphoreTake(s_net_obj.buffer_sema, portMAX_DELAY); // if tusb sending already started, we have wait before ditching the packet
s_net_obj.packet_to_send = NULL; // invalidate the argument
if (bits & TX_FINISHED_BIT) { // If transaction finished, return error code
return packet.result;
}
return ESP_ERR_TIMEOUT;
}
esp_err_t tinyusb_net_init(tinyusb_usbdev_t usb_dev, const tinyusb_net_config_t *cfg)
{
(void) usb_dev;
ESP_RETURN_ON_FALSE(s_net_obj.initialized == false, ESP_ERR_INVALID_STATE, TAG, "TinyUSB Net class is already initialized");
// the semaphore and event flags are initialized only if needed
s_net_obj.rx_cb = cfg->on_recv_callback;
s_net_obj.init_cb = cfg->on_init_callback;
s_net_obj.tx_buff_free_cb = cfg->free_tx_buffer;
s_net_obj.ctx = cfg->user_context;
const uint8_t *mac = &cfg->mac_addr[0];
snprintf(s_net_obj.mac_str, sizeof(s_net_obj.mac_str), "%02X%02X%02X%02X%02X%02X",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
uint8_t mac_id = tusb_get_mac_string_id();
// Pass it to Descriptor control module
tinyusb_set_str_descriptor(s_net_obj.mac_str, mac_id);
s_net_obj.initialized = true;
return ESP_OK;
}
//--------------------------------------------------------------------+
// tinyusb callbacks
//--------------------------------------------------------------------+
bool tud_network_recv_cb(const uint8_t *src, uint16_t size)
{
if (s_net_obj.rx_cb) {
s_net_obj.rx_cb((void *)src, size, s_net_obj.ctx);
}
tud_network_recv_renew();
return true;
}
uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg)
{
packet_t *packet = ref;
uint16_t len = arg;
memcpy(dst, packet->buffer, packet->len);
if (s_net_obj.tx_buff_free_cb) {
s_net_obj.tx_buff_free_cb(packet->buff_free_arg, s_net_obj.ctx);
}
return len;
}
void tud_network_init_cb(void)
{
if (s_net_obj.init_cb) {
s_net_obj.init_cb(s_net_obj.ctx);
}
}
@@ -1,346 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include "esp_check.h"
#include "esp_err.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "tusb.h"
#include "tusb_cdc_acm.h"
#include "cdc.h"
#include "sdkconfig.h"
#ifndef MIN
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#endif
// CDC-ACM spinlock
static portMUX_TYPE cdc_acm_lock = portMUX_INITIALIZER_UNLOCKED;
#define CDC_ACM_ENTER_CRITICAL() portENTER_CRITICAL(&cdc_acm_lock)
#define CDC_ACM_EXIT_CRITICAL() portEXIT_CRITICAL(&cdc_acm_lock)
typedef struct {
tusb_cdcacm_callback_t callback_rx;
tusb_cdcacm_callback_t callback_rx_wanted_char;
tusb_cdcacm_callback_t callback_line_state_changed;
tusb_cdcacm_callback_t callback_line_coding_changed;
} esp_tusb_cdcacm_t; /*!< CDC_ACM object */
static const char *TAG = "tusb_cdc_acm";
static inline esp_tusb_cdcacm_t *get_acm(tinyusb_cdcacm_itf_t itf)
{
esp_tusb_cdc_t *cdc_inst = tinyusb_cdc_get_intf(itf);
if (cdc_inst == NULL) {
return (esp_tusb_cdcacm_t *)NULL;
}
return (esp_tusb_cdcacm_t *)(cdc_inst->subclass_obj);
}
/* TinyUSB callbacks
********************************************************************* */
/* Invoked by cdc interface when line state changed e.g connected/disconnected */
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (dtr && rts) { // connected
if (acm != NULL) {
ESP_LOGV(TAG, "Host connected to CDC no.%d.", itf);
} else {
ESP_LOGW(TAG, "Host is connected to CDC no.%d, but it is not initialized. Initialize it using `tinyusb_cdc_init`.", itf);
return;
}
} else { // disconnected
if (acm != NULL) {
ESP_LOGV(TAG, "Serial device is ready to connect to CDC no.%d", itf);
} else {
return;
}
}
if (acm) {
CDC_ACM_ENTER_CRITICAL();
tusb_cdcacm_callback_t cb = acm->callback_line_state_changed;
CDC_ACM_EXIT_CRITICAL();
if (cb) {
cdcacm_event_t event = {
.type = CDC_EVENT_LINE_STATE_CHANGED,
.line_state_changed_data = {
.dtr = dtr,
.rts = rts
}
};
cb(itf, &event);
}
}
}
/* Invoked when CDC interface received data from host */
void tud_cdc_rx_cb(uint8_t itf)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (acm) {
CDC_ACM_ENTER_CRITICAL();
tusb_cdcacm_callback_t cb = acm->callback_rx;
CDC_ACM_EXIT_CRITICAL();
if (cb) {
cdcacm_event_t event = {
.type = CDC_EVENT_RX
};
cb(itf, &event);
}
}
}
// Invoked when line coding is change via SET_LINE_CODING
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const *p_line_coding)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (acm) {
CDC_ACM_ENTER_CRITICAL();
tusb_cdcacm_callback_t cb = acm->callback_line_coding_changed;
CDC_ACM_EXIT_CRITICAL();
if (cb) {
cdcacm_event_t event = {
.type = CDC_EVENT_LINE_CODING_CHANGED,
.line_coding_changed_data = {
.p_line_coding = p_line_coding,
}
};
cb(itf, &event);
}
}
}
// Invoked when received `wanted_char`
void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (acm) {
CDC_ACM_ENTER_CRITICAL();
tusb_cdcacm_callback_t cb = acm->callback_rx_wanted_char;
CDC_ACM_EXIT_CRITICAL();
if (cb) {
cdcacm_event_t event = {
.type = CDC_EVENT_RX_WANTED_CHAR,
.rx_wanted_char_data = {
.wanted_char = wanted_char,
}
};
cb(itf, &event);
}
}
}
esp_err_t tinyusb_cdcacm_register_callback(tinyusb_cdcacm_itf_t itf,
cdcacm_event_type_t event_type,
tusb_cdcacm_callback_t callback)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (acm) {
switch (event_type) {
case CDC_EVENT_RX:
CDC_ACM_ENTER_CRITICAL();
acm->callback_rx = callback;
CDC_ACM_EXIT_CRITICAL();
return ESP_OK;
case CDC_EVENT_RX_WANTED_CHAR:
CDC_ACM_ENTER_CRITICAL();
acm->callback_rx_wanted_char = callback;
CDC_ACM_EXIT_CRITICAL();
return ESP_OK;
case CDC_EVENT_LINE_STATE_CHANGED:
CDC_ACM_ENTER_CRITICAL();
acm->callback_line_state_changed = callback;
CDC_ACM_EXIT_CRITICAL();
return ESP_OK;
case CDC_EVENT_LINE_CODING_CHANGED:
CDC_ACM_ENTER_CRITICAL();
acm->callback_line_coding_changed = callback;
CDC_ACM_EXIT_CRITICAL();
return ESP_OK;
default:
ESP_LOGE(TAG, "Wrong event type");
return ESP_ERR_INVALID_ARG;
}
} else {
ESP_LOGE(TAG, "CDC-ACM is not initialized");
return ESP_ERR_INVALID_STATE;
}
}
esp_err_t tinyusb_cdcacm_unregister_callback(tinyusb_cdcacm_itf_t itf,
cdcacm_event_type_t event_type)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (!acm) {
ESP_LOGE(TAG, "Interface is not initialized. Use `tinyusb_cdc_init` for initialization");
return ESP_ERR_INVALID_STATE;
}
switch (event_type) {
case CDC_EVENT_RX:
CDC_ACM_ENTER_CRITICAL();
acm->callback_rx = NULL;
CDC_ACM_EXIT_CRITICAL();
return ESP_OK;
case CDC_EVENT_RX_WANTED_CHAR:
CDC_ACM_ENTER_CRITICAL();
acm->callback_rx_wanted_char = NULL;
CDC_ACM_EXIT_CRITICAL();
return ESP_OK;
case CDC_EVENT_LINE_STATE_CHANGED:
CDC_ACM_ENTER_CRITICAL();
acm->callback_line_state_changed = NULL;
CDC_ACM_EXIT_CRITICAL();
return ESP_OK;
case CDC_EVENT_LINE_CODING_CHANGED:
CDC_ACM_ENTER_CRITICAL();
acm->callback_line_coding_changed = NULL;
CDC_ACM_EXIT_CRITICAL();
return ESP_OK;
default:
ESP_LOGE(TAG, "Wrong event type");
return ESP_ERR_INVALID_ARG;
}
}
/*********************************************************************** TinyUSB callbacks*/
/* CDC-ACM
********************************************************************* */
esp_err_t tinyusb_cdcacm_read(tinyusb_cdcacm_itf_t itf, uint8_t *out_buf, size_t out_buf_sz, size_t *rx_data_size)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
ESP_RETURN_ON_FALSE(acm, ESP_ERR_INVALID_STATE, TAG, "Interface is not initialized. Use `tinyusb_cdc_init` for initialization");
if (tud_cdc_n_available(itf) == 0) {
*rx_data_size = 0;
} else {
*rx_data_size = tud_cdc_n_read(itf, out_buf, out_buf_sz);
}
return ESP_OK;
}
size_t tinyusb_cdcacm_write_queue_char(tinyusb_cdcacm_itf_t itf, char ch)
{
if (!get_acm(itf)) { // non-initialized
return 0;
}
return tud_cdc_n_write_char(itf, ch);
}
size_t tinyusb_cdcacm_write_queue(tinyusb_cdcacm_itf_t itf, const uint8_t *in_buf, size_t in_size)
{
if (!get_acm(itf)) { // non-initialized
return 0;
}
const uint32_t size_available = tud_cdc_n_write_available(itf);
return tud_cdc_n_write(itf, in_buf, MIN(in_size, size_available));
}
static uint32_t tud_cdc_n_write_occupied(tinyusb_cdcacm_itf_t itf)
{
return CFG_TUD_CDC_TX_BUFSIZE - tud_cdc_n_write_available(itf);
}
esp_err_t tinyusb_cdcacm_write_flush(tinyusb_cdcacm_itf_t itf, uint32_t timeout_ticks)
{
if (!get_acm(itf)) { // non-initialized
return ESP_FAIL;
}
if (!timeout_ticks) { // if no timeout - nonblocking mode
// It might take some time until TinyUSB flushes the endpoint
// Since this call is non-blocking, we don't wait for flush finished,
// We only inform the user by returning ESP_ERR_NOT_FINISHED
tud_cdc_n_write_flush(itf);
if (tud_cdc_n_write_occupied(itf)) {
return ESP_ERR_NOT_FINISHED;
}
} else { // trying during the timeout
uint32_t ticks_start = xTaskGetTickCount();
uint32_t ticks_now = ticks_start;
while (1) { // loop until success or until the time runs out
ticks_now = xTaskGetTickCount();
tud_cdc_n_write_flush(itf);
if (tud_cdc_n_write_occupied(itf) == 0) {
break; // All data flushed
}
if ( (ticks_now - ticks_start) > timeout_ticks ) { // Time is up
ESP_LOGW(TAG, "Flush failed");
return ESP_ERR_TIMEOUT;
}
vTaskDelay(1);
}
}
return ESP_OK;
}
static esp_err_t alloc_obj(tinyusb_cdcacm_itf_t itf)
{
esp_tusb_cdc_t *cdc_inst = tinyusb_cdc_get_intf(itf);
if (cdc_inst == NULL) {
return ESP_FAIL;
}
cdc_inst->subclass_obj = calloc(1, sizeof(esp_tusb_cdcacm_t));
if (cdc_inst->subclass_obj == NULL) {
return ESP_FAIL;
}
return ESP_OK;
}
esp_err_t tusb_cdc_acm_init(const tinyusb_config_cdcacm_t *cfg)
{
esp_err_t ret = ESP_OK;
int itf = (int)cfg->cdc_port;
/* Creating a CDC object */
const tinyusb_config_cdc_t cdc_cfg = {
.usb_dev = cfg->usb_dev,
.cdc_class = TUSB_CLASS_CDC,
.cdc_subclass.comm_subclass = CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL
};
ESP_RETURN_ON_ERROR(tinyusb_cdc_init(itf, &cdc_cfg), TAG, "tinyusb_cdc_init failed");
ESP_GOTO_ON_ERROR(alloc_obj(itf), fail, TAG, "alloc_obj failed");
/* Callbacks setting up*/
if (cfg->callback_rx) {
tinyusb_cdcacm_register_callback(itf, CDC_EVENT_RX, cfg->callback_rx);
}
if (cfg->callback_rx_wanted_char) {
tinyusb_cdcacm_register_callback(itf, CDC_EVENT_RX_WANTED_CHAR, cfg->callback_rx_wanted_char);
}
if (cfg->callback_line_state_changed) {
tinyusb_cdcacm_register_callback(itf, CDC_EVENT_LINE_STATE_CHANGED, cfg->callback_line_state_changed);
}
if (cfg->callback_line_coding_changed) {
tinyusb_cdcacm_register_callback( itf, CDC_EVENT_LINE_CODING_CHANGED, cfg->callback_line_coding_changed);
}
return ESP_OK;
fail:
tinyusb_cdc_deinit(itf);
return ret;
}
esp_err_t tusb_cdc_acm_deinit(int itf)
{
return tinyusb_cdc_deinit(itf);
}
bool tusb_cdc_acm_initialized(tinyusb_cdcacm_itf_t itf)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (acm) {
return true;
} else {
return false;
}
}
/*********************************************************************** CDC-ACM*/
@@ -1,115 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <stdio_ext.h>
#include "esp_log.h"
#include "cdc.h"
#include "tusb_console.h"
#include "tinyusb.h"
#include "vfs_tinyusb.h"
#include "esp_check.h"
#define STRINGIFY(s) STRINGIFY2(s)
#define STRINGIFY2(s) #s
static const char *TAG = "tusb_console";
typedef struct {
FILE *in;
FILE *out;
FILE *err;
} console_handle_t;
static console_handle_t con;
/**
* @brief Reopen standard streams using a new path
*
* @param f_in - pointer to a pointer holding a file for in or NULL to don't change stdin
* @param f_out - pointer to a pointer holding a file for out or NULL to don't change stdout
* @param f_err - pointer to a pointer holding a file for err or NULL to don't change stderr
* @param path - mount point
* @return esp_err_t ESP_FAIL or ESP_OK
*/
static esp_err_t redirect_std_streams_to(FILE **f_in, FILE **f_out, FILE **f_err, const char *path)
{
if (f_in) {
*f_in = freopen(path, "r", stdin);
if (*f_in == NULL) {
ESP_LOGE(TAG, "Failed to reopen in!");
return ESP_FAIL;
}
}
if (f_out) {
*f_out = freopen(path, "w", stdout);
if (*f_out == NULL) {
ESP_LOGE(TAG, "Failed to reopen out!");
return ESP_FAIL;
}
}
if (f_err) {
*f_err = freopen(path, "w", stderr);
if (*f_err == NULL) {
ESP_LOGE(TAG, "Failed to reopen err!");
return ESP_FAIL;
}
}
return ESP_OK;
}
/**
* @brief Restore output to default
*
* @param f_in - pointer to a pointer of an in file updated with `redirect_std_streams_to` or NULL to don't change stdin
* @param f_out - pointer to a pointer of an out file updated with `redirect_std_streams_to` or NULL to don't change stdout
* @param f_err - pointer to a pointer of an err file updated with `redirect_std_streams_to` or NULL to don't change stderr
* @return esp_err_t ESP_FAIL or ESP_OK
*/
static esp_err_t restore_std_streams(FILE **f_in, FILE **f_out, FILE **f_err)
{
const char *default_uart_dev = "/dev/uart/" STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM);
if (f_in) {
stdin = freopen(default_uart_dev, "r", *f_in);
if (stdin == NULL) {
ESP_LOGE(TAG, "Failed to reopen stdin!");
return ESP_FAIL;
}
}
if (f_out) {
stdout = freopen(default_uart_dev, "w", *f_out);
if (stdout == NULL) {
ESP_LOGE(TAG, "Failed to reopen stdout!");
return ESP_FAIL;
}
}
if (f_err) {
stderr = freopen(default_uart_dev, "w", *f_err);
if (stderr == NULL) {
ESP_LOGE(TAG, "Failed to reopen stderr!");
return ESP_FAIL;
}
}
return ESP_OK;
}
esp_err_t esp_tusb_init_console(int cdc_intf)
{
/* Registering TUSB at VFS */
ESP_RETURN_ON_ERROR(esp_vfs_tusb_cdc_register(cdc_intf, NULL), TAG, "");
ESP_RETURN_ON_ERROR(redirect_std_streams_to(&con.in, &con.out, &con.err, VFS_TUSB_PATH_DEFAULT), TAG, "Failed to redirect STD streams");
return ESP_OK;
}
esp_err_t esp_tusb_deinit_console(int cdc_intf)
{
ESP_RETURN_ON_ERROR(restore_std_streams(&con.in, &con.out, &con.err), TAG, "Failed to restore STD streams");
esp_vfs_tusb_cdc_unregister(NULL);
return ESP_OK;
}
@@ -1,640 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include "esp_log.h"
#include "esp_err.h"
#include "esp_check.h"
#include "esp_vfs_fat.h"
#include "diskio_impl.h"
#include "diskio_wl.h"
#include "wear_levelling.h"
#include "esp_partition.h"
#include "vfs_fat_internal.h"
#include "tinyusb.h"
#include "class/msc/msc_device.h"
#include "tusb_msc_storage.h"
#include "esp_vfs_fat.h"
#if SOC_SDMMC_HOST_SUPPORTED
#include "diskio_sdmmc.h"
#endif
static const char *TAG = "tinyusb_msc_storage";
typedef struct {
bool is_fat_mounted;
const char *base_path;
union {
wl_handle_t wl_handle;
#if SOC_SDMMC_HOST_SUPPORTED
sdmmc_card_t *card;
#endif
};
esp_err_t (*mount)(BYTE pdrv);
esp_err_t (*unmount)(void);
uint32_t (*sector_count)(void);
uint32_t (*sector_size)(void);
esp_err_t (*read)(size_t sector_size, uint32_t lba, uint32_t offset, size_t size, void *dest);
esp_err_t (*write)(size_t sector_size, size_t addr, uint32_t lba, uint32_t offset, size_t size, const void *src);
tusb_msc_callback_t callback_mount_changed;
tusb_msc_callback_t callback_premount_changed;
int max_files;
} tinyusb_msc_storage_handle_s; /*!< MSC object */
/* handle of tinyusb driver connected to application */
static tinyusb_msc_storage_handle_s *s_storage_handle;
static esp_err_t _mount_spiflash(BYTE pdrv)
{
return ff_diskio_register_wl_partition(pdrv, s_storage_handle->wl_handle);
}
static esp_err_t _unmount_spiflash(void)
{
BYTE pdrv;
pdrv = ff_diskio_get_pdrv_wl(s_storage_handle->wl_handle);
if (pdrv == 0xff) {
ESP_LOGE(TAG, "Invalid state");
return ESP_ERR_INVALID_STATE;
}
ff_diskio_clear_pdrv_wl(s_storage_handle->wl_handle);
char drv[3] = {(char)('0' + pdrv), ':', 0};
f_mount(0, drv, 0);
ff_diskio_unregister(pdrv);
return ESP_OK;
}
static uint32_t _get_sector_count_spiflash(void)
{
uint32_t result = 0;
assert(s_storage_handle->wl_handle != WL_INVALID_HANDLE);
size_t size = wl_sector_size(s_storage_handle->wl_handle);
if (size == 0) {
ESP_LOGW(TAG, "WL Sector size is zero !!!");
result = 0;
} else {
result = (uint32_t)(wl_size(s_storage_handle->wl_handle) / size);
}
return result;
}
static uint32_t _get_sector_size_spiflash(void)
{
assert(s_storage_handle->wl_handle != WL_INVALID_HANDLE);
return (uint32_t)wl_sector_size(s_storage_handle->wl_handle);
}
static esp_err_t _read_sector_spiflash(size_t sector_size,
uint32_t lba,
uint32_t offset,
size_t size,
void *dest)
{
size_t temp = 0;
size_t addr = 0; // Address of the data to be read, relative to the beginning of the partition.
ESP_RETURN_ON_FALSE(!__builtin_umul_overflow(lba, sector_size, &temp), ESP_ERR_INVALID_SIZE, TAG, "overflow lba %lu sector_size %u", lba, sector_size);
ESP_RETURN_ON_FALSE(!__builtin_uadd_overflow(temp, offset, &addr), ESP_ERR_INVALID_SIZE, TAG, "overflow addr %u offset %lu", temp, offset);
return wl_read(s_storage_handle->wl_handle, addr, dest, size);
}
static esp_err_t _write_sector_spiflash(size_t sector_size,
size_t addr,
uint32_t lba,
uint32_t offset,
size_t size,
const void *src)
{
ESP_RETURN_ON_ERROR(wl_erase_range(s_storage_handle->wl_handle, addr, size),
TAG, "Failed to erase");
return wl_write(s_storage_handle->wl_handle, addr, src, size);
}
#if SOC_SDMMC_HOST_SUPPORTED
static esp_err_t _mount_sdmmc(BYTE pdrv)
{
ff_diskio_register_sdmmc(pdrv, s_storage_handle->card);
ff_sdmmc_set_disk_status_check(pdrv, false);
return ESP_OK;
}
static esp_err_t _unmount_sdmmc(void)
{
BYTE pdrv;
pdrv = ff_diskio_get_pdrv_card(s_storage_handle->card);
if (pdrv == 0xff) {
ESP_LOGE(TAG, "Invalid state");
return ESP_ERR_INVALID_STATE;
}
char drv[3] = {(char)('0' + pdrv), ':', 0};
f_mount(0, drv, 0);
ff_diskio_unregister(pdrv);
return ESP_OK;
}
static uint32_t _get_sector_count_sdmmc(void)
{
assert(s_storage_handle->card);
return (uint32_t)s_storage_handle->card->csd.capacity;
}
static uint32_t _get_sector_size_sdmmc(void)
{
assert(s_storage_handle->card);
return (uint32_t)s_storage_handle->card->csd.sector_size;
}
static esp_err_t _read_sector_sdmmc(size_t sector_size,
uint32_t lba,
uint32_t offset,
size_t size,
void *dest)
{
return sdmmc_read_sectors(s_storage_handle->card, dest, lba, size / sector_size);
}
static esp_err_t _write_sector_sdmmc(size_t sector_size,
size_t addr,
uint32_t lba,
uint32_t offset,
size_t size,
const void *src)
{
return sdmmc_write_sectors(s_storage_handle->card, src, lba, size / sector_size);
}
#endif
static esp_err_t msc_storage_read_sector(uint32_t lba,
uint32_t offset,
size_t size,
void *dest)
{
assert(s_storage_handle);
size_t sector_size = tinyusb_msc_storage_get_sector_size();
return (s_storage_handle->read)(sector_size, lba, offset, size, dest);
}
static esp_err_t msc_storage_write_sector(uint32_t lba,
uint32_t offset,
size_t size,
const void *src)
{
assert(s_storage_handle);
if (s_storage_handle->is_fat_mounted) {
ESP_LOGE(TAG, "can't write, FAT mounted");
return ESP_ERR_INVALID_STATE;
}
size_t sector_size = tinyusb_msc_storage_get_sector_size();
size_t temp = 0;
size_t addr = 0; // Address of the data to be read, relative to the beginning of the partition.
ESP_RETURN_ON_FALSE(!__builtin_umul_overflow(lba, sector_size, &temp), ESP_ERR_INVALID_SIZE, TAG, "overflow lba %lu sector_size %u", lba, sector_size);
ESP_RETURN_ON_FALSE(!__builtin_uadd_overflow(temp, offset, &addr), ESP_ERR_INVALID_SIZE, TAG, "overflow addr %u offset %lu", temp, offset);
if (addr % sector_size != 0 || size % sector_size != 0) {
ESP_LOGE(TAG, "Invalid Argument lba(%lu) offset(%lu) size(%u) sector_size(%u)", lba, offset, size, sector_size);
return ESP_ERR_INVALID_ARG;
}
return (s_storage_handle->write)(sector_size, addr, lba, offset, size, src);
}
static esp_err_t _mount(char *drv, FATFS *fs)
{
void *workbuf = NULL;
const size_t workbuf_size = 4096;
esp_err_t ret;
// Try to mount partition
FRESULT fresult = f_mount(fs, drv, 1);
if (fresult != FR_OK) {
ESP_LOGW(TAG, "f_mount failed (%d)", fresult);
if (!((fresult == FR_NO_FILESYSTEM || fresult == FR_INT_ERR))) {
ret = ESP_FAIL;
goto fail;
}
workbuf = ff_memalloc(workbuf_size);
if (workbuf == NULL) {
ret = ESP_ERR_NO_MEM;
goto fail;
}
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(
CONFIG_WL_SECTOR_SIZE,
4096);
ESP_LOGW(TAG, "formatting card, allocation unit size=%d", alloc_unit_size);
const MKFS_PARM opt = {(BYTE)FM_FAT, 0, 0, 0, alloc_unit_size};
fresult = f_mkfs("", &opt, workbuf, workbuf_size); // Use default volume
if (fresult != FR_OK) {
ret = ESP_FAIL;
ESP_LOGE(TAG, "f_mkfs failed (%d)", fresult);
goto fail;
}
free(workbuf);
workbuf = NULL;
fresult = f_mount(fs, drv, 0);
if (fresult != FR_OK) {
ret = ESP_FAIL;
ESP_LOGE(TAG, "f_mount failed after formatting (%d)", fresult);
goto fail;
}
}
return ESP_OK;
fail:
if (workbuf) {
free(workbuf);
}
return ret;
}
esp_err_t tinyusb_msc_storage_mount(const char *base_path)
{
esp_err_t ret = ESP_OK;
assert(s_storage_handle);
if (s_storage_handle->is_fat_mounted) {
return ESP_OK;
}
tusb_msc_callback_t cb = s_storage_handle->callback_premount_changed;
if (cb) {
tinyusb_msc_event_t event = {
.type = TINYUSB_MSC_EVENT_PREMOUNT_CHANGED,
.mount_changed_data = {
.is_mounted = s_storage_handle->is_fat_mounted
}
};
cb(&event);
}
if (!base_path) {
base_path = CONFIG_TINYUSB_MSC_MOUNT_PATH;
}
// connect driver to FATFS
BYTE pdrv = 0xFF;
ESP_RETURN_ON_ERROR(ff_diskio_get_drive(&pdrv), TAG,
"The maximum count of volumes is already mounted");
char drv[3] = {(char)('0' + pdrv), ':', 0};
ESP_GOTO_ON_ERROR((s_storage_handle->mount)(pdrv), fail, TAG, "Failed pdrv=%d", pdrv);
FATFS *fs = NULL;
ret = esp_vfs_fat_register(base_path, drv, s_storage_handle->max_files, &fs);
if (ret == ESP_ERR_INVALID_STATE) {
ESP_LOGD(TAG, "it's okay, already registered with VFS");
} else if (ret != ESP_OK) {
ESP_LOGE(TAG, "esp_vfs_fat_register failed (0x%x)", ret);
goto fail;
}
ESP_GOTO_ON_ERROR(_mount(drv, fs), fail, TAG, "Failed _mount");
s_storage_handle->is_fat_mounted = true;
s_storage_handle->base_path = base_path;
cb = s_storage_handle->callback_mount_changed;
if (cb) {
tinyusb_msc_event_t event = {
.type = TINYUSB_MSC_EVENT_MOUNT_CHANGED,
.mount_changed_data = {
.is_mounted = s_storage_handle->is_fat_mounted
}
};
cb(&event);
}
return ret;
fail:
if (fs) {
esp_vfs_fat_unregister_path(base_path);
}
ff_diskio_unregister(pdrv);
s_storage_handle->is_fat_mounted = false;
ESP_LOGW(TAG, "Failed to mount storage (0x%x)", ret);
return ret;
}
esp_err_t tinyusb_msc_storage_unmount(void)
{
if (!s_storage_handle) {
return ESP_FAIL;
}
if (!s_storage_handle->is_fat_mounted) {
return ESP_OK;
}
tusb_msc_callback_t cb = s_storage_handle->callback_premount_changed;
if (cb) {
tinyusb_msc_event_t event = {
.type = TINYUSB_MSC_EVENT_PREMOUNT_CHANGED,
.mount_changed_data = {
.is_mounted = s_storage_handle->is_fat_mounted
}
};
cb(&event);
}
esp_err_t err = (s_storage_handle->unmount)();
if (err) {
return err;
}
err = esp_vfs_fat_unregister_path(s_storage_handle->base_path);
s_storage_handle->base_path = NULL;
s_storage_handle->is_fat_mounted = false;
cb = s_storage_handle->callback_mount_changed;
if (cb) {
tinyusb_msc_event_t event = {
.type = TINYUSB_MSC_EVENT_MOUNT_CHANGED,
.mount_changed_data = {
.is_mounted = s_storage_handle->is_fat_mounted
}
};
cb(&event);
}
return err;
}
uint32_t tinyusb_msc_storage_get_sector_count(void)
{
assert(s_storage_handle);
return (s_storage_handle->sector_count)();
}
uint32_t tinyusb_msc_storage_get_sector_size(void)
{
assert(s_storage_handle);
return (s_storage_handle->sector_size)();
}
esp_err_t tinyusb_msc_storage_init_spiflash(const tinyusb_msc_spiflash_config_t *config)
{
assert(!s_storage_handle);
s_storage_handle = (tinyusb_msc_storage_handle_s *)malloc(sizeof(tinyusb_msc_storage_handle_s));
ESP_RETURN_ON_FALSE(s_storage_handle, ESP_ERR_NO_MEM, TAG, "could not allocate new handle for storage");
s_storage_handle->mount = &_mount_spiflash;
s_storage_handle->unmount = &_unmount_spiflash;
s_storage_handle->sector_count = &_get_sector_count_spiflash;
s_storage_handle->sector_size = &_get_sector_size_spiflash;
s_storage_handle->read = &_read_sector_spiflash;
s_storage_handle->write = &_write_sector_spiflash;
s_storage_handle->is_fat_mounted = false;
s_storage_handle->base_path = NULL;
s_storage_handle->wl_handle = config->wl_handle;
// In case the user does not set mount_config.max_files
// and for backward compatibility with versions <1.4.2
// max_files is set to 2
const int max_files = config->mount_config.max_files;
s_storage_handle->max_files = max_files > 0 ? max_files : 2;
/* Callbacks setting up*/
if (config->callback_mount_changed) {
tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED, config->callback_mount_changed);
} else {
tinyusb_msc_unregister_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED);
}
if (config->callback_premount_changed) {
tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_PREMOUNT_CHANGED, config->callback_premount_changed);
} else {
tinyusb_msc_unregister_callback(TINYUSB_MSC_EVENT_PREMOUNT_CHANGED);
}
return ESP_OK;
}
#if SOC_SDMMC_HOST_SUPPORTED
esp_err_t tinyusb_msc_storage_init_sdmmc(const tinyusb_msc_sdmmc_config_t *config)
{
assert(!s_storage_handle);
s_storage_handle = (tinyusb_msc_storage_handle_s *)malloc(sizeof(tinyusb_msc_storage_handle_s));
ESP_RETURN_ON_FALSE(s_storage_handle, ESP_ERR_NO_MEM, TAG, "could not allocate new handle for storage");
s_storage_handle->mount = &_mount_sdmmc;
s_storage_handle->unmount = &_unmount_sdmmc;
s_storage_handle->sector_count = &_get_sector_count_sdmmc;
s_storage_handle->sector_size = &_get_sector_size_sdmmc;
s_storage_handle->read = &_read_sector_sdmmc;
s_storage_handle->write = &_write_sector_sdmmc;
s_storage_handle->is_fat_mounted = false;
s_storage_handle->base_path = NULL;
s_storage_handle->card = config->card;
// In case the user does not set mount_config.max_files
// and for backward compatibility with versions <1.4.2
// max_files is set to 2
const int max_files = config->mount_config.max_files;
s_storage_handle->max_files = max_files > 0 ? max_files : 2;
/* Callbacks setting up*/
if (config->callback_mount_changed) {
tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED, config->callback_mount_changed);
} else {
tinyusb_msc_unregister_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED);
}
if (config->callback_premount_changed) {
tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_PREMOUNT_CHANGED, config->callback_premount_changed);
} else {
tinyusb_msc_unregister_callback(TINYUSB_MSC_EVENT_PREMOUNT_CHANGED);
}
return ESP_OK;
}
#endif
void tinyusb_msc_storage_deinit(void)
{
assert(s_storage_handle);
free(s_storage_handle);
s_storage_handle = NULL;
}
esp_err_t tinyusb_msc_register_callback(tinyusb_msc_event_type_t event_type,
tusb_msc_callback_t callback)
{
assert(s_storage_handle);
switch (event_type) {
case TINYUSB_MSC_EVENT_MOUNT_CHANGED:
s_storage_handle->callback_mount_changed = callback;
return ESP_OK;
case TINYUSB_MSC_EVENT_PREMOUNT_CHANGED:
s_storage_handle->callback_premount_changed = callback;
return ESP_OK;
default:
ESP_LOGE(TAG, "Wrong event type");
return ESP_ERR_INVALID_ARG;
}
}
esp_err_t tinyusb_msc_unregister_callback(tinyusb_msc_event_type_t event_type)
{
assert(s_storage_handle);
switch (event_type) {
case TINYUSB_MSC_EVENT_MOUNT_CHANGED:
s_storage_handle->callback_mount_changed = NULL;
return ESP_OK;
case TINYUSB_MSC_EVENT_PREMOUNT_CHANGED:
s_storage_handle->callback_premount_changed = NULL;
return ESP_OK;
default:
ESP_LOGE(TAG, "Wrong event type");
return ESP_ERR_INVALID_ARG;
}
}
bool tinyusb_msc_storage_in_use_by_usb_host(void)
{
assert(s_storage_handle);
return !s_storage_handle->is_fat_mounted;
}
/* TinyUSB MSC callbacks
********************************************************************* */
/** SCSI ASC/ASCQ codes. **/
/** User can add and use more codes as per the need of the application **/
#define SCSI_CODE_ASC_MEDIUM_NOT_PRESENT 0x3A /** SCSI ASC code for 'MEDIUM NOT PRESENT' **/
#define SCSI_CODE_ASC_INVALID_COMMAND_OPERATION_CODE 0x20 /** SCSI ASC code for 'INVALID COMMAND OPERATION CODE' **/
#define SCSI_CODE_ASCQ 0x00
// Invoked when received SCSI_CMD_INQUIRY
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
{
(void) lun;
const char vid[] = "TinyUSB";
const char pid[] = "Flash Storage";
const char rev[] = "0.1";
memcpy(vendor_id, vid, strlen(vid));
memcpy(product_id, pid, strlen(pid));
memcpy(product_rev, rev, strlen(rev));
}
// Invoked when received Test Unit Ready command.
// return true allowing host to read/write this LUN e.g SD card inserted
bool tud_msc_test_unit_ready_cb(uint8_t lun)
{
(void) lun;
bool result = false;
if (s_storage_handle->is_fat_mounted) {
tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, SCSI_CODE_ASC_MEDIUM_NOT_PRESENT, SCSI_CODE_ASCQ);
result = false;
} else {
if (tinyusb_msc_storage_unmount() != ESP_OK) {
ESP_LOGW(TAG, "tud_msc_test_unit_ready_cb() unmount Fails");
}
result = true;
}
return result;
}
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
// Application update block count and block size
void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size)
{
(void) lun;
uint32_t sec_count = tinyusb_msc_storage_get_sector_count();
uint32_t sec_size = tinyusb_msc_storage_get_sector_size();
*block_count = sec_count;
*block_size = (uint16_t)sec_size;
}
// Invoked when received Start Stop Unit command
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
{
(void) lun;
(void) power_condition;
if (load_eject && !start) {
if (tinyusb_msc_storage_mount(s_storage_handle->base_path) != ESP_OK) {
ESP_LOGW(TAG, "tud_msc_start_stop_cb() mount Fails");
}
}
return true;
}
// Invoked when received SCSI READ10 command
// - Address = lba * BLOCK_SIZE + offset
// - Application fill the buffer (up to bufsize) with address contents and return number of read byte.
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize)
{
esp_err_t err = msc_storage_read_sector(lba, offset, bufsize, buffer);
if (err != ESP_OK) {
ESP_LOGE(TAG, "msc_storage_read_sector failed: 0x%x", err);
return 0;
}
return bufsize;
}
// Invoked when received SCSI WRITE10 command
// - Address = lba * BLOCK_SIZE + offset
// - Application write data from buffer to address contents (up to bufsize) and return number of written byte.
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize)
{
esp_err_t err = msc_storage_write_sector(lba, offset, bufsize, buffer);
if (err != ESP_OK) {
ESP_LOGE(TAG, "msc_storage_write_sector failed: 0x%x", err);
return 0;
}
return bufsize;
}
/**
* Invoked when received an SCSI command not in built-in list below.
* - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, TEST_UNIT_READY, START_STOP_UNIT, MODE_SENSE6, REQUEST_SENSE
* - READ10 and WRITE10 has their own callbacks
*
* \param[in] lun Logical unit number
* \param[in] scsi_cmd SCSI command contents which application must examine to response accordingly
* \param[out] buffer Buffer for SCSI Data Stage.
* - For INPUT: application must fill this with response.
* - For OUTPUT it holds the Data from host
* \param[in] bufsize Buffer's length.
*
* \return Actual bytes processed, can be zero for no-data command.
* \retval negative Indicate error e.g unsupported command, tinyusb will \b STALL the corresponding
* endpoint and return failed status in command status wrapper phase.
*/
int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize)
{
int32_t ret;
switch (scsi_cmd[0]) {
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
/* SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL is the Prevent/Allow Medium Removal
command (1Eh) that requests the library to enable or disable user access to
the storage media/partition. */
ret = 0;
break;
default:
ESP_LOGW(TAG, "tud_msc_scsi_cb() invoked: %d", scsi_cmd[0]);
tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_CODE_ASC_INVALID_COMMAND_OPERATION_CODE, SCSI_CODE_ASCQ);
ret = -1;
break;
}
return ret;
}
// Invoked when device is unmounted
void tud_umount_cb(void)
{
if (tinyusb_msc_storage_mount(s_storage_handle->base_path) != ESP_OK) {
ESP_LOGW(TAG, "tud_umount_cb() mount Fails");
}
}
// Invoked when device is mounted (configured)
void tud_mount_cb(void)
{
tinyusb_msc_storage_unmount();
}
/*********************************************************************** TinyUSB MSC callbacks*/
@@ -1,77 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
#include "esp_check.h"
#include "tinyusb.h"
#include "tusb_tasks.h"
const static char *TAG = "tusb_tsk";
static TaskHandle_t s_tusb_tskh;
#if CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK
const static int INIT_OK = BIT0;
const static int INIT_FAILED = BIT1;
#endif
/**
* @brief This top level thread processes all usb events and invokes callbacks
*/
static void tusb_device_task(void *arg)
{
ESP_LOGD(TAG, "tinyusb task started");
#if CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK
EventGroupHandle_t *init_flags = arg;
if (!tusb_init()) {
ESP_LOGI(TAG, "Init TinyUSB stack failed");
xEventGroupSetBits(*init_flags, INIT_FAILED);
vTaskDelete(NULL);
}
ESP_LOGD(TAG, "tinyusb task has been initialized");
xEventGroupSetBits(*init_flags, INIT_OK);
#endif // CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK
while (1) { // RTOS forever loop
tud_task();
}
}
esp_err_t tusb_run_task(void)
{
// This function is not guaranteed to be thread safe, if invoked multiple times without calling `tusb_stop_task`, will cause memory leak
// doing a sanity check anyway
ESP_RETURN_ON_FALSE(!s_tusb_tskh, ESP_ERR_INVALID_STATE, TAG, "TinyUSB main task already started");
void *task_arg = NULL;
#if CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK
// need to synchronize to potentially report issue if init failed
EventGroupHandle_t init_flags = xEventGroupCreate();
ESP_RETURN_ON_FALSE(init_flags, ESP_ERR_NO_MEM, TAG, "Failed to allocate task sync flags");
task_arg = &init_flags;
#endif
// Create a task for tinyusb device stack:
xTaskCreatePinnedToCore(tusb_device_task, "TinyUSB", CONFIG_TINYUSB_TASK_STACK_SIZE, task_arg, CONFIG_TINYUSB_TASK_PRIORITY, &s_tusb_tskh, CONFIG_TINYUSB_TASK_AFFINITY);
ESP_RETURN_ON_FALSE(s_tusb_tskh, ESP_FAIL, TAG, "create TinyUSB main task failed");
#if CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK
// wait until tusb initialization has completed
EventBits_t bits = xEventGroupWaitBits(init_flags, INIT_OK | INIT_FAILED, pdFALSE, pdFALSE, portMAX_DELAY);
vEventGroupDelete(init_flags);
ESP_RETURN_ON_FALSE(bits & INIT_OK, ESP_FAIL, TAG, "Init TinyUSB stack failed");
#endif
return ESP_OK;
}
esp_err_t tusb_stop_task(void)
{
ESP_RETURN_ON_FALSE(s_tusb_tskh, ESP_ERR_INVALID_STATE, TAG, "TinyUSB main task not started yet");
vTaskDelete(s_tusb_tskh);
s_tusb_tskh = NULL;
return ESP_OK;
}
@@ -1,251 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "usb_descriptors.h"
#include "sdkconfig.h"
#include "tinyusb_types.h"
/*
* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] HID | MSC | CDC [LSB]
*/
#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n))
#define USB_TUSB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
_PID_MAP(MIDI, 3) ) //| _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) )
/**** Kconfig driven Descriptor ****/
//------------- Device Descriptor -------------//
const tusb_desc_device_t descriptor_dev_default = {
.bLength = sizeof(descriptor_dev_default),
.bDescriptorType = TUSB_DESC_DEVICE,
.bcdUSB = 0x0200,
#if CFG_TUD_CDC
// Use Interface Association Descriptor (IAD) for CDC
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
#else
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
#endif
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
#if CONFIG_TINYUSB_DESC_USE_ESPRESSIF_VID
.idVendor = USB_ESPRESSIF_VID,
#else
.idVendor = CONFIG_TINYUSB_DESC_CUSTOM_VID,
#endif
#if CONFIG_TINYUSB_DESC_USE_DEFAULT_PID
.idProduct = USB_TUSB_PID,
#else
.idProduct = CONFIG_TINYUSB_DESC_CUSTOM_PID,
#endif
.bcdDevice = CONFIG_TINYUSB_DESC_BCD_DEVICE,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
#if (TUD_OPT_HIGH_SPEED)
const tusb_desc_device_qualifier_t descriptor_qualifier_default = {
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = 0x0200,
#if CFG_TUD_CDC
// Use Interface Association Descriptor (IAD) for CDC
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
#else
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
#endif
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.bNumConfigurations = 0x01,
.bReserved = 0
};
#endif // TUD_OPT_HIGH_SPEED
//------------- Array of String Descriptors -------------//
const char *descriptor_str_default[] = {
// array of pointer to string descriptors
(char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
CONFIG_TINYUSB_DESC_MANUFACTURER_STRING, // 1: Manufacturer
CONFIG_TINYUSB_DESC_PRODUCT_STRING, // 2: Product
CONFIG_TINYUSB_DESC_SERIAL_STRING, // 3: Serials, should use chip ID
#if CONFIG_TINYUSB_CDC_ENABLED
CONFIG_TINYUSB_DESC_CDC_STRING, // 4: CDC Interface
#else
"",
#endif
#if CONFIG_TINYUSB_MSC_ENABLED
CONFIG_TINYUSB_DESC_MSC_STRING, // 5: MSC Interface
#else
"",
#endif
#if CONFIG_TINYUSB_NET_MODE_ECM_RNDIS || CONFIG_TINYUSB_NET_MODE_NCM
"USB net", // 6. NET Interface
"", // 7. MAC
#endif
NULL // NULL: Must be last. Indicates end of array
};
//------------- Interfaces enumeration -------------//
enum {
#if CFG_TUD_CDC
ITF_NUM_CDC = 0,
ITF_NUM_CDC_DATA,
#endif
#if CFG_TUD_CDC > 1
ITF_NUM_CDC1,
ITF_NUM_CDC1_DATA,
#endif
#if CFG_TUD_MSC
ITF_NUM_MSC,
#endif
#if CFG_TUD_NCM
ITF_NUM_NET,
ITF_NUM_NET_DATA,
#endif
ITF_NUM_TOTAL
};
enum {
TUSB_DESC_TOTAL_LEN = TUD_CONFIG_DESC_LEN +
CFG_TUD_CDC * TUD_CDC_DESC_LEN +
CFG_TUD_MSC * TUD_MSC_DESC_LEN +
CFG_TUD_NCM * TUD_CDC_NCM_DESC_LEN
};
//------------- USB Endpoint numbers -------------//
enum {
// Available USB Endpoints: 5 IN/OUT EPs and 1 IN EP
EP_EMPTY = 0,
#if CFG_TUD_CDC
EPNUM_0_CDC_NOTIF,
EPNUM_0_CDC,
#endif
#if CFG_TUD_CDC > 1
EPNUM_1_CDC_NOTIF,
EPNUM_1_CDC,
#endif
#if CFG_TUD_MSC
EPNUM_MSC,
#endif
#if CFG_TUD_NCM
EPNUM_NET_NOTIF,
EPNUM_NET_DATA,
#endif
};
//------------- STRID -------------//
enum {
STRID_LANGID = 0,
STRID_MANUFACTURER,
STRID_PRODUCT,
STRID_SERIAL,
#if CFG_TUD_CDC
STRID_CDC_INTERFACE,
#endif
#if CFG_TUD_MSC
STRID_MSC_INTERFACE,
#endif
#if CFG_TUD_NCM
STRID_NET_INTERFACE,
STRID_MAC,
#endif
};
//------------- Configuration Descriptor -------------//
uint8_t const descriptor_fs_cfg_default[] = {
// Configuration number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
#if CFG_TUD_CDC
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, STRID_CDC_INTERFACE, 0x80 | EPNUM_0_CDC_NOTIF, 8, EPNUM_0_CDC, 0x80 | EPNUM_0_CDC, 64),
#endif
#if CFG_TUD_CDC > 1
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC1, STRID_CDC_INTERFACE, 0x80 | EPNUM_1_CDC_NOTIF, 8, EPNUM_1_CDC, 0x80 | EPNUM_1_CDC, 64),
#endif
#if CFG_TUD_MSC
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, STRID_MSC_INTERFACE, EPNUM_MSC, 0x80 | EPNUM_MSC, 64),
#endif
#if CFG_TUD_NCM
// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size.
TUD_CDC_NCM_DESCRIPTOR(ITF_NUM_NET, STRID_NET_INTERFACE, STRID_MAC, (0x80 | EPNUM_NET_NOTIF), 64, EPNUM_NET_DATA, (0x80 | EPNUM_NET_DATA), 64, CFG_TUD_NET_MTU),
#endif
};
#if (TUD_OPT_HIGH_SPEED)
uint8_t const descriptor_hs_cfg_default[] = {
// Configuration number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
#if CFG_TUD_CDC
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, STRID_CDC_INTERFACE, 0x80 | EPNUM_0_CDC_NOTIF, 8, EPNUM_0_CDC, 0x80 | EPNUM_0_CDC, 512),
#endif
#if CFG_TUD_CDC > 1
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC1, STRID_CDC_INTERFACE, 0x80 | EPNUM_1_CDC_NOTIF, 8, EPNUM_1_CDC, 0x80 | EPNUM_1_CDC, 512),
#endif
#if CFG_TUD_MSC
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, STRID_MSC_INTERFACE, EPNUM_MSC, 0x80 | EPNUM_MSC, 512),
#endif
#if CFG_TUD_NCM
// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size.
TUD_CDC_NCM_DESCRIPTOR(ITF_NUM_NET, STRID_NET_INTERFACE, STRID_MAC, (0x80 | EPNUM_NET_NOTIF), 64, EPNUM_NET_DATA, (0x80 | EPNUM_NET_DATA), 512, CFG_TUD_NET_MTU),
#endif
};
#endif // TUD_OPT_HIGH_SPEED
#if CFG_TUD_NCM
uint8_t tusb_get_mac_string_id(void)
{
return STRID_MAC;
}
#endif
/* End of Kconfig driven Descriptor */
@@ -1,301 +0,0 @@
/*
* SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdio_ext.h>
#include <string.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <sys/lock.h>
#include <sys/param.h>
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_vfs.h"
#include "esp_vfs_dev.h"
#include "tinyusb.h"
#include "tusb_cdc_acm.h"
#include "vfs_tinyusb.h"
#include "sdkconfig.h"
const static char *TAG = "tusb_vfs";
// Token signifying that no character is available
#define NONE -1
#define FD_CHECK(fd, ret_val) do { \
if ((fd) != 0) { \
errno = EBADF; \
return (ret_val); \
} \
} while (0)
#if CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF
# define DEFAULT_TX_MODE ESP_LINE_ENDINGS_CRLF
#elif CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR
# define DEFAULT_TX_MODE ESP_LINE_ENDINGS_CR
#else
# define DEFAULT_TX_MODE ESP_LINE_ENDINGS_LF
#endif
#if CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF
# define DEFAULT_RX_MODE ESP_LINE_ENDINGS_CRLF
#elif CONFIG_NEWLIB_STDIN_LINE_ENDING_CR
# define DEFAULT_RX_MODE ESP_LINE_ENDINGS_CR
#else
# define DEFAULT_RX_MODE ESP_LINE_ENDINGS_LF
#endif
typedef struct {
_lock_t write_lock;
_lock_t read_lock;
esp_line_endings_t tx_mode; // Newline conversion mode when transmitting
esp_line_endings_t rx_mode; // Newline conversion mode when receiving
uint32_t flags;
char vfs_path[VFS_TUSB_MAX_PATH];
int cdc_intf;
} vfs_tinyusb_t;
static vfs_tinyusb_t s_vfstusb;
static esp_err_t apply_path(char const *path)
{
if (path == NULL) {
path = VFS_TUSB_PATH_DEFAULT;
}
size_t path_len = strlen(path) + 1;
if (path_len > VFS_TUSB_MAX_PATH) {
ESP_LOGE(TAG, "The path is too long; maximum is %d characters", VFS_TUSB_MAX_PATH);
return ESP_ERR_INVALID_ARG;
}
strncpy(s_vfstusb.vfs_path, path, (VFS_TUSB_MAX_PATH - 1));
ESP_LOGV(TAG, "Path is set to `%s`", path);
return ESP_OK;
}
/**
* @brief Fill s_vfstusb
*
* @param cdc_intf - interface of tusb for registration
* @param path - a path where the CDC will be registered
* @return esp_err_t ESP_OK or ESP_ERR_INVALID_ARG
*/
static esp_err_t vfstusb_init(int cdc_intf, char const *path)
{
s_vfstusb.cdc_intf = cdc_intf;
s_vfstusb.tx_mode = DEFAULT_TX_MODE;
s_vfstusb.rx_mode = DEFAULT_RX_MODE;
return apply_path(path);
}
/**
* @brief Clear s_vfstusb to default values
*/
static void vfstusb_deinit(void)
{
memset(&s_vfstusb, 0, sizeof(s_vfstusb));
}
static int tusb_open(const char *path, int flags, int mode)
{
(void) mode;
(void) path;
s_vfstusb.flags = flags | O_NONBLOCK; // for now only non-blocking mode is implemented
return 0;
}
static ssize_t tusb_write(int fd, const void *data, size_t size)
{
FD_CHECK(fd, -1);
size_t written_sz = 0;
const char *data_c = (const char *)data;
_lock_acquire(&(s_vfstusb.write_lock));
for (size_t i = 0; i < size; i++) {
int c = data_c[i];
if (c != '\n') {
if (!tinyusb_cdcacm_write_queue_char(s_vfstusb.cdc_intf, c)) {
break; // can't write anymore
}
} else {
if (s_vfstusb.tx_mode == ESP_LINE_ENDINGS_CRLF || s_vfstusb.tx_mode == ESP_LINE_ENDINGS_CR) {
char cr = '\r';
if (!tinyusb_cdcacm_write_queue_char(s_vfstusb.cdc_intf, cr)) {
break; // can't write anymore
}
}
if (s_vfstusb.tx_mode == ESP_LINE_ENDINGS_CRLF || s_vfstusb.tx_mode == ESP_LINE_ENDINGS_LF) {
char lf = '\n';
if (!tinyusb_cdcacm_write_queue_char(s_vfstusb.cdc_intf, lf)) {
break; // can't write anymore
}
}
}
written_sz++;
}
tud_cdc_n_write_flush(s_vfstusb.cdc_intf);
_lock_release(&(s_vfstusb.write_lock));
return written_sz;
}
static int tusb_close(int fd)
{
FD_CHECK(fd, -1);
return 0;
}
static ssize_t tusb_read(int fd, void *data, size_t size)
{
FD_CHECK(fd, -1);
char *data_c = (char *) data;
size_t received = 0;
_lock_acquire(&(s_vfstusb.read_lock));
if (tud_cdc_n_available(s_vfstusb.cdc_intf) == 0) {
goto finish;
}
while (received < size) {
int c = tud_cdc_n_read_char(s_vfstusb.cdc_intf);
if ( c == NONE) { // if data ends
break;
}
// Handle line endings. From configured mode -> LF mode
if (s_vfstusb.rx_mode == ESP_LINE_ENDINGS_CR) {
// Change CRs to newlines
if (c == '\r') {
c = '\n';
}
} else if (s_vfstusb.rx_mode == ESP_LINE_ENDINGS_CRLF) {
if (c == '\r') {
uint8_t next_char = NONE;
// Check if next char is newline. If yes, we got CRLF sequence
tud_cdc_n_peek(s_vfstusb.cdc_intf, &next_char);
if (next_char == '\n') {
c = tud_cdc_n_read_char(s_vfstusb.cdc_intf); // Remove '\n' from the fifo
}
}
}
data_c[received] = (char) c;
++received;
if (c == '\n') {
break;
}
}
finish:
_lock_release(&(s_vfstusb.read_lock));
if (received > 0) {
return received;
}
errno = EWOULDBLOCK;
return -1;
}
static int tusb_fstat(int fd, struct stat *st)
{
FD_CHECK(fd, -1);
memset(st, 0, sizeof(*st));
st->st_mode = S_IFCHR;
return 0;
}
static int tusb_fcntl(int fd, int cmd, int arg)
{
FD_CHECK(fd, -1);
int result = 0;
switch (cmd) {
case F_GETFL:
result = s_vfstusb.flags;
break;
case F_SETFL:
s_vfstusb.flags = arg;
break;
default:
result = -1;
errno = ENOSYS;
break;
}
return result;
}
esp_err_t esp_vfs_tusb_cdc_unregister(char const *path)
{
ESP_LOGD(TAG, "Unregistering CDC-VFS driver");
int res;
if (path == NULL) { // NULL means using the default path for unregistering: VFS_TUSB_PATH_DEFAULT
path = VFS_TUSB_PATH_DEFAULT;
}
res = strcmp(s_vfstusb.vfs_path, path);
if (res) {
res = ESP_ERR_INVALID_ARG;
ESP_LOGE(TAG, "There is no CDC-VFS driver registered to path '%s' (err: 0x%x)", path, res);
return res;
}
res = esp_vfs_unregister(s_vfstusb.vfs_path);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Can't unregister CDC-VFS driver from '%s' (err: 0x%x)", s_vfstusb.vfs_path, res);
} else {
ESP_LOGD(TAG, "Unregistered CDC-VFS driver");
vfstusb_deinit();
}
return res;
}
esp_err_t esp_vfs_tusb_cdc_register(int cdc_intf, char const *path)
{
ESP_LOGD(TAG, "Registering CDC-VFS driver");
int res;
if (!tusb_cdc_acm_initialized(cdc_intf)) {
ESP_LOGE(TAG, "TinyUSB CDC#%d is not initialized", cdc_intf);
return ESP_ERR_INVALID_STATE;
}
res = vfstusb_init(cdc_intf, path);
if (res != ESP_OK) {
return res;
}
esp_vfs_t vfs = {
.flags = ESP_VFS_FLAG_DEFAULT,
.close = &tusb_close,
.fcntl = &tusb_fcntl,
.fstat = &tusb_fstat,
.open = &tusb_open,
.read = &tusb_read,
.write = &tusb_write,
};
res = esp_vfs_register(s_vfstusb.vfs_path, &vfs, NULL);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Can't register CDC-VFS driver (err: %x)", res);
} else {
ESP_LOGD(TAG, "CDC-VFS registered (%s)", s_vfstusb.vfs_path);
}
return res;
}
void esp_vfs_tusb_cdc_set_rx_line_endings(esp_line_endings_t mode)
{
_lock_acquire(&(s_vfstusb.read_lock));
s_vfstusb.rx_mode = mode;
_lock_release(&(s_vfstusb.read_lock));
}
void esp_vfs_tusb_cdc_set_tx_line_endings(esp_line_endings_t mode)
{
_lock_acquire(&(s_vfstusb.write_lock));
s_vfstusb.tx_mode = mode;
_lock_release(&(s_vfstusb.write_lock));
}
@@ -0,0 +1,4 @@
## IDF Component Manager Manifest File
dependencies:
espressif/esp_tinyusb: "^1"
idf: "^5.0"
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
@@ -8,32 +8,61 @@
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "tinyusb.h"
#include "tusb_cdc_acm.h"
#include "sdkconfig.h"
static const char *TAG = "example";
static uint8_t buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1];
static uint8_t rx_buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1];
/**
* @brief Application Queue
*/
static QueueHandle_t app_queue;
typedef struct {
uint8_t buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1]; // Data buffer
size_t buf_len; // Number of bytes received
uint8_t itf; // Index of CDC device interface
} app_message_t;
/**
* @brief CDC device RX callback
*
* CDC device signals, that new data were received
*
* @param[in] itf CDC device index
* @param[in] event CDC event type
*/
void tinyusb_cdc_rx_callback(int itf, cdcacm_event_t *event)
{
/* initialization */
size_t rx_size = 0;
/* read */
esp_err_t ret = tinyusb_cdcacm_read(itf, buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size);
esp_err_t ret = tinyusb_cdcacm_read(itf, rx_buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "Data from channel %d:", itf);
ESP_LOG_BUFFER_HEXDUMP(TAG, buf, rx_size, ESP_LOG_INFO);
} else {
ESP_LOGE(TAG, "Read error");
}
/* write back */
tinyusb_cdcacm_write_queue(itf, buf, rx_size);
tinyusb_cdcacm_write_flush(itf, 0);
app_message_t tx_msg = {
.buf_len = rx_size,
.itf = itf,
};
memcpy(tx_msg.buf, rx_buf, rx_size);
xQueueSend(app_queue, &tx_msg, 0);
} else {
ESP_LOGE(TAG, "Read Error");
}
}
/**
* @brief CDC device line change callback
*
* CDC device signals, that the DTR, RTS states changed
*
* @param[in] itf CDC device index
* @param[in] event CDC event type
*/
void tinyusb_cdc_line_state_changed_callback(int itf, cdcacm_event_t *event)
{
int dtr = event->line_state_changed_data.dtr;
@@ -43,12 +72,23 @@ void tinyusb_cdc_line_state_changed_callback(int itf, cdcacm_event_t *event)
void app_main(void)
{
// Create FreeRTOS primitives
app_queue = xQueueCreate(5, sizeof(app_message_t));
assert(app_queue);
app_message_t msg;
ESP_LOGI(TAG, "USB initialization");
const tinyusb_config_t tusb_cfg = {
.device_descriptor = NULL,
.string_descriptor = NULL,
.external_phy = false,
#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
};
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
@@ -80,4 +120,21 @@ void app_main(void)
#endif
ESP_LOGI(TAG, "USB initialization DONE");
while (1) {
if (xQueueReceive(app_queue, &msg, portMAX_DELAY)) {
if (msg.buf_len) {
/* Print received data*/
ESP_LOGI(TAG, "Data from channel %d:", msg.itf);
ESP_LOG_BUFFER_HEXDUMP(TAG, msg.buf, msg.buf_len, ESP_LOG_INFO);
/* write back */
tinyusb_cdcacm_write_queue(msg.itf, msg.buf, msg.buf_len);
esp_err_t err = tinyusb_cdcacm_write_flush(msg.itf, 0);
if (err != ESP_OK) {
ESP_LOGE(TAG, "CDC ACM write flush error: %s", esp_err_to_name(err));
}
}
}
}
}