Initial project setup
This commit is contained in:
@@ -0,0 +1 @@
|
||||
6a50305bc61c7a361da8c0833642be824e92dacb0a6001719a832a4e96e471bf
|
||||
107
managed_components/espressif__esp_tinyusb/CHANGELOG.md
Normal file
107
managed_components/espressif__esp_tinyusb/CHANGELOG.md
Normal file
@@ -0,0 +1,107 @@
|
||||
## 1.7.6~2
|
||||
|
||||
- esp_tinyusb: Added support for IDF 6.0 after removal of the USB component
|
||||
|
||||
## 1.7.6~1
|
||||
|
||||
- esp_tinyusb: Added documentation to README.md
|
||||
|
||||
## 1.7.6
|
||||
|
||||
- MSC: Fixed the possibility to use SD/MMC storage with large capacity (more than 4 GB)
|
||||
|
||||
## 1.7.5
|
||||
|
||||
- esp_tinyusb: Provide forward compatibility with IDF 6.0
|
||||
|
||||
## 1.7.4
|
||||
|
||||
- MSC: WL Sector runtime check during spiflash init (fix for build time error check)
|
||||
|
||||
## 1.7.3 [yanked]
|
||||
|
||||
- MSC: Improved transfer speed to SD cards and SPI flash
|
||||
|
||||
## 1.7.2
|
||||
|
||||
- esp_tinyusb: Fixed crash on logging from ISR
|
||||
- PHY: Fixed crash with external_phy=true configuration
|
||||
|
||||
## 1.7.1
|
||||
|
||||
- NCM: Changed default NTB config to decrease DRAM memory usage (fix for DRAM overflow on ESP32S2)
|
||||
|
||||
## 1.7.0 [yanked]
|
||||
|
||||
- NCM: Added possibility to configure NCM Transfer Blocks (NTB) via menuconfig
|
||||
- esp_tinyusb: Added option to select TinyUSB peripheral on esp32p4 via menuconfig (USB_PHY_SUPPORTS_P4_OTG11 in esp-idf is required)
|
||||
- esp_tinyusb: Fixed uninstall tinyusb driver with not default task configuration
|
||||
|
||||
## 1.6.0
|
||||
|
||||
- CDC-ACM: Fixed memory leak on deinit
|
||||
- esp_tinyusb: Added Teardown
|
||||
|
||||
## 1.5.0
|
||||
|
||||
- esp_tinyusb: Added DMA mode option to tinyusb DCD DWC2 configuration
|
||||
- esp_tinyusb: Changed the default affinity mask of the task to CPU1
|
||||
|
||||
## 1.4.5
|
||||
|
||||
- CDC-ACM: Fixed memory leak at VFS unregister
|
||||
- Vendor specific: Provided default configuration
|
||||
|
||||
## 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
managed_components/espressif__esp_tinyusb/CHECKSUMS.json
Normal file
1
managed_components/espressif__esp_tinyusb/CHECKSUMS.json
Normal file
File diff suppressed because one or more lines are too long
59
managed_components/espressif__esp_tinyusb/CMakeLists.txt
Normal file
59
managed_components/espressif__esp_tinyusb/CMakeLists.txt
Normal file
@@ -0,0 +1,59 @@
|
||||
set(srcs
|
||||
"descriptors_control.c"
|
||||
"tinyusb.c"
|
||||
"usb_descriptors.c"
|
||||
)
|
||||
|
||||
set(priv_req "")
|
||||
|
||||
if(${IDF_VERSION_MAJOR} LESS 6)
|
||||
list(APPEND priv_req "usb")
|
||||
endif()
|
||||
|
||||
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 ${priv_req}
|
||||
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")
|
||||
379
managed_components/espressif__esp_tinyusb/Kconfig
Normal file
379
managed_components/espressif__esp_tinyusb/Kconfig
Normal file
@@ -0,0 +1,379 @@
|
||||
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
|
||||
prompt "USB Peripheral"
|
||||
default TINYUSB_RHPORT_HS if IDF_TARGET_ESP32P4
|
||||
default TINYUSB_RHPORT_FS
|
||||
help
|
||||
Allows set the USB Peripheral Controller for TinyUSB.
|
||||
|
||||
- High-speed (USB OTG2.0 Peripheral for High-, Full- and Low-speed)
|
||||
- Full-speed (USB OTG1.1 Peripheral for Full- and Low-speed)
|
||||
|
||||
config TINYUSB_RHPORT_HS
|
||||
bool "OTG2.0"
|
||||
depends on IDF_TARGET_ESP32P4
|
||||
config TINYUSB_RHPORT_FS
|
||||
bool "OTG1.1"
|
||||
endchoice
|
||||
|
||||
menu "TinyUSB DCD"
|
||||
choice TINYUSB_MODE
|
||||
prompt "DCD Mode"
|
||||
default TINYUSB_MODE_DMA
|
||||
help
|
||||
TinyUSB DCD DWC2 Driver supports two modes: Slave mode (based on IRQ) and Buffer DMA mode.
|
||||
|
||||
config TINYUSB_MODE_SLAVE
|
||||
bool "Slave/IRQ"
|
||||
config TINYUSB_MODE_DMA
|
||||
bool "Buffer DMA"
|
||||
endchoice
|
||||
endmenu # "TinyUSB DCD"
|
||||
|
||||
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_CPU1 if !FREERTOS_UNICORE
|
||||
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 # "TinyUSB task configuration"
|
||||
|
||||
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 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
||||
default 8192 if IDF_TARGET_ESP32P4
|
||||
range 64 8192 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
||||
range 64 32768 if IDF_TARGET_ESP32P4
|
||||
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.
|
||||
|
||||
menu "TinyUSB FAT Format Options"
|
||||
choice TINYUSB_FAT_FORMAT_TYPE
|
||||
prompt "FatFS Format Type"
|
||||
default TINYUSB_FAT_FORMAT_ANY
|
||||
help
|
||||
Select the FAT filesystem type used when formatting storage.
|
||||
|
||||
config TINYUSB_FAT_FORMAT_ANY
|
||||
bool "FM_ANY - Automatically select from FAT12/FAT16/FAT32"
|
||||
|
||||
config TINYUSB_FAT_FORMAT_FAT
|
||||
bool "FM_FAT - Allow only FAT12/FAT16"
|
||||
|
||||
config TINYUSB_FAT_FORMAT_FAT32
|
||||
bool "FM_FAT32 - Force FAT32 only"
|
||||
|
||||
config TINYUSB_FAT_FORMAT_EXFAT
|
||||
bool "FM_EXFAT - Force exFAT (requires exFAT enabled)"
|
||||
|
||||
endchoice
|
||||
|
||||
config TINYUSB_FAT_FORMAT_SFD
|
||||
bool "FM_SFD - Use SFD (no partition table)"
|
||||
default n
|
||||
help
|
||||
Format as a Super Floppy Disk (no partition table).
|
||||
This is typical for USB flash drives and small volumes.
|
||||
endmenu
|
||||
|
||||
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
|
||||
|
||||
config TINYUSB_NCM_OUT_NTB_BUFFS_COUNT
|
||||
int "Number of NCM NTB buffers for reception side"
|
||||
depends on TINYUSB_NET_MODE_NCM
|
||||
default 3
|
||||
range 1 6
|
||||
help
|
||||
Number of NTB buffers for reception side.
|
||||
Can be increased to improve performance and stability with the cost of additional RAM requirements.
|
||||
Helps to mitigate "tud_network_can_xmit: request blocked" warning message when running NCM device.
|
||||
|
||||
config TINYUSB_NCM_IN_NTB_BUFFS_COUNT
|
||||
int "Number of NCM NTB buffers for transmission side"
|
||||
depends on TINYUSB_NET_MODE_NCM
|
||||
default 3
|
||||
range 1 6
|
||||
help
|
||||
Number of NTB buffers for transmission side.
|
||||
Can be increased to improve performance and stability with the cost of additional RAM requirements.
|
||||
Helps to mitigate "tud_network_can_xmit: request blocked" warning message when running NCM device.
|
||||
|
||||
config TINYUSB_NCM_OUT_NTB_BUFF_MAX_SIZE
|
||||
int "NCM NTB Buffer size for reception size"
|
||||
depends on TINYUSB_NET_MODE_NCM
|
||||
default 3200
|
||||
range 1600 10240
|
||||
help
|
||||
Size of NTB buffers on the reception side. The minimum size used by Linux is 2048 bytes.
|
||||
NTB buffer size must be significantly larger than the MTU (Maximum Transmission Unit).
|
||||
The typical default MTU size for Ethernet is 1500 bytes, plus an additional packet overhead.
|
||||
To improve performance, the NTB buffer size should be large enough to fit multiple MTU-sized
|
||||
frames in a single NTB buffer and it's length should be multiple of 4.
|
||||
|
||||
config TINYUSB_NCM_IN_NTB_BUFF_MAX_SIZE
|
||||
int "NCM NTB Buffer size for transmission size"
|
||||
depends on TINYUSB_NET_MODE_NCM
|
||||
default 3200
|
||||
range 1600 10240
|
||||
help
|
||||
Size of NTB buffers on the transmission side. The minimum size used by Linux is 2048 bytes.
|
||||
NTB buffer size must be significantly larger than the MTU (Maximum Transmission Unit).
|
||||
The typical default MTU size for Ethernet is 1500 bytes, plus an additional packet overhead.
|
||||
To improve performance, the NTB buffer size should be large enough to fit multiple MTU-sized
|
||||
frames in a single NTB buffer and it's length should be multiple of 4.
|
||||
|
||||
endmenu # "Network driver (ECM/NCM/RNDIS)"
|
||||
|
||||
menu "Vendor Specific Interface"
|
||||
config TINYUSB_VENDOR_COUNT
|
||||
int "TinyUSB Vendor specific interfaces count"
|
||||
default 0
|
||||
range 0 2
|
||||
help
|
||||
Setting value greater than 0 will enable TinyUSB Vendor specific feature.
|
||||
endmenu # "Vendor Specific Interface"
|
||||
endmenu # "TinyUSB Stack"
|
||||
202
managed_components/espressif__esp_tinyusb/LICENSE
Normal file
202
managed_components/espressif__esp_tinyusb/LICENSE
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
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.
|
||||
166
managed_components/espressif__esp_tinyusb/README.md
Normal file
166
managed_components/espressif__esp_tinyusb/README.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# Espressif's additions to TinyUSB
|
||||
|
||||
[](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.
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Hardware-related documentation could be found in [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/usb_device.html).
|
||||
|
||||
### Device Stack Structure
|
||||
|
||||
The Device Stack is built on top of TinyUSB and provides:
|
||||
|
||||
- Custom USB descriptor support
|
||||
- Serial device (CDC-ACM) support
|
||||
- Standard stream redirection through the serial device
|
||||
- Storage media support (SPI-Flash and SD-Card) for USB MSC Class
|
||||
- A dedicated task for TinyUSB servicing
|
||||
|
||||
### Configuration Options
|
||||
|
||||
Configure the Device Stack using `menuconfig`:
|
||||
|
||||
- TinyUSB log verbosity
|
||||
- Device Stack task options
|
||||
- Default device/string descriptor options
|
||||
- Class-specific options
|
||||
|
||||
### Descriptor Configuration
|
||||
|
||||
Configure USB descriptors using the `tinyusb_config_t` structure:
|
||||
|
||||
- `device_descriptor`
|
||||
- `string_descriptor`
|
||||
- `configuration_descriptor` (full-speed)
|
||||
- For high-speed devices: `fs_configuration_descriptor`, `hs_configuration_descriptor`, `qualifier_descriptor`
|
||||
|
||||
If any descriptor field is set to `NULL`, default descriptors (based on menuconfig) are used.
|
||||
|
||||
### Installation
|
||||
|
||||
Install the Device Stack by calling `tinyusb_driver_install` with a `tinyusb_config_t` structure. Members set to `0` or `NULL` use default values.
|
||||
|
||||
```c
|
||||
const tinyusb_config_t partial_init = {
|
||||
.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
|
||||
};
|
||||
```
|
||||
|
||||
### Self-Powered Device
|
||||
|
||||
Self-powered devices must monitor VBUS voltage. Use a GPIO pin with a voltage divider or comparator to detect VBUS state. Set `self_powered = true` and assign the VBUS monitor GPIO in `tinyusb_config_t`.
|
||||
|
||||
### USB Serial Device (CDC-ACM)
|
||||
|
||||
If enabled, initialize the USB Serial Device with `tusb_cdc_acm_init` and a `tinyusb_config_cdcacm_t` structure:
|
||||
|
||||
```c
|
||||
const tinyusb_config_cdcacm_t acm_cfg = {
|
||||
.usb_dev = TINYUSB_USBDEV_0,
|
||||
.cdc_port = TINYUSB_CDC_ACM_0,
|
||||
.rx_unread_buf_sz = 64,
|
||||
.callback_rx = NULL,
|
||||
.callback_rx_wanted_char = NULL,
|
||||
.callback_line_state_changed = NULL,
|
||||
.callback_line_coding_changed = NULL
|
||||
};
|
||||
tusb_cdc_acm_init(&acm_cfg);
|
||||
```
|
||||
|
||||
Redirect standard I/O streams to USB with `esp_tusb_init_console` and revert with `esp_tusb_deinit_console`.
|
||||
|
||||
### USB Mass Storage Device (MSC)
|
||||
|
||||
If enabled, initialize storage media for MSC:
|
||||
|
||||
**SPI-Flash Example:**
|
||||
```c
|
||||
static esp_err_t storage_init_spiflash(wl_handle_t *wl_handle) {
|
||||
// ... partition and mount logic ...
|
||||
}
|
||||
storage_init_spiflash(&wl_handle);
|
||||
|
||||
const tinyusb_msc_spiflash_config_t config_spi = {
|
||||
.wl_handle = wl_handle
|
||||
};
|
||||
tinyusb_msc_storage_init_spiflash(&config_spi);
|
||||
```
|
||||
|
||||
**SD-Card Example:**
|
||||
```c
|
||||
static esp_err_t storage_init_sdmmc(sdmmc_card_t **card) {
|
||||
// ... SDMMC host and slot config ...
|
||||
}
|
||||
storage_init_sdmmc(&card);
|
||||
|
||||
const tinyusb_msc_sdmmc_config_t config_sdmmc = {
|
||||
.card = card
|
||||
};
|
||||
tinyusb_msc_storage_init_sdmmc(&config_sdmmc);
|
||||
```
|
||||
|
||||
### MSC Performance Optimization
|
||||
|
||||
- **Single-buffer approach:** Buffer size is set via `CONFIG_TINYUSB_MSC_BUFSIZE`.
|
||||
- **Performance:** SD cards offer higher throughput than internal SPI flash due to architectural constraints.
|
||||
|
||||
**Performance Table (ESP32-S3):**
|
||||
|
||||
| FIFO Size | Read Speed | Write Speed |
|
||||
|-----------|------------|-------------|
|
||||
| 512 B | 0.566 MB/s | 0.236 MB/s |
|
||||
| 8192 B | 0.925 MB/s | 0.928 MB/s |
|
||||
|
||||
**Performance Table (ESP32-P4):**
|
||||
|
||||
| FIFO Size | Read Speed | Write Speed |
|
||||
|-----------|------------|-------------|
|
||||
| 512 B | 1.174 MB/s | 0.238 MB/s |
|
||||
| 8192 B | 4.744 MB/s | 2.157 MB/s |
|
||||
| 32768 B | 5.998 MB/s | 4.485 MB/s |
|
||||
|
||||
**Performance Table (ESP32-S2, SPI Flash):**
|
||||
|
||||
| FIFO Size | Write Speed |
|
||||
|-----------|-------------|
|
||||
| 512 B | 5.59 KB/s |
|
||||
| 8192 B | 21.54 KB/s |
|
||||
|
||||
**Note:** Internal SPI flash is for demonstration only; use SD cards or external flash for higher performance.
|
||||
|
||||
## Examples
|
||||
You can find examples in [ESP-IDF on GitHub](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb/device).
|
||||
109
managed_components/espressif__esp_tinyusb/cdc.c
Normal file
109
managed_components/espressif__esp_tinyusb/cdc.c
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
294
managed_components/espressif__esp_tinyusb/descriptors_control.c
Normal file
294
managed_components/espressif__esp_tinyusb/descriptors_control.c
Normal file
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* 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 must be provided for the following classes
|
||||
#if (CFG_TUD_HID > 0 || CFG_TUD_MIDI > 0 || CFG_TUD_ECM_RNDIS > 0 || CFG_TUD_DFU > 0 || CFG_TUD_DFU_RUNTIME > 0 || CFG_TUD_BTH > 0)
|
||||
ESP_GOTO_ON_FALSE(config->configuration_descriptor, ESP_ERR_INVALID_ARG, fail, TAG, "Configuration descriptor must be provided for this device");
|
||||
#else
|
||||
ESP_LOGW(TAG, "No FullSpeed configuration descriptor provided, using default.");
|
||||
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 must be provided for the following classes
|
||||
#if (CFG_TUD_HID > 0 || CFG_TUD_MIDI > 0 || CFG_TUD_ECM_RNDIS > 0 || CFG_TUD_DFU > 0 || CFG_TUD_DFU_RUNTIME > 0 || CFG_TUD_BTH > 0)
|
||||
ESP_GOTO_ON_FALSE(config->hs_configuration_descriptor, ESP_ERR_INVALID_ARG, fail, TAG, "HighSpeed configuration descriptor must be provided for this device");
|
||||
#else
|
||||
ESP_LOGW(TAG, "No HighSpeed configuration descriptor provided, using default.");
|
||||
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
|
||||
}
|
||||
13
managed_components/espressif__esp_tinyusb/idf_component.yml
Normal file
13
managed_components/espressif__esp_tinyusb/idf_component.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
dependencies:
|
||||
idf: '>=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: c0be948c1acc6ee1f7ef00ab183c571971fccefd
|
||||
path: device/esp_tinyusb
|
||||
url: https://github.com/espressif/esp-usb/tree/master/device/esp_tinyusb
|
||||
version: 1.7.6~2
|
||||
85
managed_components/espressif__esp_tinyusb/include/tinyusb.h
Normal file
85
managed_components/espressif__esp_tinyusb/include/tinyusb.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* @brief This is an all-in-one helper function, including:
|
||||
* 1. Stops the task to handle usb events
|
||||
* 2. TinyUSB stack tearing down
|
||||
* 2. Freeing resources after descriptors preparation
|
||||
* 3. Deletes USB PHY
|
||||
*
|
||||
* @retval ESP_FAIL Uninstall driver or tinyusb stack failed because of internal error
|
||||
* @retval ESP_OK Uninstall driver, tinyusb stack and USB PHY successfully
|
||||
*/
|
||||
esp_err_t tinyusb_driver_uninstall(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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
|
||||
206
managed_components/espressif__esp_tinyusb/include/tusb_cdc_acm.h
Normal file
206
managed_components/espressif__esp_tinyusb/include/tusb_cdc_acm.h
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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
|
||||
185
managed_components/espressif__esp_tinyusb/include/tusb_config.h
Normal file
185
managed_components/espressif__esp_tinyusb/include/tusb_config.h
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019 Ha Thach (tinyusb.org),
|
||||
* SPDX-FileContributor: 2020-2025 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_VENDOR_COUNT
|
||||
# define CONFIG_TINYUSB_VENDOR_COUNT 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
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// DCD DWC2 Mode
|
||||
// ------------------------------------------------------------------------
|
||||
#define CFG_TUD_DWC2_SLAVE_ENABLE 1 // Enable Slave/IRQ by default
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// DMA & Cache
|
||||
// ------------------------------------------------------------------------
|
||||
#ifdef CONFIG_TINYUSB_MODE_DMA
|
||||
// DMA Mode has a priority over Slave/IRQ mode and will be used if hardware supports it
|
||||
#define CFG_TUD_DWC2_DMA_ENABLE 1 // Enable DMA
|
||||
|
||||
#if CONFIG_CACHE_L1_CACHE_LINE_SIZE
|
||||
// To enable the dcd_dcache clean/invalidate/clean_invalidate calls
|
||||
# define CFG_TUD_MEM_DCACHE_ENABLE 1
|
||||
#define CFG_TUD_MEM_DCACHE_LINE_SIZE CONFIG_CACHE_L1_CACHE_LINE_SIZE
|
||||
// NOTE: starting with esp-idf v5.3 there is specific attribute present: DRAM_DMA_ALIGNED_ATTR
|
||||
# define CFG_TUSB_MEM_SECTION __attribute__((aligned(CONFIG_CACHE_L1_CACHE_LINE_SIZE))) DRAM_ATTR
|
||||
#else
|
||||
# define CFG_TUD_MEM_CACHE_ENABLE 0
|
||||
# define CFG_TUSB_MEM_SECTION TU_ATTR_ALIGNED(4) DRAM_ATTR
|
||||
#endif // CONFIG_CACHE_L1_CACHE_LINE_SIZE
|
||||
#endif // CONFIG_TINYUSB_MODE_DMA
|
||||
|
||||
#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
|
||||
#define CFG_TUSB_DEBUG_PRINTF esp_rom_printf // TinyUSB can print logs from ISR, so we must use esp_rom_printf()
|
||||
|
||||
// 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
|
||||
#define CFG_TUD_VENDOR_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
#define CFG_TUD_VENDOR_TX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
|
||||
|
||||
// DFU macros
|
||||
#define CFG_TUD_DFU_XFER_BUFSIZE CONFIG_TINYUSB_DFU_BUFSIZE
|
||||
|
||||
// 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_VENDOR CONFIG_TINYUSB_VENDOR_COUNT
|
||||
#define CFG_TUD_ECM_RNDIS CONFIG_TINYUSB_NET_MODE_ECM_RNDIS
|
||||
#define CFG_TUD_NCM CONFIG_TINYUSB_NET_MODE_NCM
|
||||
#define CFG_TUD_DFU CONFIG_TINYUSB_DFU_MODE_DFU
|
||||
#define CFG_TUD_DFU_RUNTIME CONFIG_TINYUSB_DFU_MODE_DFU_RUNTIME
|
||||
#define CFG_TUD_BTH CONFIG_TINYUSB_BTH_ENABLED
|
||||
|
||||
// NCM NET Mode NTB buffers configuration
|
||||
#define CFG_TUD_NCM_OUT_NTB_N CONFIG_TINYUSB_NCM_OUT_NTB_BUFFS_COUNT
|
||||
#define CFG_TUD_NCM_IN_NTB_N CONFIG_TINYUSB_NCM_IN_NTB_BUFFS_COUNT
|
||||
#define CFG_TUD_NCM_OUT_NTB_MAX_SIZE CONFIG_TINYUSB_NCM_OUT_NTB_BUFF_MAX_SIZE
|
||||
#define CFG_TUD_NCM_IN_NTB_MAX_SIZE CONFIG_TINYUSB_NCM_IN_NTB_BUFF_MAX_SIZE
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2025 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_NOT_SUPPORTED, if wear leveling sector size CONFIG_WL_SECTOR_SIZE is bigger than
|
||||
* the tinyusb MSC buffer size CONFIG_TINYUSB_MSC_BUFSIZE
|
||||
*/
|
||||
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
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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
|
||||
2
managed_components/espressif__esp_tinyusb/sbom.yml
Normal file
2
managed_components/espressif__esp_tinyusb/sbom.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
supplier: 'Organization: Espressif Systems (Shanghai) CO LTD'
|
||||
originator: 'Organization: Espressif Systems (Shanghai) CO LTD'
|
||||
@@ -0,0 +1,7 @@
|
||||
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)
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
131
managed_components/espressif__esp_tinyusb/test_apps/README.md
Normal file
131
managed_components/espressif__esp_tinyusb/test_apps/README.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# CI target runner setup
|
||||
|
||||
To allow a Docker container, running on a CI target runner, to access USB devices connected to the CI target runner, some modifications must be made.
|
||||
In our case, it's an `RPI` target runner.
|
||||
|
||||
The main idea comes from this response on [stackoverflow](https://stackoverflow.com/a/66427245/19840830). The same approach is also recommended in the official Docker [documentation](https://docs.docker.com/reference/cli/docker/container/run/#device-cgroup-rule)
|
||||
|
||||
|
||||
### Following changes shall be made on a CI target runner
|
||||
|
||||
- [`UDEV rules`](#udev-rules)
|
||||
- [`Docker tty script`](#docker-tty-script)
|
||||
- [`Logging`](#logging)
|
||||
- [`Running a docker container`](#running-a-docker-container)
|
||||
- [`GitHub CI target runner setup`](#github-ci-target-runner-setup)
|
||||
- [`GitLab CI target runner setup`](#gitlab-ci-target-runner-setup)
|
||||
|
||||
## UDEV rules
|
||||
|
||||
- This UDEV rule will trigger a `docker_tty.sh` script every time a USB device is connected, disconnected, or enumerated by the host machine (CI target runner)
|
||||
- Location: `/etc/udev/rules.d/99-docker-tty.rules`
|
||||
- `99-docker-tty.rules` file content:
|
||||
|
||||
``` sh
|
||||
ACTION=="add", SUBSYSTEM=="tty", RUN+="/usr/local/bin/docker_tty.sh 'added' '%E{DEVNAME}' '%M' '%m'"
|
||||
ACTION=="remove", SUBSYSTEM=="tty", RUN+="/usr/local/bin/docker_tty.sh 'removed' '%E{DEVNAME}' '%M' '%m'"
|
||||
```
|
||||
|
||||
## Docker tty script
|
||||
|
||||
- This `.sh` script, triggered by the UDEV rule above, will propagate USB devices to a running Docker container.
|
||||
- Location: `/usr/local/bin/docker_tty.sh`
|
||||
- `docker_tty.sh` file content:
|
||||
|
||||
``` sh
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Log the USB event with parameters
|
||||
echo "USB event: $1 $2 $3 $4" >> /tmp/docker_tty.log
|
||||
|
||||
# Find a running Docker container (using the first one found)
|
||||
docker_name=$(docker ps --format "{{.Names}}" | head -n 1)
|
||||
|
||||
# Check if a container was found
|
||||
if [ ! -z "$docker_name" ]; then
|
||||
if [ "$1" == "added" ]; then
|
||||
docker exec -u 0 "$docker_name" mknod $2 c $3 $4
|
||||
docker exec -u 0 "$docker_name" chmod -R 777 $2
|
||||
echo "Adding $2 to Docker container $docker_name" >> /tmp/docker_tty.log
|
||||
else
|
||||
docker exec -u 0 "$docker_name" rm $2
|
||||
echo "Removing $2 from Docker container $docker_name" >> /tmp/docker_tty.log
|
||||
fi
|
||||
else
|
||||
echo "No running Docker containers found." >> /tmp/docker_tty.log
|
||||
fi
|
||||
```
|
||||
|
||||
### Making the script executable
|
||||
|
||||
Don't forget to make the created script executable:
|
||||
``` sh
|
||||
root@~$ chmod +x /usr/local/bin/docker_tty.sh
|
||||
```
|
||||
|
||||
## Logging
|
||||
|
||||
- The `docker_tty.sh` script logs information about the USB devices it processes.
|
||||
- Location: `/tmp/docker_tty.log`
|
||||
- Example of a log from the `docker_tty.log` file, showing a flow of the `pytest_usb_device.py` test
|
||||
|
||||
```
|
||||
USB event: added /dev/ttyACM0 166 0
|
||||
USB event: added /dev/ttyACM1 166 1
|
||||
Adding /dev/ttyACM0 to Docker container d5e5c774174b435b8befea864f8fcb7f_python311bookworm_6a975d
|
||||
Adding /dev/ttyACM1 to Docker container d5e5c774174b435b8befea864f8fcb7f_python311bookworm_6a975d
|
||||
USB event: removed /dev/ttyACM0 166 0
|
||||
USB event: removed /dev/ttyACM1 166 1
|
||||
```
|
||||
|
||||
## Running a docker container
|
||||
|
||||
### Check Major and Minor numbers of connected devices
|
||||
|
||||
Check the Major and Minor numbers assigned by the Linux kernel to devices that you want the Docker container to access.
|
||||
In our case, we want to access `/dev/ttyUSB0`, `/dev/ttyACM0` and `/dev/ttyACM1`
|
||||
|
||||
`/dev/ttyUSB0`: Major 188, Minor 0
|
||||
``` sh
|
||||
peter@BrnoRPIG007:~ $ ls -l /dev/ttyUSB0
|
||||
crw-rw-rw- 1 root dialout 188, 0 Nov 12 11:08 /dev/ttyUSB0
|
||||
```
|
||||
|
||||
`/dev/ttyACM0` and `/dev/ttyACM1`: Major 166, Minor 0 (1)
|
||||
``` sh
|
||||
peter@BrnoRPIG007:~ $ ls -l /dev/ttyACM0
|
||||
crw-rw---- 1 root dialout 166, 0 Nov 13 10:26 /dev/ttyACM0
|
||||
peter@BrnoRPIG007:~ $ ls -l /dev/ttyACM1
|
||||
crw-rw---- 1 root dialout 166, 1 Nov 13 10:26 /dev/ttyACM1
|
||||
```
|
||||
|
||||
### Run a docker container
|
||||
|
||||
Run a Docker container with the following extra options:
|
||||
``` sh
|
||||
docker run --device-cgroup-rule='c 188:* rmw' --device-cgroup-rule='c 166:* rmw' --privileged ..
|
||||
```
|
||||
- `--device-cgroup-rule='c 188:* rmw'`: allow access to `ttyUSBx` (Major 188, all Minors)
|
||||
- `--device-cgroup-rule='c 166:* rmw'`: allow access to `ttyACMx` (Major 166, all Minors)
|
||||
|
||||
## GitHub CI target runner setup
|
||||
|
||||
To apply these changes to a GitHub target runner a `.yml` file used to run a Docker container for pytest must be modified. The Docker container is then run with the following options:
|
||||
|
||||
``` yaml
|
||||
container:
|
||||
image: python:3.11-bookworm
|
||||
options: --privileged --device-cgroup-rule="c 188:* rmw" --device-cgroup-rule="c 166:* rmw"
|
||||
```
|
||||
|
||||
## GitLab CI target runner setup
|
||||
|
||||
To apply these changes to a GitLab runner the `config.toml` file located at `/etc/gitlab-runner/config.toml` on each GitLab target runner must be modified.
|
||||
|
||||
According to GitLab's [documentation](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runnersdocker-section) the `[runners.docker]` section of the `config.toml` file should include the `device_cgroup_rules` parameter:
|
||||
|
||||
``` toml
|
||||
[runners.docker]
|
||||
...
|
||||
device_cgroup_rules = ["c 188:* rmw", "c 166:* rmw"]
|
||||
```
|
||||
@@ -0,0 +1,9 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
set(COMPONENTS main)
|
||||
|
||||
project(test_app_cdc)
|
||||
@@ -0,0 +1,4 @@
|
||||
idf_component_register(SRC_DIRS .
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES unity
|
||||
WHOLE_ARCHIVE)
|
||||
@@ -0,0 +1,5 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_tinyusb:
|
||||
version: "*"
|
||||
override_path: "../../../"
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "unity_test_utils_memory.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/*
|
||||
_ _ _
|
||||
| | (_) | |
|
||||
___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__
|
||||
/ _ \/ __| '_ \| __| | '_ \| | | | | | / __| '_ \
|
||||
| __/\__ \ |_) | |_| | | | | |_| | |_| \__ \ |_) |
|
||||
\___||___/ .__/ \__|_|_| |_|\__, |\__,_|___/_.__/
|
||||
| |______ __/ |
|
||||
|_|______| |___/
|
||||
_____ _____ _____ _____
|
||||
|_ _| ___/ ___|_ _|
|
||||
| | | |__ \ `--. | |
|
||||
| | | __| `--. \ | |
|
||||
| | | |___/\__/ / | |
|
||||
\_/ \____/\____/ \_/
|
||||
*/
|
||||
|
||||
printf(" _ _ _ \n");
|
||||
printf(" | | (_) | | \n");
|
||||
printf(" ___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__ \n");
|
||||
printf(" / _ \\/ __| '_ \\| __| | '_ \\| | | | | | / __| '_ \\ \n");
|
||||
printf("| __/\\__ \\ |_) | |_| | | | | |_| | |_| \\__ \\ |_) |\n");
|
||||
printf(" \\___||___/ .__/ \\__|_|_| |_|\\__, |\\__,_|___/_.__/ \n");
|
||||
printf(" | |______ __/ | \n");
|
||||
printf(" |_|______| |___/ \n");
|
||||
printf(" _____ _____ _____ _____ \n");
|
||||
printf("|_ _| ___/ ___|_ _| \n");
|
||||
printf(" | | | |__ \\ `--. | | \n");
|
||||
printf(" | | | __| `--. \\ | | \n");
|
||||
printf(" | | | |___/\\__/ / | | \n");
|
||||
printf(" \\_/ \\____/\\____/ \\_/ \n");
|
||||
|
||||
unity_utils_setup_heap_record(80);
|
||||
unity_utils_set_leak_level(128);
|
||||
unity_run_menu();
|
||||
}
|
||||
|
||||
/* setUp runs before every test */
|
||||
void setUp(void)
|
||||
{
|
||||
unity_utils_record_free_mem();
|
||||
}
|
||||
|
||||
/* tearDown runs after every test */
|
||||
void tearDown(void)
|
||||
{
|
||||
unity_utils_evaluate_leaks();
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_USB_OTG_SUPPORTED
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "esp_system.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#include "unity.h"
|
||||
#include "tinyusb.h"
|
||||
#include "tusb_cdc_acm.h"
|
||||
#include "vfs_tinyusb.h"
|
||||
|
||||
#define VFS_PATH "/dev/usb-cdc1"
|
||||
|
||||
static const tusb_desc_device_t cdc_device_descriptor = {
|
||||
.bLength = sizeof(cdc_device_descriptor),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE,
|
||||
.bcdUSB = 0x0200,
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.idVendor = USB_ESPRESSIF_VID,
|
||||
.idProduct = 0x4002,
|
||||
.bcdDevice = 0x0100,
|
||||
.iManufacturer = 0x01,
|
||||
.iProduct = 0x02,
|
||||
.iSerialNumber = 0x03,
|
||||
.bNumConfigurations = 0x01
|
||||
};
|
||||
|
||||
static const uint16_t cdc_desc_config_len = TUD_CONFIG_DESC_LEN + CFG_TUD_CDC * TUD_CDC_DESC_LEN;
|
||||
static const uint8_t cdc_desc_configuration[] = {
|
||||
TUD_CONFIG_DESCRIPTOR(1, 4, 0, cdc_desc_config_len, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
|
||||
TUD_CDC_DESCRIPTOR(0, 4, 0x81, 8, 0x02, 0x82, (TUD_OPT_HIGH_SPEED ? 512 : 64)),
|
||||
TUD_CDC_DESCRIPTOR(2, 4, 0x83, 8, 0x04, 0x84, (TUD_OPT_HIGH_SPEED ? 512 : 64)),
|
||||
};
|
||||
|
||||
#if (TUD_OPT_HIGH_SPEED)
|
||||
static const tusb_desc_device_qualifier_t device_qualifier = {
|
||||
.bLength = sizeof(tusb_desc_device_qualifier_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
|
||||
.bcdUSB = 0x0200,
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.bNumConfigurations = 0x01,
|
||||
.bReserved = 0
|
||||
};
|
||||
#endif // TUD_OPT_HIGH_SPEED
|
||||
|
||||
static void tinyusb_cdc_rx_callback(int itf, cdcacm_event_t *event)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TinyUSB CDC testcase
|
||||
*
|
||||
* This is not a 'standard' testcase, as it never exits. The testcase runs in a loop where it echoes back all the data received.
|
||||
*
|
||||
* - Init TinyUSB with standard CDC device and configuration descriptors
|
||||
* - Init 2 CDC-ACM interfaces
|
||||
* - Map CDC1 to Virtual File System
|
||||
* - In a loop: Read data from CDC0 and CDC1. Echo received data back
|
||||
*
|
||||
* Note: CDC0 appends 'novfs' to echoed data, so the host (test runner) can easily determine which port is which.
|
||||
*/
|
||||
TEST_CASE("tinyusb_cdc", "[esp_tinyusb][cdc]")
|
||||
{
|
||||
// Install TinyUSB driver
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.device_descriptor = &cdc_device_descriptor,
|
||||
.string_descriptor = NULL,
|
||||
.string_descriptor_count = 0,
|
||||
.external_phy = false,
|
||||
#if (TUD_OPT_HIGH_SPEED)
|
||||
.fs_configuration_descriptor = cdc_desc_configuration,
|
||||
.hs_configuration_descriptor = cdc_desc_configuration,
|
||||
.qualifier_descriptor = &device_qualifier,
|
||||
#else
|
||||
.configuration_descriptor = cdc_desc_configuration,
|
||||
#endif // TUD_OPT_HIGH_SPEED
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));
|
||||
|
||||
tinyusb_config_cdcacm_t acm_cfg = {
|
||||
.usb_dev = TINYUSB_USBDEV_0,
|
||||
.cdc_port = TINYUSB_CDC_ACM_0,
|
||||
.rx_unread_buf_sz = 64,
|
||||
.callback_rx = &tinyusb_cdc_rx_callback,
|
||||
.callback_rx_wanted_char = NULL,
|
||||
.callback_line_state_changed = NULL,
|
||||
.callback_line_coding_changed = NULL
|
||||
};
|
||||
|
||||
// Init CDC 0
|
||||
TEST_ASSERT_FALSE(tusb_cdc_acm_initialized(TINYUSB_CDC_ACM_0));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, tusb_cdc_acm_init(&acm_cfg));
|
||||
TEST_ASSERT_TRUE(tusb_cdc_acm_initialized(TINYUSB_CDC_ACM_0));
|
||||
|
||||
// Init CDC 1
|
||||
acm_cfg.cdc_port = TINYUSB_CDC_ACM_1;
|
||||
acm_cfg.callback_rx = NULL;
|
||||
TEST_ASSERT_FALSE(tusb_cdc_acm_initialized(TINYUSB_CDC_ACM_1));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, tusb_cdc_acm_init(&acm_cfg));
|
||||
TEST_ASSERT_TRUE(tusb_cdc_acm_initialized(TINYUSB_CDC_ACM_1));
|
||||
|
||||
// Install VFS to CDC 1
|
||||
TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_tusb_cdc_register(TINYUSB_CDC_ACM_1, VFS_PATH));
|
||||
esp_vfs_tusb_cdc_set_rx_line_endings(ESP_LINE_ENDINGS_CRLF);
|
||||
esp_vfs_tusb_cdc_set_tx_line_endings(ESP_LINE_ENDINGS_LF);
|
||||
FILE *cdc = fopen(VFS_PATH, "r+");
|
||||
TEST_ASSERT_NOT_NULL(cdc);
|
||||
|
||||
uint8_t buf[CONFIG_TINYUSB_CDC_RX_BUFSIZE + 1];
|
||||
while (true) {
|
||||
size_t b = fread(buf, 1, sizeof(buf), cdc);
|
||||
if (b > 0) {
|
||||
printf("Intf VFS, RX %d bytes\n", b);
|
||||
//ESP_LOG_BUFFER_HEXDUMP("test", buf, b, ESP_LOG_INFO);
|
||||
fwrite(buf, 1, b, cdc);
|
||||
}
|
||||
vTaskDelay(1);
|
||||
|
||||
size_t rx_size = 0;
|
||||
int itf = 0;
|
||||
ESP_ERROR_CHECK(tinyusb_cdcacm_read(itf, buf, CONFIG_TINYUSB_CDC_RX_BUFSIZE, &rx_size));
|
||||
if (rx_size > 0) {
|
||||
printf("Intf %d, RX %d bytes\n", itf, rx_size);
|
||||
|
||||
// Add 'novfs' to reply so the host can identify the port
|
||||
strcpy((char *)&buf[rx_size - 2], "novfs\r\n");
|
||||
tinyusb_cdcacm_write_queue(itf, buf, rx_size + sizeof("novfs") - 1);
|
||||
|
||||
tinyusb_cdcacm_write_flush(itf, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import pytest
|
||||
from pytest_embedded_idf.dut import IdfDut
|
||||
from time import sleep
|
||||
from serial import Serial, SerialException
|
||||
from serial.tools.list_ports import comports
|
||||
|
||||
|
||||
@pytest.mark.esp32s2
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.esp32p4
|
||||
@pytest.mark.usb_device
|
||||
def test_usb_device_cdc(dut) -> None:
|
||||
'''
|
||||
Running the test locally:
|
||||
1. Build the testa app for your DUT (ESP32-S2 or S3)
|
||||
2. Connect you DUT to your test runner (local machine) with USB port and flashing port
|
||||
3. Run `pytest --target esp32s3`
|
||||
|
||||
Test procedure:
|
||||
1. Run the test on the DUT
|
||||
2. Expect 2 Virtual COM Ports in the system
|
||||
3. Open both comports and send some data. Expect echoed data
|
||||
'''
|
||||
dut.expect_exact('Press ENTER to see the list of tests.')
|
||||
dut.write('[cdc]')
|
||||
dut.expect_exact('TinyUSB: TinyUSB Driver installed')
|
||||
sleep(2) # Some time for the OS to enumerate our USB device
|
||||
|
||||
# Find devices with Espressif TinyUSB VID/PID
|
||||
s = []
|
||||
ports = comports()
|
||||
|
||||
for port, _, hwid in ports:
|
||||
if '303A:4002' in hwid:
|
||||
s.append(port)
|
||||
|
||||
if len(s) != 2:
|
||||
raise Exception('TinyUSB COM port not found')
|
||||
|
||||
try:
|
||||
with Serial(s[0]) as cdc0:
|
||||
with Serial(s[1]) as cdc1:
|
||||
# Write dummy string and check for echo
|
||||
cdc0.write('text\r\n'.encode())
|
||||
res = cdc0.readline()
|
||||
assert b'text' in res
|
||||
if b'novfs' in res:
|
||||
novfs_cdc = cdc0
|
||||
vfs_cdc = cdc1
|
||||
|
||||
cdc1.write('text\r\n'.encode())
|
||||
res = cdc1.readline()
|
||||
assert b'text' in res
|
||||
if b'novfs' in res:
|
||||
novfs_cdc = cdc1
|
||||
vfs_cdc = cdc0
|
||||
|
||||
# Write more than MPS, check that the transfer is not divided
|
||||
novfs_cdc.write(bytes(100))
|
||||
dut.expect_exact("Intf 0, RX 100 bytes")
|
||||
|
||||
# Write more than RX buffer, check correct reception
|
||||
novfs_cdc.write(bytes(600))
|
||||
transfer_len1 = int(dut.expect(r'Intf 0, RX (\d+) bytes')[1].decode())
|
||||
transfer_len2 = int(dut.expect(r'Intf 0, RX (\d+) bytes')[1].decode())
|
||||
assert transfer_len1 + transfer_len2 == 600
|
||||
|
||||
# The VFS is setup for CRLF RX and LF TX
|
||||
vfs_cdc.write('text\r\n'.encode())
|
||||
res = vfs_cdc.readline()
|
||||
assert b'text\n' in res
|
||||
|
||||
return
|
||||
|
||||
except SerialException as e:
|
||||
print(f"SerialException occurred: {e}")
|
||||
raise
|
||||
|
||||
except Exception as e:
|
||||
print(f"An unexpected error occurred: {e}")
|
||||
raise
|
||||
@@ -0,0 +1,18 @@
|
||||
# Configure TinyUSB, it will be used to mock USB devices
|
||||
CONFIG_TINYUSB_MSC_ENABLED=n
|
||||
CONFIG_TINYUSB_CDC_ENABLED=y
|
||||
CONFIG_TINYUSB_CDC_COUNT=2
|
||||
CONFIG_TINYUSB_HID_COUNT=0
|
||||
|
||||
# Disable watchdogs, they'd get triggered during unity interactive menu
|
||||
CONFIG_ESP_INT_WDT=n
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
|
||||
# Run-time checks of Heap and Stack
|
||||
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
|
||||
CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y
|
||||
CONFIG_COMPILER_STACK_CHECK=y
|
||||
|
||||
CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y
|
||||
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
@@ -0,0 +1,9 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
set(COMPONENTS main)
|
||||
|
||||
project(test_app_configuration_descriptor)
|
||||
@@ -0,0 +1,4 @@
|
||||
idf_component_register(SRC_DIRS .
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES unity
|
||||
WHOLE_ARCHIVE)
|
||||
@@ -0,0 +1,5 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_tinyusb:
|
||||
version: "*"
|
||||
override_path: "../../../"
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "unity_test_utils_memory.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/*
|
||||
_ _ _
|
||||
| | (_) | |
|
||||
___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__
|
||||
/ _ \/ __| '_ \| __| | '_ \| | | | | | / __| '_ \
|
||||
| __/\__ \ |_) | |_| | | | | |_| | |_| \__ \ |_) |
|
||||
\___||___/ .__/ \__|_|_| |_|\__, |\__,_|___/_.__/
|
||||
| |______ __/ |
|
||||
|_|______| |___/
|
||||
_____ _____ _____ _____
|
||||
|_ _| ___/ ___|_ _|
|
||||
| | | |__ \ `--. | |
|
||||
| | | __| `--. \ | |
|
||||
| | | |___/\__/ / | |
|
||||
\_/ \____/\____/ \_/
|
||||
*/
|
||||
|
||||
printf(" _ _ _ \n");
|
||||
printf(" | | (_) | | \n");
|
||||
printf(" ___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__ \n");
|
||||
printf(" / _ \\/ __| '_ \\| __| | '_ \\| | | | | | / __| '_ \\ \n");
|
||||
printf("| __/\\__ \\ |_) | |_| | | | | |_| | |_| \\__ \\ |_) |\n");
|
||||
printf(" \\___||___/ .__/ \\__|_|_| |_|\\__, |\\__,_|___/_.__/ \n");
|
||||
printf(" | |______ __/ | \n");
|
||||
printf(" |_|______| |___/ \n");
|
||||
printf(" _____ _____ _____ _____ \n");
|
||||
printf("|_ _| ___/ ___|_ _| \n");
|
||||
printf(" | | | |__ \\ `--. | | \n");
|
||||
printf(" | | | __| `--. \\ | | \n");
|
||||
printf(" | | | |___/\\__/ / | | \n");
|
||||
printf(" \\_/ \\____/\\____/ \\_/ \n");
|
||||
|
||||
unity_utils_setup_heap_record(80);
|
||||
unity_utils_set_leak_level(128);
|
||||
unity_run_menu();
|
||||
}
|
||||
|
||||
/* setUp runs before every test */
|
||||
void setUp(void)
|
||||
{
|
||||
unity_utils_record_free_mem();
|
||||
}
|
||||
|
||||
/* tearDown runs after every test */
|
||||
void tearDown(void)
|
||||
{
|
||||
unity_utils_evaluate_leaks();
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 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 "unity.h"
|
||||
#include "tinyusb.h"
|
||||
|
||||
static const char *TAG = "config_test";
|
||||
|
||||
#define TEARDOWN_DEVICE_ATTACH_TIMEOUT_MS 1000
|
||||
#define TEARDOWN_DEVICE_DETACH_DELAY_MS 1000
|
||||
|
||||
// ========================= TinyUSB descriptors ===============================
|
||||
|
||||
// Here we need to create dual CDC device, to match the CONFIG_TINYUSB_CDC_COUNT from sdkconfig.defaults
|
||||
static const uint16_t cdc_desc_config_len = TUD_CONFIG_DESC_LEN + CFG_TUD_CDC * TUD_CDC_DESC_LEN;
|
||||
static const uint8_t test_fs_configuration_descriptor[] = {
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, CFG_TUD_CDC * 2, 0, cdc_desc_config_len, TUSB_DESC_CONFIG_ATT_SELF_POWERED | TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
|
||||
TUD_CDC_DESCRIPTOR(0, 4, 0x81, 8, 0x02, 0x82, 64),
|
||||
#if CFG_TUD_CDC > 1
|
||||
TUD_CDC_DESCRIPTOR(2, 4, 0x83, 8, 0x04, 0x84, 64),
|
||||
#endif
|
||||
};
|
||||
|
||||
#if (TUD_OPT_HIGH_SPEED)
|
||||
static const uint8_t test_hs_configuration_descriptor[] = {
|
||||
// Config number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, 4, 0, cdc_desc_config_len, TUSB_DESC_CONFIG_ATT_SELF_POWERED | TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
|
||||
TUD_CDC_DESCRIPTOR(0, 4, 0x81, 8, 0x02, 0x82, 512),
|
||||
TUD_CDC_DESCRIPTOR(2, 4, 0x83, 8, 0x04, 0x84, 512),
|
||||
};
|
||||
|
||||
static const tusb_desc_device_qualifier_t device_qualifier = {
|
||||
.bLength = sizeof(tusb_desc_device_qualifier_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
|
||||
.bcdUSB = 0x0200,
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.bNumConfigurations = 0x01,
|
||||
.bReserved = 0
|
||||
};
|
||||
#endif // TUD_OPT_HIGH_SPEED
|
||||
|
||||
static 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 wait_mount = NULL;
|
||||
|
||||
/**
|
||||
* @brief Creates test additional resources.
|
||||
*
|
||||
* Is called before start test to create/init internal resources.
|
||||
*/
|
||||
static bool __test_init(void)
|
||||
{
|
||||
wait_mount = xSemaphoreCreateBinary();
|
||||
return (wait_mount != NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Indicates device connection event.
|
||||
*
|
||||
* Is called in tud_mount callback.
|
||||
*/
|
||||
static void __test_conn(void)
|
||||
{
|
||||
if (wait_mount) {
|
||||
xSemaphoreGive(wait_mount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Awaits device connection event.
|
||||
*
|
||||
* Is used for waiting the event in test logic.
|
||||
* Timeout could be configured via TEARDOWN_DEVICE_ATTACH_TIMEOUT_MS define.
|
||||
*/
|
||||
static esp_err_t __test_wait_conn(void)
|
||||
{
|
||||
if (!wait_mount) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
return ( xSemaphoreTake(wait_mount, pdMS_TO_TICKS(TEARDOWN_DEVICE_ATTACH_TIMEOUT_MS))
|
||||
? ESP_OK
|
||||
: ESP_ERR_TIMEOUT );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Releases test resources.
|
||||
*
|
||||
* Is called in the end of the test to release internal resources.
|
||||
*/
|
||||
static void __test_release(void)
|
||||
{
|
||||
if (wait_mount) {
|
||||
vSemaphoreDelete(wait_mount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief One round of setup configuration for TinyUSB.
|
||||
*
|
||||
* Steps:
|
||||
* 1. Init internal resources
|
||||
* 2. Installs TinyUSB with provided configuration
|
||||
* 3. Waits for the connection event from the Host
|
||||
* 4. Gives some extra time for Host to configure the device (configures via TEARDOWN_DEVICE_DETACH_DELAY_MS)
|
||||
* 5. Uninstall TinyUSB
|
||||
* 6. Release internal resources
|
||||
*
|
||||
* Is called in the end of the test to release internal resources.
|
||||
*/
|
||||
static void __test_tinyusb_set_config(const tinyusb_config_t *tusb_cfg)
|
||||
{
|
||||
TEST_ASSERT_EQUAL(true, __test_init());
|
||||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(tusb_cfg));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, __test_wait_conn());
|
||||
vTaskDelay(pdMS_TO_TICKS(TEARDOWN_DEVICE_DETACH_DELAY_MS));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall());
|
||||
__test_release();
|
||||
}
|
||||
|
||||
// ========================== Callbacks ========================================
|
||||
/**
|
||||
* @brief TinyUSB callback for device mount.
|
||||
*
|
||||
* @note
|
||||
* For Linux-based Hosts: Reflects the SetConfiguration() request from the Host Driver.
|
||||
* For Win-based Hosts: SetConfiguration() request is present only with available Class in device descriptor.
|
||||
*/
|
||||
void tud_mount_cb(void)
|
||||
{
|
||||
ESP_LOGD(TAG, "%s", __FUNCTION__);
|
||||
__test_conn();
|
||||
}
|
||||
|
||||
// ============================= Tests =========================================
|
||||
/**
|
||||
* @brief TinyUSB Configuration test case.
|
||||
*
|
||||
* Verifies:
|
||||
* Configuration without specifying any parameters.
|
||||
* Configuration & descriptors are provided by esp_tinyusb wrapper.
|
||||
*/
|
||||
TEST_CASE("descriptors_config_all_default", "[esp_tinyusb][usb_device][config]")
|
||||
{
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.external_phy = false,
|
||||
.device_descriptor = NULL,
|
||||
.configuration_descriptor = NULL,
|
||||
#if (CONFIG_TINYUSB_RHPORT_HS)
|
||||
.hs_configuration_descriptor = NULL,
|
||||
#endif // CONFIG_TINYUSB_RHPORT_HS
|
||||
};
|
||||
// Install TinyUSB driver
|
||||
__test_tinyusb_set_config(&tusb_cfg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TinyUSB Configuration test case.
|
||||
*
|
||||
* Verifies:
|
||||
* Configuration with specifying only device descriptor.
|
||||
* For High-speed qualifier descriptor as well.
|
||||
*/
|
||||
TEST_CASE("descriptors_config_device", "[esp_tinyusb][usb_device][config]")
|
||||
{
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.external_phy = false,
|
||||
.device_descriptor = &test_device_descriptor,
|
||||
.configuration_descriptor = NULL,
|
||||
#if (CONFIG_TINYUSB_RHPORT_HS)
|
||||
.hs_configuration_descriptor = NULL,
|
||||
.qualifier_descriptor = &device_qualifier,
|
||||
#endif // CONFIG_TINYUSB_RHPORT_HS
|
||||
};
|
||||
__test_tinyusb_set_config(&tusb_cfg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TinyUSB Configuration test case.
|
||||
*
|
||||
* Verifies:
|
||||
* Configuration with specifying device & configuration descriptors.
|
||||
*
|
||||
* For High-speed:
|
||||
* - HS configuration descriptor is not provided by user (legacy compatibility) and default HS config descriptor is using when possible.
|
||||
* - Qualifier descriptor.
|
||||
*/
|
||||
TEST_CASE("descriptors_config_device_and_config", "[esp_tinyusb][usb_device][config]")
|
||||
{
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.external_phy = false,
|
||||
.device_descriptor = &test_device_descriptor,
|
||||
.configuration_descriptor = test_fs_configuration_descriptor,
|
||||
#if (CONFIG_TINYUSB_RHPORT_HS)
|
||||
.hs_configuration_descriptor = NULL,
|
||||
.qualifier_descriptor = &device_qualifier,
|
||||
#endif // CONFIG_TINYUSB_RHPORT_HS
|
||||
};
|
||||
__test_tinyusb_set_config(&tusb_cfg);
|
||||
}
|
||||
|
||||
#if (CONFIG_IDF_TARGET_ESP32P4)
|
||||
/**
|
||||
* @brief TinyUSB High-speed Configuration test case.
|
||||
*
|
||||
* Verifies:
|
||||
* Configuration with specifying device & HS configuration descriptor only.
|
||||
* FS configuration descriptor is not provided by user (legacy compatibility) and default configuration descriptor for FS is using when possible.
|
||||
*/
|
||||
TEST_CASE("descriptors_config_device_and_hs_config_only", "[esp_tinyusb][usb_device][config]")
|
||||
{
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.external_phy = false,
|
||||
.device_descriptor = &test_device_descriptor,
|
||||
.configuration_descriptor = NULL,
|
||||
.hs_configuration_descriptor = test_hs_configuration_descriptor,
|
||||
.qualifier_descriptor = &device_qualifier,
|
||||
};
|
||||
__test_tinyusb_set_config(&tusb_cfg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TinyUSB High-speed Configuration test case.
|
||||
*
|
||||
* Verifies:
|
||||
* Configuration with specifying all descriptors by user.
|
||||
*/
|
||||
TEST_CASE("descriptors_config_all_configured", "[esp_tinyusb][usb_device][config]")
|
||||
{
|
||||
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,
|
||||
.qualifier_descriptor = &device_qualifier,
|
||||
};
|
||||
__test_tinyusb_set_config(&tusb_cfg);
|
||||
}
|
||||
#endif // CONFIG_IDF_TARGET_ESP32P4
|
||||
|
||||
#endif // SOC_USB_OTG_SUPPORTED
|
||||
@@ -0,0 +1,13 @@
|
||||
# SPDX-FileCopyrightText: 2024-2025 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.esp32p4
|
||||
@pytest.mark.usb_device
|
||||
def test_usb_device_configuration(dut: IdfDut) -> None:
|
||||
dut.run_all_single_board_cases(group='config')
|
||||
@@ -0,0 +1,16 @@
|
||||
# Configure TinyUSB, it will be used to mock USB devices
|
||||
CONFIG_TINYUSB_CDC_ENABLED=y
|
||||
CONFIG_TINYUSB_CDC_COUNT=2
|
||||
|
||||
# Disable watchdogs, they'd get triggered during unity interactive menu
|
||||
CONFIG_ESP_INT_WDT=n
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
|
||||
# Run-time checks of Heap and Stack
|
||||
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
|
||||
CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y
|
||||
CONFIG_COMPILER_STACK_CHECK=y
|
||||
|
||||
CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y
|
||||
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
@@ -0,0 +1,9 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
set(COMPONENTS main)
|
||||
|
||||
project(test_app_dconn_detection)
|
||||
@@ -0,0 +1,4 @@
|
||||
idf_component_register(SRC_DIRS .
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES unity
|
||||
WHOLE_ARCHIVE)
|
||||
@@ -0,0 +1,5 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_tinyusb:
|
||||
version: "*"
|
||||
override_path: "../../../"
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "unity_test_utils_memory.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/*
|
||||
_ _ _
|
||||
| | (_) | |
|
||||
___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__
|
||||
/ _ \/ __| '_ \| __| | '_ \| | | | | | / __| '_ \
|
||||
| __/\__ \ |_) | |_| | | | | |_| | |_| \__ \ |_) |
|
||||
\___||___/ .__/ \__|_|_| |_|\__, |\__,_|___/_.__/
|
||||
| |______ __/ |
|
||||
|_|______| |___/
|
||||
_____ _____ _____ _____
|
||||
|_ _| ___/ ___|_ _|
|
||||
| | | |__ \ `--. | |
|
||||
| | | __| `--. \ | |
|
||||
| | | |___/\__/ / | |
|
||||
\_/ \____/\____/ \_/
|
||||
*/
|
||||
|
||||
printf(" _ _ _ \n");
|
||||
printf(" | | (_) | | \n");
|
||||
printf(" ___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__ \n");
|
||||
printf(" / _ \\/ __| '_ \\| __| | '_ \\| | | | | | / __| '_ \\ \n");
|
||||
printf("| __/\\__ \\ |_) | |_| | | | | |_| | |_| \\__ \\ |_) |\n");
|
||||
printf(" \\___||___/ .__/ \\__|_|_| |_|\\__, |\\__,_|___/_.__/ \n");
|
||||
printf(" | |______ __/ | \n");
|
||||
printf(" |_|______| |___/ \n");
|
||||
printf(" _____ _____ _____ _____ \n");
|
||||
printf("|_ _| ___/ ___|_ _| \n");
|
||||
printf(" | | | |__ \\ `--. | | \n");
|
||||
printf(" | | | __| `--. \\ | | \n");
|
||||
printf(" | | | |___/\\__/ / | | \n");
|
||||
printf(" \\_/ \\____/\\____/ \\_/ \n");
|
||||
|
||||
unity_utils_setup_heap_record(80);
|
||||
unity_utils_set_leak_level(128);
|
||||
unity_run_menu();
|
||||
}
|
||||
|
||||
/* setUp runs before every test */
|
||||
void setUp(void)
|
||||
{
|
||||
unity_utils_record_free_mem();
|
||||
}
|
||||
|
||||
/* tearDown runs after every test */
|
||||
void tearDown(void)
|
||||
{
|
||||
unity_utils_evaluate_leaks();
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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
|
||||
};
|
||||
|
||||
#if (TUD_OPT_HIGH_SPEED)
|
||||
static const tusb_desc_device_qualifier_t device_qualifier = {
|
||||
.bLength = sizeof(tusb_desc_device_qualifier_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
|
||||
.bcdUSB = 0x0200,
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.bNumConfigurations = 0x01,
|
||||
.bReserved = 0
|
||||
};
|
||||
#endif // TUD_OPT_HIGH_SPEED
|
||||
|
||||
// Invoked when device is mounted
|
||||
void tud_mount_cb(void)
|
||||
{
|
||||
/**
|
||||
* @attention Tests relying on this callback only pass on Linux USB Host!
|
||||
*
|
||||
* This callback is issued after SetConfiguration command from USB Host.
|
||||
* However, Windows issues SetConfiguration only after a USB driver was assigned to the device.
|
||||
* So in case you are implementing a Vendor Specific class, or your device has 0 interfaces, this callback is not issued on Windows host.
|
||||
*/
|
||||
printf("%s\n", __FUNCTION__);
|
||||
dev_mounted++;
|
||||
}
|
||||
|
||||
// Invoked when device is unmounted
|
||||
void tud_umount_cb(void)
|
||||
{
|
||||
printf("%s\n", __FUNCTION__);
|
||||
dev_umounted++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TinyUSB Disconnect Detection test case
|
||||
*
|
||||
* This is specific artificial test for verifying the disconnection detection event.
|
||||
* Normally, this event comes as a result of detaching USB device from the port and disappearing the VBUS voltage.
|
||||
* In this test case, we use GPIO matrix and connect the signal to the ZERO or ONE constant inputs.
|
||||
* Connection to constant ONE input emulates the attachment to the USB Host port (appearing VBUS).
|
||||
* Connection to constant ZERO input emulates the detachment from the USB Host port (removing VBUS).
|
||||
*
|
||||
* Test logic:
|
||||
* - Install TinyUSB Device stack without any class
|
||||
* - In cycle:
|
||||
* - Emulate the detachment, get the tud_umount_cb(), increase the dev_umounted value
|
||||
* - Emulate the attachment, get the tud_mount_cb(), increase the dev_mounted value
|
||||
* - Verify that dev_umounted == dev_mounted
|
||||
* - Verify that dev_mounted == DEVICE_DETACH_TEST_ROUNDS, where DEVICE_DETACH_TEST_ROUNDS - amount of rounds
|
||||
* - Uninstall TinyUSB Device stack
|
||||
*
|
||||
*/
|
||||
TEST_CASE("dconn_detection", "[esp_tinyusb][dconn]")
|
||||
{
|
||||
unsigned int rounds = DEVICE_DETACH_TEST_ROUNDS;
|
||||
|
||||
// Install TinyUSB driver
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.device_descriptor = &test_device_descriptor,
|
||||
.string_descriptor = NULL,
|
||||
.string_descriptor_count = 0,
|
||||
.external_phy = false,
|
||||
#if (TUD_OPT_HIGH_SPEED)
|
||||
.fs_configuration_descriptor = test_configuration_descriptor,
|
||||
.hs_configuration_descriptor = test_configuration_descriptor,
|
||||
.qualifier_descriptor = &device_qualifier,
|
||||
#else
|
||||
.configuration_descriptor = test_configuration_descriptor,
|
||||
#endif // TUD_OPT_HIGH_SPEED
|
||||
};
|
||||
|
||||
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());
|
||||
}
|
||||
#endif // SOC_USB_OTG_SUPPORTED
|
||||
@@ -0,0 +1,13 @@
|
||||
# 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.esp32p4
|
||||
#@pytest.mark.usb_device Disable in CI: unavailable teardown for P4
|
||||
def test_usb_device_dconn_detection(dut: IdfDut) -> None:
|
||||
dut.run_all_single_board_cases(group='dconn')
|
||||
@@ -0,0 +1,18 @@
|
||||
# Configure TinyUSB, it will be used to mock USB devices
|
||||
CONFIG_TINYUSB_MSC_ENABLED=n
|
||||
CONFIG_TINYUSB_CDC_ENABLED=n
|
||||
CONFIG_TINYUSB_CDC_COUNT=0
|
||||
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
|
||||
@@ -0,0 +1,9 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
set(COMPONENTS main)
|
||||
|
||||
project(test_app_default_task_without_init)
|
||||
@@ -0,0 +1,4 @@
|
||||
idf_component_register(SRC_DIRS .
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES unity
|
||||
WHOLE_ARCHIVE)
|
||||
@@ -0,0 +1,5 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_tinyusb:
|
||||
version: "*"
|
||||
override_path: "../../../"
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "unity_test_utils_memory.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/*
|
||||
_ _ _
|
||||
| | (_) | |
|
||||
___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__
|
||||
/ _ \/ __| '_ \| __| | '_ \| | | | | | / __| '_ \
|
||||
| __/\__ \ |_) | |_| | | | | |_| | |_| \__ \ |_) |
|
||||
\___||___/ .__/ \__|_|_| |_|\__, |\__,_|___/_.__/
|
||||
| |______ __/ |
|
||||
|_|______| |___/
|
||||
_____ _____ _____ _____
|
||||
|_ _| ___/ ___|_ _|
|
||||
| | | |__ \ `--. | |
|
||||
| | | __| `--. \ | |
|
||||
| | | |___/\__/ / | |
|
||||
\_/ \____/\____/ \_/
|
||||
*/
|
||||
|
||||
printf(" _ _ _ \n");
|
||||
printf(" | | (_) | | \n");
|
||||
printf(" ___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__ \n");
|
||||
printf(" / _ \\/ __| '_ \\| __| | '_ \\| | | | | | / __| '_ \\ \n");
|
||||
printf("| __/\\__ \\ |_) | |_| | | | | |_| | |_| \\__ \\ |_) |\n");
|
||||
printf(" \\___||___/ .__/ \\__|_|_| |_|\\__, |\\__,_|___/_.__/ \n");
|
||||
printf(" | |______ __/ | \n");
|
||||
printf(" |_|______| |___/ \n");
|
||||
printf(" _____ _____ _____ _____ \n");
|
||||
printf("|_ _| ___/ ___|_ _| \n");
|
||||
printf(" | | | |__ \\ `--. | | \n");
|
||||
printf(" | | | __| `--. \\ | | \n");
|
||||
printf(" | | | |___/\\__/ / | | \n");
|
||||
printf(" \\_/ \\____/\\____/ \\_/ \n");
|
||||
|
||||
unity_utils_setup_heap_record(80);
|
||||
unity_utils_set_leak_level(128);
|
||||
unity_run_menu();
|
||||
}
|
||||
|
||||
/* setUp runs before every test */
|
||||
void setUp(void)
|
||||
{
|
||||
unity_utils_record_free_mem();
|
||||
}
|
||||
|
||||
/* tearDown runs after every test */
|
||||
void tearDown(void)
|
||||
{
|
||||
unity_utils_evaluate_leaks();
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 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 "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
//
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
//
|
||||
#include "unity.h"
|
||||
#include "tinyusb.h"
|
||||
|
||||
static const char *TAG = "default_task";
|
||||
|
||||
SemaphoreHandle_t wait_mount = NULL;
|
||||
|
||||
#define TEARDOWN_DEVICE_DELAY_MS 1000
|
||||
|
||||
#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN)
|
||||
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
|
||||
};
|
||||
|
||||
#if (TUD_OPT_HIGH_SPEED)
|
||||
static const tusb_desc_device_qualifier_t device_qualifier = {
|
||||
.bLength = sizeof(tusb_desc_device_qualifier_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
|
||||
.bcdUSB = 0x0200,
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.bNumConfigurations = 0x01,
|
||||
.bReserved = 0
|
||||
};
|
||||
#endif // TUD_OPT_HIGH_SPEED
|
||||
|
||||
// Invoked when device is mounted
|
||||
void tud_mount_cb(void)
|
||||
{
|
||||
xSemaphoreGive(wait_mount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TinyUSB Task specific testcase
|
||||
*
|
||||
* Scenario:
|
||||
* 1. Install TinyUSB driver
|
||||
* 2. Wait tud_mount_cb() until TUSB_DEVICE_DELAY_MS
|
||||
* 3. Wait TUSB_DEVICE_DELAY_MS
|
||||
* 4. Teardown TinyUSB
|
||||
* 5. Release resources
|
||||
*/
|
||||
TEST_CASE("tinyusb_default_task_with_init", "[esp_tinyusb][tusb_task]")
|
||||
{
|
||||
wait_mount = xSemaphoreCreateBinary();
|
||||
TEST_ASSERT_NOT_EQUAL(NULL, wait_mount);
|
||||
|
||||
// TinyUSB driver configuration
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.device_descriptor = &test_device_descriptor,
|
||||
.string_descriptor = NULL,
|
||||
.string_descriptor_count = 0,
|
||||
.external_phy = false,
|
||||
#if (TUD_OPT_HIGH_SPEED)
|
||||
.fs_configuration_descriptor = test_configuration_descriptor,
|
||||
.hs_configuration_descriptor = test_configuration_descriptor,
|
||||
.qualifier_descriptor = &device_qualifier,
|
||||
#else
|
||||
.configuration_descriptor = test_configuration_descriptor,
|
||||
#endif // TUD_OPT_HIGH_SPEED
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));
|
||||
// Wait for the usb event
|
||||
ESP_LOGD(TAG, "wait mount...");
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(wait_mount, pdMS_TO_TICKS(TEARDOWN_DEVICE_DELAY_MS)));
|
||||
ESP_LOGD(TAG, "mounted");
|
||||
|
||||
// Teardown
|
||||
vTaskDelay(pdMS_TO_TICKS(TEARDOWN_DEVICE_DELAY_MS));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall());
|
||||
// Remove primitives
|
||||
vSemaphoreDelete(wait_mount);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
# SPDX-FileCopyrightText: 2024-2025 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.esp32p4
|
||||
@pytest.mark.usb_device
|
||||
def test_usb_default_task_with_init(dut: IdfDut) -> None:
|
||||
dut.run_all_single_board_cases(group='tusb_task')
|
||||
@@ -0,0 +1,16 @@
|
||||
# Configure TinyUSB
|
||||
CONFIG_TINYUSB_NO_DEFAULT_TASK=n
|
||||
CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK=n
|
||||
|
||||
# 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
|
||||
@@ -0,0 +1,9 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
set(COMPONENTS main)
|
||||
|
||||
project(test_app_default_task_with_init)
|
||||
@@ -0,0 +1,4 @@
|
||||
idf_component_register(SRC_DIRS .
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES unity
|
||||
WHOLE_ARCHIVE)
|
||||
@@ -0,0 +1,5 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_tinyusb:
|
||||
version: "*"
|
||||
override_path: "../../../"
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "unity_test_utils_memory.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/*
|
||||
_ _ _
|
||||
| | (_) | |
|
||||
___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__
|
||||
/ _ \/ __| '_ \| __| | '_ \| | | | | | / __| '_ \
|
||||
| __/\__ \ |_) | |_| | | | | |_| | |_| \__ \ |_) |
|
||||
\___||___/ .__/ \__|_|_| |_|\__, |\__,_|___/_.__/
|
||||
| |______ __/ |
|
||||
|_|______| |___/
|
||||
_____ _____ _____ _____
|
||||
|_ _| ___/ ___|_ _|
|
||||
| | | |__ \ `--. | |
|
||||
| | | __| `--. \ | |
|
||||
| | | |___/\__/ / | |
|
||||
\_/ \____/\____/ \_/
|
||||
*/
|
||||
|
||||
printf(" _ _ _ \n");
|
||||
printf(" | | (_) | | \n");
|
||||
printf(" ___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__ \n");
|
||||
printf(" / _ \\/ __| '_ \\| __| | '_ \\| | | | | | / __| '_ \\ \n");
|
||||
printf("| __/\\__ \\ |_) | |_| | | | | |_| | |_| \\__ \\ |_) |\n");
|
||||
printf(" \\___||___/ .__/ \\__|_|_| |_|\\__, |\\__,_|___/_.__/ \n");
|
||||
printf(" | |______ __/ | \n");
|
||||
printf(" |_|______| |___/ \n");
|
||||
printf(" _____ _____ _____ _____ \n");
|
||||
printf("|_ _| ___/ ___|_ _| \n");
|
||||
printf(" | | | |__ \\ `--. | | \n");
|
||||
printf(" | | | __| `--. \\ | | \n");
|
||||
printf(" | | | |___/\\__/ / | | \n");
|
||||
printf(" \\_/ \\____/\\____/ \\_/ \n");
|
||||
|
||||
unity_utils_setup_heap_record(80);
|
||||
unity_utils_set_leak_level(128);
|
||||
unity_run_menu();
|
||||
}
|
||||
|
||||
/* setUp runs before every test */
|
||||
void setUp(void)
|
||||
{
|
||||
unity_utils_record_free_mem();
|
||||
}
|
||||
|
||||
/* tearDown runs after every test */
|
||||
void tearDown(void)
|
||||
{
|
||||
unity_utils_evaluate_leaks();
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 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 "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
//
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
//
|
||||
#include "unity.h"
|
||||
#include "tinyusb.h"
|
||||
|
||||
static const char *TAG = "default_task_with_init";
|
||||
|
||||
SemaphoreHandle_t wait_mount = NULL;
|
||||
|
||||
#define TEARDOWN_DEVICE_DELAY_MS 1000
|
||||
|
||||
#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN)
|
||||
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
|
||||
};
|
||||
|
||||
#if (TUD_OPT_HIGH_SPEED)
|
||||
static const tusb_desc_device_qualifier_t device_qualifier = {
|
||||
.bLength = sizeof(tusb_desc_device_qualifier_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
|
||||
.bcdUSB = 0x0200,
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.bNumConfigurations = 0x01,
|
||||
.bReserved = 0
|
||||
};
|
||||
#endif // TUD_OPT_HIGH_SPEED
|
||||
|
||||
// Invoked when device is mounted
|
||||
void tud_mount_cb(void)
|
||||
{
|
||||
xSemaphoreGive(wait_mount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TinyUSB Task specific testcase
|
||||
*
|
||||
* Scenario:
|
||||
* 1. Install TinyUSB driver
|
||||
* 2. Wait tud_mount_cb() until TUSB_DEVICE_DELAY_MS
|
||||
* 3. Wait TUSB_DEVICE_DELAY_MS
|
||||
* 4. Teardown TinyUSB
|
||||
* 5. Release resources
|
||||
*/
|
||||
TEST_CASE("tinyusb_default_task_with_init", "[esp_tinyusb][tusb_task]")
|
||||
{
|
||||
wait_mount = xSemaphoreCreateBinary();
|
||||
TEST_ASSERT_NOT_EQUAL(NULL, wait_mount);
|
||||
|
||||
// TinyUSB driver configuration
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.device_descriptor = &test_device_descriptor,
|
||||
.string_descriptor = NULL,
|
||||
.string_descriptor_count = 0,
|
||||
.external_phy = false,
|
||||
#if (TUD_OPT_HIGH_SPEED)
|
||||
.fs_configuration_descriptor = test_configuration_descriptor,
|
||||
.hs_configuration_descriptor = test_configuration_descriptor,
|
||||
.qualifier_descriptor = &device_qualifier,
|
||||
#else
|
||||
.configuration_descriptor = test_configuration_descriptor,
|
||||
#endif // TUD_OPT_HIGH_SPEED
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));
|
||||
// Wait for the usb event
|
||||
ESP_LOGD(TAG, "wait mount...");
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(wait_mount, pdMS_TO_TICKS(TEARDOWN_DEVICE_DELAY_MS)));
|
||||
ESP_LOGD(TAG, "mounted");
|
||||
|
||||
// Teardown
|
||||
vTaskDelay(pdMS_TO_TICKS(TEARDOWN_DEVICE_DELAY_MS));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall());
|
||||
// Remove primitives
|
||||
vSemaphoreDelete(wait_mount);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
# SPDX-FileCopyrightText: 2024-2025 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.esp32p4
|
||||
@pytest.mark.usb_device
|
||||
def test_usb_default_task_with_init(dut: IdfDut) -> None:
|
||||
dut.run_all_single_board_cases(group='tusb_task')
|
||||
@@ -0,0 +1,16 @@
|
||||
# Configure TinyUSB
|
||||
CONFIG_TINYUSB_NO_DEFAULT_TASK=n
|
||||
CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK=y
|
||||
|
||||
# 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
|
||||
@@ -0,0 +1,9 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
set(COMPONENTS main)
|
||||
|
||||
project(test_app_external_task_without_init)
|
||||
@@ -0,0 +1,4 @@
|
||||
idf_component_register(SRC_DIRS .
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES unity
|
||||
WHOLE_ARCHIVE)
|
||||
@@ -0,0 +1,5 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_tinyusb:
|
||||
version: "*"
|
||||
override_path: "../../../"
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "unity_test_utils_memory.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/*
|
||||
_ _ _
|
||||
| | (_) | |
|
||||
___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__
|
||||
/ _ \/ __| '_ \| __| | '_ \| | | | | | / __| '_ \
|
||||
| __/\__ \ |_) | |_| | | | | |_| | |_| \__ \ |_) |
|
||||
\___||___/ .__/ \__|_|_| |_|\__, |\__,_|___/_.__/
|
||||
| |______ __/ |
|
||||
|_|______| |___/
|
||||
_____ _____ _____ _____
|
||||
|_ _| ___/ ___|_ _|
|
||||
| | | |__ \ `--. | |
|
||||
| | | __| `--. \ | |
|
||||
| | | |___/\__/ / | |
|
||||
\_/ \____/\____/ \_/
|
||||
*/
|
||||
|
||||
printf(" _ _ _ \n");
|
||||
printf(" | | (_) | | \n");
|
||||
printf(" ___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__ \n");
|
||||
printf(" / _ \\/ __| '_ \\| __| | '_ \\| | | | | | / __| '_ \\ \n");
|
||||
printf("| __/\\__ \\ |_) | |_| | | | | |_| | |_| \\__ \\ |_) |\n");
|
||||
printf(" \\___||___/ .__/ \\__|_|_| |_|\\__, |\\__,_|___/_.__/ \n");
|
||||
printf(" | |______ __/ | \n");
|
||||
printf(" |_|______| |___/ \n");
|
||||
printf(" _____ _____ _____ _____ \n");
|
||||
printf("|_ _| ___/ ___|_ _| \n");
|
||||
printf(" | | | |__ \\ `--. | | \n");
|
||||
printf(" | | | __| `--. \\ | | \n");
|
||||
printf(" | | | |___/\\__/ / | | \n");
|
||||
printf(" \\_/ \\____/\\____/ \\_/ \n");
|
||||
|
||||
unity_utils_setup_heap_record(80);
|
||||
unity_utils_set_leak_level(128);
|
||||
unity_run_menu();
|
||||
}
|
||||
|
||||
/* setUp runs before every test */
|
||||
void setUp(void)
|
||||
{
|
||||
unity_utils_record_free_mem();
|
||||
}
|
||||
|
||||
/* tearDown runs after every test */
|
||||
void tearDown(void)
|
||||
{
|
||||
unity_utils_evaluate_leaks();
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 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 "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
//
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
//
|
||||
#include "unity.h"
|
||||
#include "tinyusb.h"
|
||||
|
||||
static const char *TAG = "external_task";
|
||||
|
||||
static SemaphoreHandle_t wait_mount = NULL;
|
||||
static TaskHandle_t s_test_tusb_tskh;
|
||||
|
||||
#define TUSB_DEVICE_DELAY_MS 1000
|
||||
#define TUSB_EXTERNAL_TASK_SIZE 4096
|
||||
#define TUSB_EXTERNAL_TASK_PRIO 5
|
||||
#define TUSB_EXTERNAL_TASK_AFFINITY 0x7FFFFFFF /* FREERTOS_NO_AFFINITY */
|
||||
|
||||
#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN)
|
||||
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
|
||||
};
|
||||
|
||||
#if (TUD_OPT_HIGH_SPEED)
|
||||
static const tusb_desc_device_qualifier_t device_qualifier = {
|
||||
.bLength = sizeof(tusb_desc_device_qualifier_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
|
||||
.bcdUSB = 0x0200,
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.bNumConfigurations = 0x01,
|
||||
.bReserved = 0
|
||||
};
|
||||
#endif // TUD_OPT_HIGH_SPEED
|
||||
|
||||
// Invoked when device is mounted
|
||||
void tud_mount_cb(void)
|
||||
{
|
||||
xSemaphoreGive(wait_mount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This top level thread processes all usb events and invokes callbacks
|
||||
*/
|
||||
static void test_tusb_external_task(void *arg)
|
||||
{
|
||||
ESP_LOGD(TAG, "External TinyUSB task started");
|
||||
while (1) { // RTOS forever loop
|
||||
tud_task();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TinyUSB Task specific testcase
|
||||
*
|
||||
* Scenario:
|
||||
* 1. Install TinyUSB driver
|
||||
* 2. Create external TinyUSB task for tud_task()
|
||||
* 3. Wait tud_mount_cb() until TUSB_DEVICE_DELAY_MS
|
||||
* 4. Wait TUSB_DEVICE_DELAY_MS
|
||||
* 5. Teardown TinyUSB
|
||||
* 6. Release resources
|
||||
*
|
||||
* @note If run the task before installing the tinyusb driver, the external task will lead to cpu starvation.
|
||||
*/
|
||||
TEST_CASE("tinyusb_external_task", "[esp_tinyusb][tusb_task]")
|
||||
{
|
||||
wait_mount = xSemaphoreCreateBinary();
|
||||
TEST_ASSERT_NOT_EQUAL(NULL, wait_mount);
|
||||
|
||||
// TinyUSB driver configuration
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.device_descriptor = &test_device_descriptor,
|
||||
.string_descriptor = NULL,
|
||||
.string_descriptor_count = 0,
|
||||
.external_phy = false,
|
||||
#if (TUD_OPT_HIGH_SPEED)
|
||||
.fs_configuration_descriptor = test_configuration_descriptor,
|
||||
.hs_configuration_descriptor = test_configuration_descriptor,
|
||||
.qualifier_descriptor = &device_qualifier,
|
||||
#else
|
||||
.configuration_descriptor = test_configuration_descriptor,
|
||||
#endif // TUD_OPT_HIGH_SPEED
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));
|
||||
// Create an external task for tinyusb device stack
|
||||
xTaskCreate(test_tusb_external_task,
|
||||
"TinyUSB",
|
||||
TUSB_EXTERNAL_TASK_SIZE,
|
||||
NULL,
|
||||
TUSB_EXTERNAL_TASK_PRIO,
|
||||
&s_test_tusb_tskh);
|
||||
TEST_ASSERT_NOT_NULL(s_test_tusb_tskh);
|
||||
|
||||
// Wait for the usb event
|
||||
ESP_LOGD(TAG, "wait mount...");
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(wait_mount, pdMS_TO_TICKS(TUSB_DEVICE_DELAY_MS)));
|
||||
ESP_LOGD(TAG, "mounted");
|
||||
// Teardown
|
||||
vTaskDelay(pdMS_TO_TICKS(TUSB_DEVICE_DELAY_MS));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall());
|
||||
// Remove primitives
|
||||
vTaskDelete(s_test_tusb_tskh);
|
||||
vSemaphoreDelete(wait_mount);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
# SPDX-FileCopyrightText: 2024-2025 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.esp32p4
|
||||
@pytest.mark.usb_device
|
||||
def test_usb_external_task_internal_init(dut: IdfDut) -> None:
|
||||
dut.run_all_single_board_cases(group='tusb_task')
|
||||
@@ -0,0 +1,16 @@
|
||||
# Configure TinyUSB
|
||||
CONFIG_TINYUSB_NO_DEFAULT_TASK=y
|
||||
CONFIG_TINYUSB_INIT_IN_DEFAULT_TASK=n
|
||||
|
||||
# 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
|
||||
@@ -0,0 +1,9 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
set(COMPONENTS main)
|
||||
|
||||
project(test_app_teardown_device)
|
||||
@@ -0,0 +1,4 @@
|
||||
idf_component_register(SRC_DIRS .
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES unity
|
||||
WHOLE_ARCHIVE)
|
||||
@@ -0,0 +1,5 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_tinyusb:
|
||||
version: "*"
|
||||
override_path: "../../../"
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "unity_test_utils_memory.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/*
|
||||
_ _ _
|
||||
| | (_) | |
|
||||
___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__
|
||||
/ _ \/ __| '_ \| __| | '_ \| | | | | | / __| '_ \
|
||||
| __/\__ \ |_) | |_| | | | | |_| | |_| \__ \ |_) |
|
||||
\___||___/ .__/ \__|_|_| |_|\__, |\__,_|___/_.__/
|
||||
| |______ __/ |
|
||||
|_|______| |___/
|
||||
_____ _____ _____ _____
|
||||
|_ _| ___/ ___|_ _|
|
||||
| | | |__ \ `--. | |
|
||||
| | | __| `--. \ | |
|
||||
| | | |___/\__/ / | |
|
||||
\_/ \____/\____/ \_/
|
||||
*/
|
||||
|
||||
printf(" _ _ _ \n");
|
||||
printf(" | | (_) | | \n");
|
||||
printf(" ___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__ \n");
|
||||
printf(" / _ \\/ __| '_ \\| __| | '_ \\| | | | | | / __| '_ \\ \n");
|
||||
printf("| __/\\__ \\ |_) | |_| | | | | |_| | |_| \\__ \\ |_) |\n");
|
||||
printf(" \\___||___/ .__/ \\__|_|_| |_|\\__, |\\__,_|___/_.__/ \n");
|
||||
printf(" | |______ __/ | \n");
|
||||
printf(" |_|______| |___/ \n");
|
||||
printf(" _____ _____ _____ _____ \n");
|
||||
printf("|_ _| ___/ ___|_ _| \n");
|
||||
printf(" | | | |__ \\ `--. | | \n");
|
||||
printf(" | | | __| `--. \\ | | \n");
|
||||
printf(" | | | |___/\\__/ / | | \n");
|
||||
printf(" \\_/ \\____/\\____/ \\_/ \n");
|
||||
|
||||
unity_utils_setup_heap_record(80);
|
||||
unity_utils_set_leak_level(128);
|
||||
unity_run_menu();
|
||||
}
|
||||
|
||||
/* setUp runs before every test */
|
||||
void setUp(void)
|
||||
{
|
||||
unity_utils_record_free_mem();
|
||||
}
|
||||
|
||||
/* tearDown runs after every test */
|
||||
void tearDown(void)
|
||||
{
|
||||
unity_utils_evaluate_leaks();
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* 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 "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
//
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
//
|
||||
#include "unity.h"
|
||||
#include "tinyusb.h"
|
||||
#include "tusb_cdc_acm.h"
|
||||
|
||||
static const char *TAG = "teardown";
|
||||
|
||||
SemaphoreHandle_t wait_mount = NULL;
|
||||
|
||||
#define TEARDOWN_DEVICE_INIT_DELAY_MS 1000
|
||||
#define TEARDOWN_DEVICE_ATTACH_TIMEOUT_MS 1000
|
||||
#define TEARDOWN_DEVICE_DETACH_DELAY_MS 1000
|
||||
|
||||
#define TEARDOWN_AMOUNT 10
|
||||
|
||||
#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN)
|
||||
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
|
||||
};
|
||||
|
||||
#if (TUD_OPT_HIGH_SPEED)
|
||||
static const tusb_desc_device_qualifier_t device_qualifier = {
|
||||
.bLength = sizeof(tusb_desc_device_qualifier_t),
|
||||
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
|
||||
.bcdUSB = 0x0200,
|
||||
.bDeviceClass = TUSB_CLASS_MISC,
|
||||
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
|
||||
.bDeviceProtocol = MISC_PROTOCOL_IAD,
|
||||
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
|
||||
.bNumConfigurations = 0x01,
|
||||
.bReserved = 0
|
||||
};
|
||||
#endif // TUD_OPT_HIGH_SPEED
|
||||
|
||||
// Invoked when device is mounted
|
||||
void tud_mount_cb(void)
|
||||
{
|
||||
xSemaphoreGive(wait_mount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TinyUSB Teardown specific testcase
|
||||
*
|
||||
* Scenario:
|
||||
* 1. Install TinyUSB device without any class
|
||||
* 2. Wait SetConfiguration() (tud_mount_cb)
|
||||
* 3. If attempts == 0 goto step 8
|
||||
* 4. Wait TEARDOWN_DEVICE_DETACH_DELAY_MS
|
||||
* 5. Uninstall TinyUSB device
|
||||
* 6. Wait TEARDOWN_DEVICE_INIT_DELAY_MS
|
||||
* 7. Decrease attempts by 1, goto step 3
|
||||
* 8. Wait TEARDOWN_DEVICE_DETACH_DELAY_MS
|
||||
* 9. Uninstall TinyUSB device
|
||||
*/
|
||||
TEST_CASE("tinyusb_teardown", "[esp_tinyusb][teardown]")
|
||||
{
|
||||
wait_mount = xSemaphoreCreateBinary();
|
||||
TEST_ASSERT_NOT_EQUAL(NULL, wait_mount);
|
||||
|
||||
// TinyUSB driver configuration
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.device_descriptor = &test_device_descriptor,
|
||||
.string_descriptor = NULL,
|
||||
.string_descriptor_count = 0,
|
||||
.external_phy = false,
|
||||
#if (TUD_OPT_HIGH_SPEED)
|
||||
.fs_configuration_descriptor = test_configuration_descriptor,
|
||||
.hs_configuration_descriptor = test_configuration_descriptor,
|
||||
.qualifier_descriptor = &device_qualifier,
|
||||
#else
|
||||
.configuration_descriptor = test_configuration_descriptor,
|
||||
#endif // TUD_OPT_HIGH_SPEED
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));
|
||||
// Wait for the usb event
|
||||
ESP_LOGD(TAG, "wait mount...");
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(wait_mount, pdMS_TO_TICKS(TEARDOWN_DEVICE_ATTACH_TIMEOUT_MS)));
|
||||
ESP_LOGD(TAG, "mounted");
|
||||
|
||||
// Teardown routine
|
||||
int attempts = TEARDOWN_AMOUNT;
|
||||
while (attempts--) {
|
||||
// Keep device attached
|
||||
vTaskDelay(pdMS_TO_TICKS(TEARDOWN_DEVICE_DETACH_DELAY_MS));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall());
|
||||
// Teardown
|
||||
vTaskDelay(pdMS_TO_TICKS(TEARDOWN_DEVICE_INIT_DELAY_MS));
|
||||
// Reconnect
|
||||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));
|
||||
// Wait for the usb event
|
||||
ESP_LOGD(TAG, "wait mount...");
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(wait_mount, pdMS_TO_TICKS(TEARDOWN_DEVICE_ATTACH_TIMEOUT_MS)));
|
||||
ESP_LOGD(TAG, "mounted");
|
||||
}
|
||||
|
||||
// Teardown
|
||||
vTaskDelay(pdMS_TO_TICKS(TEARDOWN_DEVICE_DETACH_DELAY_MS));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_uninstall());
|
||||
// Remove primitives
|
||||
vSemaphoreDelete(wait_mount);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,75 @@
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import pytest
|
||||
from pytest_embedded_idf.dut import IdfDut
|
||||
import subprocess
|
||||
from time import sleep, time
|
||||
|
||||
class DeviceNotFoundError(Exception):
|
||||
"""Custom exception for device not found within the timeout period."""
|
||||
pass
|
||||
|
||||
def tusb_dev_in_list(vid, pid):
|
||||
try:
|
||||
output = subprocess.check_output(["lsusb"], text=True)
|
||||
search_string = f"{vid}:{pid}"
|
||||
return search_string in output
|
||||
except Exception as e:
|
||||
print(f"Error while executing lsusb: {e}")
|
||||
raise
|
||||
|
||||
def wait_tusb_dev_appeared(vid, pid, timeout):
|
||||
start_time = time()
|
||||
while True:
|
||||
if tusb_dev_in_list(vid, pid):
|
||||
return True
|
||||
if time() - start_time > timeout:
|
||||
raise DeviceNotFoundError(f"Device with VID: 0x{vid:04x}, PID: 0x{pid:04x} not found within {timeout} seconds.")
|
||||
sleep(0.5)
|
||||
|
||||
def wait_tusb_dev_removed(vid, pid, timeout):
|
||||
start_time = time()
|
||||
while True:
|
||||
if not tusb_dev_in_list(vid, pid):
|
||||
return True
|
||||
if time() - start_time > timeout:
|
||||
raise DeviceNotFoundError(f"Device with VID: 0x{vid:04x}, PID: 0x{pid:04x} wasn't removed within {timeout} seconds.")
|
||||
sleep(0.5)
|
||||
|
||||
def tusb_device_teardown(iterations, timeout):
|
||||
TUSB_VID = "303a" # Espressif TinyUSB VID
|
||||
TUSB_PID = "4002" # Espressif TinyUSB VID
|
||||
|
||||
for i in range(iterations):
|
||||
# Wait until the device is present
|
||||
print(f"Waiting for device ...")
|
||||
wait_tusb_dev_appeared(TUSB_VID, TUSB_PID, timeout)
|
||||
print("Device detected.")
|
||||
|
||||
# Wait until the device is removed
|
||||
print("Waiting for the device to be removed...")
|
||||
wait_tusb_dev_removed(TUSB_VID, TUSB_PID, timeout)
|
||||
print("Device removed.")
|
||||
print("Monitoring completed.")
|
||||
|
||||
@pytest.mark.esp32s2
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.esp32p4
|
||||
@pytest.mark.usb_device
|
||||
def test_usb_teardown_device(dut: IdfDut) -> None:
|
||||
dut.expect_exact('Press ENTER to see the list of tests.')
|
||||
dut.write('[teardown]')
|
||||
dut.expect_exact('TinyUSB: TinyUSB Driver installed')
|
||||
sleep(2) # Some time for the OS to enumerate our USB device
|
||||
|
||||
try:
|
||||
tusb_device_teardown(10, 10) # Teardown tusb device: amount, timeout
|
||||
|
||||
except DeviceNotFoundError as e:
|
||||
print(f"Error: {e}")
|
||||
raise
|
||||
|
||||
except Exception as e:
|
||||
print(f"An unexpected error occurred: {e}")
|
||||
raise
|
||||
@@ -0,0 +1,16 @@
|
||||
# Configure TinyUSB, it will be used to mock USB devices
|
||||
CONFIG_TINYUSB_CDC_ENABLED=y
|
||||
CONFIG_TINYUSB_CDC_COUNT=1
|
||||
|
||||
# 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
|
||||
9
managed_components/espressif__esp_tinyusb/test_apps/vendor/CMakeLists.txt
vendored
Normal file
9
managed_components/espressif__esp_tinyusb/test_apps/vendor/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
set(COMPONENTS main)
|
||||
|
||||
project(test_app_vendor_specific)
|
||||
4
managed_components/espressif__esp_tinyusb/test_apps/vendor/main/CMakeLists.txt
vendored
Normal file
4
managed_components/espressif__esp_tinyusb/test_apps/vendor/main/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
idf_component_register(SRC_DIRS .
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES unity
|
||||
WHOLE_ARCHIVE)
|
||||
5
managed_components/espressif__esp_tinyusb/test_apps/vendor/main/idf_component.yml
vendored
Normal file
5
managed_components/espressif__esp_tinyusb/test_apps/vendor/main/idf_component.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/esp_tinyusb:
|
||||
version: "*"
|
||||
override_path: "../../../"
|
||||
48
managed_components/espressif__esp_tinyusb/test_apps/vendor/main/test_app_main.c
vendored
Normal file
48
managed_components/espressif__esp_tinyusb/test_apps/vendor/main/test_app_main.c
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/*
|
||||
_ _ _
|
||||
| | (_) | |
|
||||
___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__
|
||||
/ _ \/ __| '_ \| __| | '_ \| | | | | | / __| '_ \
|
||||
| __/\__ \ |_) | |_| | | | | |_| | |_| \__ \ |_) |
|
||||
\___||___/ .__/ \__|_|_| |_|\__, |\__,_|___/_.__/
|
||||
| |______ __/ |
|
||||
|_|______| |___/
|
||||
_____ _____ _____ _____
|
||||
|_ _| ___/ ___|_ _|
|
||||
| | | |__ \ `--. | |
|
||||
| | | __| `--. \ | |
|
||||
| | | |___/\__/ / | |
|
||||
\_/ \____/\____/ \_/
|
||||
*/
|
||||
|
||||
printf(" _ _ _ \n");
|
||||
printf(" | | (_) | | \n");
|
||||
printf(" ___ ___ _ __ | |_ _ _ __ _ _ _ _ ___| |__ \n");
|
||||
printf(" / _ \\/ __| '_ \\| __| | '_ \\| | | | | | / __| '_ \\ \n");
|
||||
printf("| __/\\__ \\ |_) | |_| | | | | |_| | |_| \\__ \\ |_) |\n");
|
||||
printf(" \\___||___/ .__/ \\__|_|_| |_|\\__, |\\__,_|___/_.__/ \n");
|
||||
printf(" | |______ __/ | \n");
|
||||
printf(" |_|______| |___/ \n");
|
||||
printf(" _____ _____ _____ _____ \n");
|
||||
printf("|_ _| ___/ ___|_ _| \n");
|
||||
printf(" | | | |__ \\ `--. | | \n");
|
||||
printf(" | | | __| `--. \\ | | \n");
|
||||
printf(" | | | |___/\\__/ / | | \n");
|
||||
printf(" \\_/ \\____/\\____/ \\_/ \n");
|
||||
|
||||
// We don't check memory leaks here because we cannot uninstall TinyUSB yet
|
||||
unity_run_menu();
|
||||
}
|
||||
71
managed_components/espressif__esp_tinyusb/test_apps/vendor/main/test_vendor.c
vendored
Normal file
71
managed_components/espressif__esp_tinyusb/test_apps/vendor/main/test_vendor.c
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_USB_OTG_SUPPORTED
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "esp_system.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#include "unity.h"
|
||||
#include "tinyusb.h"
|
||||
|
||||
static const char *TAG = "vendor_test";
|
||||
|
||||
char buffer_in[64];
|
||||
#if (TUSB_VERSION_MINOR >= 17)
|
||||
void tud_vendor_rx_cb(uint8_t itf, uint8_t const *buffer, uint16_t bufsize)
|
||||
#else
|
||||
void tud_vendor_rx_cb(uint8_t itf)
|
||||
#endif // TUSB_VERSION_MINOR
|
||||
{
|
||||
ESP_LOGI(TAG, "tud_vendor_rx_cb(itf=%d)", itf);
|
||||
int available = tud_vendor_n_available(itf);
|
||||
int read = tud_vendor_n_read(itf, buffer_in, available);
|
||||
ESP_LOGI(TAG, "actual read: %d. buffer message: %s", read, buffer_in);
|
||||
}
|
||||
|
||||
// Invoked when a control transfer occurred on an interface of this class
|
||||
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
|
||||
// return false to stall control endpoint (e.g unsupported request)
|
||||
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
|
||||
{
|
||||
// nothing to with DATA & ACK stage
|
||||
if (stage != CONTROL_STAGE_SETUP) {
|
||||
return true;
|
||||
}
|
||||
// stall unknown request
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief TinyUSB Vendor specific testcase
|
||||
*/
|
||||
TEST_CASE("tinyusb_vendor", "[esp_tinyusb][vendor]")
|
||||
{
|
||||
// Install TinyUSB driver
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.external_phy = false,
|
||||
.device_descriptor = NULL,
|
||||
#if (TUD_OPT_HIGH_SPEED)
|
||||
.fs_configuration_descriptor = NULL,
|
||||
.hs_configuration_descriptor = NULL,
|
||||
.qualifier_descriptor = NULL,
|
||||
#else
|
||||
.configuration_descriptor = NULL,
|
||||
#endif // TUD_OPT_HIGH_SPEED
|
||||
};
|
||||
TEST_ASSERT_EQUAL(ESP_OK, tinyusb_driver_install(&tusb_cfg));
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
96
managed_components/espressif__esp_tinyusb/test_apps/vendor/pytest_vendor.py
vendored
Normal file
96
managed_components/espressif__esp_tinyusb/test_apps/vendor/pytest_vendor.py
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import pytest
|
||||
from pytest_embedded_idf.dut import IdfDut
|
||||
import usb.core
|
||||
import usb.util
|
||||
from time import sleep
|
||||
|
||||
|
||||
def find_interface_by_index(device, interface_index):
|
||||
'''
|
||||
Function to find the interface by index
|
||||
'''
|
||||
for cfg in device:
|
||||
for intf in cfg:
|
||||
if intf.bInterfaceNumber == interface_index:
|
||||
return intf
|
||||
return None
|
||||
|
||||
|
||||
def send_data_to_intf(VID, PID, interface_index):
|
||||
'''
|
||||
Find a device, its interface and dual BULK endpoints
|
||||
Send some data to it
|
||||
'''
|
||||
# Find the USB device by VID and PID
|
||||
dev = usb.core.find(idVendor=VID, idProduct=PID)
|
||||
if dev is None:
|
||||
raise ValueError("Device not found")
|
||||
|
||||
# Find the interface by index
|
||||
intf = find_interface_by_index(dev, interface_index)
|
||||
if intf is None:
|
||||
raise ValueError(f"Interface with index {interface_index} not found")
|
||||
|
||||
if intf:
|
||||
def ep_read(len):
|
||||
try:
|
||||
return ep_in.read(len, 100)
|
||||
except:
|
||||
return None
|
||||
def ep_write(buf):
|
||||
try:
|
||||
ep_out.write(buf, 100)
|
||||
except:
|
||||
pass
|
||||
|
||||
maximum_packet_size = 64
|
||||
|
||||
ep_in = usb.util.find_descriptor(intf, custom_match = \
|
||||
lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_IN)
|
||||
|
||||
ep_out = usb.util.find_descriptor(intf, custom_match = \
|
||||
lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_OUT)
|
||||
|
||||
#print(ep_in)
|
||||
#print(ep_out)
|
||||
buf = "IF{}\n".format(interface_index).encode('utf-8')
|
||||
ep_write(bytes(buf))
|
||||
|
||||
ep_read(maximum_packet_size)
|
||||
else:
|
||||
print("NOT found")
|
||||
|
||||
|
||||
@pytest.mark.esp32s2
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.esp32p4
|
||||
#@pytest.mark.usb_device Disable in CI, for now, not possible to run this test in Docker container
|
||||
def test_usb_device_vendor(dut: IdfDut) -> None:
|
||||
'''
|
||||
Running the test locally:
|
||||
1. Build the test app for your DUT
|
||||
2. Connect you DUT to your test runner (local machine) with USB port and flashing port
|
||||
3. Run `pytest --target esp32s3`
|
||||
|
||||
Important note: On Windows you must manually assign a driver the device, otherwise it will never be configured.
|
||||
On Linux this is automatic
|
||||
|
||||
Test procedure:
|
||||
1. Run the test on the DUT
|
||||
2. Expect 2 Vendor specific interfaces in the system
|
||||
3. Send some data to it, check log output
|
||||
'''
|
||||
dut.run_all_single_board_cases(group='vendor')
|
||||
|
||||
sleep(2) # Wait until the device is enumerated
|
||||
|
||||
VID = 0x303A # Replace with your device's Vendor ID
|
||||
PID = 0x4040 # Replace with your device's Product ID
|
||||
|
||||
send_data_to_intf(VID, PID, 0)
|
||||
dut.expect_exact('vendor_test: actual read: 4. buffer message: IF0')
|
||||
send_data_to_intf(VID, PID, 1)
|
||||
dut.expect_exact('vendor_test: actual read: 4. buffer message: IF1')
|
||||
15
managed_components/espressif__esp_tinyusb/test_apps/vendor/sdkconfig.defaults
vendored
Normal file
15
managed_components/espressif__esp_tinyusb/test_apps/vendor/sdkconfig.defaults
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# Configure TinyUSB, it will be used to mock USB devices
|
||||
CONFIG_TINYUSB_VENDOR_COUNT=2
|
||||
|
||||
# Disable watchdogs, they'd get triggered during unity interactive menu
|
||||
CONFIG_ESP_INT_WDT=n
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
|
||||
# Run-time checks of Heap and Stack
|
||||
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
|
||||
CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y
|
||||
CONFIG_COMPILER_STACK_CHECK=y
|
||||
|
||||
CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y
|
||||
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
112
managed_components/espressif__esp_tinyusb/tinyusb.c
Normal file
112
managed_components/espressif__esp_tinyusb/tinyusb.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_private/usb_phy.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;
|
||||
|
||||
// For the tinyusb component without tusb_teardown() implementation
|
||||
#ifndef tusb_teardown
|
||||
# define tusb_teardown() (true)
|
||||
#endif // tusb_teardown
|
||||
|
||||
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,
|
||||
#if (USB_PHY_SUPPORTS_P4_OTG11)
|
||||
.otg_speed = (TUD_OPT_HIGH_SPEED) ? USB_PHY_SPEED_HIGH : USB_PHY_SPEED_FULL,
|
||||
#else
|
||||
#if (CONFIG_IDF_TARGET_ESP32P4 && CONFIG_TINYUSB_RHPORT_FS)
|
||||
#error "USB PHY for OTG1.1 is not supported, please update your esp-idf."
|
||||
#endif // IDF_TARGET_ESP32P4 && CONFIG_TINYUSB_RHPORT_FS
|
||||
#endif // USB_PHY_SUPPORTS_P4_OTG11
|
||||
};
|
||||
|
||||
/*
|
||||
Following ext. PHY IO configuration is here to provide compatibility with IDFv5.x releases,
|
||||
where ext. PHY IOs were mapped to predefined GPIOs.
|
||||
In reality, ESP32-S2 and ESP32-S3 can map ext. PHY IOs to any GPIOs.
|
||||
This option is implemented in esp_tinyusb v2.0.0 and later.
|
||||
*/
|
||||
usb_phy_ext_io_conf_t ext_io_conf;
|
||||
// Use memset to be compatible with IDF < 5.4.1 where suspend_n_io_num and fs_edge_sel_io_num were added
|
||||
memset(&ext_io_conf, -1, sizeof(usb_phy_ext_io_conf_t));
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
ext_io_conf.vp_io_num = 33;
|
||||
ext_io_conf.vm_io_num = 34;
|
||||
ext_io_conf.rcv_io_num = 35;
|
||||
ext_io_conf.oen_io_num = 36;
|
||||
ext_io_conf.vpo_io_num = 37;
|
||||
ext_io_conf.vmo_io_num = 38;
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
ext_io_conf.vp_io_num = 42;
|
||||
ext_io_conf.vm_io_num = 41;
|
||||
ext_io_conf.rcv_io_num = 21;
|
||||
ext_io_conf.oen_io_num = 40;
|
||||
ext_io_conf.vpo_io_num = 39;
|
||||
ext_io_conf.vmo_io_num = 38;
|
||||
#endif // IDF_TARGET_ESP32S3
|
||||
|
||||
if (config->external_phy) {
|
||||
phy_conf.target = USB_PHY_TARGET_EXT;
|
||||
phy_conf.ext_io_conf = &ext_io_conf;
|
||||
|
||||
/*
|
||||
There is a bug in esp-idf that does not allow device speed selection
|
||||
when External PHY is used.
|
||||
Remove this when proper fix is implemented in IDF-11144
|
||||
*/
|
||||
phy_conf.otg_speed = USB_PHY_SPEED_UNDEFINED;
|
||||
} 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(void)
|
||||
{
|
||||
#if !CONFIG_TINYUSB_NO_DEFAULT_TASK
|
||||
ESP_RETURN_ON_ERROR(tusb_stop_task(), TAG, "Unable to stop TinyUSB task");
|
||||
#endif // !CONFIG_TINYUSB_NO_DEFAULT_TASK
|
||||
ESP_RETURN_ON_FALSE(tusb_teardown(), ESP_ERR_NOT_FINISHED, TAG, "Unable to teardown TinyUSB");
|
||||
tinyusb_free_descriptors();
|
||||
ESP_RETURN_ON_ERROR(usb_del_phy(phy_hdl), TAG, "Unable to delete PHY");
|
||||
return ESP_OK;
|
||||
}
|
||||
174
managed_components/espressif__esp_tinyusb/tinyusb_net.c
Normal file
174
managed_components/espressif__esp_tinyusb/tinyusb_net.c
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
359
managed_components/espressif__esp_tinyusb/tusb_cdc_acm.c
Normal file
359
managed_components/espressif__esp_tinyusb/tusb_cdc_acm.c
Normal file
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2024 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;
|
||||
}
|
||||
|
||||
static esp_err_t obj_free(tinyusb_cdcacm_itf_t itf)
|
||||
{
|
||||
esp_tusb_cdc_t *cdc_inst = tinyusb_cdc_get_intf(itf);
|
||||
if (cdc_inst == NULL || cdc_inst->subclass_obj == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
free(cdc_inst->subclass_obj);
|
||||
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)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_ERROR(obj_free(itf), TAG, "obj_free failed");
|
||||
ESP_RETURN_ON_ERROR(tinyusb_cdc_deinit(itf), TAG, "tinyusb_cdc_deinit failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
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*/
|
||||
115
managed_components/espressif__esp_tinyusb/tusb_console.c
Normal file
115
managed_components/espressif__esp_tinyusb/tusb_console.c
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
732
managed_components/espressif__esp_tinyusb/tusb_msc_storage.c
Normal file
732
managed_components/espressif__esp_tinyusb/tusb_msc_storage.c
Normal file
@@ -0,0 +1,732 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2025 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 "esp_memory_utils.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "vfs_fat_internal.h"
|
||||
#include "tinyusb.h"
|
||||
#include "device/usbd_pvt.h"
|
||||
#include "class/msc/msc_device.h"
|
||||
#include "tusb_msc_storage.h"
|
||||
#if SOC_SDMMC_HOST_SUPPORTED
|
||||
#include "diskio_sdmmc.h"
|
||||
#endif
|
||||
|
||||
static const char *TAG = "tinyusb_msc_storage";
|
||||
|
||||
#define MSC_STORAGE_MEM_ALIGN 4
|
||||
#define MSC_STORAGE_BUFFER_SIZE CONFIG_TINYUSB_MSC_BUFSIZE /*!< Size of the buffer, configured via menuconfig (MSC FIFO size) */
|
||||
|
||||
#if ((MSC_STORAGE_BUFFER_SIZE) % MSC_STORAGE_MEM_ALIGN != 0)
|
||||
#error "CONFIG_TINYUSB_MSC_BUFSIZE must be divisible by MSC_STORAGE_MEM_ALIGN. Adjust your configuration (MSC FIFO size) in menuconfig."
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Structure representing a single write buffer for MSC operations.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t data_buffer[MSC_STORAGE_BUFFER_SIZE]; /*!< Buffer to store write data. The size is defined by MSC_STORAGE_BUFFER_SIZE. */
|
||||
uint32_t lba; /*!< Logical Block Address for the current WRITE10 operation. */
|
||||
uint32_t offset; /*!< Offset within the specified LBA for the current write operation. */
|
||||
uint32_t bufsize; /*!< Number of bytes to be written in this operation. */
|
||||
} msc_storage_buffer_t;
|
||||
|
||||
/**
|
||||
* @brief Handle for TinyUSB MSC storage interface.
|
||||
*
|
||||
* This structure holds metadata and function pointers required to
|
||||
* manage the underlying storage medium (SPI flash, SDMMC).
|
||||
*/
|
||||
typedef struct {
|
||||
msc_storage_buffer_t storage_buffer;
|
||||
bool is_fat_mounted; /*!< Indicates if the FAT filesystem is currently mounted. */
|
||||
const char *base_path; /*!< Base path where the filesystem is mounted. */
|
||||
union {
|
||||
wl_handle_t wl_handle; /*!< Handle for wear leveling on SPI flash. */
|
||||
#if SOC_SDMMC_HOST_SUPPORTED
|
||||
sdmmc_card_t *card; /*!< Handle for SDMMC card. */
|
||||
#endif
|
||||
};
|
||||
esp_err_t (*mount)(BYTE pdrv); /*!< Pointer to the mount function. */
|
||||
esp_err_t (*unmount)(void); /*!< Pointer to the unmount function. */
|
||||
uint32_t sector_count; /*!< Total number of sectors in the storage medium. */
|
||||
uint32_t sector_size; /*!< Size of a single sector in bytes. */
|
||||
esp_err_t (*read)(size_t sector_size, /*!< Function pointer for reading data. */
|
||||
uint32_t lba, uint32_t offset, size_t size, void *dest);
|
||||
esp_err_t (*write)(size_t sector_size, /*!< Function pointer for writing data. */
|
||||
size_t addr, uint32_t lba, uint32_t offset, size_t size, const void *src);
|
||||
tusb_msc_callback_t callback_mount_changed; /*!< Callback for mount state change. */
|
||||
tusb_msc_callback_t callback_premount_changed; /*!< Callback for pre-mount state change. */
|
||||
int max_files; /*!< Maximum number of files that can be open simultaneously. */
|
||||
} tinyusb_msc_storage_handle_s;
|
||||
|
||||
/* 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)
|
||||
{
|
||||
(void) addr; // addr argument is not used in this function, we calculate it based on lba and offset.
|
||||
size_t temp = 0;
|
||||
size_t src_addr = 0; // Address of the data to be write, 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, &src_addr), ESP_ERR_INVALID_SIZE, TAG, "overflow addr %u offset %lu", temp, offset);
|
||||
ESP_RETURN_ON_ERROR(wl_erase_range(s_storage_handle->wl_handle, src_addr, size), TAG, "Failed to erase");
|
||||
return wl_write(s_storage_handle->wl_handle, src_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)
|
||||
{
|
||||
(void) addr; // addr argument is not used in this function, we use lba directly
|
||||
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();
|
||||
|
||||
if (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, 0 /* not used */, 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);
|
||||
|
||||
BYTE format_flags;
|
||||
#if defined(CONFIG_TINYUSB_FAT_FORMAT_ANY)
|
||||
format_flags = FM_ANY;
|
||||
|
||||
#elif defined(CONFIG_TINYUSB_FAT_FORMAT_FAT)
|
||||
format_flags = FM_FAT;
|
||||
|
||||
#elif defined(CONFIG_TINYUSB_FAT_FORMAT_FAT32)
|
||||
format_flags = FM_FAT32;
|
||||
|
||||
#elif defined(CONFIG_TINYUSB_FAT_FORMAT_EXFAT)
|
||||
format_flags = FM_EXFAT;
|
||||
#else
|
||||
|
||||
#error "No FAT format type selected"
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_TINYUSB_FAT_FORMAT_SFD
|
||||
format_flags |= FM_SFD;
|
||||
#endif
|
||||
const MKFS_PARM opt = {format_flags, 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handles deferred USB MSC write operations.
|
||||
*
|
||||
* This function is invoked via TinyUSB's deferred execution mechanism to perform
|
||||
* write operations to the underlying storage. It writes data from the
|
||||
* `storage_buffer` stored within the `s_storage_handle`.
|
||||
*
|
||||
* @param param Unused. Present for compatibility with deferred function signature.
|
||||
*/
|
||||
static void _write_func(void *param)
|
||||
{
|
||||
// Process the data in storage_buffer
|
||||
esp_err_t err = _msc_storage_write_sector(
|
||||
s_storage_handle->storage_buffer.lba,
|
||||
s_storage_handle->storage_buffer.offset,
|
||||
s_storage_handle->storage_buffer.bufsize,
|
||||
(const void *)s_storage_handle->storage_buffer.data_buffer
|
||||
);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Write failed, error=0x%x", err);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
ESP_RETURN_ON_FALSE(CONFIG_TINYUSB_MSC_BUFSIZE >= CONFIG_WL_SECTOR_SIZE,
|
||||
ESP_ERR_NOT_SUPPORTED, TAG,
|
||||
"CONFIG_TINYUSB_MSC_BUFSIZE (%d) must be at least the size of CONFIG_WL_SECTOR_SIZE (%d)", (int)(CONFIG_TINYUSB_MSC_BUFSIZE), (int)(CONFIG_WL_SECTOR_SIZE));
|
||||
s_storage_handle = (tinyusb_msc_storage_handle_s *)heap_caps_aligned_alloc(MSC_STORAGE_MEM_ALIGN, sizeof(tinyusb_msc_storage_handle_s), MALLOC_CAP_DMA);
|
||||
ESP_RETURN_ON_FALSE(s_storage_handle, ESP_ERR_NO_MEM, TAG, "Failed to allocate memory for storage handle");
|
||||
s_storage_handle->mount = &_mount_spiflash;
|
||||
s_storage_handle->unmount = &_unmount_spiflash;
|
||||
s_storage_handle->wl_handle = config->wl_handle;
|
||||
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;
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (!esp_ptr_dma_capable((const void *)s_storage_handle->storage_buffer.data_buffer)) {
|
||||
ESP_LOGW(TAG, "storage buffer is not DMA capable");
|
||||
}
|
||||
|
||||
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 *)heap_caps_aligned_alloc(MSC_STORAGE_MEM_ALIGN, sizeof(tinyusb_msc_storage_handle_s), MALLOC_CAP_DMA);
|
||||
ESP_RETURN_ON_FALSE(s_storage_handle, ESP_ERR_NO_MEM, TAG, "Failed to allocate memory for storage handle");
|
||||
s_storage_handle->mount = &_mount_sdmmc;
|
||||
s_storage_handle->unmount = &_unmount_sdmmc;
|
||||
s_storage_handle->card = config->card;
|
||||
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;
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (!esp_ptr_dma_capable((const void *)s_storage_handle->storage_buffer.data_buffer)) {
|
||||
ESP_LOGW(TAG, "storage buffer is not DMA capable");
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
void tinyusb_msc_storage_deinit(void)
|
||||
{
|
||||
if (s_storage_handle) {
|
||||
heap_caps_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.2";
|
||||
|
||||
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)
|
||||
{
|
||||
assert(bufsize <= MSC_STORAGE_BUFFER_SIZE);
|
||||
// Copy data to the buffer
|
||||
memcpy((void *)s_storage_handle->storage_buffer.data_buffer, buffer, bufsize);
|
||||
s_storage_handle->storage_buffer.lba = lba;
|
||||
s_storage_handle->storage_buffer.offset = offset;
|
||||
s_storage_handle->storage_buffer.bufsize = bufsize;
|
||||
|
||||
// Defer execution of the write to the TinyUSB task
|
||||
usbd_defer_func(_write_func, NULL, false);
|
||||
|
||||
// Return the number of bytes accepted
|
||||
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*/
|
||||
77
managed_components/espressif__esp_tinyusb/tusb_tasks.c
Normal file
77
managed_components/espressif__esp_tinyusb/tusb_tasks.c
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
291
managed_components/espressif__esp_tinyusb/usb_descriptors.c
Normal file
291
managed_components/espressif__esp_tinyusb/usb_descriptors.c
Normal file
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* 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
|
||||
#endif
|
||||
|
||||
#if CONFIG_TINYUSB_MSC_ENABLED
|
||||
CONFIG_TINYUSB_DESC_MSC_STRING, // 5: MSC Interface
|
||||
#endif
|
||||
|
||||
#if CONFIG_TINYUSB_NET_MODE_ECM_RNDIS || CONFIG_TINYUSB_NET_MODE_NCM
|
||||
"USB net", // 6. NET Interface
|
||||
"", // 7. MAC
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR
|
||||
"Vendor specific", // 8. Vendor specific
|
||||
#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
|
||||
|
||||
#if CFG_TUD_VENDOR
|
||||
ITF_VENDOR,
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR > 1
|
||||
ITF_VENDOR1,
|
||||
#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 +
|
||||
CFG_TUD_VENDOR * TUD_VENDOR_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
|
||||
|
||||
#if CFG_TUD_VENDOR
|
||||
EPNUM_0_VENDOR,
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR > 1
|
||||
EPNUM_1_VENDOR
|
||||
#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
|
||||
|
||||
#if CFG_TUD_VENDOR
|
||||
STRID_VENDOR_INTERFACE,
|
||||
#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 CFG_TUD_VENDOR
|
||||
// Interface number, string index, EP Out & IN address, EP size
|
||||
TUD_VENDOR_DESCRIPTOR(ITF_VENDOR, STRID_VENDOR_INTERFACE, EPNUM_0_VENDOR, 0x80 | EPNUM_0_VENDOR, 64),
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR > 1
|
||||
// Interface number, string index, EP Out & IN address, EP size
|
||||
TUD_VENDOR_DESCRIPTOR(ITF_VENDOR1, STRID_VENDOR_INTERFACE, EPNUM_1_VENDOR, 0x80 | EPNUM_1_VENDOR, 64),
|
||||
#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
|
||||
|
||||
#if CFG_TUD_VENDOR
|
||||
// Interface number, string index, EP Out & IN address, EP size
|
||||
TUD_VENDOR_DESCRIPTOR(ITF_VENDOR, STRID_VENDOR_INTERFACE, EPNUM_0_VENDOR, 0x80 | EPNUM_0_VENDOR, 512),
|
||||
#endif
|
||||
|
||||
#if CFG_TUD_VENDOR > 1
|
||||
// Interface number, string index, EP Out & IN address, EP size
|
||||
TUD_VENDOR_DESCRIPTOR(ITF_VENDOR1, STRID_VENDOR_INTERFACE, EPNUM_1_VENDOR, 0x80 | EPNUM_1_VENDOR, 512),
|
||||
#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 */
|
||||
303
managed_components/espressif__esp_tinyusb/vfs_tinyusb.c
Normal file
303
managed_components/espressif__esp_tinyusb/vfs_tinyusb.c
Normal file
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020-2024 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)
|
||||
{
|
||||
_lock_close(&(s_vfstusb.write_lock));
|
||||
_lock_close(&(s_vfstusb.read_lock));
|
||||
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));
|
||||
}
|
||||
Reference in New Issue
Block a user