summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorHiFiPhile <[email protected]>2026-05-22 13:19:45 +0200
committerHiFiPhile <[email protected]>2026-05-22 13:19:45 +0200
commitc24016991f1b58544cf3bfe05815df637f466e0e (patch)
tree8ba7e710d390b9c012327dec04c767f66d42a53a /examples
parent650e8a194fcfaa4b91203e9aedfecd614ea70105 (diff)
parente1cb1b5b6fa183a1473f05b8d1ce4ab93a373ef4 (diff)
Merge remote-tracking branch 'tinyusb/master' into fix/Dwc2Stm32U5
Diffstat (limited to 'examples')
-rw-r--r--examples/device/CMakeLists.txt1
-rw-r--r--examples/device/audio_4_channel_mic/src/usb_descriptors.c4
-rw-r--r--examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c4
-rw-r--r--examples/device/audio_test/src/usb_descriptors.c4
-rw-r--r--examples/device/audio_test_freertos/src/usb_descriptors.c4
-rw-r--r--examples/device/audio_test_multi_rate/src/usb_descriptors.c4
-rw-r--r--examples/device/cdc_dual_ports/src/usb_descriptors.c25
-rw-r--r--examples/device/cdc_msc/src/usb_descriptors.c22
-rw-r--r--examples/device/cdc_msc_freertos/src/main.c4
-rw-r--r--examples/device/cdc_msc_freertos/src/usb_descriptors.c22
-rw-r--r--examples/device/cdc_msc_throughput/CMakeLists.txt35
-rw-r--r--examples/device/cdc_msc_throughput/Makefile11
-rw-r--r--examples/device/cdc_msc_throughput/src/main.c151
-rw-r--r--examples/device/cdc_msc_throughput/src/tusb_config.h103
-rw-r--r--examples/device/cdc_msc_throughput/src/usb_descriptors.c186
-rw-r--r--examples/device/cdc_uac2/src/usb_descriptors.c22
-rw-r--r--examples/device/dfu/skip.txt1
-rw-r--r--examples/device/dynamic_configuration/src/usb_descriptors.c2
-rw-r--r--examples/device/hid_generic_inout/src/usb_descriptors.c2
-rw-r--r--examples/device/midi_test/src/usb_descriptors.c2
-rw-r--r--examples/device/midi_test_freertos/src/usb_descriptors.c2
-rw-r--r--examples/device/msc_dual_lun/src/usb_descriptors.c12
-rw-r--r--examples/device/mtp/src/usb_descriptors.c2
-rw-r--r--examples/device/net_lwip_webserver/skip.txt2
-rw-r--r--examples/device/net_lwip_webserver/src/lwipopts.h20
-rw-r--r--examples/device/net_lwip_webserver/src/tusb_config.h40
-rw-r--r--examples/device/net_lwip_webserver/src/usb_descriptors.c181
-rw-r--r--examples/device/printer_to_cdc/src/usb_descriptors.c21
-rw-r--r--examples/device/uac2_headset/src/usb_descriptors.c15
-rw-r--r--examples/device/uac2_speaker_fb/src/usb_descriptors.c15
-rw-r--r--examples/device/webusb_serial/src/usb_descriptors.c22
-rw-r--r--examples/dual/dynamic_switch/src/usb_descriptors.c2
-rw-r--r--examples/host/CMakeLists.txt1
-rw-r--r--examples/host/msc_file_explorer_freertos/CMakeLists.txt42
-rw-r--r--examples/host/msc_file_explorer_freertos/CMakePresets.json6
-rw-r--r--examples/host/msc_file_explorer_freertos/Makefile27
-rw-r--r--examples/host/msc_file_explorer_freertos/README.md105
-rw-r--r--examples/host/msc_file_explorer_freertos/only.txt28
-rw-r--r--examples/host/msc_file_explorer_freertos/skip.txt3
-rw-r--r--examples/host/msc_file_explorer_freertos/src/CMakeLists.txt13
-rw-r--r--examples/host/msc_file_explorer_freertos/src/ffconf.h313
-rw-r--r--examples/host/msc_file_explorer_freertos/src/main.c158
-rw-r--r--examples/host/msc_file_explorer_freertos/src/msc_app.c709
-rw-r--r--examples/host/msc_file_explorer_freertos/src/msc_app.h34
-rw-r--r--examples/host/msc_file_explorer_freertos/src/tusb_config.h126
45 files changed, 2413 insertions, 95 deletions
diff --git a/examples/device/CMakeLists.txt b/examples/device/CMakeLists.txt
index 7173f455e..088872711 100644
--- a/examples/device/CMakeLists.txt
+++ b/examples/device/CMakeLists.txt
@@ -16,6 +16,7 @@ set(EXAMPLE_LIST
cdc_dual_ports
cdc_msc
cdc_msc_freertos
+ cdc_msc_throughput
cdc_uac2
dfu
dfu_runtime
diff --git a/examples/device/audio_4_channel_mic/src/usb_descriptors.c b/examples/device/audio_4_channel_mic/src/usb_descriptors.c
index 00337eee7..2380ea0ae 100644
--- a/examples/device/audio_4_channel_mic/src/usb_descriptors.c
+++ b/examples/device/audio_4_channel_mic/src/usb_descriptors.c
@@ -91,6 +91,10 @@ enum
// nRF5x ISO can only be endpoint 8
#define EPNUM_AUDIO 0x08
+#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO 0x0A
+
#else
#define EPNUM_AUDIO 0x01
#endif
diff --git a/examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c b/examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c
index 3bb93f67d..216cd062a 100644
--- a/examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c
+++ b/examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c
@@ -91,6 +91,10 @@ enum
// nRF5x ISO can only be endpoint 8
#define EPNUM_AUDIO 0x08
+#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO 0x0A
+
#else
#define EPNUM_AUDIO 0x01
#endif
diff --git a/examples/device/audio_test/src/usb_descriptors.c b/examples/device/audio_test/src/usb_descriptors.c
index ad161939e..37ebf84d3 100644
--- a/examples/device/audio_test/src/usb_descriptors.c
+++ b/examples/device/audio_test/src/usb_descriptors.c
@@ -91,6 +91,10 @@ enum
// nRF5x ISO can only be endpoint 8
#define EPNUM_AUDIO 0x08
+#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO 0x0A
+
#else
#define EPNUM_AUDIO 0x01
#endif
diff --git a/examples/device/audio_test_freertos/src/usb_descriptors.c b/examples/device/audio_test_freertos/src/usb_descriptors.c
index ad161939e..37ebf84d3 100644
--- a/examples/device/audio_test_freertos/src/usb_descriptors.c
+++ b/examples/device/audio_test_freertos/src/usb_descriptors.c
@@ -91,6 +91,10 @@ enum
// nRF5x ISO can only be endpoint 8
#define EPNUM_AUDIO 0x08
+#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO 0x0A
+
#else
#define EPNUM_AUDIO 0x01
#endif
diff --git a/examples/device/audio_test_multi_rate/src/usb_descriptors.c b/examples/device/audio_test_multi_rate/src/usb_descriptors.c
index 505936fdb..31333dcd3 100644
--- a/examples/device/audio_test_multi_rate/src/usb_descriptors.c
+++ b/examples/device/audio_test_multi_rate/src/usb_descriptors.c
@@ -88,6 +88,10 @@ enum {
// nRF5x ISO can only be endpoint 8
#define EPNUM_AUDIO 0x08
+#elif TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO 0x0A
+
#else
#define EPNUM_AUDIO 0x01
#endif
diff --git a/examples/device/cdc_dual_ports/src/usb_descriptors.c b/examples/device/cdc_dual_ports/src/usb_descriptors.c
index e6011c35a..adfd8cf9d 100644
--- a/examples/device/cdc_dual_ports/src/usb_descriptors.c
+++ b/examples/device/cdc_dual_ports/src/usb_descriptors.c
@@ -106,16 +106,27 @@ enum {
#define EPNUM_CDC_1_OUT 0x05
#define EPNUM_CDC_1_IN 0x84
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_CDC_0_NOTIF 0x81
- #define EPNUM_CDC_0_OUT 0x02
- #define EPNUM_CDC_0_IN 0x83
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_CDC_0_NOTIF 0x81
+ #define EPNUM_CDC_0_OUT 0x08
+ #define EPNUM_CDC_0_IN 0x89
- #define EPNUM_CDC_1_NOTIF 0x84
- #define EPNUM_CDC_1_OUT 0x05
- #define EPNUM_CDC_1_IN 0x86
+ #define EPNUM_CDC_1_NOTIF 0x82
+ #define EPNUM_CDC_1_OUT 0x0A
+ #define EPNUM_CDC_1_IN 0x8B
+ #else
+ #define EPNUM_CDC_0_NOTIF 0x81
+ #define EPNUM_CDC_0_OUT 0x02
+ #define EPNUM_CDC_0_IN 0x83
+
+ #define EPNUM_CDC_1_NOTIF 0x84
+ #define EPNUM_CDC_1_OUT 0x05
+ #define EPNUM_CDC_1_IN 0x86
+ #endif
#else
#define EPNUM_CDC_0_NOTIF 0x81
diff --git a/examples/device/cdc_msc/src/usb_descriptors.c b/examples/device/cdc_msc/src/usb_descriptors.c
index c668ea3a7..5dc80dee3 100644
--- a/examples/device/cdc_msc/src/usb_descriptors.c
+++ b/examples/device/cdc_msc/src/usb_descriptors.c
@@ -102,15 +102,25 @@ enum {
#define EPNUM_MSC_OUT 0x05
#define EPNUM_MSC_IN 0x84
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_CDC_NOTIF 0x81
- #define EPNUM_CDC_OUT 0x02
- #define EPNUM_CDC_IN 0x83
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x08
+ #define EPNUM_CDC_IN 0x89
- #define EPNUM_MSC_OUT 0x04
- #define EPNUM_MSC_IN 0x85
+ #define EPNUM_MSC_OUT 0x0A
+ #define EPNUM_MSC_IN 0x8B
+ #else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x83
+
+ #define EPNUM_MSC_OUT 0x04
+ #define EPNUM_MSC_IN 0x85
+ #endif
#else
#define EPNUM_CDC_NOTIF 0x81
diff --git a/examples/device/cdc_msc_freertos/src/main.c b/examples/device/cdc_msc_freertos/src/main.c
index 4fb209fd0..f2f71d089 100644
--- a/examples/device/cdc_msc_freertos/src/main.c
+++ b/examples/device/cdc_msc_freertos/src/main.c
@@ -34,10 +34,10 @@
#define USBD_STACK_SIZE 4096
#else
// Increase stack size when debug log is enabled
- #define USBD_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1)
+ #define USBD_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 4 : 2))
#endif
-#define CDC_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 2 : 1))
+#define CDC_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 3 : 2))
#define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE
//--------------------------------------------------------------------+
diff --git a/examples/device/cdc_msc_freertos/src/usb_descriptors.c b/examples/device/cdc_msc_freertos/src/usb_descriptors.c
index 4950f02e0..f5b015051 100644
--- a/examples/device/cdc_msc_freertos/src/usb_descriptors.c
+++ b/examples/device/cdc_msc_freertos/src/usb_descriptors.c
@@ -102,15 +102,25 @@ enum {
#define EPNUM_MSC_OUT 0x05
#define EPNUM_MSC_IN 0x84
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_CDC_NOTIF 0x81
- #define EPNUM_CDC_OUT 0x02
- #define EPNUM_CDC_IN 0x83
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x08
+ #define EPNUM_CDC_IN 0x89
- #define EPNUM_MSC_OUT 0x04
- #define EPNUM_MSC_IN 0x85
+ #define EPNUM_MSC_OUT 0x0A
+ #define EPNUM_MSC_IN 0x8B
+ #else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x83
+
+ #define EPNUM_MSC_OUT 0x04
+ #define EPNUM_MSC_IN 0x85
+ #endif
#else
#define EPNUM_CDC_NOTIF 0x81
diff --git a/examples/device/cdc_msc_throughput/CMakeLists.txt b/examples/device/cdc_msc_throughput/CMakeLists.txt
new file mode 100644
index 000000000..69c1caa6a
--- /dev/null
+++ b/examples/device/cdc_msc_throughput/CMakeLists.txt
@@ -0,0 +1,35 @@
+cmake_minimum_required(VERSION 3.20)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+
+project(cdc_msc_throughput C CXX ASM)
+
+# Checks this example is valid for the family and initializes the project
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
+
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+ return()
+endif()
+
+if (RTOS STREQUAL zephyr)
+ set(EXE_NAME app)
+else()
+ set(EXE_NAME ${PROJECT_NAME})
+ add_executable(${EXE_NAME})
+endif()
+
+# Example source
+target_sources(${EXE_NAME} PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
+ )
+
+# Example include
+target_include_directories(${EXE_NAME} PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
+ )
+
+# Configure compilation flags and libraries for the example without RTOS.
+# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
+family_configure_device_example(${EXE_NAME} ${RTOS})
diff --git a/examples/device/cdc_msc_throughput/Makefile b/examples/device/cdc_msc_throughput/Makefile
new file mode 100644
index 000000000..035e90308
--- /dev/null
+++ b/examples/device/cdc_msc_throughput/Makefile
@@ -0,0 +1,11 @@
+include ../../../hw/bsp/family_support.mk
+
+INC += \
+ src \
+
+
+# Example source
+EXAMPLE_SOURCE += $(wildcard src/*.c)
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
+
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/device/cdc_msc_throughput/src/main.c b/examples/device/cdc_msc_throughput/src/main.c
new file mode 100644
index 000000000..116cbe13f
--- /dev/null
+++ b/examples/device/cdc_msc_throughput/src/main.c
@@ -0,0 +1,151 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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.
+ *
+ */
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+
+// cdc_msc_throughput: minimal CDC+MSC device aimed at measuring pure USB bulk throughput.
+// MSC read/write callbacks don't touch any backing storage - write discards the
+// data and read only zero-fills the low LBAs the host scans during enumeration
+// (partition table, GPT header). Higher LBAs return whatever is already in the
+// transfer buffer, so `dd` numbers reflect the USB/driver ceiling, not any
+// simulated storage or per-byte memset cost.
+// CDC path drains RX in tud_cdc_rx_cb and sources TX from a static filler in the
+// main loop so `dd` can target /dev/ttyACMx in either direction.
+
+static void cdc_throughput_task(void);
+
+//--------------------------------------------------------------------+
+// Main
+//--------------------------------------------------------------------+
+int main(void) {
+ board_init();
+
+ tusb_rhport_init_t dev_init = {.role = TUSB_ROLE_DEVICE, .speed = TUSB_SPEED_AUTO};
+ tusb_init(BOARD_TUD_RHPORT, &dev_init);
+
+ board_init_after_tusb();
+
+ while (1) {
+ tud_task();
+ cdc_throughput_task();
+ }
+}
+
+//--------------------------------------------------------------------+
+// CDC callbacks + tasks
+//--------------------------------------------------------------------+
+void tud_cdc_rx_cb(uint8_t itf) {
+ (void) itf;
+ tud_cdc_read_flush(); // Drain RX
+}
+
+static void cdc_throughput_task(void) {
+ if (!tud_cdc_connected()) return;
+
+ // Source TX: fill whatever write room is free.
+ static uint8_t const filler[CFG_TUD_CDC_TX_EPSIZE] = {0};
+ uint32_t room = tud_cdc_write_available();
+ while (room > 0) {
+ uint32_t n = tud_cdc_write(filler, tu_min32(room, sizeof(filler)));
+ if (n == 0) {
+ break;
+ }
+ room -= n;
+ }
+ tud_cdc_write_flush();
+}
+
+//--------------------------------------------------------------------+
+// MSC callbacks
+//--------------------------------------------------------------------+
+
+// 1 GiB logical capacity so `dd` can run long enough for stable numbers.
+// No real backing store - block content is synthesised on read, discarded on write.
+enum {
+ DISK_BLOCK_SIZE = 512,
+ DISK_BLOCK_COUNT = 0x00200000u, // 2 Mi blocks = 1 GiB
+ // Kernel probes partition-table / filesystem-superblock locations near the
+ // start of the disk during enumeration. Zero-fill only this head range so the
+ // block layer sees "no partition, no filesystem" and leaves us alone; higher
+ // LBAs skip the memset so `dd` measures pure USB/driver throughput.
+ DISK_ZEROFILL_LBA = 64, // 32 KiB
+};
+
+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[] = "Mass Storage";
+ const char rev[] = "1.0";
+ (void) strncpy((char*) vendor_id, vid, 8);
+ (void) strncpy((char*) product_id, pid, 16);
+ (void) strncpy((char*) product_rev, rev, 4);
+}
+
+bool tud_msc_test_unit_ready_cb(uint8_t lun) {
+ (void) lun;
+ return true;
+}
+
+void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size) {
+ (void) lun;
+ *block_count = DISK_BLOCK_COUNT;
+ *block_size = DISK_BLOCK_SIZE;
+}
+
+bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) {
+ (void) lun; (void) power_condition; (void) start; (void) load_eject;
+ return true;
+}
+
+bool tud_msc_is_writable_cb(uint8_t lun) {
+ (void) lun;
+ return true;
+}
+
+// READ10: zero-fill only the head range the kernel inspects, skip memset everywhere
+// else so we measure the USB / driver path rather than memset cost.
+int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize) {
+ (void) lun; (void) offset;
+ if (lba < DISK_ZEROFILL_LBA) {
+ memset(buffer, 0, bufsize);
+ } else {
+ (void) buffer;
+ }
+ return (int32_t) bufsize;
+}
+
+// WRITE10: discard the received data entirely - this is the pure USB-speed test.
+int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize) {
+ (void) lun; (void) lba; (void) offset; (void) buffer;
+ return (int32_t) bufsize;
+}
+
+// Unknown SCSI commands: stall with Invalid Command sense.
+int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize) {
+ (void) scsi_cmd; (void) buffer; (void) bufsize;
+ tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
+ return -1;
+}
diff --git a/examples/device/cdc_msc_throughput/src/tusb_config.h b/examples/device/cdc_msc_throughput/src/tusb_config.h
new file mode 100644
index 000000000..6c8655719
--- /dev/null
+++ b/examples/device/cdc_msc_throughput/src/tusb_config.h
@@ -0,0 +1,103 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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.
+ *
+ */
+
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------------------------
+// Board Specific Configuration
+//--------------------------------------------------------------------
+
+#ifndef BOARD_TUD_RHPORT
+ #define BOARD_TUD_RHPORT 0
+#endif
+
+#ifndef BOARD_TUD_MAX_SPEED
+ #define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------
+// Common Configuration
+//--------------------------------------------------------------------
+
+#ifndef CFG_TUSB_MCU
+ #error CFG_TUSB_MCU must be defined
+#endif
+
+#ifndef CFG_TUSB_OS
+ #define CFG_TUSB_OS OPT_OS_NONE
+#endif
+
+#ifndef CFG_TUSB_DEBUG
+ #define CFG_TUSB_DEBUG 0
+#endif
+
+// Enable Device stack
+#define CFG_TUD_ENABLED 1
+#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
+
+#ifndef CFG_TUSB_MEM_SECTION
+ #define CFG_TUSB_MEM_SECTION
+#endif
+
+#ifndef CFG_TUSB_MEM_ALIGN
+ #define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
+#endif
+
+//--------------------------------------------------------------------
+// DEVICE CONFIGURATION
+//--------------------------------------------------------------------
+
+#ifndef CFG_TUD_ENDPOINT0_SIZE
+ #define CFG_TUD_ENDPOINT0_SIZE 64
+#endif
+
+//------------- CLASS -------------//
+#define CFG_TUD_CDC 1
+#define CFG_TUD_MSC 1
+
+// Large MSC bulk buffer: host transfers big CBW payloads (e.g. dd bs=1M does 64KiB
+// chunks). A 4K per-bulk-IO buffer lets the class driver amortise the per-CBW
+// overhead across many USB packets, approximating the maximum USB bulk throughput.
+#define CFG_TUD_MSC_EP_BUFSIZE (TUD_OPT_HIGH_SPEED ? 4096 : 1024)
+
+// #define CFG_TUD_CDC_TX_PERSISTENT 1
+
+// CDC throughput: size for HS; tinyusb will auto-scale for FS via TUD_OPT_HIGH_SPEED.
+#define CFG_TUD_CDC_RX_EPSIZE (TUD_OPT_HIGH_SPEED ? 2*512 : 2*64)
+#define CFG_TUD_CDC_TX_EPSIZE CFG_TUD_CDC_RX_EPSIZE
+
+#define CFG_TUD_CDC_RX_BUFSIZE (TUD_OPT_HIGH_SPEED ? 2*512 : 2*64)
+#define CFG_TUD_CDC_TX_BUFSIZE CFG_TUD_CDC_RX_BUFSIZE
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/device/cdc_msc_throughput/src/usb_descriptors.c b/examples/device/cdc_msc_throughput/src/usb_descriptors.c
new file mode 100644
index 000000000..3b0ff6e17
--- /dev/null
+++ b/examples/device/cdc_msc_throughput/src/usb_descriptors.c
@@ -0,0 +1,186 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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.
+ *
+ */
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+
+#define USB_PID (0x4000 | ((CFG_TUD_CDC) ? (1 << 0) : 0) | ((CFG_TUD_MSC) ? (1 << 1) : 0))
+#define USB_VID 0xCafe
+#define USB_BCD 0x0200
+
+static tusb_desc_device_t const desc_device = {
+ .bLength = sizeof(tusb_desc_device_t),
+ .bDescriptorType = TUSB_DESC_DEVICE,
+ .bcdUSB = USB_BCD,
+
+ // IAD required for composite CDC + MSC
+ .bDeviceClass = TUSB_CLASS_MISC,
+ .bDeviceSubClass = MISC_SUBCLASS_COMMON,
+ .bDeviceProtocol = MISC_PROTOCOL_IAD,
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+
+ .idVendor = USB_VID,
+ .idProduct = USB_PID,
+ .bcdDevice = 0x0100,
+
+ .iManufacturer = 0x01,
+ .iProduct = 0x02,
+ .iSerialNumber = 0x03,
+
+ .bNumConfigurations = 0x01,
+};
+
+uint8_t const *tud_descriptor_device_cb(void) {
+ return (uint8_t const *) &desc_device;
+}
+
+enum {
+ ITF_NUM_CDC = 0,
+ ITF_NUM_CDC_DATA,
+ ITF_NUM_MSC,
+ ITF_NUM_TOTAL,
+};
+
+// Place bulk endpoints on EP>=8 for MAX32690 class parts (bigger FIFO, DPB-capable).
+#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x08
+ #define EPNUM_CDC_IN 0x89
+ #define EPNUM_MSC_OUT 0x0A
+ #define EPNUM_MSC_IN 0x8B
+ #else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x83
+ #define EPNUM_MSC_OUT 0x04
+ #define EPNUM_MSC_IN 0x85
+ #endif
+#else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x82
+ #define EPNUM_MSC_OUT 0x03
+ #define EPNUM_MSC_IN 0x83
+#endif
+
+#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + TUD_MSC_DESC_LEN)
+
+static uint8_t const desc_fs_configuration[] = {
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+ TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 16, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
+ TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 64),
+};
+
+#if TUD_OPT_HIGH_SPEED
+static uint8_t const desc_hs_configuration[] = {
+ TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+ TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 16, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512),
+ TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 5, EPNUM_MSC_OUT, EPNUM_MSC_IN, 512),
+};
+
+static uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
+
+static tusb_desc_device_qualifier_t const desc_device_qualifier = {
+ .bLength = sizeof(tusb_desc_device_qualifier_t),
+ .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
+ .bcdUSB = USB_BCD,
+ .bDeviceClass = 0x00,
+ .bDeviceSubClass = 0x00,
+ .bDeviceProtocol = 0x00,
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+ .bNumConfigurations = 0x01,
+ .bReserved = 0x00,
+};
+
+uint8_t const *tud_descriptor_device_qualifier_cb(void) {
+ return (uint8_t const *) &desc_device_qualifier;
+}
+
+uint8_t const *tud_descriptor_other_speed_configuration_cb(uint8_t index) {
+ (void) index;
+ memcpy(desc_other_speed_config,
+ (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration,
+ CONFIG_TOTAL_LEN);
+ desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
+ return desc_other_speed_config;
+}
+#endif
+
+uint8_t const *tud_descriptor_configuration_cb(uint8_t index) {
+ (void) index;
+#if TUD_OPT_HIGH_SPEED
+ return (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_hs_configuration : desc_fs_configuration;
+#else
+ return desc_fs_configuration;
+#endif
+}
+
+enum {
+ STRID_LANGID = 0,
+ STRID_MANUFACTURER,
+ STRID_PRODUCT,
+ STRID_SERIAL,
+};
+
+static char const *string_desc_arr[] = {
+ (const char[]) { 0x09, 0x04 },
+ "TinyUSB",
+ "Throughput",
+ NULL,
+ "TinyUSB CDC",
+ "TinyUSB MSC",
+};
+
+static uint16_t _desc_str[32 + 1];
+
+uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
+ (void) langid;
+ size_t chr_count;
+
+ switch (index) {
+ case STRID_LANGID:
+ memcpy(&_desc_str[1], string_desc_arr[0], 2);
+ chr_count = 1;
+ break;
+
+ case STRID_SERIAL:
+ chr_count = board_usb_get_serial(_desc_str + 1, 32);
+ break;
+
+ default:
+ if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) return NULL;
+ const char *str = string_desc_arr[index];
+ chr_count = strlen(str);
+ size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1;
+ if (chr_count > max_count) chr_count = max_count;
+ for (size_t i = 0; i < chr_count; i++) _desc_str[1 + i] = str[i];
+ break;
+ }
+
+ _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
+ return _desc_str;
+}
diff --git a/examples/device/cdc_uac2/src/usb_descriptors.c b/examples/device/cdc_uac2/src/usb_descriptors.c
index e6caaa971..fdffc761e 100644
--- a/examples/device/cdc_uac2/src/usb_descriptors.c
+++ b/examples/device/cdc_uac2/src/usb_descriptors.c
@@ -97,15 +97,25 @@ uint8_t const * tud_descriptor_device_cb(void)
#define EPNUM_CDC_OUT 0x02
#define EPNUM_CDC_IN 0x82
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_AUDIO_IN 0x01
- #define EPNUM_AUDIO_OUT 0x02
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put CDC bulk on EP>=8 and audio iso on EP10/11 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO_OUT 0x0A
+ #define EPNUM_AUDIO_IN 0x0B
- #define EPNUM_CDC_NOTIF 0x83
- #define EPNUM_CDC_OUT 0x04
- #define EPNUM_CDC_IN 0x85
+ #define EPNUM_CDC_NOTIF 0x83
+ #define EPNUM_CDC_OUT 0x08
+ #define EPNUM_CDC_IN 0x89
+ #else
+ #define EPNUM_AUDIO_IN 0x01
+ #define EPNUM_AUDIO_OUT 0x02
+
+ #define EPNUM_CDC_NOTIF 0x83
+ #define EPNUM_CDC_OUT 0x04
+ #define EPNUM_CDC_IN 0x85
+ #endif
#else
#define EPNUM_AUDIO_IN 0x01
diff --git a/examples/device/dfu/skip.txt b/examples/device/dfu/skip.txt
index 79d3da9d2..ccff857ac 100644
--- a/examples/device/dfu/skip.txt
+++ b/examples/device/dfu/skip.txt
@@ -1,3 +1,2 @@
-mcu:TM4C
mcu:BCM2835
family:espressif
diff --git a/examples/device/dynamic_configuration/src/usb_descriptors.c b/examples/device/dynamic_configuration/src/usb_descriptors.c
index 458b7c2a5..c4049414f 100644
--- a/examples/device/dynamic_configuration/src/usb_descriptors.c
+++ b/examples/device/dynamic_configuration/src/usb_descriptors.c
@@ -132,7 +132,7 @@ enum
#define EPNUM_1_MSC_OUT 0x02
#define EPNUM_1_MSC_IN 0x82
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_0_CDC_NOTIF 0x81
diff --git a/examples/device/hid_generic_inout/src/usb_descriptors.c b/examples/device/hid_generic_inout/src/usb_descriptors.c
index 929b2fd3a..93e718461 100644
--- a/examples/device/hid_generic_inout/src/usb_descriptors.c
+++ b/examples/device/hid_generic_inout/src/usb_descriptors.c
@@ -97,7 +97,7 @@ enum
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_INOUT_DESC_LEN)
-#if defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_HID_OUT 0x01
diff --git a/examples/device/midi_test/src/usb_descriptors.c b/examples/device/midi_test/src/usb_descriptors.c
index e969f33a3..99c798ce1 100644
--- a/examples/device/midi_test/src/usb_descriptors.c
+++ b/examples/device/midi_test/src/usb_descriptors.c
@@ -87,7 +87,7 @@ enum {
#define EPNUM_MIDI_OUT 0x02
#define EPNUM_MIDI_IN 0x81
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_MIDI_OUT 0x01
diff --git a/examples/device/midi_test_freertos/src/usb_descriptors.c b/examples/device/midi_test_freertos/src/usb_descriptors.c
index e969f33a3..99c798ce1 100644
--- a/examples/device/midi_test_freertos/src/usb_descriptors.c
+++ b/examples/device/midi_test_freertos/src/usb_descriptors.c
@@ -87,7 +87,7 @@ enum {
#define EPNUM_MIDI_OUT 0x02
#define EPNUM_MIDI_IN 0x81
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_MIDI_OUT 0x01
diff --git a/examples/device/msc_dual_lun/src/usb_descriptors.c b/examples/device/msc_dual_lun/src/usb_descriptors.c
index f73935ee0..b328cf17f 100644
--- a/examples/device/msc_dual_lun/src/usb_descriptors.c
+++ b/examples/device/msc_dual_lun/src/usb_descriptors.c
@@ -91,11 +91,17 @@ enum
#define EPNUM_MSC_OUT 0x02
#define EPNUM_MSC_IN 0x81
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_MSC_OUT 0x01
- #define EPNUM_MSC_IN 0x82
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_MSC_OUT 0x08
+ #define EPNUM_MSC_IN 0x89
+ #else
+ #define EPNUM_MSC_OUT 0x01
+ #define EPNUM_MSC_IN 0x82
+ #endif
#else
#define EPNUM_MSC_OUT 0x01
diff --git a/examples/device/mtp/src/usb_descriptors.c b/examples/device/mtp/src/usb_descriptors.c
index f0aa3de6b..4c840560e 100644
--- a/examples/device/mtp/src/usb_descriptors.c
+++ b/examples/device/mtp/src/usb_descriptors.c
@@ -94,7 +94,7 @@ enum
#define EPNUM_MTP_OUT 0x02
#define EPNUM_MTP_IN 0x81
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_MTP_EVT 0x81
diff --git a/examples/device/net_lwip_webserver/skip.txt b/examples/device/net_lwip_webserver/skip.txt
index ecb9eb7ec..5e5562087 100644
--- a/examples/device/net_lwip_webserver/skip.txt
+++ b/examples/device/net_lwip_webserver/skip.txt
@@ -19,6 +19,6 @@ board:at_start_f425
board:curiosity_nano
board:frdm_kl25z
# lpc55 has weird error 'ncm_interface' causes a section type conflict with 'ntb_parameters'
-family:lpc55
+#family:lpc55
family:nuc126
family:nuc100_120
diff --git a/examples/device/net_lwip_webserver/src/lwipopts.h b/examples/device/net_lwip_webserver/src/lwipopts.h
index 11686ce2a..350120423 100644
--- a/examples/device/net_lwip_webserver/src/lwipopts.h
+++ b/examples/device/net_lwip_webserver/src/lwipopts.h
@@ -32,6 +32,14 @@
#ifndef LWIPOPTS_H__
#define LWIPOPTS_H__
+// Pulls in tusb_option.h → tusb_config.h, which defines LWIP_HIGH_THROUGHPUT
+// based on the target MCU's SRAM tier.
+#include "tusb_option.h"
+
+#ifndef LWIP_HIGH_THROUGHPUT
+ #define LWIP_HIGH_THROUGHPUT 0
+#endif
+
/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */
#define NO_SYS 1
#define MEM_ALIGNMENT 4
@@ -49,7 +57,15 @@
#define TCP_MSS (1500 /*mtu*/ - 20 /*iphdr*/ - 20 /*tcphhr*/)
#define TCP_SND_BUF (4 * TCP_MSS)
-#define TCP_WND (4 * TCP_MSS)
+#if LWIP_HIGH_THROUGHPUT
+ #define TCP_WND (8 * TCP_MSS)
+ #define PBUF_POOL_SIZE 8
+ // Must grow in step with TCP_SND_BUF (default MEMP_NUM_TCP_SEG=16 caps TCP_SND_BUF at 4*MSS).
+ #define MEMP_NUM_TCP_SEG 16
+#else
+ #define TCP_WND (4 * TCP_MSS)
+ #define PBUF_POOL_SIZE 4
+#endif
#define ETHARP_SUPPORT_STATIC_ENTRIES 1
@@ -60,8 +76,6 @@
#define LWIP_SINGLE_NETIF 1
#define LWIP_NETIF_LINK_CALLBACK 1
-#define PBUF_POOL_SIZE 4
-
#define HTTPD_USE_CUSTOM_FSDATA 0
#define LWIP_MULTICAST_PING 1
diff --git a/examples/device/net_lwip_webserver/src/tusb_config.h b/examples/device/net_lwip_webserver/src/tusb_config.h
index 3285ea52c..aff75866d 100644
--- a/examples/device/net_lwip_webserver/src/tusb_config.h
+++ b/examples/device/net_lwip_webserver/src/tusb_config.h
@@ -30,8 +30,6 @@
extern "C" {
#endif
-#include "lwipopts.h"
-
//--------------------------------------------------------------------+
// Board Specific Configuration
//--------------------------------------------------------------------+
@@ -92,32 +90,50 @@ extern "C" {
#define USE_ECM 1
#elif TU_CHECK_MCU(OPT_MCU_STM32F0, OPT_MCU_STM32F1)
#define USE_ECM 1
-#elif TU_CHECK_MCU(OPT_MCU_MAX32690, OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX78002)
- #define USE_ECM 1
#else
#define USE_ECM 0
- #define INCLUDE_IPERF
#endif
#endif
+// MCU SRAM tier — drives the bigger lwIP buffers in lwipopts.h, the larger
+// NCM OUT NTB size below, and whether iperf is built. Small-RAM MCUs
+// (stm32c0/f1/wb, lpc11/13, samd11) keep modest defaults to fit.
+#ifndef LWIP_HIGH_THROUGHPUT
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002) || \
+ TU_CHECK_MCU(OPT_MCU_STM32F2, OPT_MCU_STM32F4, OPT_MCU_STM32F7) || \
+ TU_CHECK_MCU(OPT_MCU_STM32H5, OPT_MCU_STM32H7, OPT_MCU_STM32H7RS) || \
+ TU_CHECK_MCU(OPT_MCU_STM32U5, OPT_MCU_STM32N6) || \
+ TU_CHECK_MCU(OPT_MCU_RP2040) || \
+ TU_CHECK_MCU(OPT_MCU_MIMXRT1XXX) || \
+ TU_CHECK_MCU(OPT_MCU_NRF5X)
+ #define LWIP_HIGH_THROUGHPUT 1
+ #else
+ #define LWIP_HIGH_THROUGHPUT 0
+ #endif
+#endif
+
+#if LWIP_HIGH_THROUGHPUT && !defined(INCLUDE_IPERF)
+ #define INCLUDE_IPERF
+#endif
+
//--------------------------------------------------------------------
// NCM CLASS CONFIGURATION, SEE "ncm.h" FOR PERFORMANCE TUNING
//--------------------------------------------------------------------
-// Must be >> MTU
-// Can be set to 2048 without impact
-#define CFG_TUD_NCM_IN_NTB_MAX_SIZE (2 * TCP_MSS + 100)
+// CDC-NCM 1.0 Table 6-4 defines 2048 as the minimum required NTB size
+#define CFG_TUD_NCM_IN_NTB_MAX_SIZE 2048
-// Must be >> MTU
-// Can be set to smaller values if wNtbOutMaxDatagrams==1
-#define CFG_TUD_NCM_OUT_NTB_MAX_SIZE (2 * TCP_MSS + 100)
+#if LWIP_HIGH_THROUGHPUT
+ #define CFG_TUD_NCM_OUT_NTB_MAX_SIZE 4096
+#else
+ #define CFG_TUD_NCM_OUT_NTB_MAX_SIZE 2048
+#endif
// Number of NCM transfer blocks for reception side
#ifndef CFG_TUD_NCM_OUT_NTB_N
#define CFG_TUD_NCM_OUT_NTB_N 1
#endif
-// Number of NCM transfer blocks for transmission side
#ifndef CFG_TUD_NCM_IN_NTB_N
#define CFG_TUD_NCM_IN_NTB_N 1
#endif
diff --git a/examples/device/net_lwip_webserver/src/usb_descriptors.c b/examples/device/net_lwip_webserver/src/usb_descriptors.c
index c976cb62b..09090bb92 100644
--- a/examples/device/net_lwip_webserver/src/usb_descriptors.c
+++ b/examples/device/net_lwip_webserver/src/usb_descriptors.c
@@ -65,17 +65,19 @@ enum {
CONFIG_ID_COUNT
};
+#if CFG_TUD_NCM
+#define USB_BCD 0x0201
+#else
+#define USB_BCD 0x0200
+#endif
+
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
static const tusb_desc_device_t desc_device = {
.bLength = sizeof(tusb_desc_device_t),
.bDescriptorType = TUSB_DESC_DEVICE,
-#if CFG_TUD_NCM
- .bcdUSB = 0x0201,
-#else
- .bcdUSB = 0x0200,
-#endif
+ .bcdUSB = USB_BCD,
// Use Interface Association Descriptor (IAD) device class
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
@@ -121,12 +123,20 @@ const uint8_t *tud_descriptor_device_cb(void) {
#define EPNUM_NET_OUT 0x02
#define EPNUM_NET_IN 0x81
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
-// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
+// MCUs that don't support the same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
+
+#if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+// endpoint 8,9 has FIFO of 2048 bytes
+#define EPNUM_NET_NOTIF 0x81
+#define EPNUM_NET_OUT 0x08
+#define EPNUM_NET_IN 0x89
+#else
#define EPNUM_NET_NOTIF 0x81
#define EPNUM_NET_OUT 0x02
#define EPNUM_NET_IN 0x83
+#endif
#else
#define EPNUM_NET_NOTIF 0x81
@@ -136,57 +146,184 @@ const uint8_t *tud_descriptor_device_cb(void) {
#if CFG_TUD_ECM_RNDIS
-static uint8_t const rndis_configuration[] = {
+// full speed configuration
+static uint8_t const rndis_fs_configuration[] = {
// Config number (index+1), interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(CONFIG_ID_RNDIS + 1, ITF_NUM_TOTAL, 0, MAIN_CONFIG_TOTAL_LEN, 0, 100),
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_RNDIS_DESCRIPTOR(
- ITF_NUM_CDC, STRID_INTERFACE, EPNUM_NET_NOTIF, 8, EPNUM_NET_OUT, EPNUM_NET_IN, CFG_TUD_NET_ENDPOINT_SIZE),
+ ITF_NUM_CDC, STRID_INTERFACE, EPNUM_NET_NOTIF, 8, EPNUM_NET_OUT, EPNUM_NET_IN, 64),
};
-static const uint8_t ecm_configuration[] = {
+static const uint8_t ecm_fs_configuration[] = {
// Config number (index+1), interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(CONFIG_ID_ECM + 1, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100),
// 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_ECM_DESCRIPTOR(
ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN,
- CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU),
+ 64, CFG_TUD_NET_MTU),
};
-#else
+#if TUD_OPT_HIGH_SPEED
+// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
-static uint8_t const ncm_configuration[] = {
+// high speed configuration
+static uint8_t const rndis_hs_configuration[] = {
// Config number (index+1), interface count, string index, total length, attribute, power in mA
- TUD_CONFIG_DESCRIPTOR(CONFIG_ID_NCM + 1, ITF_NUM_TOTAL, 0, NCM_CONFIG_TOTAL_LEN, 0, 100),
+ TUD_CONFIG_DESCRIPTOR(CONFIG_ID_RNDIS + 1, ITF_NUM_TOTAL, 0, MAIN_CONFIG_TOTAL_LEN, 0, 100),
+
+ // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
+ TUD_RNDIS_DESCRIPTOR(
+ ITF_NUM_CDC, STRID_INTERFACE, EPNUM_NET_NOTIF, 8, EPNUM_NET_OUT, EPNUM_NET_IN, 512),
+};
+
+static const uint8_t ecm_hs_configuration[] = {
+ // Config number (index+1), interface count, string index, total length, attribute, power in mA
+ TUD_CONFIG_DESCRIPTOR(CONFIG_ID_ECM + 1, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100),
// 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(
+ TUD_CDC_ECM_DESCRIPTOR(
ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN,
- CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU),
+ 512, CFG_TUD_NET_MTU),
+};
+#endif // highspeed
+
+#else
+
+// full speed configuration
+static uint8_t const ncm_fs_configuration[] = {
+ // Config number (index+1), interface count, string index, total length, attribute, power in mA
+ TUD_CONFIG_DESCRIPTOR(CONFIG_ID_NCM + 1, ITF_NUM_TOTAL, 0, NCM_CONFIG_TOTAL_LEN, 0, 100),
+
+ // Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size, EP notification bInterval, NCM capabilities.
+ TUD_CDC_NCM_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN,
+ 64, CFG_TUD_NET_MTU, 50, (uint8_t)((uint8_t)NCM_NETWORK_CAPS_ETH_FILTER | (uint8_t)NCM_NETWORK_CAPS_NTB_INPUT_SIZE)),
+};
+
+#if TUD_OPT_HIGH_SPEED
+// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
+
+// high speed configuration
+// bInterval: FS=50 means 50ms; HS encodes as 2^(n-1) * 125us, so 9 = 2^8 * 125us = 32ms
+static uint8_t const ncm_hs_configuration[] = {
+ // Config number (index+1), interface count, string index, total length, attribute, power in mA
+ TUD_CONFIG_DESCRIPTOR(CONFIG_ID_NCM + 1, ITF_NUM_TOTAL, 0, NCM_CONFIG_TOTAL_LEN, 0, 100),
+
+ // Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size, EP notification bInterval, NCM capabilities.
+ TUD_CDC_NCM_DESCRIPTOR(ITF_NUM_CDC, STRID_INTERFACE, STRID_MAC, EPNUM_NET_NOTIF, 64, EPNUM_NET_OUT, EPNUM_NET_IN,
+ 512, CFG_TUD_NET_MTU, 9, (uint8_t)((uint8_t)NCM_NETWORK_CAPS_ETH_FILTER | (uint8_t)NCM_NETWORK_CAPS_NTB_INPUT_SIZE)),
};
+#endif // highspeed
#endif
-// Configuration array: RNDIS and CDC-ECM
+// NCM work with all latest OS i.e macos 10.10+, windows 10+, and Linux.
+// For older system Configuration array of RNDIS and CDC-ECM may be needed for better compatibility.
// - Windows only works with RNDIS
// - MacOS only works with CDC-ECM
// - Linux will work on both
-static const uint8_t *const configuration_arr[CONFIG_ID_COUNT] = {
#if CFG_TUD_ECM_RNDIS
- [CONFIG_ID_RNDIS] = rndis_configuration,
- [CONFIG_ID_ECM] = ecm_configuration
+
+static const uint8_t *const configuration_fs_arr[CONFIG_ID_COUNT] = {
+ [CONFIG_ID_RNDIS] = rndis_fs_configuration,
+ [CONFIG_ID_ECM] = ecm_fs_configuration
+};
+
+#if TUD_OPT_HIGH_SPEED
+static const uint8_t *const configuration_hs_arr[CONFIG_ID_COUNT] = {
+ [CONFIG_ID_RNDIS] = rndis_hs_configuration,
+ [CONFIG_ID_ECM] = ecm_hs_configuration
+};
+
+// Size array for each configuration
+static const uint16_t configuration_sz_arr[CONFIG_ID_COUNT] = {
+ [CONFIG_ID_RNDIS] = MAIN_CONFIG_TOTAL_LEN,
+ [CONFIG_ID_ECM] = ALT_CONFIG_TOTAL_LEN
+};
+
+// Scratch buffer for other speed configuration (sized to hold the largest config)
+#define MAX_CONFIG_TOTAL_LEN TU_MAX(MAIN_CONFIG_TOTAL_LEN, ALT_CONFIG_TOTAL_LEN)
+#endif
+
#else
- [CONFIG_ID_NCM] = ncm_configuration
+
+static const uint8_t *const configuration_fs_arr[CONFIG_ID_COUNT] = {
+ [CONFIG_ID_NCM] = ncm_fs_configuration
+};
+
+#if TUD_OPT_HIGH_SPEED
+static const uint8_t *const configuration_hs_arr[CONFIG_ID_COUNT] = {
+ [CONFIG_ID_NCM] = ncm_hs_configuration
+};
+
+// Size array for each configuration
+static const uint16_t configuration_sz_arr[CONFIG_ID_COUNT] = {
+ [CONFIG_ID_NCM] = NCM_CONFIG_TOTAL_LEN
+};
+
+// Scratch buffer for other speed configuration (sized to hold the largest config)
+#define MAX_CONFIG_TOTAL_LEN NCM_CONFIG_TOTAL_LEN
#endif
+
+#endif
+
+#if TUD_OPT_HIGH_SPEED
+static uint8_t desc_other_speed_config[MAX_CONFIG_TOTAL_LEN];
+
+// device qualifier: device descriptor fields that differ at other speed
+static tusb_desc_device_qualifier_t const desc_device_qualifier = {
+ .bLength = sizeof(tusb_desc_device_qualifier_t),
+ .bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
+ .bcdUSB = USB_BCD,
+
+ .bDeviceClass = TUSB_CLASS_MISC,
+ .bDeviceSubClass = MISC_SUBCLASS_COMMON,
+ .bDeviceProtocol = MISC_PROTOCOL_IAD,
+
+ .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
+ .bNumConfigurations = CONFIG_ID_COUNT,
+ .bReserved = 0x00
};
+// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
+// device_qualifier descriptor describes information about a high-speed capable device that would
+// change if the device were operating at the other speed. If not highspeed capable stall this request.
+uint8_t const *tud_descriptor_device_qualifier_cb(void) {
+ return (uint8_t const *) &desc_device_qualifier;
+}
+
+// Invoked when received GET OTHER SPEED CONFIGURATION DESCRIPTOR request
+// Application return pointer to descriptor, whose 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) {
+ if (index >= CONFIG_ID_COUNT) return NULL;
+
+ // if link speed is high return fullspeed config, and vice versa
+ const uint8_t *const *arr = (tud_speed_get() == TUSB_SPEED_HIGH) ? configuration_fs_arr : configuration_hs_arr;
+
+ // Note: the descriptor type is OTHER_SPEED_CONFIG instead of CONFIG
+ memcpy(desc_other_speed_config, arr[index], configuration_sz_arr[index]);
+ desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
+
+ return desc_other_speed_config;
+}
+
+#endif // highspeed
+
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
const uint8_t *tud_descriptor_configuration_cb(uint8_t index) {
- return (index < CONFIG_ID_COUNT) ? configuration_arr[index] : NULL;
+ if (index >= CONFIG_ID_COUNT) return NULL;
+#if TUD_OPT_HIGH_SPEED
+ // Although we are highspeed, host may be fullspeed.
+ return (tud_speed_get() == TUSB_SPEED_HIGH) ? configuration_hs_arr[index] : configuration_fs_arr[index];
+#else
+ return configuration_fs_arr[index];
+#endif
}
#if CFG_TUD_NCM
diff --git a/examples/device/printer_to_cdc/src/usb_descriptors.c b/examples/device/printer_to_cdc/src/usb_descriptors.c
index 30d309ed4..db7bfe97a 100644
--- a/examples/device/printer_to_cdc/src/usb_descriptors.c
+++ b/examples/device/printer_to_cdc/src/usb_descriptors.c
@@ -67,12 +67,21 @@ uint8_t const *tud_descriptor_device_cb(void) {
//--------------------------------------------------------------------+
// Endpoint numbers
-#if defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
- #define EPNUM_CDC_NOTIF 0x81
- #define EPNUM_CDC_OUT 0x02
- #define EPNUM_CDC_IN 0x83
- #define EPNUM_PRINTER_OUT 0x04
- #define EPNUM_PRINTER_IN 0x85
+#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x08
+ #define EPNUM_CDC_IN 0x89
+ #define EPNUM_PRINTER_OUT 0x0A
+ #define EPNUM_PRINTER_IN 0x8B
+ #else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x83
+ #define EPNUM_PRINTER_OUT 0x04
+ #define EPNUM_PRINTER_IN 0x85
+ #endif
#else
#define EPNUM_CDC_NOTIF 0x81
#define EPNUM_CDC_OUT 0x02
diff --git a/examples/device/uac2_headset/src/usb_descriptors.c b/examples/device/uac2_headset/src/usb_descriptors.c
index e4fbbf8a5..b554e7195 100644
--- a/examples/device/uac2_headset/src/usb_descriptors.c
+++ b/examples/device/uac2_headset/src/usb_descriptors.c
@@ -97,12 +97,19 @@ uint8_t const * tud_descriptor_device_cb(void)
#define EPNUM_AUDIO_OUT 0x08
#define EPNUM_AUDIO_INT 0x01
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_AUDIO_IN 0x01
- #define EPNUM_AUDIO_OUT 0x02
- #define EPNUM_AUDIO_INT 0x03
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP10/11 so the 4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO_OUT 0x0A
+ #define EPNUM_AUDIO_IN 0x0B
+ #define EPNUM_AUDIO_INT 0x01
+ #else
+ #define EPNUM_AUDIO_IN 0x01
+ #define EPNUM_AUDIO_OUT 0x02
+ #define EPNUM_AUDIO_INT 0x03
+ #endif
#else
#define EPNUM_AUDIO_IN 0x01
diff --git a/examples/device/uac2_speaker_fb/src/usb_descriptors.c b/examples/device/uac2_speaker_fb/src/usb_descriptors.c
index c5a161a1e..f0c780e38 100644
--- a/examples/device/uac2_speaker_fb/src/usb_descriptors.c
+++ b/examples/device/uac2_speaker_fb/src/usb_descriptors.c
@@ -115,12 +115,19 @@ uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf) {
#define EPNUM_AUDIO_FB 0x08
#define EPNUM_DEBUG 0x01
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_AUDIO 0x02
- #define EPNUM_AUDIO_FB 0x01
- #define EPNUM_DEBUG 0x03
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put audio iso on EP10/11 so the 4096-byte FIFOs can back double packet buffering
+ #define EPNUM_AUDIO 0x0A
+ #define EPNUM_AUDIO_FB 0x0B
+ #define EPNUM_DEBUG 0x01
+ #else
+ #define EPNUM_AUDIO 0x02
+ #define EPNUM_AUDIO_FB 0x01
+ #define EPNUM_DEBUG 0x03
+ #endif
#else
#define EPNUM_AUDIO 0x01
diff --git a/examples/device/webusb_serial/src/usb_descriptors.c b/examples/device/webusb_serial/src/usb_descriptors.c
index 0ef41a68e..527837161 100644
--- a/examples/device/webusb_serial/src/usb_descriptors.c
+++ b/examples/device/webusb_serial/src/usb_descriptors.c
@@ -104,15 +104,25 @@ enum
#define EPNUM_VENDOR_OUT 0x05
#define EPNUM_VENDOR_IN 0x84
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
- #define EPNUM_CDC_NOTIF 0x81
- #define EPNUM_CDC_OUT 0x02
- #define EPNUM_CDC_IN 0x83
+ #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002)
+ // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x08
+ #define EPNUM_CDC_IN 0x89
+
+ #define EPNUM_VENDOR_OUT 0x0A
+ #define EPNUM_VENDOR_IN 0x8B
+ #else
+ #define EPNUM_CDC_NOTIF 0x81
+ #define EPNUM_CDC_OUT 0x02
+ #define EPNUM_CDC_IN 0x83
- #define EPNUM_VENDOR_OUT 0x04
- #define EPNUM_VENDOR_IN 0x85
+ #define EPNUM_VENDOR_OUT 0x04
+ #define EPNUM_VENDOR_IN 0x85
+ #endif
#else
#define EPNUM_CDC_NOTIF 0x81
diff --git a/examples/dual/dynamic_switch/src/usb_descriptors.c b/examples/dual/dynamic_switch/src/usb_descriptors.c
index 54ffc2c18..ef6d795b7 100644
--- a/examples/dual/dynamic_switch/src/usb_descriptors.c
+++ b/examples/dual/dynamic_switch/src/usb_descriptors.c
@@ -86,7 +86,7 @@ enum {
#define EPNUM_CDC_OUT 0x02
#define EPNUM_CDC_IN 0x82
-#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY)
+#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY
// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h
// e.g EP1 OUT & EP1 IN cannot exist together
#define EPNUM_CDC_NOTIF 0x81
diff --git a/examples/host/CMakeLists.txt b/examples/host/CMakeLists.txt
index f8e0ce692..70e0427ab 100644
--- a/examples/host/CMakeLists.txt
+++ b/examples/host/CMakeLists.txt
@@ -14,6 +14,7 @@ set(EXAMPLE_LIST
hid_controller
midi_rx
msc_file_explorer
+ msc_file_explorer_freertos
)
foreach (example ${EXAMPLE_LIST})
diff --git a/examples/host/msc_file_explorer_freertos/CMakeLists.txt b/examples/host/msc_file_explorer_freertos/CMakeLists.txt
new file mode 100644
index 000000000..4893dd1fb
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/CMakeLists.txt
@@ -0,0 +1,42 @@
+cmake_minimum_required(VERSION 3.20)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+
+project(msc_file_explorer_freertos C CXX ASM)
+
+# Checks this example is valid for the family and initializes the project
+family_initialize_project(${PROJECT_NAME} ${CMAKE_CURRENT_LIST_DIR})
+
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+ return()
+endif()
+
+add_executable(${PROJECT_NAME})
+
+# Example source
+target_sources(${PROJECT_NAME} PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/msc_app.c
+ ${TOP}/lib/fatfs/source/ff.c
+ ${TOP}/lib/fatfs/source/ffsystem.c
+ ${TOP}/lib/fatfs/source/ffunicode.c
+ )
+
+# Example include
+target_include_directories(${PROJECT_NAME} PUBLIC
+ ${CMAKE_CURRENT_SOURCE_DIR}/src
+ ${TOP}/lib/fatfs/source
+ ${TOP}/lib/embedded-cli
+ )
+
+# Configure compilation flags and libraries for the example with FreeRTOS.
+# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
+family_configure_host_example(${PROJECT_NAME} freertos)
+
+# Suppress warnings on fatfs
+if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
+ set_source_files_properties(${TOP}/lib/fatfs/source/ff.c PROPERTIES
+ COMPILE_OPTIONS "-Wno-conversion;-Wno-cast-qual"
+ )
+endif ()
diff --git a/examples/host/msc_file_explorer_freertos/CMakePresets.json b/examples/host/msc_file_explorer_freertos/CMakePresets.json
new file mode 100644
index 000000000..5cd8971e9
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/CMakePresets.json
@@ -0,0 +1,6 @@
+{
+ "version": 6,
+ "include": [
+ "../../../hw/bsp/BoardPresets.json"
+ ]
+}
diff --git a/examples/host/msc_file_explorer_freertos/Makefile b/examples/host/msc_file_explorer_freertos/Makefile
new file mode 100644
index 000000000..15c7420d4
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/Makefile
@@ -0,0 +1,27 @@
+RTOS = freertos
+include ../../../hw/bsp/family_support.mk
+
+FATFS_PATH = lib/fatfs/source
+
+INC += \
+ src \
+ $(TOP)/$(FATFS_PATH) \
+ $(TOP)/lib/embedded-cli \
+
+# Example source
+EXAMPLE_SOURCE = \
+ src/main.c \
+ src/msc_app.c \
+
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
+
+# FatFS source
+SRC_C += \
+ $(FATFS_PATH)/ff.c \
+ $(FATFS_PATH)/ffsystem.c \
+ $(FATFS_PATH)/ffunicode.c \
+
+# suppress warning caused by fatfs
+CFLAGS += -Wno-error=cast-qual
+
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/host/msc_file_explorer_freertos/README.md b/examples/host/msc_file_explorer_freertos/README.md
new file mode 100644
index 000000000..4ee6b96fa
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/README.md
@@ -0,0 +1,105 @@
+# MSC File Explorer (FreeRTOS)
+
+This host example implements an interactive command-line file browser for USB Mass Storage devices.
+When a USB flash drive is connected, the device is automatically mounted using FatFS and a shell-like
+CLI is presented over the board's serial console.
+
+## Features
+
+- Automatic mount/unmount of USB storage devices
+- FAT12/16/32 filesystem support via FatFS
+- Interactive CLI with command history
+- Read speed benchmarking with `dd`
+- Support for up to 4 simultaneous USB storage devices (via hub)
+
+## Supported Commands
+
+| Command | Usage | Description |
+|---------|--------------------|------------------------------------------------------|
+| help | `help` | Print list of available commands |
+| cat | `cat <file>` | Print file contents to the console |
+| cd | `cd <dir>` | Change current working directory |
+| cp | `cp <src> <dest>` | Copy a file |
+| dd | `dd [count]` | Read sectors and report speed (default 1024 sectors) |
+| ls | `ls [dir]` | List directory contents |
+| pwd | `pwd` | Print current working directory |
+| mkdir | `mkdir <dir>` | Create a directory |
+| mv | `mv <src> <dest>` | Rename/move a file or directory |
+| rm | `rm <file>` | Remove a file |
+
+## Build
+
+Build for a specific board using CMake (see [Getting Started](https://docs.tinyusb.org/en/latest/getting_started.html)):
+
+```bash
+# Example: build for STM32F407 Discovery board
+cmake -B build -DBOARD=stm32f407disco -GNinja examples/host/msc_file_explorer_freertos
+cmake --build build
+```
+
+## Usage
+
+1. Flash the firmware to your board.
+2. Open a serial terminal (e.g. `minicom`, `screen`, `PuTTY`) at 115200 baud.
+3. Plug a USB flash drive into the board's USB host port.
+4. The device is auto-mounted and the prompt appears:
+
+```
+TinyUSB MSC File Explorer Example
+
+Device connected
+ Vendor : Kingston
+ Product : DataTraveler 2.0
+ Rev : 1.0
+ Capacity: 1.9 GB
+
+0:/> _
+```
+
+### Browsing Files
+
+```
+0:/> ls
+----a 1234 readme.txt
+d---- 0 photos
+d---- 0 docs
+
+0:/> cd photos
+0:/photos> ls
+----a 520432 vacation.jpg
+----a 312088 family.png
+
+0:/> cat readme.txt
+Hello from USB drive!
+```
+
+### Copying and Moving Files
+
+```
+0:/> cp readme.txt backup.txt
+0:/> mv backup.txt docs/backup.txt
+```
+
+### Measuring Read Speed
+
+```
+0:/> dd
+Reading 1024 sectors...
+ Data speed: 823 KB/s
+```
+
+### Multiple Devices
+
+When using a USB hub, multiple drives are mounted as `0:`, `1:`, etc. Use the drive prefix to
+navigate between them:
+
+```
+0:/> cd 1:
+1:/> ls
+```
+
+## Testing
+
+Build-time validation follows the standard TinyUSB host example flow. Runtime behavior should be
+verified on hardware by attaching an MSC device and exercising CLI commands such as `ls`, `pwd`,
+and `dd`.
diff --git a/examples/host/msc_file_explorer_freertos/only.txt b/examples/host/msc_file_explorer_freertos/only.txt
new file mode 100644
index 000000000..519ac2ebd
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/only.txt
@@ -0,0 +1,28 @@
+family:espressif
+family:samd21
+family:samd5x_e5x
+mcu:LPC175X_6X
+mcu:LPC177X_8X
+mcu:LPC18XX
+mcu:LPC40XX
+mcu:LPC43XX
+mcu:LPC54
+mcu:LPC55
+mcu:MAX3421
+mcu:MIMXRT10XX
+mcu:MIMXRT11XX
+mcu:MIMXRT1XXX
+mcu:MSP432E4
+mcu:RP2040
+mcu:RW61X
+mcu:RX65X
+mcu:STM32C0
+mcu:STM32F4
+mcu:STM32F7
+mcu:STM32G0
+mcu:STM32H5
+mcu:STM32H7
+mcu:STM32H7RS
+mcu:STM32N6
+mcu:STM32U3
+mcu:STM32U5
diff --git a/examples/host/msc_file_explorer_freertos/skip.txt b/examples/host/msc_file_explorer_freertos/skip.txt
new file mode 100644
index 000000000..f0be07d25
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/skip.txt
@@ -0,0 +1,3 @@
+mcu:CH32F20X
+board:lpcxpresso54114
+mcu:FT90X
diff --git a/examples/host/msc_file_explorer_freertos/src/CMakeLists.txt b/examples/host/msc_file_explorer_freertos/src/CMakeLists.txt
new file mode 100644
index 000000000..c3fb35607
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/src/CMakeLists.txt
@@ -0,0 +1,13 @@
+# This file is for ESP-IDF only
+set(FATFS_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../../lib/fatfs/source)
+set(EMBEDDED_CLI_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../../lib/embedded-cli)
+
+idf_component_register(
+ SRCS "main.c" "msc_app.c"
+ ${FATFS_DIR}/ff.c
+ ${FATFS_DIR}/ffsystem.c
+ ${FATFS_DIR}/ffunicode.c
+ INCLUDE_DIRS "." ${FATFS_DIR} ${EMBEDDED_CLI_DIR}
+ REQUIRES boards tinyusb_src)
+
+target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-error=format)
diff --git a/examples/host/msc_file_explorer_freertos/src/ffconf.h b/examples/host/msc_file_explorer_freertos/src/ffconf.h
new file mode 100644
index 000000000..5c89136fe
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/src/ffconf.h
@@ -0,0 +1,313 @@
+/*---------------------------------------------------------------------------/
+/ Configurations of FatFs Module
+/---------------------------------------------------------------------------*/
+
+#define FFCONF_DEF 80386 /* Revision ID */
+
+/*---------------------------------------------------------------------------/
+/ Function Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_FS_READONLY 0
+/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
+/ Read-only configuration removes writing API functions, f_write(), f_sync(),
+/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
+/ and optional writing functions as well. */
+
+
+#define FF_FS_MINIMIZE 0
+/* This option defines minimization level to remove some basic API functions.
+/
+/ 0: Basic functions are fully enabled.
+/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
+/ are removed.
+/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
+/ 3: f_lseek() function is removed in addition to 2. */
+
+
+#define FF_USE_FIND 0
+/* This option switches filtered directory read functions, f_findfirst() and
+/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
+
+
+#define FF_USE_MKFS 0
+/* This option switches f_mkfs(). (0:Disable or 1:Enable) */
+
+
+#define FF_USE_FASTSEEK 0
+/* This option switches fast seek feature. (0:Disable or 1:Enable) */
+
+
+#define FF_USE_EXPAND 0
+/* This option switches f_expand(). (0:Disable or 1:Enable) */
+
+
+#define FF_USE_CHMOD 0
+/* This option switches attribute control API functions, f_chmod() and f_utime().
+/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
+
+
+#define FF_USE_LABEL 0
+/* This option switches volume label API functions, f_getlabel() and f_setlabel().
+/ (0:Disable or 1:Enable) */
+
+
+#define FF_USE_FORWARD 0
+/* This option switches f_forward(). (0:Disable or 1:Enable) */
+
+
+#define FF_USE_STRFUNC 0
+#define FF_PRINT_LLI 0
+#define FF_PRINT_FLOAT 0
+#define FF_STRF_ENCODE 0
+/* FF_USE_STRFUNC switches string API functions, f_gets(), f_putc(), f_puts() and
+/ f_printf().
+/
+/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
+/ 1: Enable without LF-CRLF conversion.
+/ 2: Enable with LF-CRLF conversion.
+/
+/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
+/ makes f_printf() support floating point argument. These features want C99 or later.
+/ When FF_LFN_UNICODE >= 1 with LFN enabled, string API functions convert the character
+/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
+/ to be read/written via those functions.
+/
+/ 0: ANSI/OEM in current CP
+/ 1: Unicode in UTF-16LE
+/ 2: Unicode in UTF-16BE
+/ 3: Unicode in UTF-8
+*/
+
+
+/*---------------------------------------------------------------------------/
+/ Locale and Namespace Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_CODE_PAGE 437
+/* This option specifies the OEM code page to be used on the target system.
+/ Incorrect code page setting can cause a file open failure.
+/
+/ 437 - U.S.
+/ 720 - Arabic
+/ 737 - Greek
+/ 771 - KBL
+/ 775 - Baltic
+/ 850 - Latin 1
+/ 852 - Latin 2
+/ 855 - Cyrillic
+/ 857 - Turkish
+/ 860 - Portuguese
+/ 861 - Icelandic
+/ 862 - Hebrew
+/ 863 - Canadian French
+/ 864 - Arabic
+/ 865 - Nordic
+/ 866 - Russian
+/ 869 - Greek 2
+/ 932 - Japanese (DBCS)
+/ 936 - Simplified Chinese (DBCS)
+/ 949 - Korean (DBCS)
+/ 950 - Traditional Chinese (DBCS)
+/ 0 - Include all code pages above and configured by f_setcp()
+*/
+
+
+#define FF_USE_LFN 1
+#define FF_MAX_LFN 255
+/* The FF_USE_LFN switches the support for LFN (long file name).
+/
+/ 0: Disable LFN. FF_MAX_LFN has no effect.
+/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
+/ 2: Enable LFN with dynamic working buffer on the STACK.
+/ 3: Enable LFN with dynamic working buffer on the HEAP.
+/
+/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN feature
+/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
+/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
+/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
+/ be in range of 12 to 255. It is recommended to be set 255 to fully support the LFN
+/ specification.
+/ When use stack for the working buffer, take care on stack overflow. When use heap
+/ memory for the working buffer, memory management functions, ff_memalloc() and
+/ ff_memfree() exemplified in ffsystem.c, need to be added to the project. */
+
+
+#define FF_LFN_UNICODE 0
+/* This option switches the character encoding on the API when LFN is enabled.
+/
+/ 0: ANSI/OEM in current CP (TCHAR = char)
+/ 1: Unicode in UTF-16 (TCHAR = WCHAR)
+/ 2: Unicode in UTF-8 (TCHAR = char)
+/ 3: Unicode in UTF-32 (TCHAR = DWORD)
+/
+/ Also behavior of string I/O functions will be affected by this option.
+/ When LFN is not enabled, this option has no effect. */
+
+
+#define FF_LFN_BUF 255
+#define FF_SFN_BUF 12
+/* This set of options defines size of file name members in the FILINFO structure
+/ which is used to read out directory items. These values should be sufficient for
+/ the file names to read. The maximum possible length of the read file name depends
+/ on character encoding. When LFN is not enabled, these options have no effect. */
+
+
+#define FF_FS_RPATH 2
+/* This option configures support for relative path feature.
+/
+/ 0: Disable relative path and remove related API functions.
+/ 1: Enable relative path and dot names. f_chdir() and f_chdrive() are available.
+/ 2: f_getcwd() is available in addition to 1.
+*/
+
+
+#define FF_PATH_DEPTH 10
+/* This option defines maximum depth of directory in the exFAT volume. It is NOT
+/ relevant to FAT/FAT32 volume.
+/ For example, FF_PATH_DEPTH = 3 will able to follow a path "/dir1/dir2/dir3/file"
+/ but a sub-directory in the dir3 will not able to be followed and set current
+/ directory.
+/ The size of filesystem object (FATFS) increases FF_PATH_DEPTH * 24 bytes.
+/ When FF_FS_EXFAT == 0 or FF_FS_RPATH == 0, this option has no effect.
+*/
+
+
+
+/*---------------------------------------------------------------------------/
+/ Drive/Volume Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_VOLUMES 4
+/* Number of volumes (logical drives) to be used. (1-10) */
+
+
+#define FF_STR_VOLUME_ID 0
+#define FF_VOLUME_STRS "RAM","NAND","CF","SD","SD2","USB","USB2","USB3"
+/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
+/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
+/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
+/ logical drive. Number of items must not be less than FF_VOLUMES. Valid
+/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
+/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
+/ not defined, a user defined volume string table is needed as:
+/
+/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
+*/
+
+
+#define FF_MULTI_PARTITION 0
+/* This option switches support for multiple volumes on the physical drive.
+/ By default (0), each logical drive number is bound to the same physical drive
+/ number and only an FAT volume found on the physical drive will be mounted.
+/ When this feature is enabled (1), each logical drive number can be bound to
+/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
+/ will be available. */
+
+
+#define FF_MIN_SS 512
+#define FF_MAX_SS 512
+/* This set of options configures the range of sector size to be supported. (512,
+/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
+/ harddisk, but a larger value may be required for on-board flash memory and some
+/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is
+/ configured for variable sector size mode and disk_ioctl() needs to implement
+/ GET_SECTOR_SIZE command. */
+
+
+#define FF_LBA64 0
+/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
+/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
+
+
+#define FF_MIN_GPT 0x10000000
+/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs() and
+/ f_fdisk(). 2^32 sectors maximum. This option has no effect when FF_LBA64 == 0. */
+
+
+#define FF_USE_TRIM 0
+/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
+/ To enable this feature, also CTRL_TRIM command should be implemented to
+/ the disk_ioctl(). */
+
+
+
+/*---------------------------------------------------------------------------/
+/ System Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_FS_TINY 0
+/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
+/ At the tiny configuration, size of file object (FIL) is reduced FF_MAX_SS bytes.
+/ Instead of private sector buffer eliminated from the file object, common sector
+/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
+
+
+#define FF_FS_EXFAT 0
+/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
+/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
+/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
+
+
+#define FF_FS_NORTC 1
+#define FF_NORTC_MON 1
+#define FF_NORTC_MDAY 1
+#define FF_NORTC_YEAR 2025
+/* The option FF_FS_NORTC switches timestamp feature. If the system does not have
+/ an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the
+/ timestamp feature. Every object modified by FatFs will have a fixed timestamp
+/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
+/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() need to be added
+/ to the project to read current time form real-time clock. FF_NORTC_MON,
+/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
+/ These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
+
+
+#define FF_FS_CRTIME 0
+/* This option enables(1)/disables(0) the timestamp of the file created. When
+/ set 1, the file created time is available in FILINFO structure. */
+
+
+#define FF_FS_NOFSINFO 0
+/* If you need to know the correct free space on the FAT32 volume, set bit 0 of
+/ this option, and f_getfree() on the first time after volume mount will force
+/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
+/
+/ bit0=0: Use free cluster count in the FSINFO if available.
+/ bit0=1: Do not trust free cluster count in the FSINFO.
+/ bit1=0: Use last allocated cluster number in the FSINFO if available.
+/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
+*/
+
+
+#define FF_FS_LOCK 0
+/* The option FF_FS_LOCK switches file lock function to control duplicated file open
+/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
+/ is 1.
+/
+/ 0: Disable file lock function. To avoid volume corruption, application program
+/ should avoid illegal open, remove and rename to the open objects.
+/ >0: Enable file lock function. The value defines how many files/sub-directories
+/ can be opened simultaneously under file lock control. Note that the file
+/ lock control is independent of re-entrancy. */
+
+
+#define FF_FS_REENTRANT 0
+#define FF_FS_TIMEOUT 1000
+/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
+/ module itself. Note that regardless of this option, file access to different
+/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
+/ and f_fdisk(), are always not re-entrant. Only file/directory access to
+/ the same volume is under control of this featuer.
+/
+/ 0: Disable re-entrancy. FF_FS_TIMEOUT have no effect.
+/ 1: Enable re-entrancy. Also user provided synchronization handlers,
+/ ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give(),
+/ must be added to the project. Samples are available in ffsystem.c.
+/
+/ The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick.
+*/
+
+
+
+/*--- End of configuration options ---*/
diff --git a/examples/host/msc_file_explorer_freertos/src/main.c b/examples/host/msc_file_explorer_freertos/src/main.c
new file mode 100644
index 000000000..d1e627f4f
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/src/main.c
@@ -0,0 +1,158 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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.
+ *
+ */
+
+#include <string.h>
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+#ifdef ESP_PLATFORM
+ // ESP-IDF need "freertos/" prefix in include path.
+ // CFG_TUSB_OS_INC_PATH should be defined accordingly.
+ #include "freertos/FreeRTOS.h"
+ #include "freertos/task.h"
+ #include "freertos/timers.h"
+#else
+ #include "FreeRTOS.h"
+ #include "task.h"
+ #include "timers.h"
+#endif
+
+#include "msc_app.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF PROTYPES
+//--------------------------------------------------------------------+
+#ifdef ESP_PLATFORM
+ #define USBH_STACK_SIZE 4096
+#else
+ // Increase stack size when debug log is enabled.
+ #define USBH_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 4 : 3))
+#endif
+
+enum {
+ BLINK_MOUNTED = 1000,
+};
+
+#if configSUPPORT_STATIC_ALLOCATION
+StaticTimer_t blinky_tmdef;
+
+StackType_t usb_host_stack[USBH_STACK_SIZE];
+StaticTask_t usb_host_taskdef;
+#endif
+
+TimerHandle_t blinky_tm;
+
+static void led_blinky_cb(TimerHandle_t xTimer);
+static void usb_host_task(void* param);
+
+/*------------- MAIN -------------*/
+int main(void) {
+ board_init();
+
+ printf("TinyUSB Host MassStorage Explorer FreeRTOS Example\r\n");
+
+ // Create soft timer for blinky and task for TinyUSB host stack.
+#if configSUPPORT_STATIC_ALLOCATION
+ blinky_tm = xTimerCreateStatic(NULL, pdMS_TO_TICKS(BLINK_MOUNTED), true, NULL, led_blinky_cb, &blinky_tmdef);
+ xTaskCreateStatic(usb_host_task, "usbh", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, usb_host_stack,
+ &usb_host_taskdef);
+#else
+ blinky_tm = xTimerCreate(NULL, pdMS_TO_TICKS(BLINK_MOUNTED), true, NULL, led_blinky_cb);
+ xTaskCreate(usb_host_task, "usbh", USBH_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
+#endif
+
+ xTimerStart(blinky_tm, 0);
+
+ // only start scheduler for non-espressif mcu
+#ifndef ESP_PLATFORM
+ vTaskStartScheduler();
+#endif
+
+ return 0;
+}
+
+#ifdef ESP_PLATFORM
+void app_main(void) {
+ main();
+}
+#endif
+
+// USB Host task
+// This top-level thread processes all USB events and invokes callbacks.
+static void usb_host_task(void* param) {
+ (void) param;
+
+ // init host stack on configured roothub port
+ tusb_rhport_init_t host_init = {
+ .role = TUSB_ROLE_HOST,
+ .speed = TUSB_SPEED_AUTO
+ };
+
+ if (!tusb_init(BOARD_TUH_RHPORT, &host_init)) {
+ printf("Failed to init USB Host Stack\r\n");
+ vTaskSuspend(NULL);
+ }
+
+ board_init_after_tusb();
+
+#if CFG_TUH_ENABLED && CFG_TUH_MAX3421
+ // FeatherWing MAX3421E uses MAX3421E GPIO0 for VBUS enable.
+ enum { IOPINS1_ADDR = 20u << 3 };
+ tuh_max3421_reg_write(BOARD_TUH_RHPORT, IOPINS1_ADDR, 0x01, false);
+#endif
+
+ if (!msc_app_init()) {
+ printf("Failed to init MSC app\r\n");
+ vTaskSuspend(NULL);
+ }
+
+ while (1) {
+ // TinyUSB host task.
+ tuh_task();
+ }
+}
+
+//--------------------------------------------------------------------+
+// TinyUSB Callbacks
+//--------------------------------------------------------------------+
+
+void tuh_mount_cb(uint8_t dev_addr) {
+ (void) dev_addr;
+}
+
+void tuh_umount_cb(uint8_t dev_addr) {
+ (void) dev_addr;
+}
+
+//--------------------------------------------------------------------+
+// Blinking Task
+//--------------------------------------------------------------------+
+static void led_blinky_cb(TimerHandle_t xTimer) {
+ (void) xTimer;
+ static bool led_state = false;
+
+ board_led_write(led_state);
+ led_state = 1 - led_state; // toggle
+}
diff --git a/examples/host/msc_file_explorer_freertos/src/msc_app.c b/examples/host/msc_file_explorer_freertos/src/msc_app.c
new file mode 100644
index 000000000..c7e00e52a
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/src/msc_app.c
@@ -0,0 +1,709 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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.
+ *
+ */
+
+#include <ctype.h>
+#include "tusb.h"
+#include "bsp/board_api.h"
+#ifdef ESP_PLATFORM
+ // ESP-IDF need "freertos/" prefix in include path.
+ // CFG_TUSB_OS_INC_PATH should be defined accordingly.
+ #include "freertos/FreeRTOS.h"
+ #include "freertos/task.h"
+ #include "freertos/timers.h"
+#else
+ #include "FreeRTOS.h"
+ #include "task.h"
+ #include "timers.h"
+#endif
+
+#include "ff.h"
+#include "diskio.h"
+
+// lib/embedded-cli
+#define EMBEDDED_CLI_IMPL
+#include "embedded_cli.h"
+
+#include "msc_app.h"
+
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM DECLARATION
+//--------------------------------------------------------------------+
+
+//------------- embedded-cli -------------//
+#define CLI_BUFFER_SIZE 512
+#define CLI_RX_BUFFER_SIZE 16
+#define CLI_CMD_BUFFER_SIZE 64
+#define CLI_HISTORY_SIZE 32
+#define CLI_BINDING_COUNT 9
+
+#ifdef ESP_PLATFORM
+ #define MSC_APP_STACK_SIZE 4096
+#else
+ #define MSC_APP_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 3 : 2))
+#endif
+
+static EmbeddedCli *_cli;
+static CLI_UINT cli_buffer[BYTES_TO_CLI_UINTS(CLI_BUFFER_SIZE)];
+
+#if configSUPPORT_STATIC_ALLOCATION
+StackType_t msc_app_stack[MSC_APP_STACK_SIZE];
+StaticTask_t msc_app_taskdef;
+#endif
+
+//------------- Elm Chan FatFS -------------//
+static CFG_TUH_MEM_SECTION FATFS fatfs[CFG_TUH_DEVICE_MAX]; // for simplicity only support 1 LUN per device
+static volatile bool _disk_busy[CFG_TUH_DEVICE_MAX];
+static volatile bool _mount_pending[CFG_TUH_DEVICE_MAX];
+
+static CFG_TUH_MEM_SECTION FIL file1, file2;
+
+#ifndef CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE
+#define CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE 4096
+#endif
+static CFG_TUH_MEM_SECTION uint8_t rw_buf[CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE];
+
+// define the buffer to be place in USB/DMA memory with correct alignment/cache line size
+CFG_TUH_MEM_SECTION static struct {
+ TUH_EPBUF_TYPE_DEF(scsi_inquiry_resp_t, inquiry);
+} scsi_resp;
+
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+static bool cli_init(void);
+static void msc_app_task(void* param);
+static void process_pending_mount(void);
+
+bool msc_app_init(void) {
+ for (size_t i = 0; i < CFG_TUH_DEVICE_MAX; i++) {
+ _disk_busy[i] = false;
+ _mount_pending[i] = false;
+ }
+
+// disable stdout buffered for echoing typing command
+#ifndef __ICCARM__ // TODO IAR doesn't support stream control ?
+ setbuf(stdout, NULL);
+#endif
+
+ cli_init();
+
+#if configSUPPORT_STATIC_ALLOCATION
+ TaskHandle_t task_hdl = xTaskCreateStatic(msc_app_task, "msc", MSC_APP_STACK_SIZE, NULL,
+ configMAX_PRIORITIES - 2, msc_app_stack, &msc_app_taskdef);
+ TU_ASSERT(task_hdl != NULL);
+#else
+ TU_ASSERT(xTaskCreate(msc_app_task, "msc", MSC_APP_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL) == pdPASS);
+#endif
+
+ return true;
+}
+
+static void msc_app_task(void* param) {
+ (void) param;
+
+ while (1) {
+ process_pending_mount();
+
+ if (!_cli) {
+ vTaskDelay(1);
+ continue;
+ }
+
+ int ch = board_getchar();
+ if (ch > 0) {
+ while (ch > 0) {
+ embeddedCliReceiveChar(_cli, (char) ch);
+ ch = board_getchar();
+ }
+ embeddedCliProcess(_cli);
+ }
+
+ vTaskDelay(1);
+ }
+}
+
+static void process_pending_mount(void) {
+ for (uint8_t drive_num = 0; drive_num < CFG_TUH_DEVICE_MAX; drive_num++) {
+ if (!_mount_pending[drive_num]) {
+ continue;
+ }
+
+ _mount_pending[drive_num] = false;
+
+ const uint8_t dev_addr = drive_num + 1;
+ if (!tuh_msc_mounted(dev_addr)) {
+ continue;
+ }
+
+ char drive_path[3] = "0:";
+ drive_path[0] += drive_num;
+
+ if (f_mount(&fatfs[drive_num], drive_path, 1) != FR_OK) {
+ printf("mount failed\r\n");
+ continue;
+ }
+
+ f_chdrive(drive_path);
+ FRESULT rc = f_chdir("/");
+ if (rc != FR_OK) {
+ printf("chdir failed: %d\r\n", rc);
+ }
+ }
+}
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+static bool inquiry_complete_cb(uint8_t dev_addr, const tuh_msc_complete_data_t *cb_data) {
+ const msc_cbw_t *cbw = cb_data->cbw;
+ const msc_csw_t *csw = cb_data->csw;
+
+ if (csw->status != 0) {
+ printf("Inquiry failed\r\n");
+ return false;
+ }
+
+ // Print out Vendor ID, Product ID and Rev
+ printf("%.8s %.16s %.4s\r\n", scsi_resp.inquiry.vendor_id, scsi_resp.inquiry.product_id,
+ scsi_resp.inquiry.product_rev);
+
+ // Get capacity of device
+ const uint32_t block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
+ const uint32_t block_size = tuh_msc_get_block_size(dev_addr, cbw->lun);
+
+ printf("Disk Size: %" PRIu32 " %" PRIu32 "-byte blocks: %" PRIu32 " MB\r\n",
+ block_count, block_size, block_count / ((1024 * 1024) / block_size));
+
+ // For simplicity: we only mount 1 LUN per device
+ const uint8_t drive_num = dev_addr - 1;
+ _mount_pending[drive_num] = true;
+
+ // print the drive label
+ // char label[34];
+ // if ( FR_OK == f_getlabel(drive_path, label, NULL) )
+ // {
+ // puts(label);
+ // }
+
+ return true;
+}
+
+//------------- IMPLEMENTATION -------------//
+void tuh_msc_mount_cb(uint8_t dev_addr) {
+ printf("A MassStorage device (addr = %u) is mounted\r\n", dev_addr);
+
+ const uint8_t lun = 0;
+ tuh_msc_inquiry(dev_addr, lun, &scsi_resp.inquiry, inquiry_complete_cb, 0);
+}
+
+void tuh_msc_umount_cb(uint8_t dev_addr) {
+ printf("A MassStorage device is unmounted\r\n");
+
+ const uint8_t drive_num = dev_addr - 1;
+ char drive_path[3] = "0:";
+ drive_path[0] += drive_num;
+
+ _mount_pending[drive_num] = false;
+
+ f_unmount(drive_path);
+
+ // if ( phy_disk == f_get_current_drive() )
+ // { // active drive is unplugged --> change to other drive
+ // for(uint8_t i=0; i<CFG_TUH_DEVICE_MAX; i++)
+ // {
+ // if ( disk_is_ready(i) )
+ // {
+ // f_chdrive(i);
+ // cli_init(); // refractor, rename
+ // }
+ // }
+ // }
+}
+
+//--------------------------------------------------------------------+
+// DiskIO
+//--------------------------------------------------------------------+
+
+static void wait_for_disk_io(BYTE pdrv) {
+ while (_disk_busy[pdrv]) {
+ vTaskDelay(1);
+ }
+}
+
+static bool disk_io_complete(uint8_t dev_addr, const tuh_msc_complete_data_t *cb_data) {
+ (void)dev_addr;
+ (void)cb_data;
+ _disk_busy[dev_addr - 1] = false;
+ return true;
+}
+
+DSTATUS disk_status(BYTE pdrv /* Physical drive nmuber to identify the drive */
+) {
+ uint8_t dev_addr = pdrv + 1;
+ return tuh_msc_mounted(dev_addr) ? 0 : STA_NODISK;
+}
+
+DSTATUS disk_initialize(BYTE pdrv /* Physical drive nmuber to identify the drive */
+) {
+ (void)pdrv;
+ return 0; // nothing to do
+}
+
+DRESULT disk_read(BYTE pdrv, /* Physical drive nmuber to identify the drive */
+ BYTE *buff, /* Data buffer to store read data */
+ LBA_t sector, /* Start sector in LBA */
+ UINT count /* Number of sectors to read */
+) {
+ const uint8_t dev_addr = pdrv + 1;
+ const uint8_t lun = 0;
+
+ _disk_busy[pdrv] = true;
+ tuh_msc_read10(dev_addr, lun, buff, sector, (uint16_t)count, disk_io_complete, 0);
+ wait_for_disk_io(pdrv);
+
+ return RES_OK;
+}
+
+#if FF_FS_READONLY == 0
+
+DRESULT disk_write(BYTE pdrv, /* Physical drive nmuber to identify the drive */
+ const BYTE *buff, /* Data to be written */
+ LBA_t sector, /* Start sector in LBA */
+ UINT count /* Number of sectors to write */
+) {
+ const uint8_t dev_addr = pdrv + 1;
+ const uint8_t lun = 0;
+
+ _disk_busy[pdrv] = true;
+ tuh_msc_write10(dev_addr, lun, buff, sector, (uint16_t)count, disk_io_complete, 0);
+ wait_for_disk_io(pdrv);
+
+ return RES_OK;
+}
+
+#endif
+
+DRESULT disk_ioctl(BYTE pdrv, /* Physical drive nmuber (0..) */
+ BYTE cmd, /* Control code */
+ void *buff /* Buffer to send/receive control data */
+) {
+ const uint8_t dev_addr = pdrv + 1;
+ const uint8_t lun = 0;
+ switch (cmd) {
+ case CTRL_SYNC:
+ // nothing to do since we do blocking
+ return RES_OK;
+
+ case GET_SECTOR_COUNT:
+ *((DWORD *)buff) = (DWORD)tuh_msc_get_block_count(dev_addr, lun);
+ return RES_OK;
+
+ case GET_SECTOR_SIZE:
+ *((WORD *)buff) = (WORD)tuh_msc_get_block_size(dev_addr, lun);
+ return RES_OK;
+
+ case GET_BLOCK_SIZE:
+ *((DWORD *)buff) = 1; // erase block size in units of sector size
+ return RES_OK;
+
+ default:
+ return RES_PARERR;
+ }
+}
+
+//--------------------------------------------------------------------+
+// CLI Commands
+//--------------------------------------------------------------------+
+
+void cli_cmd_cat(EmbeddedCli *cli, char *args, void *context);
+void cli_cmd_cd(EmbeddedCli *cli, char *args, void *context);
+void cli_cmd_cp(EmbeddedCli *cli, char *args, void *context);
+void cli_cmd_dd(EmbeddedCli *cli, char *args, void *context);
+void cli_cmd_ls(EmbeddedCli *cli, char *args, void *context);
+void cli_cmd_pwd(EmbeddedCli *cli, char *args, void *context);
+void cli_cmd_mkdir(EmbeddedCli *cli, char *args, void *context);
+void cli_cmd_mv(EmbeddedCli *cli, char *args, void *context);
+void cli_cmd_rm(EmbeddedCli *cli, char *args, void *context);
+
+static void cli_write_char(EmbeddedCli *cli, char c) {
+ (void)cli;
+ putchar((int)c);
+}
+
+bool cli_init(void) {
+ EmbeddedCliConfig *config = embeddedCliDefaultConfig();
+ config->cliBuffer = cli_buffer;
+ config->cliBufferSize = CLI_BUFFER_SIZE;
+ config->rxBufferSize = CLI_RX_BUFFER_SIZE;
+ config->cmdBufferSize = CLI_CMD_BUFFER_SIZE;
+ config->historyBufferSize = CLI_HISTORY_SIZE;
+ config->maxBindingCount = CLI_BINDING_COUNT;
+
+ TU_ASSERT(embeddedCliRequiredSize(config) <= CLI_BUFFER_SIZE);
+
+ _cli = embeddedCliNew(config);
+ TU_ASSERT(_cli != NULL);
+
+ _cli->writeChar = cli_write_char;
+
+ embeddedCliAddBinding(_cli,
+ (CliCommandBinding){"cat", "Usage: cat [FILE]...\r\n\tConcatenate FILE(s) to standard output..",
+ true, NULL, cli_cmd_cat});
+
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"cd", "Usage: cd [DIR]...\r\n\tChange the current directory to DIR.",
+ true, NULL, cli_cmd_cd});
+
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"cp", "Usage: cp SOURCE DEST\r\n\tCopy SOURCE to DEST.", true, NULL,
+ cli_cmd_cp});
+
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"dd", "Usage: dd [COUNT]\r\n\t" "Read COUNT sectors (default 1024) and report speed.", true, NULL,
+ cli_cmd_dd});
+
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"ls",
+ "Usage: ls [DIR]...\r\n\tList information about the FILEs (the "
+ "current directory by default).",
+ true, NULL, cli_cmd_ls});
+
+ embeddedCliAddBinding(_cli,
+ (CliCommandBinding){"pwd", "Usage: pwd\r\n\tPrint the name of the current working directory.",
+ true, NULL, cli_cmd_pwd});
+
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"mkdir",
+ "Usage: mkdir DIR...\r\n\tCreate the DIRECTORY(ies), if they do not "
+ "already exist..",
+ true, NULL, cli_cmd_mkdir});
+
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"mv", "Usage: mv SOURCE DEST...\r\n\tRename SOURCE to DEST.", true,
+ NULL, cli_cmd_mv});
+
+ embeddedCliAddBinding(_cli, (CliCommandBinding){"rm", "Usage: rm [FILE]...\r\n\tRemove (unlink) the FILE(s).", true,
+ NULL, cli_cmd_rm});
+
+ return true;
+}
+
+void cli_cmd_dd(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
+
+ uint32_t count = 1024; // default sectors to read
+ if (embeddedCliGetTokenCount(args) >= 1) {
+ count = (uint32_t)atoi(embeddedCliGetToken(args, 1));
+ if (count == 0) {
+ count = 1024;
+ }
+ }
+
+ // find first mounted MSC device
+ uint8_t dev_addr = 0;
+ for (uint8_t i = 1; i <= CFG_TUH_DEVICE_MAX; i++) {
+ if (tuh_msc_mounted(i)) {
+ dev_addr = i;
+ break;
+ }
+ }
+ if (dev_addr == 0) {
+ printf("no MSC device mounted\r\n");
+ return;
+ }
+
+ const uint8_t lun = 0;
+ const uint32_t block_size = tuh_msc_get_block_size(dev_addr, lun);
+ const uint32_t block_count = tuh_msc_get_block_count(dev_addr, lun);
+ if (count > block_count) {
+ count = block_count;
+ }
+
+ const uint16_t sectors_per_xfer = (uint16_t)(sizeof(rw_buf) / block_size);
+ const uint32_t xfer_count = (count + sectors_per_xfer - 1) / sectors_per_xfer;
+
+ printf("dd: reading %" PRIu32 " sectors (%" PRIu32 " bytes), %u sectors/xfer ...\r\n",
+ count, count * block_size, sectors_per_xfer);
+
+ const uint32_t start_ms = tusb_time_millis_api();
+ const uint8_t pdrv = dev_addr - 1;
+ bool submit_failed = false;
+
+ for (uint32_t i = 0; i < count; i += sectors_per_xfer) {
+ const uint16_t n = (uint16_t)((count - i < sectors_per_xfer) ? (count - i) : sectors_per_xfer);
+ _disk_busy[pdrv] = true;
+
+ if (!tuh_msc_read10(dev_addr, lun, rw_buf, i, n, disk_io_complete, 0)) {
+ _disk_busy[pdrv] = false;
+ printf("dd: failed to submit read at sector %" PRIu32 " (%u sectors)\r\n", i, n);
+ submit_failed = true;
+ break;
+ }
+
+ wait_for_disk_io(pdrv);
+ }
+
+ if (submit_failed) {
+ return;
+ }
+
+ const uint32_t elapsed_ms = tusb_time_millis_api() - start_ms;
+ const uint32_t total_data = count * block_size;
+ // each SCSI transaction has 31-byte CBW + data + 13-byte CSW
+ const uint32_t total_bus = total_data + xfer_count * (31 + 13);
+
+ if (elapsed_ms > 0) {
+ const uint32_t data_kbs = total_data / elapsed_ms; // KB/s (bytes/ms = KB/s)
+ const uint32_t bus_kbs = total_bus / elapsed_ms;
+ printf("dd: %" PRIu32 " bytes in %" PRIu32 " ms = %" PRIu32 " KB/s (bus %" PRIu32 " KB/s)\r\n",
+ total_data, elapsed_ms, data_kbs, bus_kbs);
+ } else {
+ printf("dd: %" PRIu32 " bytes in <1 ms\r\n", total_data);
+ }
+}
+
+void cli_cmd_cat(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
+
+ uint16_t argc = embeddedCliGetTokenCount(args);
+
+ // need at least 1 argument
+ if (argc == 0) {
+ printf("invalid arguments\r\n");
+ return;
+ }
+
+ for (uint16_t i = 0; i < argc; i++) {
+ FIL *fi = &file1;
+ const char *fpath = embeddedCliGetToken(args, i + 1); // token count from 1
+
+ if (FR_OK != f_open(fi, fpath, FA_READ)) {
+ printf("%s: No such file or directory\r\n", fpath);
+ } else {
+ UINT count = 0;
+ while ((FR_OK == f_read(fi, rw_buf, sizeof(rw_buf), &count)) && (count > 0)) {
+ for (UINT c = 0; c < count; c++) {
+ const uint8_t ch = rw_buf[c];
+ if (isprint(ch) || iscntrl(ch)) {
+ putchar(ch);
+ } else {
+ putchar('.');
+ }
+ }
+ }
+ }
+
+ f_close(fi);
+ }
+}
+
+void cli_cmd_cd(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
+
+ uint16_t argc = embeddedCliGetTokenCount(args);
+
+ // only support 1 argument
+ if (argc != 1) {
+ printf("invalid arguments\r\n");
+ return;
+ }
+
+ // default is current directory
+ const char *dpath = args;
+
+ if (FR_OK != f_chdir(dpath)) {
+ printf("%s: No such file or directory\r\n", dpath);
+ return;
+ }
+}
+
+void cli_cmd_cp(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
+
+ uint16_t argc = embeddedCliGetTokenCount(args);
+ if (argc != 2) {
+ printf("invalid arguments\r\n");
+ return;
+ }
+
+ // default is current directory
+ const char *src = embeddedCliGetToken(args, 1);
+ const char *dst = embeddedCliGetToken(args, 2);
+
+ FIL *f_src = &file1;
+ FIL *f_dst = &file2;
+
+ if (FR_OK != f_open(f_src, src, FA_READ)) {
+ printf("cannot stat '%s': No such file or directory\r\n", src);
+ return;
+ }
+
+ if (FR_OK != f_open(f_dst, dst, FA_WRITE | FA_CREATE_ALWAYS)) {
+ printf("cannot create '%s'\r\n", dst);
+ f_close(f_src);
+ return;
+ } else {
+ UINT rd_count = 0;
+ while ((FR_OK == f_read(f_src, rw_buf, sizeof(rw_buf), &rd_count)) && (rd_count > 0)) {
+ UINT wr_count = 0;
+
+ if (FR_OK != f_write(f_dst, rw_buf, rd_count, &wr_count)) {
+ printf("cannot write to '%s'\r\n", dst);
+ break;
+ }
+ }
+ }
+
+ f_close(f_src);
+ f_close(f_dst);
+}
+
+void cli_cmd_ls(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
+
+ uint16_t argc = embeddedCliGetTokenCount(args);
+
+ // only support 1 argument
+ if (argc > 1) {
+ printf("invalid arguments\r\n");
+ return;
+ }
+
+ // default is current directory
+ const char *dpath = ".";
+ if (argc) {
+ dpath = args;
+ }
+
+ DIR dir;
+ if (FR_OK != f_opendir(&dir, dpath)) {
+ printf("cannot access '%s': No such file or directory\r\n", dpath);
+ return;
+ }
+
+ FILINFO fno;
+ while ((f_readdir(&dir, &fno) == FR_OK) && (fno.fname[0] != 0)) {
+ if (fno.fname[0] != '.') // ignore . and .. entry
+ {
+ if (fno.fattrib & AM_DIR) {
+ // directory
+ printf("/%s\r\n", fno.fname);
+ } else {
+ printf("%-40s", fno.fname);
+ if (fno.fsize < 1024) {
+ printf("%" PRIu32 " B\r\n", fno.fsize);
+ } else {
+ printf("%" PRIu32 " KB\r\n", fno.fsize / 1024);
+ }
+ }
+ }
+ }
+
+ f_closedir(&dir);
+}
+
+void cli_cmd_pwd(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
+ uint16_t argc = embeddedCliGetTokenCount(args);
+
+ if (argc != 0) {
+ printf("invalid arguments\r\n");
+ return;
+ }
+
+ char path[256];
+ if (FR_OK != f_getcwd(path, sizeof(path))) {
+ printf("cannot get current working directory\r\n");
+ return;
+ }
+
+ puts(path);
+}
+
+void cli_cmd_mkdir(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
+
+ uint16_t argc = embeddedCliGetTokenCount(args);
+
+ // only support 1 argument
+ if (argc != 1) {
+ printf("invalid arguments\r\n");
+ return;
+ }
+
+ // default is current directory
+ const char *dpath = args;
+
+ if (FR_OK != f_mkdir(dpath)) {
+ printf("%s: cannot create this directory\r\n", dpath);
+ return;
+ }
+}
+
+void cli_cmd_mv(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
+
+ uint16_t argc = embeddedCliGetTokenCount(args);
+ if (argc != 2) {
+ printf("invalid arguments\r\n");
+ return;
+ }
+
+ // default is current directory
+ const char *src = embeddedCliGetToken(args, 1);
+ const char *dst = embeddedCliGetToken(args, 2);
+
+ if (FR_OK != f_rename(src, dst)) {
+ printf("cannot mv %s to %s\r\n", src, dst);
+ return;
+ }
+}
+
+void cli_cmd_rm(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
+
+ uint16_t argc = embeddedCliGetTokenCount(args);
+
+ // need at least 1 argument
+ if (argc == 0) {
+ printf("invalid arguments\r\n");
+ return;
+ }
+
+ for (uint16_t i = 0; i < argc; i++) {
+ const char *fpath = embeddedCliGetToken(args, i + 1); // token count from 1
+
+ if (FR_OK != f_unlink(fpath)) {
+ printf("cannot remove '%s': No such file or directory\r\n", fpath);
+ }
+ }
+}
diff --git a/examples/host/msc_file_explorer_freertos/src/msc_app.h b/examples/host/msc_file_explorer_freertos/src/msc_app.h
new file mode 100644
index 000000000..eff195b1a
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/src/msc_app.h
@@ -0,0 +1,34 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2025 Ha Thach (tinyusb.org)
+ *
+ * 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.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+#ifndef MSC_APP_H
+#define MSC_APP_H
+
+#include <stdbool.h>
+#include <stdio.h>
+
+bool msc_app_init(void);
+
+#endif
diff --git a/examples/host/msc_file_explorer_freertos/src/tusb_config.h b/examples/host/msc_file_explorer_freertos/src/tusb_config.h
new file mode 100644
index 000000000..c3fc4624f
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/src/tusb_config.h
@@ -0,0 +1,126 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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.
+ *
+ */
+
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------
+// Common Configuration
+//--------------------------------------------------------------------
+
+// defined by compiler flags for flexibility
+#ifndef CFG_TUSB_MCU
+#error CFG_TUSB_MCU must be defined
+#endif
+
+#ifndef CFG_TUSB_OS
+#define CFG_TUSB_OS OPT_OS_FREERTOS
+#endif
+
+// Espressif IDF requires "freertos/" prefix in include path
+#ifdef ESP_PLATFORM
+#define CFG_TUSB_OS_INC_PATH freertos/
+#endif
+
+#ifndef CFG_TUSB_DEBUG
+#define CFG_TUSB_DEBUG 0
+#endif
+
+/* 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_TUH_MEM_SECTION
+#define CFG_TUH_MEM_SECTION
+#endif
+
+#ifndef CFG_TUH_MEM_ALIGN
+#define CFG_TUH_MEM_ALIGN __attribute__ ((aligned(4)))
+#endif
+
+//--------------------------------------------------------------------
+// Host Configuration
+//--------------------------------------------------------------------
+
+// Enable Host stack
+#define CFG_TUH_ENABLED 1
+
+// #define CFG_TUH_MAX3421 1 // use max3421 as host controller
+
+#if CFG_TUSB_MCU == OPT_MCU_RP2040
+ // #define CFG_TUH_RPI_PIO_USB 1 // use pio-usb as host controller
+
+ // host roothub port is 1 if using either pio-usb or max3421
+ #if (defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB) || (defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)
+ #define BOARD_TUH_RHPORT 1
+ #endif
+#endif
+
+// Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED
+
+//------------------------- Board Specific --------------------------
+
+// RHPort number used for host can be defined by board.mk, default to port 0
+#ifndef BOARD_TUH_RHPORT
+#define BOARD_TUH_RHPORT 0
+#endif
+
+// RHPort max operational speed can defined by board.mk
+#ifndef BOARD_TUH_MAX_SPEED
+#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------
+// Driver Configuration
+//--------------------------------------------------------------------
+
+// Size of buffer to hold descriptors and other data used for enumeration
+#define CFG_TUH_ENUMERATION_BUFSIZE 256
+
+#define CFG_TUH_HUB 1 // number of supported hubs
+#define CFG_TUH_MSC 1
+#define CFG_TUH_CDC 0
+#define CFG_TUH_HID 0 // typical keyboard + mouse device can have 3-4 HID interfaces
+#define CFG_TUH_VENDOR 0
+
+// max device support (excluding hub device): 1 hub typically has 4 ports
+#define CFG_TUH_DEVICE_MAX (3*CFG_TUH_HUB + 1)
+
+//------------- MSC -------------//
+#define CFG_TUH_MSC_MAXLUN 4 // typical for most card reader
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* TUSB_CONFIG_H_ */