summaryrefslogtreecommitdiff
path: root/examples/host
diff options
context:
space:
mode:
Diffstat (limited to 'examples/host')
-rw-r--r--examples/host/CMakeLists.txt22
-rw-r--r--examples/host/bare_api/CMakeLists.txt15
-rw-r--r--examples/host/bare_api/Makefile8
-rw-r--r--examples/host/bare_api/only.txt22
-rw-r--r--examples/host/bare_api/skip.txt1
-rw-r--r--examples/host/bare_api/src/main.c69
-rw-r--r--examples/host/cdc_msc_hid/CMakeLists.txt15
-rw-r--r--examples/host/cdc_msc_hid/Makefile8
-rw-r--r--examples/host/cdc_msc_hid/only.txt22
-rw-r--r--examples/host/cdc_msc_hid/skip.txt1
-rw-r--r--examples/host/cdc_msc_hid/src/app.h34
-rw-r--r--examples/host/cdc_msc_hid/src/cdc_app.c79
-rw-r--r--examples/host/cdc_msc_hid/src/hid_app.c2
-rw-r--r--examples/host/cdc_msc_hid/src/main.c5
-rw-r--r--examples/host/cdc_msc_hid/src/msc_app.c7
-rw-r--r--examples/host/cdc_msc_hid/src/tusb_config.h11
-rw-r--r--examples/host/cdc_msc_hid_freertos/CMakeLists.txt15
-rw-r--r--examples/host/cdc_msc_hid_freertos/Makefile8
-rw-r--r--examples/host/cdc_msc_hid_freertos/only.txt19
-rw-r--r--examples/host/cdc_msc_hid_freertos/skip.txt4
-rw-r--r--examples/host/cdc_msc_hid_freertos/src/app.h35
-rw-r--r--examples/host/cdc_msc_hid_freertos/src/cdc_app.c19
-rw-r--r--examples/host/cdc_msc_hid_freertos/src/hid_app.c4
-rw-r--r--examples/host/cdc_msc_hid_freertos/src/main.c6
-rw-r--r--examples/host/cdc_msc_hid_freertos/src/msc_app.c9
-rw-r--r--examples/host/cdc_msc_hid_freertos/src/tusb_config.h6
-rw-r--r--examples/host/device_info/CMakeLists.txt15
-rw-r--r--examples/host/device_info/Makefile8
-rw-r--r--examples/host/device_info/only.txt22
-rw-r--r--examples/host/device_info/skip.txt1
-rw-r--r--examples/host/device_info/src/main.c83
-rw-r--r--examples/host/hid_controller/CMakeLists.txt15
-rw-r--r--examples/host/hid_controller/Makefile8
-rw-r--r--examples/host/hid_controller/only.txt20
-rw-r--r--examples/host/hid_controller/skip.txt1
-rw-r--r--examples/host/hid_controller/src/app.h33
-rw-r--r--examples/host/hid_controller/src/hid_app.c3
-rw-r--r--examples/host/hid_controller/src/main.c22
-rw-r--r--examples/host/hid_controller/src/tusb_config.h6
-rw-r--r--examples/host/midi2_host/CMakeLists.txt29
-rw-r--r--examples/host/midi2_host/CMakePresets.json6
-rw-r--r--examples/host/midi2_host/Makefile13
-rw-r--r--examples/host/midi2_host/only.txt34
-rw-r--r--examples/host/midi2_host/skip.txt1
-rw-r--r--examples/host/midi2_host/src/main.c163
-rw-r--r--examples/host/midi2_host/src/tusb_config.h106
-rw-r--r--examples/host/midi_rx/CMakeLists.txt15
-rw-r--r--examples/host/midi_rx/Makefile8
-rw-r--r--examples/host/midi_rx/only.txt20
-rw-r--r--examples/host/midi_rx/skip.txt1
-rw-r--r--examples/host/midi_rx/src/main.c4
-rw-r--r--examples/host/msc_file_explorer/CMakeLists.txt31
-rw-r--r--examples/host/msc_file_explorer/Makefile9
-rw-r--r--examples/host/msc_file_explorer/README.md105
-rw-r--r--examples/host/msc_file_explorer/only.txt22
-rw-r--r--examples/host/msc_file_explorer/skip.txt1
-rw-r--r--examples/host/msc_file_explorer/src/ffconf.h313
-rw-r--r--examples/host/msc_file_explorer/src/main.c38
-rw-r--r--examples/host/msc_file_explorer/src/msc_app.c558
-rw-r--r--examples/host/msc_file_explorer/src/msc_app.h1
-rw-r--r--examples/host/msc_file_explorer/src/tusb_config.h6
-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.txt4
-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
73 files changed, 3118 insertions, 614 deletions
diff --git a/examples/host/CMakeLists.txt b/examples/host/CMakeLists.txt
index 2783dd84e..7c74e3c73 100644
--- a/examples/host/CMakeLists.txt
+++ b/examples/host/CMakeLists.txt
@@ -6,10 +6,18 @@ project(tinyusb_host_examples C CXX ASM)
family_initialize_project(tinyusb_host_examples ${CMAKE_CURRENT_LIST_DIR})
# family_add_subdirectory will filter what to actually add based on selected FAMILY
-family_add_subdirectory(bare_api)
-family_add_subdirectory(cdc_msc_hid)
-family_add_subdirectory(cdc_msc_hid_freertos)
-family_add_subdirectory(device_info)
-family_add_subdirectory(hid_controller)
-family_add_subdirectory(midi_rx)
-family_add_subdirectory(msc_file_explorer)
+set(EXAMPLE_LIST
+ bare_api
+ cdc_msc_hid
+ cdc_msc_hid_freertos
+ device_info
+ hid_controller
+ midi_rx
+ midi2_host
+ msc_file_explorer
+ msc_file_explorer_freertos
+ )
+
+foreach (example ${EXAMPLE_LIST})
+ family_add_subdirectory(${example})
+endforeach ()
diff --git a/examples/host/bare_api/CMakeLists.txt b/examples/host/bare_api/CMakeLists.txt
index 0efe84b60..4993f93b9 100644
--- a/examples/host/bare_api/CMakeLists.txt
+++ b/examples/host/bare_api/CMakeLists.txt
@@ -2,31 +2,28 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(bare_api C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+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})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_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_host_example(${PROJECT} noos)
+family_configure_host_example(${PROJECT_NAME} noos)
diff --git a/examples/host/bare_api/Makefile b/examples/host/bare_api/Makefile
index 0235e08c3..f8292385e 100644
--- a/examples/host/bare_api/Makefile
+++ b/examples/host/bare_api/Makefile
@@ -1,13 +1,13 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += \
src/main.c
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/host/bare_api/only.txt b/examples/host/bare_api/only.txt
index cba58f8e8..a2ff93be5 100644
--- a/examples/host/bare_api/only.txt
+++ b/examples/host/bare_api/only.txt
@@ -1,3 +1,6 @@
+family:hpmicro
+family:samd21
+family:samd5x_e5x
mcu:CH32V20X
mcu:KINETIS_KL
mcu:LPC175X_6X
@@ -5,18 +8,25 @@ mcu:LPC177X_8X
mcu:LPC18XX
mcu:LPC40XX
mcu:LPC43XX
-mcu:MIMXRT1XXX
+mcu:LPC54
+mcu:LPC55
+mcu:MAX3421
mcu:MIMXRT10XX
mcu:MIMXRT11XX
-mcu:RP2040
+mcu:MIMXRT1XXX
mcu:MSP432E4
-mcu:RX65X
mcu:RAXXX
-mcu:MAX3421
+mcu:RP2040
+mcu:RW61X
+mcu:RX65X
+mcu:STM32C0
+mcu:STM32C5
mcu:STM32F4
mcu:STM32F7
+mcu:STM32G0
+mcu:STM32H5
mcu:STM32H7
mcu:STM32H7RS
mcu:STM32N6
-family:samd21
-family:samd5x_e5x
+mcu:STM32U3
+mcu:STM32U5
diff --git a/examples/host/bare_api/skip.txt b/examples/host/bare_api/skip.txt
new file mode 100644
index 000000000..308796869
--- /dev/null
+++ b/examples/host/bare_api/skip.txt
@@ -0,0 +1 @@
+board:lpcxpresso54114
diff --git a/examples/host/bare_api/src/main.c b/examples/host/bare_api/src/main.c
index c693d6b00..679ce6f43 100644
--- a/examples/host/bare_api/src/main.c
+++ b/examples/host/bare_api/src/main.c
@@ -37,23 +37,30 @@
#define BUF_COUNT 4
-tusb_desc_device_t desc_device;
+CFG_TUH_MEM_SECTION tusb_desc_device_t desc_device;
-uint8_t buf_pool[BUF_COUNT][64];
+CFG_TUH_MEM_SECTION uint8_t buf_pool[BUF_COUNT][64];
uint8_t buf_owner[BUF_COUNT] = { 0 }; // device address that owns buffer
+CFG_TUH_MEM_SECTION uint16_t temp_buf[128]; // temp buffer for string descriptor
+
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
void led_blinking_task(void);
+void print_devinfo_task(void);
static void print_utf16(uint16_t *temp_buf, size_t buf_len);
-void print_device_descriptor(tuh_xfer_t* xfer);
+static void print_one_device(uint8_t daddr);
void parse_config_descriptor(uint8_t dev_addr, tusb_desc_configuration_t const* desc_cfg);
uint8_t* get_hid_buf(uint8_t daddr);
void free_hid_buf(uint8_t daddr);
+// One flag per possible device address — set in tuh_mount_cb (host task) and
+// cleared by print_devinfo_task (main loop) once the descriptors are printed.
+static volatile bool need_devinfo[CFG_TUH_DEVICE_MAX + 1];
+
/*------------- MAIN -------------*/
int main(void) {
board_init();
@@ -72,40 +79,53 @@ int main(void) {
while (1) {
// tinyusb host task
tuh_task();
+ print_devinfo_task();
led_blinking_task();
}
-
- return 0;
}
/*------------- TinyUSB Callbacks -------------*/
-// Invoked when device is mounted (configured)
+// Invoked when device is mounted (configured). Runs in the host task — keep
+// it minimal. The descriptor fetching/printing happens in print_devinfo_task()
+// below where the sync helpers are safe (different context).
void tuh_mount_cb(uint8_t daddr) {
printf("Device attached, address = %d\r\n", daddr);
-
- // Get Device Descriptor
- // TODO: invoking control transfer now has issue with mounting hub with multiple devices attached, fix later
- tuh_descriptor_get_device(daddr, &desc_device, 18, print_device_descriptor, 0);
+ if (daddr < TU_ARRAY_SIZE(need_devinfo)) {
+ need_devinfo[daddr] = true;
+ }
}
/// Invoked when device is unmounted (bus reset/unplugged)
void tuh_umount_cb(uint8_t daddr) {
printf("Device removed, address = %d\r\n", daddr);
+ if (daddr < TU_ARRAY_SIZE(need_devinfo)) {
+ need_devinfo[daddr] = false;
+ }
free_hid_buf(daddr);
}
//--------------------------------------------------------------------+
-// Device Descriptor
+// Print device info task — serialises descriptor fetching across all
+// mounted devices via sync helpers. Sync calls are safe here because this
+// runs in the main loop (outside any host-stack callback context).
//--------------------------------------------------------------------+
-void print_device_descriptor(tuh_xfer_t *xfer) {
- if (XFER_RESULT_SUCCESS != xfer->result) {
+void print_devinfo_task(void) {
+ for (uint8_t daddr = 1; daddr < TU_ARRAY_SIZE(need_devinfo); daddr++) {
+ if (need_devinfo[daddr]) {
+ need_devinfo[daddr] = false;
+ print_one_device(daddr);
+ }
+ }
+}
+
+static void print_one_device(uint8_t daddr) {
+ // Get Device Descriptor
+ if (XFER_RESULT_SUCCESS != tuh_descriptor_get_device_sync(daddr, &desc_device, 18)) {
printf("Failed to get device descriptor\r\n");
return;
}
- uint8_t const daddr = xfer->daddr;
-
printf("Device %u: ID %04x:%04x\r\n", daddr, desc_device.idVendor, desc_device.idProduct);
printf("Device Descriptor:\r\n");
printf(" bLength %u\r\n" , desc_device.bLength);
@@ -119,9 +139,6 @@ void print_device_descriptor(tuh_xfer_t *xfer) {
printf(" idProduct 0x%04x\r\n" , desc_device.idProduct);
printf(" bcdDevice %04x\r\n" , desc_device.bcdDevice);
- // Get String descriptor using Sync API
- uint16_t temp_buf[128];
-
printf(" iManufacturer %u ", desc_device.iManufacturer);
if (XFER_RESULT_SUCCESS == tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, temp_buf, sizeof(temp_buf))) {
print_utf16(temp_buf, TU_ARRAY_SIZE(temp_buf));
@@ -335,7 +352,7 @@ void led_blinking_task(void) {
static bool led_state = false;
// Blink every interval ms
- if (board_millis() - start_ms < interval_ms) {
+ if (tusb_time_millis_api() - start_ms < interval_ms) {
return; // not enough time
}
start_ms += interval_ms;
@@ -386,12 +403,12 @@ static int _count_utf8_bytes(const uint16_t *buf, size_t len) {
return (int) total_bytes;
}
-static void print_utf16(uint16_t *temp_buf, size_t buf_len) {
- if ((temp_buf[0] & 0xff) == 0) return;// empty
- size_t utf16_len = ((temp_buf[0] & 0xff) - 2) / sizeof(uint16_t);
- size_t utf8_len = (size_t) _count_utf8_bytes(temp_buf + 1, utf16_len);
- _convert_utf16le_to_utf8(temp_buf + 1, utf16_len, (uint8_t *) temp_buf, sizeof(uint16_t) * buf_len);
- ((uint8_t *) temp_buf)[utf8_len] = '\0';
+static void print_utf16(uint16_t *buf, size_t buf_len) {
+ if ((buf[0] & 0xff) == 0) return;// empty
+ size_t utf16_len = ((buf[0] & 0xff) - 2) / sizeof(uint16_t);
+ size_t utf8_len = (size_t) _count_utf8_bytes(buf + 1, utf16_len);
+ _convert_utf16le_to_utf8(buf + 1, utf16_len, (uint8_t *) buf, sizeof(uint16_t) * buf_len);
+ ((uint8_t *) buf)[utf8_len] = '\0';
- printf("%s", (char *) temp_buf);
+ printf("%s", (char *) buf);
}
diff --git a/examples/host/cdc_msc_hid/CMakeLists.txt b/examples/host/cdc_msc_hid/CMakeLists.txt
index e8928cda5..3098c37fa 100644
--- a/examples/host/cdc_msc_hid/CMakeLists.txt
+++ b/examples/host/cdc_msc_hid/CMakeLists.txt
@@ -2,23 +2,20 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(cdc_msc_hid C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+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})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/cdc_app.c
${CMAKE_CURRENT_SOURCE_DIR}/src/hid_app.c
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
@@ -26,10 +23,10 @@ target_sources(${PROJECT} PUBLIC
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_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_host_example(${PROJECT} noos)
+family_configure_host_example(${PROJECT_NAME} noos)
diff --git a/examples/host/cdc_msc_hid/Makefile b/examples/host/cdc_msc_hid/Makefile
index 213c02f9c..d72e91e74 100644
--- a/examples/host/cdc_msc_hid/Makefile
+++ b/examples/host/cdc_msc_hid/Makefile
@@ -1,8 +1,8 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE = \
@@ -11,6 +11,6 @@ EXAMPLE_SOURCE = \
src/main.c \
src/msc_app.c \
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/host/cdc_msc_hid/only.txt b/examples/host/cdc_msc_hid/only.txt
index cba58f8e8..a2ff93be5 100644
--- a/examples/host/cdc_msc_hid/only.txt
+++ b/examples/host/cdc_msc_hid/only.txt
@@ -1,3 +1,6 @@
+family:hpmicro
+family:samd21
+family:samd5x_e5x
mcu:CH32V20X
mcu:KINETIS_KL
mcu:LPC175X_6X
@@ -5,18 +8,25 @@ mcu:LPC177X_8X
mcu:LPC18XX
mcu:LPC40XX
mcu:LPC43XX
-mcu:MIMXRT1XXX
+mcu:LPC54
+mcu:LPC55
+mcu:MAX3421
mcu:MIMXRT10XX
mcu:MIMXRT11XX
-mcu:RP2040
+mcu:MIMXRT1XXX
mcu:MSP432E4
-mcu:RX65X
mcu:RAXXX
-mcu:MAX3421
+mcu:RP2040
+mcu:RW61X
+mcu:RX65X
+mcu:STM32C0
+mcu:STM32C5
mcu:STM32F4
mcu:STM32F7
+mcu:STM32G0
+mcu:STM32H5
mcu:STM32H7
mcu:STM32H7RS
mcu:STM32N6
-family:samd21
-family:samd5x_e5x
+mcu:STM32U3
+mcu:STM32U5
diff --git a/examples/host/cdc_msc_hid/skip.txt b/examples/host/cdc_msc_hid/skip.txt
new file mode 100644
index 000000000..308796869
--- /dev/null
+++ b/examples/host/cdc_msc_hid/skip.txt
@@ -0,0 +1 @@
+board:lpcxpresso54114
diff --git a/examples/host/cdc_msc_hid/src/app.h b/examples/host/cdc_msc_hid/src/app.h
new file mode 100644
index 000000000..49783b4ae
--- /dev/null
+++ b/examples/host/cdc_msc_hid/src/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 TUSB_TINYUSB_EXAMPLES_APP_H
+#define TUSB_TINYUSB_EXAMPLES_APP_H
+
+#include <stdio.h>
+
+void cdc_app_task(void);
+void hid_app_task(void);
+
+#endif
diff --git a/examples/host/cdc_msc_hid/src/cdc_app.c b/examples/host/cdc_msc_hid/src/cdc_app.c
index 97f1a96d6..e6c190715 100644
--- a/examples/host/cdc_msc_hid/src/cdc_app.c
+++ b/examples/host/cdc_msc_hid/src/cdc_app.c
@@ -26,12 +26,15 @@
#include "tusb.h"
#include "bsp/board_api.h"
+#include "app.h"
-static size_t get_console_inputs(uint8_t* buf, size_t bufsize) {
+static size_t console_read(uint8_t *buf, size_t bufsize) {
size_t count = 0;
while (count < bufsize) {
- int ch = board_getchar();
- if (ch <= 0) { break; }
+ const int ch = board_getchar();
+ if (ch < 0) {
+ break;
+ }
buf[count] = (uint8_t) ch;
count++;
}
@@ -39,46 +42,50 @@ static size_t get_console_inputs(uint8_t* buf, size_t bufsize) {
return count;
}
+static size_t console_write(const uint8_t *buf, size_t bufsize) {
+ // Use board_uart_write directly for non-blocking behavior.
+ // board_putchar -> sys_write has a blocking retry loop that causes UART RX overrun.
+ int wr = board_uart_write(buf, (int) bufsize);
+ return (wr > 0) ? (size_t) wr : 0;
+}
+
+// forward from console to usbh
+static void console_to_usbh(uint8_t idx) {
+ uint8_t buf[64];
+ size_t count = console_read(buf, sizeof(buf));
+ if (count > 0) {
+ tuh_cdc_write(idx, buf, count);
+ }
+}
+
void cdc_app_task(void) {
- uint8_t buf[64 + 1]; // +1 for extra null character
- uint32_t const bufsize = sizeof(buf) - 1;
+ const uint8_t idx = 0;
+
+ // Bidirectional forwarding: console <-> host cdc interfaces
+ if (!tuh_cdc_mounted(idx)) {
+ return;
+ }
- uint32_t count = get_console_inputs(buf, bufsize);
- buf[count] = 0;
+ // usbh -> uart
+ uint8_t buf[64];
+ uint32_t count = tuh_cdc_read(idx, buf, sizeof(buf));
+ uint32_t wr = 0;
- // loop over all mounted interfaces
- for (uint8_t idx = 0; idx < CFG_TUH_CDC; idx++) {
- if (tuh_cdc_mounted(idx)) {
- // console --> cdc interfaces
- if (count) {
- tuh_cdc_write(idx, buf, count);
- tuh_cdc_write_flush(idx);
- }
+ do {
+ // uart write is slow, while waiting forward uart -> usbh else uart rx can be overflow
+ if (count) {
+ wr += console_write(buf + wr, count - wr);
}
- }
+ console_to_usbh(idx);
+ } while (wr < count);
+
+ tuh_cdc_write_flush(idx);
}
//--------------------------------------------------------------------+
// TinyUSB callbacks
//--------------------------------------------------------------------+
-// Invoked when received new data
-void tuh_cdc_rx_cb(uint8_t idx) {
- uint8_t buf[64 + 1]; // +1 for extra null character
- uint32_t const bufsize = sizeof(buf) - 1;
-
- // forward cdc interfaces -> console
- const uint32_t count = tuh_cdc_read(idx, buf, bufsize);
- if (count) {
- buf[count] = 0;
- printf("%s", (char*) buf);
-
- #ifndef __ICCARM__ // TODO IAR doesn't support stream control ?
- fflush(stdout);// flush right away, else nanolib will wait for newline
- #endif
- }
-}
-
// Invoked when a device with CDC interface is mounted
// idx is index of cdc interface in the internal pool.
void tuh_cdc_mount_cb(uint8_t idx) {
@@ -88,7 +95,6 @@ void tuh_cdc_mount_cb(uint8_t idx) {
printf("CDC Interface is mounted: address = %u, itf_num = %u\r\n", itf_info.daddr,
itf_info.desc.bInterfaceNumber);
-#ifdef CFG_TUH_CDC_LINE_CODING_ON_ENUM
// If CFG_TUH_CDC_LINE_CODING_ON_ENUM is defined, line coding will be set by tinyusb stack
// while eneumerating new cdc device
cdc_line_coding_t line_coding = {0};
@@ -96,11 +102,6 @@ void tuh_cdc_mount_cb(uint8_t idx) {
printf(" Baudrate: %" PRIu32 ", Stop Bits : %u\r\n", line_coding.bit_rate, line_coding.stop_bits);
printf(" Parity : %u, Data Width: %u\r\n", line_coding.parity, line_coding.data_bits);
}
-#else
- // Set Line Coding upon mounted
- cdc_line_coding_t new_line_coding = { 115200, CDC_LINE_CODING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 };
- tuh_cdc_set_line_coding(idx, &new_line_coding, NULL, 0);
-#endif
}
// Invoked when a device with CDC interface is unmounted
diff --git a/examples/host/cdc_msc_hid/src/hid_app.c b/examples/host/cdc_msc_hid/src/hid_app.c
index 6f01d6f45..eb20356cb 100644
--- a/examples/host/cdc_msc_hid/src/hid_app.c
+++ b/examples/host/cdc_msc_hid/src/hid_app.c
@@ -22,9 +22,9 @@
* THE SOFTWARE.
*
*/
-
#include "bsp/board_api.h"
#include "tusb.h"
+#include "app.h"
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
diff --git a/examples/host/cdc_msc_hid/src/main.c b/examples/host/cdc_msc_hid/src/main.c
index e2dd6e5d2..c27ad93fe 100644
--- a/examples/host/cdc_msc_hid/src/main.c
+++ b/examples/host/cdc_msc_hid/src/main.c
@@ -29,13 +29,12 @@
#include "bsp/board_api.h"
#include "tusb.h"
+#include "app.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTOTYPES
//--------------------------------------------------------------------+
void led_blinking_task(void);
-extern void cdc_app_task(void);
-extern void hid_app_task(void);
/*------------- MAIN -------------*/
int main(void) {
@@ -93,7 +92,7 @@ void led_blinking_task(void) {
static bool led_state = false;
// Blink every interval ms
- if (board_millis() - start_ms < interval_ms) {
+ if (tusb_time_millis_api() - start_ms < interval_ms) {
return;// not enough time
}
start_ms += interval_ms;
diff --git a/examples/host/cdc_msc_hid/src/msc_app.c b/examples/host/cdc_msc_hid/src/msc_app.c
index 0e9c99766..8181bdcb9 100644
--- a/examples/host/cdc_msc_hid/src/msc_app.c
+++ b/examples/host/cdc_msc_hid/src/msc_app.c
@@ -23,6 +23,7 @@
*
*/
+#include <stdio.h>
#include "tusb.h"
//--------------------------------------------------------------------+
@@ -40,14 +41,14 @@ static bool inquiry_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const
}
// Print out Vendor ID, Product ID and Rev
- printf("%.8s %.16s rev %.4s\r\n", inquiry_resp.vendor_id, inquiry_resp.product_id, inquiry_resp.product_rev);
+ printf("%.8s %.16s %.4s\r\n", inquiry_resp.vendor_id, inquiry_resp.product_id, inquiry_resp.product_rev);
// Get capacity of device
uint32_t const block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
uint32_t const block_size = tuh_msc_get_block_size(dev_addr, cbw->lun);
- printf("Disk Size: %" PRIu32 " MB\r\n", block_count / ((1024*1024)/block_size));
- printf("Block Count = %" PRIu32 ", Block Size: %" PRIu32 "\r\n", block_count, block_size);
+ printf("Disk Size: %" PRIu32 " %" PRIu32 "-byte blocks: %" PRIu32 " MB\r\n",
+ block_count, block_size, block_count / ((1024 * 1024) / block_size));
return true;
}
diff --git a/examples/host/cdc_msc_hid/src/tusb_config.h b/examples/host/cdc_msc_hid/src/tusb_config.h
index 2f8cb5e03..26fcdd1cb 100644
--- a/examples/host/cdc_msc_hid/src/tusb_config.h
+++ b/examples/host/cdc_msc_hid/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -102,8 +102,11 @@
// Size of buffer to hold descriptors and other data used for enumeration
#define CFG_TUH_ENUMERATION_BUFSIZE 256
+// Increase task event queue to handle rapid bulk transfer completions
+#define CFG_TUH_TASK_QUEUE_SZ 64
+
#define CFG_TUH_HUB 1 // number of supported hubs
-#define CFG_TUH_CDC 2 // number of supported CDC devices. also activates CDC ACM
+#define CFG_TUH_CDC 1 // number of supported CDC devices. also activates CDC ACM
#define CFG_TUH_CDC_FTDI 1 // FTDI Serial. FTDI is not part of CDC class, only to re-use CDC driver API
#define CFG_TUH_CDC_CP210X 1 // CP210x Serial. CP210X is not part of CDC class, only to re-use CDC driver API
#define CFG_TUH_CDC_CH34X 1 // CH340 or CH341 Serial. CH34X is not part of CDC class, only to re-use CDC driver API
@@ -134,4 +137,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/host/cdc_msc_hid_freertos/CMakeLists.txt b/examples/host/cdc_msc_hid_freertos/CMakeLists.txt
index 78b2784fe..3ceefb3f4 100644
--- a/examples/host/cdc_msc_hid_freertos/CMakeLists.txt
+++ b/examples/host/cdc_msc_hid_freertos/CMakeLists.txt
@@ -2,23 +2,20 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(cdc_msc_hid_freertos C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+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})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/cdc_app.c
${CMAKE_CURRENT_SOURCE_DIR}/src/hid_app.c
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
@@ -26,10 +23,10 @@ target_sources(${PROJECT} PUBLIC
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_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_host_example(${PROJECT} freertos)
+family_configure_host_example(${PROJECT_NAME} freertos)
diff --git a/examples/host/cdc_msc_hid_freertos/Makefile b/examples/host/cdc_msc_hid_freertos/Makefile
index 178341f81..2e323ed56 100644
--- a/examples/host/cdc_msc_hid_freertos/Makefile
+++ b/examples/host/cdc_msc_hid_freertos/Makefile
@@ -1,9 +1,9 @@
RTOS = freertos
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE = \
@@ -12,6 +12,6 @@ EXAMPLE_SOURCE = \
src/main.c \
src/msc_app.c \
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/host/cdc_msc_hid_freertos/only.txt b/examples/host/cdc_msc_hid_freertos/only.txt
index 576271aff..4ab8a906e 100644
--- a/examples/host/cdc_msc_hid_freertos/only.txt
+++ b/examples/host/cdc_msc_hid_freertos/only.txt
@@ -1,19 +1,28 @@
-mcu:ESP32P4
+family:espressif
+family:samd21
+family:samd5x_e5x
mcu:LPC175X_6X
mcu:LPC177X_8X
mcu:LPC18XX
mcu:LPC40XX
mcu:LPC43XX
-mcu:MIMXRT1XXX
+mcu:LPC54
+mcu:LPC55
+mcu:MAX3421
mcu:MIMXRT10XX
mcu:MIMXRT11XX
+mcu:MIMXRT1XXX
mcu:MSP432E4
+mcu:RW61X
mcu:RX65X
-mcu:MAX3421
+mcu:STM32C0
+mcu:STM32C5
mcu:STM32F4
mcu:STM32F7
+mcu:STM32G0
+mcu:STM32H5
mcu:STM32H7
mcu:STM32H7RS
mcu:STM32N6
-family:samd21
-family:samd5x_e5x
+mcu:STM32U3
+mcu:STM32U5
diff --git a/examples/host/cdc_msc_hid_freertos/skip.txt b/examples/host/cdc_msc_hid_freertos/skip.txt
index 2ba4438fd..f0be07d25 100644
--- a/examples/host/cdc_msc_hid_freertos/skip.txt
+++ b/examples/host/cdc_msc_hid_freertos/skip.txt
@@ -1 +1,3 @@
-mcu:RP2040
+mcu:CH32F20X
+board:lpcxpresso54114
+mcu:FT90X
diff --git a/examples/host/cdc_msc_hid_freertos/src/app.h b/examples/host/cdc_msc_hid_freertos/src/app.h
new file mode 100644
index 000000000..8892a1b6b
--- /dev/null
+++ b/examples/host/cdc_msc_hid_freertos/src/app.h
@@ -0,0 +1,35 @@
+/*
+ * 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 TUSB_TINYUSB_EXAMPLES_APP_H
+#define TUSB_TINYUSB_EXAMPLES_APP_H
+
+#include <stdio.h>
+
+void cdc_app_init(void);
+void hid_app_init(void);
+void msc_app_init(void);
+
+#endif
diff --git a/examples/host/cdc_msc_hid_freertos/src/cdc_app.c b/examples/host/cdc_msc_hid_freertos/src/cdc_app.c
index d99760a02..9fad775e9 100644
--- a/examples/host/cdc_msc_hid_freertos/src/cdc_app.c
+++ b/examples/host/cdc_msc_hid_freertos/src/cdc_app.c
@@ -26,18 +26,19 @@
#include "tusb.h"
#include "bsp/board_api.h"
+#include "app.h"
#ifdef ESP_PLATFORM
- #define CDC_STACK_SZIE 2048
+ #define CDC_STACK_SIZE 2048
#else
- #define CDC_STACK_SZIE (3*configMINIMAL_STACK_SIZE/2)
+ #define CDC_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2)
#endif
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
//--------------------------------------------------------------------+
#if configSUPPORT_STATIC_ALLOCATION
-StackType_t cdc_stack[CDC_STACK_SZIE];
+StackType_t cdc_stack[CDC_STACK_SIZE];
StaticTask_t cdc_taskdef;
#endif
@@ -45,18 +46,20 @@ static void cdc_app_task(void* param);
void cdc_app_init(void) {
#if configSUPPORT_STATIC_ALLOCATION
- xTaskCreateStatic(cdc_app_task, "cdc", CDC_STACK_SZIE, NULL, configMAX_PRIORITIES-2, cdc_stack, &cdc_taskdef);
+ (void) xTaskCreateStatic(cdc_app_task, "cdc", CDC_STACK_SIZE, NULL, configMAX_PRIORITIES-2, cdc_stack, &cdc_taskdef);
#else
- xTaskCreate(cdc_app_task, "cdc", CDC_STACK_SZIE, NULL, configMAX_PRIORITIES-2, NULL);
+ (void) xTaskCreate(cdc_app_task, "cdc", CDC_STACK_SIZE, NULL, configMAX_PRIORITIES-2, NULL);
#endif
}
// helper
-static size_t get_console_inputs(uint8_t *buf, size_t bufsize) {
+static size_t console_read(uint8_t *buf, size_t bufsize) {
size_t count = 0;
while (count < bufsize) {
int ch = board_getchar();
- if (ch <= 0) break;
+ if (ch <= 0) {
+ break;
+ }
buf[count] = (uint8_t) ch;
count++;
@@ -72,7 +75,7 @@ static void cdc_app_task(void* param) {
uint32_t const bufsize = sizeof(buf) - 1;
while (1) {
- uint32_t count = get_console_inputs(buf, bufsize);
+ uint32_t count = console_read(buf, bufsize);
buf[count] = 0;
if (count) {
diff --git a/examples/host/cdc_msc_hid_freertos/src/hid_app.c b/examples/host/cdc_msc_hid_freertos/src/hid_app.c
index 9ea5c1be0..79e0b5a28 100644
--- a/examples/host/cdc_msc_hid_freertos/src/hid_app.c
+++ b/examples/host/cdc_msc_hid_freertos/src/hid_app.c
@@ -25,6 +25,7 @@
#include "bsp/board_api.h"
#include "tusb.h"
+#include "app.h"
//--------------------------------------------------------------------+
// MACRO TYPEDEF CONSTANT ENUM DECLARATION
@@ -160,7 +161,7 @@ static void process_kbd_report(hid_keyboard_report_t const *report) {
// Mouse
//--------------------------------------------------------------------+
-void cursor_movement(int8_t x, int8_t y, int8_t wheel) {
+static void cursor_movement(int8_t x, int8_t y, int8_t wheel) {
#if USE_ANSI_ESCAPE
// Move X using ansi escape
if ( x < 0) {
@@ -210,6 +211,7 @@ static void process_mouse_report(hid_mouse_report_t const *report) {
//--------------------------------------------------------------------+
static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const *report, uint16_t len) {
(void) dev_addr;
+ (void) len;
uint8_t const rpt_count = hid_info[instance].report_count;
tuh_hid_report_info_t *rpt_info_arr = hid_info[instance].report_info;
diff --git a/examples/host/cdc_msc_hid_freertos/src/main.c b/examples/host/cdc_msc_hid_freertos/src/main.c
index d498c1b57..a41de2769 100644
--- a/examples/host/cdc_msc_hid_freertos/src/main.c
+++ b/examples/host/cdc_msc_hid_freertos/src/main.c
@@ -29,12 +29,13 @@
#include "bsp/board_api.h"
#include "tusb.h"
+#include "app.h"
#ifdef ESP_PLATFORM
#define USBH_STACK_SIZE 4096
#else
// Increase stack size when debug log is enabled
- #define USBH_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1)
+ #define USBH_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 4 : 2))
#endif
@@ -65,9 +66,6 @@ TimerHandle_t blinky_tm;
static void led_blinky_cb(TimerHandle_t xTimer);
static void usb_host_task(void* param);
-extern void cdc_app_init(void);
-extern void hid_app_init(void);
-extern void msc_app_init(void);
/*------------- MAIN -------------*/
int main(void) {
diff --git a/examples/host/cdc_msc_hid_freertos/src/msc_app.c b/examples/host/cdc_msc_hid_freertos/src/msc_app.c
index 6439495a8..17df11951 100644
--- a/examples/host/cdc_msc_hid_freertos/src/msc_app.c
+++ b/examples/host/cdc_msc_hid_freertos/src/msc_app.c
@@ -23,7 +23,10 @@
*
*/
+#include <stdio.h>
+
#include "tusb.h"
+#include "app.h"
// define the buffer to be place in USB/DMA memory with correct alignment/cache line size
CFG_TUH_MEM_SECTION static struct {
@@ -44,14 +47,14 @@ static bool inquiry_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const
}
// Print out Vendor ID, Product ID and Rev
- printf("%.8s %.16s rev %.4s\r\n", scsi_resp.inquiry.vendor_id, scsi_resp.inquiry.product_id, scsi_resp.inquiry.product_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
uint32_t const block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
uint32_t const block_size = tuh_msc_get_block_size(dev_addr, cbw->lun);
- printf("Disk Size: %" PRIu32 " MB\r\n", block_count / ((1024 * 1024) / block_size));
- printf("Block Count = %" PRIu32 ", Block Size: %" PRIu32 "\r\n", block_count, block_size);
+ printf("Disk Size: %" PRIu32 " %" PRIu32 "-byte blocks: %" PRIu32 " MB\r\n",
+ block_count, block_size, block_count / ((1024 * 1024) / block_size));
return true;
}
diff --git a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h
index 3cdb227e2..8583e7176 100644
--- a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h
+++ b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -139,4 +139,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/host/device_info/CMakeLists.txt b/examples/host/device_info/CMakeLists.txt
index 33953233d..9b96a8af8 100644
--- a/examples/host/device_info/CMakeLists.txt
+++ b/examples/host/device_info/CMakeLists.txt
@@ -2,31 +2,28 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(device_info C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+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})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_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_host_example(${PROJECT} noos)
+family_configure_host_example(${PROJECT_NAME} noos)
diff --git a/examples/host/device_info/Makefile b/examples/host/device_info/Makefile
index 0235e08c3..f8292385e 100644
--- a/examples/host/device_info/Makefile
+++ b/examples/host/device_info/Makefile
@@ -1,13 +1,13 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += \
src/main.c
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/host/device_info/only.txt b/examples/host/device_info/only.txt
index 133a7c9a0..4c2cb0f35 100644
--- a/examples/host/device_info/only.txt
+++ b/examples/host/device_info/only.txt
@@ -1,25 +1,33 @@
+family:espressif
+family:hpmicro
+family:samd21
+family:samd5x_e5x
mcu:CH32V20X
-mcu:ESP32S2
-mcu:ESP32S3
-mcu:ESP32P4
mcu:KINETIS_KL
mcu:LPC175X_6X
mcu:LPC177X_8X
mcu:LPC18XX
mcu:LPC40XX
mcu:LPC43XX
+mcu:LPC54
+mcu:LPC55
mcu:MAX3421
-mcu:MIMXRT1XXX
mcu:MIMXRT10XX
mcu:MIMXRT11XX
+mcu:MIMXRT1XXX
mcu:MSP432E4
+mcu:RAXXX
mcu:RP2040
+mcu:RW61X
mcu:RX65X
-mcu:RAXXX
+mcu:STM32C0
+mcu:STM32C5
mcu:STM32F4
mcu:STM32F7
+mcu:STM32G0
+mcu:STM32H5
mcu:STM32H7
mcu:STM32H7RS
mcu:STM32N6
-family:samd21
-family:samd5x_e5x
+mcu:STM32U3
+mcu:STM32U5
diff --git a/examples/host/device_info/skip.txt b/examples/host/device_info/skip.txt
new file mode 100644
index 000000000..308796869
--- /dev/null
+++ b/examples/host/device_info/skip.txt
@@ -0,0 +1 @@
+board:lpcxpresso54114
diff --git a/examples/host/device_info/src/main.c b/examples/host/device_info/src/main.c
index 419806551..f32ed1a3e 100644
--- a/examples/host/device_info/src/main.c
+++ b/examples/host/device_info/src/main.c
@@ -72,8 +72,14 @@ CFG_TUH_MEM_SECTION struct {
} desc;
void led_blinking_task(void* param);
+void print_devinfo_task(void* param);
static void print_utf16(uint16_t* temp_buf, size_t buf_len);
+// One flag per possible device address — set by tuh_mount_cb (host task) and
+// cleared by print_devinfo_task (separate task / main loop) once the device's
+// descriptor info has been printed.
+static volatile bool need_devinfo[CFG_TUH_DEVICE_MAX + 1];
+
#if CFG_TUSB_OS == OPT_OS_FREERTOS
void init_freertos_task(void);
#endif
@@ -99,21 +105,46 @@ int main(void) {
#if CFG_TUSB_OS == OPT_OS_FREERTOS
init_freertos_task();
#else
+ board_delay(100); // wait for uart to be ready
init_tinyusb();
while (1) {
tuh_task(); // tinyusb host task
+ print_devinfo_task(NULL);
led_blinking_task(NULL);
}
- return 0;
#endif
}
/*------------- TinyUSB Callbacks -------------*/
-// Invoked when device is mounted (configured)
+// Invoked when device is mounted (configured). Runs in the host task — keep
+// it minimal. The actual descriptor fetching/printing happens in
+// print_devinfo_task() below, which runs in a different context (main loop
+// on OS_NONE / dedicated task on RTOS) where the sync helpers are safe.
void tuh_mount_cb(uint8_t daddr) {
blink_interval_ms = BLINK_MOUNTED;
+ if (daddr < TU_ARRAY_SIZE(need_devinfo)) {
+ need_devinfo[daddr] = true;
+ }
+}
+// Invoked when device is unmounted (bus reset/unplugged)
+void tuh_umount_cb(uint8_t daddr) {
+ blink_interval_ms = BLINK_NOT_MOUNTED;
+ if (daddr < TU_ARRAY_SIZE(need_devinfo)) {
+ need_devinfo[daddr] = false;
+ }
+ printf("Device removed, address = %d\r\n", daddr);
+}
+
+//--------------------------------------------------------------------+
+// Print device info task — serialises descriptor fetching across all
+// mounted devices using the sync helpers. Sync calls are safe here because
+// this task runs outside the host-task callback context (main loop on
+// OS_NONE / dedicated FreeRTOS task on RTOS).
+//--------------------------------------------------------------------+
+
+static void print_one_device(uint8_t daddr) {
// Get Device Descriptor
uint8_t xfer_result = tuh_descriptor_get_device_sync(daddr, &desc.device, 18);
if (XFER_RESULT_SUCCESS != xfer_result) {
@@ -129,11 +160,9 @@ void tuh_mount_cb(uint8_t daddr) {
}
if (XFER_RESULT_SUCCESS != xfer_result) {
uint16_t* serial = (uint16_t*)(uintptr_t) desc.serial;
- serial[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * 3 + 2));
- serial[1] = 'n';
- serial[2] = '/';
- serial[3] = 'a';
- serial[4] = 0;
+ serial[0] = (uint16_t)((TUSB_DESC_STRING << 8) | (2 * 1 + 2));
+ serial[1] = '0';
+ serial[2] = 0;
}
print_utf16((uint16_t*)(uintptr_t) desc.serial, sizeof(desc.serial)/2);
printf("\r\n");
@@ -150,12 +179,9 @@ void tuh_mount_cb(uint8_t daddr) {
printf(" idProduct 0x%04x\r\n", desc.device.idProduct);
printf(" bcdDevice %04x\r\n", desc.device.bcdDevice);
- // Get String descriptor using Sync API
-
printf(" iManufacturer %u ", desc.device.iManufacturer);
if (desc.device.iManufacturer != 0) {
- xfer_result = tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, desc.buf, sizeof(desc.buf));
- if (XFER_RESULT_SUCCESS == xfer_result) {
+ if (XFER_RESULT_SUCCESS == tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, desc.buf, sizeof(desc.buf))) {
print_utf16((uint16_t*)(uintptr_t) desc.buf, sizeof(desc.buf)/2);
}
}
@@ -163,24 +189,33 @@ void tuh_mount_cb(uint8_t daddr) {
printf(" iProduct %u ", desc.device.iProduct);
if (desc.device.iProduct != 0) {
- xfer_result = tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, desc.buf, sizeof(desc.buf));
- if (XFER_RESULT_SUCCESS == xfer_result) {
+ if (XFER_RESULT_SUCCESS == tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, desc.buf, sizeof(desc.buf))) {
print_utf16((uint16_t*)(uintptr_t) desc.buf, sizeof(desc.buf)/2);
}
}
printf("\r\n");
printf(" iSerialNumber %u ", desc.device.iSerialNumber);
- printf((char*)desc.serial); // serial is already to UTF-8
- printf("\r\n");
-
+ printf("%s\r\n", (char*)desc.serial); // serial is already UTF-8
printf(" bNumConfigurations %u\r\n", desc.device.bNumConfigurations);
}
-// Invoked when device is unmounted (bus reset/unplugged)
-void tuh_umount_cb(uint8_t daddr) {
- blink_interval_ms = BLINK_NOT_MOUNTED;
- printf("Device removed, address = %d\r\n", daddr);
+void print_devinfo_task(void* param) {
+ (void) param;
+
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ while (1) {
+#endif
+ for (uint8_t daddr = 1; daddr < TU_ARRAY_SIZE(need_devinfo); daddr++) {
+ if (need_devinfo[daddr]) {
+ need_devinfo[daddr] = false;
+ print_one_device(daddr);
+ }
+ }
+#if CFG_TUSB_OS == OPT_OS_FREERTOS
+ vTaskDelay(pdMS_TO_TICKS(10));
+ }
+#endif
}
//--------------------------------------------------------------------+
@@ -248,7 +283,7 @@ void led_blinking_task(void* param) {
#if CFG_TUSB_OS == OPT_OS_FREERTOS
vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS);
#else
- if (board_millis() - start_ms < blink_interval_ms) {
+ if (tusb_time_millis_api() - start_ms < blink_interval_ms) {
return; // not enough time
}
#endif
@@ -281,6 +316,9 @@ StaticTask_t blinky_taskdef;
StackType_t usb_stack[USB_STACK_SIZE];
StaticTask_t usb_taskdef;
+
+StackType_t devinfo_stack[USB_STACK_SIZE];
+StaticTask_t devinfo_taskdef;
#endif
#ifdef ESP_PLATFORM
@@ -291,6 +329,7 @@ void app_main(void) {
void usb_host_task(void *param) {
(void) param;
+ board_delay(100); // wait for uart to be ready
init_tinyusb();
while (1) {
tuh_task();
@@ -301,9 +340,11 @@ void init_freertos_task(void) {
#if configSUPPORT_STATIC_ALLOCATION
xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef);
xTaskCreateStatic(usb_host_task, "usbh", USB_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_stack, &usb_taskdef);
+ xTaskCreateStatic(print_devinfo_task, "devinfo", USB_STACK_SIZE, NULL, configMAX_PRIORITIES-2, devinfo_stack, &devinfo_taskdef);
#else
xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL);
xTaskCreate(usb_host_task, "usbh", USB_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
+ xTaskCreate(print_devinfo_task, "devinfo", USB_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, NULL);
#endif
// only start scheduler for non-espressif mcu
diff --git a/examples/host/hid_controller/CMakeLists.txt b/examples/host/hid_controller/CMakeLists.txt
index fb5faf210..287e9fb7f 100644
--- a/examples/host/hid_controller/CMakeLists.txt
+++ b/examples/host/hid_controller/CMakeLists.txt
@@ -2,32 +2,29 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(hid_controller C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+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})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/hid_app.c
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_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_host_example(${PROJECT} noos)
+family_configure_host_example(${PROJECT_NAME} noos)
diff --git a/examples/host/hid_controller/Makefile b/examples/host/hid_controller/Makefile
index 1377f1f90..732520c63 100644
--- a/examples/host/hid_controller/Makefile
+++ b/examples/host/hid_controller/Makefile
@@ -1,14 +1,14 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += \
src/hid_app.c \
src/main.c
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/host/hid_controller/only.txt b/examples/host/hid_controller/only.txt
index cba58f8e8..45ca6846f 100644
--- a/examples/host/hid_controller/only.txt
+++ b/examples/host/hid_controller/only.txt
@@ -1,3 +1,6 @@
+family:hpmicro
+family:samd21
+family:samd5x_e5x
mcu:CH32V20X
mcu:KINETIS_KL
mcu:LPC175X_6X
@@ -5,18 +8,23 @@ mcu:LPC177X_8X
mcu:LPC18XX
mcu:LPC40XX
mcu:LPC43XX
-mcu:MIMXRT1XXX
+mcu:LPC54
+mcu:LPC55
+mcu:MAX3421
mcu:MIMXRT10XX
mcu:MIMXRT11XX
-mcu:RP2040
+mcu:MIMXRT1XXX
mcu:MSP432E4
-mcu:RX65X
mcu:RAXXX
-mcu:MAX3421
+mcu:RP2040
+mcu:RW61X
+mcu:RX65X
mcu:STM32F4
mcu:STM32F7
+mcu:STM32G0
+mcu:STM32H5
mcu:STM32H7
mcu:STM32H7RS
mcu:STM32N6
-family:samd21
-family:samd5x_e5x
+mcu:STM32U3
+mcu:STM32U5
diff --git a/examples/host/hid_controller/skip.txt b/examples/host/hid_controller/skip.txt
new file mode 100644
index 000000000..308796869
--- /dev/null
+++ b/examples/host/hid_controller/skip.txt
@@ -0,0 +1 @@
+board:lpcxpresso54114
diff --git a/examples/host/hid_controller/src/app.h b/examples/host/hid_controller/src/app.h
new file mode 100644
index 000000000..b4d072668
--- /dev/null
+++ b/examples/host/hid_controller/src/app.h
@@ -0,0 +1,33 @@
+/*
+ * 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 TUSB_TINYUSB_EXAMPLES_APP_H
+#define TUSB_TINYUSB_EXAMPLES_APP_H
+
+#include <stdio.h>
+
+void hid_app_task(void);
+
+#endif
diff --git a/examples/host/hid_controller/src/hid_app.c b/examples/host/hid_controller/src/hid_app.c
index 1d6ca8b07..5417811f0 100644
--- a/examples/host/hid_controller/src/hid_app.c
+++ b/examples/host/hid_controller/src/hid_app.c
@@ -25,6 +25,7 @@
#include "bsp/board_api.h"
#include "tusb.h"
+#include "app.h"
/* From https://www.kernel.org/doc/html/latest/input/gamepad.html
____________________________ __
@@ -167,7 +168,7 @@ void hid_app_task(void)
const uint32_t interval_ms = 200;
static uint32_t start_ms = 0;
- uint32_t current_time_ms = board_millis();
+ uint32_t current_time_ms = tusb_time_millis_api();
if ( current_time_ms - start_ms >= interval_ms)
{
start_ms = current_time_ms;
diff --git a/examples/host/hid_controller/src/main.c b/examples/host/hid_controller/src/main.c
index f3244db95..a9eebc90b 100644
--- a/examples/host/hid_controller/src/main.c
+++ b/examples/host/hid_controller/src/main.c
@@ -34,18 +34,15 @@
#include "bsp/board_api.h"
#include "tusb.h"
+#include "app.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
void led_blinking_task(void);
-extern void cdc_task(void);
-extern void hid_app_task(void);
-
/*------------- MAIN -------------*/
-int main(void)
-{
+int main(void) {
board_init();
printf("TinyUSB Host HID Controller Example\r\n");
@@ -60,19 +57,11 @@ int main(void)
board_init_after_tusb();
- while (1)
- {
+ while (1) {
// tinyusb host task
tuh_task();
led_blinking_task();
-
-#if CFG_TUH_CDC
- cdc_task();
-#endif
-
-#if CFG_TUH_HID
hid_app_task();
-#endif
}
}
@@ -83,15 +72,14 @@ int main(void)
//--------------------------------------------------------------------+
// Blinking Task
//--------------------------------------------------------------------+
-void led_blinking_task(void)
-{
+void led_blinking_task(void) {
const uint32_t interval_ms = 1000;
static uint32_t start_ms = 0;
static bool led_state = false;
// Blink every interval ms
- if ( board_millis() - start_ms < interval_ms) return; // not enough time
+ if ( tusb_time_millis_api() - start_ms < interval_ms) return; // not enough time
start_ms += interval_ms;
board_led_write(led_state);
diff --git a/examples/host/hid_controller/src/tusb_config.h b/examples/host/hid_controller/src/tusb_config.h
index 351fe0178..a5c202fda 100644
--- a/examples/host/hid_controller/src/tusb_config.h
+++ b/examples/host/hid_controller/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -119,4 +119,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
diff --git a/examples/host/midi2_host/CMakeLists.txt b/examples/host/midi2_host/CMakeLists.txt
new file mode 100644
index 000000000..0ec03bf5f
--- /dev/null
+++ b/examples/host/midi2_host/CMakeLists.txt
@@ -0,0 +1,29 @@
+cmake_minimum_required(VERSION 3.20)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+
+project(midi2_host 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
+ )
+
+# Example include
+target_include_directories(${PROJECT_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_host_example(${PROJECT_NAME} noos)
diff --git a/examples/host/midi2_host/CMakePresets.json b/examples/host/midi2_host/CMakePresets.json
new file mode 100644
index 000000000..5cd8971e9
--- /dev/null
+++ b/examples/host/midi2_host/CMakePresets.json
@@ -0,0 +1,6 @@
+{
+ "version": 6,
+ "include": [
+ "../../../hw/bsp/BoardPresets.json"
+ ]
+}
diff --git a/examples/host/midi2_host/Makefile b/examples/host/midi2_host/Makefile
new file mode 100644
index 000000000..f8292385e
--- /dev/null
+++ b/examples/host/midi2_host/Makefile
@@ -0,0 +1,13 @@
+include ../../../hw/bsp/family_support.mk
+
+INC += \
+ src \
+
+
+# Example source
+EXAMPLE_SOURCE += \
+ src/main.c
+
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
+
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/host/midi2_host/only.txt b/examples/host/midi2_host/only.txt
new file mode 100644
index 000000000..c71aacd87
--- /dev/null
+++ b/examples/host/midi2_host/only.txt
@@ -0,0 +1,34 @@
+family:hpmicro
+family:samd21
+family:samd5x_e5x
+mcu:CH32V20X
+mcu:ESP32P4
+mcu:ESP32S2
+mcu:ESP32S3
+mcu:KINETIS_KL
+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:RAXXX
+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/midi2_host/skip.txt b/examples/host/midi2_host/skip.txt
new file mode 100644
index 000000000..308796869
--- /dev/null
+++ b/examples/host/midi2_host/skip.txt
@@ -0,0 +1 @@
+board:lpcxpresso54114
diff --git a/examples/host/midi2_host/src/main.c b/examples/host/midi2_host/src/main.c
new file mode 100644
index 000000000..d81e77a33
--- /dev/null
+++ b/examples/host/midi2_host/src/main.c
@@ -0,0 +1,163 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 Saulo Verissimo
+ *
+ * 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.
+ */
+
+// Minimal USB-MIDI 2.0 host example.
+// Receives UMP from any MIDI 2.0 device, prints each packet to stdout.
+
+#include <stdio.h>
+#include <string.h>
+#include "bsp/board_api.h"
+#include "tusb.h"
+#include "class/midi/midi2_host.h"
+
+//--------------------------------------------------------------------+
+// State
+//--------------------------------------------------------------------+
+
+TU_ATTR_UNUSED static uint8_t midi2_idx = 0xFF;
+
+//--------------------------------------------------------------------+
+// UMP printer - shows MT and word(s) in hex; decodes Channel Voice
+//--------------------------------------------------------------------+
+
+static void print_ump(const uint32_t* words, uint8_t wc) {
+ uint8_t mt = (uint8_t)((words[0] >> 28) & 0x0F);
+ uint8_t group = (uint8_t)((words[0] >> 24) & 0x0F);
+
+ if (mt == 0x4 && wc >= 2) {
+ // MIDI 2.0 Channel Voice
+ uint8_t status = (uint8_t)((words[0] >> 20) & 0x0F);
+ uint8_t channel = (uint8_t)((words[0] >> 16) & 0x0F);
+ uint8_t data1 = (uint8_t)((words[0] >> 8) & 0x7F);
+ switch (status) {
+ case 0x9:
+ printf("[g%u ch%u] M2 NoteOn n=%u vel=%04X attr=%04X\r\n",
+ group, channel, data1,
+ (unsigned)((words[1] >> 16) & 0xFFFF),
+ (unsigned)(words[1] & 0xFFFF));
+ break;
+ case 0x8:
+ printf("[g%u ch%u] M2 NoteOff n=%u vel=%04X\r\n",
+ group, channel, data1,
+ (unsigned)((words[1] >> 16) & 0xFFFF));
+ break;
+ case 0xB:
+ printf("[g%u ch%u] M2 CC#%u = %08lX\r\n",
+ group, channel, data1, (unsigned long)words[1]);
+ break;
+ case 0xC:
+ printf("[g%u ch%u] M2 ProgChg %u\r\n",
+ group, channel, (unsigned)((words[1] >> 24) & 0x7F));
+ break;
+ case 0xD:
+ printf("[g%u ch%u] M2 ChanPress %08lX\r\n",
+ group, channel, (unsigned long)words[1]);
+ break;
+ case 0xE:
+ printf("[g%u ch%u] M2 PitchBend %08lX\r\n",
+ group, channel, (unsigned long)words[1]);
+ break;
+ default:
+ printf("[g%u ch%u] M2 status=0x%X w0=%08lX w1=%08lX\r\n",
+ group, channel, status,
+ (unsigned long)words[0], (unsigned long)words[1]);
+ break;
+ }
+ } else if (mt == 0x2 && wc == 1) {
+ // MIDI 1.0 Channel Voice
+ uint8_t status = (uint8_t)((words[0] >> 16) & 0xFF);
+ uint8_t data1 = (uint8_t)((words[0] >> 8) & 0x7F);
+ uint8_t data2 = (uint8_t)(words[0] & 0x7F);
+ printf("[g%u] M1 %02X %02X %02X\r\n", group, status, data1, data2);
+ } else {
+ printf("UMP MT=0x%X wc=%u w0=%08lX\r\n",
+ mt, wc, (unsigned long)words[0]);
+ }
+}
+
+//--------------------------------------------------------------------+
+// MIDI 2.0 Host Callbacks
+//--------------------------------------------------------------------+
+
+void tuh_midi2_descriptor_cb(uint8_t idx, const tuh_midi2_descriptor_cb_t* d) {
+ (void)idx;
+ printf("MIDI2 descriptor: %s, RX cables=%u TX cables=%u\r\n",
+ d->protocol_version ? "MIDI 2.0" : "MIDI 1.0",
+ d->rx_cable_count, d->tx_cable_count);
+}
+
+void tuh_midi2_mount_cb(uint8_t idx, const tuh_midi2_mount_cb_t* m) {
+ midi2_idx = idx;
+ printf("MIDI2 mounted: idx=%u protocol=%s\r\n",
+ idx, m->protocol_version ? "MIDI 2.0" : "MIDI 1.0");
+}
+
+void tuh_midi2_rx_cb(uint8_t idx, uint32_t xferred_bytes) {
+ (void)xferred_bytes;
+
+ uint32_t words[16];
+ while (1) {
+ uint32_t n = tuh_midi2_ump_read(idx, words, 16);
+ if (n == 0) break;
+
+ uint32_t i = 0;
+ while (i < n) {
+ uint8_t mt = (uint8_t)((words[i] >> 28) & 0x0F);
+ uint8_t wc = midi2_ump_word_count(mt);
+ if (i + wc > n) break;
+ print_ump(&words[i], wc);
+ i += wc;
+ }
+ }
+}
+
+void tuh_midi2_tx_cb(uint8_t idx, uint32_t xferred_bytes) {
+ (void)idx; (void)xferred_bytes;
+}
+
+void tuh_midi2_umount_cb(uint8_t idx) {
+ (void)idx;
+ midi2_idx = 0xFF;
+ printf("MIDI2 unmounted\r\n");
+}
+
+//--------------------------------------------------------------------+
+// Main
+//--------------------------------------------------------------------+
+
+int main(void) {
+ board_init();
+
+ printf("\r\nTinyUSB Host MIDI 2.0 Example\r\n");
+
+ tusb_rhport_init_t host_init = {
+ .role = TUSB_ROLE_HOST,
+ .speed = TUSB_SPEED_AUTO,
+ };
+ tusb_init(BOARD_TUH_RHPORT, &host_init);
+
+ while (1) {
+ tuh_task();
+ }
+}
diff --git a/examples/host/midi2_host/src/tusb_config.h b/examples/host/midi2_host/src/tusb_config.h
new file mode 100644
index 000000000..4bd6ef2ea
--- /dev/null
+++ b/examples/host/midi2_host/src/tusb_config.h
@@ -0,0 +1,106 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 Saulo Verissimo
+ *
+ * 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
+//--------------------------------------------------------------------+
+
+#ifndef CFG_TUSB_MCU
+#error CFG_TUSB_MCU must be defined
+#endif
+
+#ifdef ESP_PLATFORM
+#define CFG_TUSB_OS_INC_PATH freertos/
+#endif
+
+#ifndef CFG_TUSB_OS
+#define CFG_TUSB_OS OPT_OS_NONE
+#endif
+
+#ifndef CFG_TUSB_DEBUG
+#define CFG_TUSB_DEBUG 0
+#endif
+
+#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
+//--------------------------------------------------------------------+
+
+#define CFG_TUH_ENABLED 1
+
+#if CFG_TUSB_MCU == OPT_MCU_RP2040
+ // #define CFG_TUH_RPI_PIO_USB 1 // use pio-usb as host controller
+ // #define CFG_TUH_MAX3421 1 // use max3421 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
+
+#define CFG_TUH_MAX_SPEED BOARD_TUH_MAX_SPEED
+
+//------------------------- Board Specific --------------------------+
+
+#ifndef BOARD_TUH_RHPORT
+#define BOARD_TUH_RHPORT 0
+#endif
+
+#ifndef BOARD_TUH_MAX_SPEED
+#define BOARD_TUH_MAX_SPEED OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------+
+// Driver Configuration
+//--------------------------------------------------------------------+
+
+#define CFG_TUH_ENUMERATION_BUFSIZE 256
+
+#define CFG_TUH_HUB 1
+#define CFG_TUH_DEVICE_MAX (3*CFG_TUH_HUB + 1)
+
+// USB-MIDI 2.0 Host
+#define CFG_TUH_MIDI2 CFG_TUH_DEVICE_MAX
+#define CFG_TUH_MIDI2_RX_BUFSIZE 512
+#define CFG_TUH_MIDI2_TX_BUFSIZE 512
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/examples/host/midi_rx/CMakeLists.txt b/examples/host/midi_rx/CMakeLists.txt
index 33953233d..62bb89e95 100644
--- a/examples/host/midi_rx/CMakeLists.txt
+++ b/examples/host/midi_rx/CMakeLists.txt
@@ -2,31 +2,28 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(midi_rx C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+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})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+target_sources(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
)
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_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_host_example(${PROJECT} noos)
+family_configure_host_example(${PROJECT_NAME} noos)
diff --git a/examples/host/midi_rx/Makefile b/examples/host/midi_rx/Makefile
index 0235e08c3..f8292385e 100644
--- a/examples/host/midi_rx/Makefile
+++ b/examples/host/midi_rx/Makefile
@@ -1,13 +1,13 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
INC += \
src \
- $(TOP)/hw \
+
# Example source
EXAMPLE_SOURCE += \
src/main.c
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/host/midi_rx/only.txt b/examples/host/midi_rx/only.txt
index 133a7c9a0..65ef8fac9 100644
--- a/examples/host/midi_rx/only.txt
+++ b/examples/host/midi_rx/only.txt
@@ -1,25 +1,35 @@
+family:hpmicro
+family:samd21
+family:samd5x_e5x
mcu:CH32V20X
+mcu:ESP32P4
mcu:ESP32S2
mcu:ESP32S3
-mcu:ESP32P4
mcu:KINETIS_KL
mcu:LPC175X_6X
mcu:LPC177X_8X
mcu:LPC18XX
mcu:LPC40XX
mcu:LPC43XX
+mcu:LPC54
+mcu:LPC55
mcu:MAX3421
-mcu:MIMXRT1XXX
mcu:MIMXRT10XX
mcu:MIMXRT11XX
+mcu:MIMXRT1XXX
mcu:MSP432E4
+mcu:RAXXX
mcu:RP2040
+mcu:RW61X
mcu:RX65X
-mcu:RAXXX
+mcu:STM32C0
+mcu:STM32C5
mcu:STM32F4
mcu:STM32F7
+mcu:STM32G0
+mcu:STM32H5
mcu:STM32H7
mcu:STM32H7RS
mcu:STM32N6
-family:samd21
-family:samd5x_e5x
+mcu:STM32U3
+mcu:STM32U5
diff --git a/examples/host/midi_rx/skip.txt b/examples/host/midi_rx/skip.txt
new file mode 100644
index 000000000..308796869
--- /dev/null
+++ b/examples/host/midi_rx/skip.txt
@@ -0,0 +1 @@
+board:lpcxpresso54114
diff --git a/examples/host/midi_rx/src/main.c b/examples/host/midi_rx/src/main.c
index 78b1a11b9..fb36906c6 100644
--- a/examples/host/midi_rx/src/main.c
+++ b/examples/host/midi_rx/src/main.c
@@ -58,8 +58,6 @@ int main(void) {
led_blinking_task();
midi_host_rx_task();
}
-
- return 0;
}
//--------------------------------------------------------------------+
@@ -72,7 +70,7 @@ void led_blinking_task(void) {
static bool led_state = false;
// Blink every interval ms
- if (board_millis() - start_ms < interval_ms) return;// not enough time
+ if (tusb_time_millis_api() - start_ms < interval_ms) return;// not enough time
start_ms += interval_ms;
board_led_write(led_state);
diff --git a/examples/host/msc_file_explorer/CMakeLists.txt b/examples/host/msc_file_explorer/CMakeLists.txt
index 5ac75c04a..c8d196447 100644
--- a/examples/host/msc_file_explorer/CMakeLists.txt
+++ b/examples/host/msc_file_explorer/CMakeLists.txt
@@ -2,23 +2,20 @@ cmake_minimum_required(VERSION 3.20)
include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
-# gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>)
-family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
-
-project(${PROJECT} C CXX ASM)
+project(msc_file_explorer C CXX ASM)
# Checks this example is valid for the family and initializes the project
-family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+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})
+add_executable(${PROJECT_NAME})
# Example source
-target_sources(${PROJECT} PUBLIC
+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
@@ -26,17 +23,8 @@ target_sources(${PROJECT} PUBLIC
${TOP}/lib/fatfs/source/ffunicode.c
)
-# Suppress warnings on fatfs
-if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
- set_source_files_properties(
- ${TOP}/lib/fatfs/source/ff.c
- PROPERTIES
- COMPILE_FLAGS "-Wno-conversion -Wno-cast-qual"
- )
-endif ()
-
# Example include
-target_include_directories(${PROJECT} PUBLIC
+target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
${TOP}/lib/fatfs/source
${TOP}/lib/embedded-cli
@@ -44,4 +32,11 @@ target_include_directories(${PROJECT} PUBLIC
# Configure compilation flags and libraries for the example without RTOS.
# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
-family_configure_host_example(${PROJECT} noos)
+family_configure_host_example(${PROJECT_NAME} noos)
+
+# 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/Makefile b/examples/host/msc_file_explorer/Makefile
index f0872376f..8c0b012ad 100644
--- a/examples/host/msc_file_explorer/Makefile
+++ b/examples/host/msc_file_explorer/Makefile
@@ -1,10 +1,9 @@
-include ../../build_system/make/make.mk
+include ../../../hw/bsp/family_support.mk
FATFS_PATH = lib/fatfs/source
INC += \
src \
- $(TOP)/hw \
$(TOP)/$(FATFS_PATH) \
$(TOP)/lib/embedded-cli \
@@ -13,7 +12,7 @@ EXAMPLE_SOURCE = \
src/main.c \
src/msc_app.c \
-SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+SRC_C += $(addprefix $(EXAMPLE_PATH)/, $(EXAMPLE_SOURCE))
# FatFS source
SRC_C += \
@@ -22,6 +21,6 @@ SRC_C += \
$(FATFS_PATH)/ffunicode.c \
# suppress warning caused by fatfs
-CFLAGS_GCC += -Wno-error=cast-qual
+CFLAGS += -Wno-error=cast-qual
-include ../../build_system/make/rules.mk
+include ../../../hw/bsp/family_rules.mk
diff --git a/examples/host/msc_file_explorer/README.md b/examples/host/msc_file_explorer/README.md
new file mode 100644
index 000000000..e220bedea
--- /dev/null
+++ b/examples/host/msc_file_explorer/README.md
@@ -0,0 +1,105 @@
+# MSC File Explorer
+
+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 Raspberry Pi Pico
+cmake -B build -DBOARD=raspberry_pi_pico -DFAMILY=rp2040 examples/host/msc_file_explorer
+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
+
+This example is part of the TinyUSB HIL (Hardware-in-the-Loop) test suite. The HIL test
+automatically flashes, runs the example, and verifies MSC enumeration and file operations
+against a known USB drive.
diff --git a/examples/host/msc_file_explorer/only.txt b/examples/host/msc_file_explorer/only.txt
index cba58f8e8..a2ff93be5 100644
--- a/examples/host/msc_file_explorer/only.txt
+++ b/examples/host/msc_file_explorer/only.txt
@@ -1,3 +1,6 @@
+family:hpmicro
+family:samd21
+family:samd5x_e5x
mcu:CH32V20X
mcu:KINETIS_KL
mcu:LPC175X_6X
@@ -5,18 +8,25 @@ mcu:LPC177X_8X
mcu:LPC18XX
mcu:LPC40XX
mcu:LPC43XX
-mcu:MIMXRT1XXX
+mcu:LPC54
+mcu:LPC55
+mcu:MAX3421
mcu:MIMXRT10XX
mcu:MIMXRT11XX
-mcu:RP2040
+mcu:MIMXRT1XXX
mcu:MSP432E4
-mcu:RX65X
mcu:RAXXX
-mcu:MAX3421
+mcu:RP2040
+mcu:RW61X
+mcu:RX65X
+mcu:STM32C0
+mcu:STM32C5
mcu:STM32F4
mcu:STM32F7
+mcu:STM32G0
+mcu:STM32H5
mcu:STM32H7
mcu:STM32H7RS
mcu:STM32N6
-family:samd21
-family:samd5x_e5x
+mcu:STM32U3
+mcu:STM32U5
diff --git a/examples/host/msc_file_explorer/skip.txt b/examples/host/msc_file_explorer/skip.txt
new file mode 100644
index 000000000..308796869
--- /dev/null
+++ b/examples/host/msc_file_explorer/skip.txt
@@ -0,0 +1 @@
+board:lpcxpresso54114
diff --git a/examples/host/msc_file_explorer/src/ffconf.h b/examples/host/msc_file_explorer/src/ffconf.h
new file mode 100644
index 000000000..5c89136fe
--- /dev/null
+++ b/examples/host/msc_file_explorer/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/src/main.c b/examples/host/msc_file_explorer/src/main.c
index 506c3b015..07515e626 100644
--- a/examples/host/msc_file_explorer/src/main.c
+++ b/examples/host/msc_file_explorer/src/main.c
@@ -23,40 +23,6 @@
*
*/
-/* Example to show how to navigate mass storage device with built-in command line.
- * Type help for list of supported commands and syntax (mostly linux commands)
-
- > help
- * help
- Print list of commands
- * cat
- Usage: cat [FILE]...
- Concatenate FILE(s) to standard output..
- * cd
- Usage: cd [DIR]...
- Change the current directory to DIR.
- * cp
- Usage: cp SOURCE DEST
- Copy SOURCE to DEST.
- * ls
- Usage: ls [DIR]...
- List information about the FILEs (the current directory by default).
- * pwd
- Usage: pwd
- Print the name of the current working directory.
- * mkdir
- Usage: mkdir DIR...
- Create the DIRECTORY(ies), if they do not already exist..
- * mv
- Usage: mv SOURCE DEST...
- Rename SOURCE to DEST.
- * rm
- Usage: rm [FILE]...
- Remove (unlink) the FILE(s).
- */
-
-#include <stdlib.h>
-#include <stdio.h>
#include <string.h>
#include "bsp/board_api.h"
@@ -93,8 +59,6 @@ int main(void) {
msc_app_task();
led_blinking_task();
}
-
- return 0;
}
//--------------------------------------------------------------------+
@@ -119,7 +83,7 @@ void led_blinking_task(void) {
static bool led_state = false;
// Blink every interval ms
- if (board_millis() - start_ms < interval_ms) return; // not enough time
+ if (tusb_time_millis_api() - start_ms < interval_ms) return; // not enough time
start_ms += interval_ms;
board_led_write(led_state);
diff --git a/examples/host/msc_file_explorer/src/msc_app.c b/examples/host/msc_file_explorer/src/msc_app.c
index 40a9ef57e..3f15bb84f 100644
--- a/examples/host/msc_file_explorer/src/msc_app.c
+++ b/examples/host/msc_file_explorer/src/msc_app.c
@@ -46,14 +46,21 @@
#define CLI_RX_BUFFER_SIZE 16
#define CLI_CMD_BUFFER_SIZE 64
#define CLI_HISTORY_SIZE 32
-#define CLI_BINDING_COUNT 8
+#define CLI_BINDING_COUNT 9
static EmbeddedCli *_cli;
-static CLI_UINT cli_buffer[BYTES_TO_CLI_UINTS(CLI_BUFFER_SIZE)];
+static CLI_UINT cli_buffer[BYTES_TO_CLI_UINTS(CLI_BUFFER_SIZE)];
//------------- Elm Chan FatFS -------------//
-static FATFS fatfs[CFG_TUH_DEVICE_MAX]; // for simplicity only support 1 LUN per device
-static volatile bool _disk_busy[CFG_TUH_DEVICE_MAX];
+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 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 {
@@ -67,34 +74,30 @@ CFG_TUH_MEM_SECTION static struct {
bool cli_init(void);
-bool msc_app_init(void)
-{
- for(size_t i=0; i<CFG_TUH_DEVICE_MAX; i++) {
+bool msc_app_init(void) {
+ for (size_t i = 0; i < CFG_TUH_DEVICE_MAX; i++) {
_disk_busy[i] = false;
}
- // disable stdout buffered for echoing typing command
- #ifndef __ICCARM__ // TODO IAR doesn't support stream control ?
+// disable stdout buffered for echoing typing command
+#ifndef __ICCARM__ // TODO IAR doesn't support stream control ?
setbuf(stdout, NULL);
- #endif
+#endif
cli_init();
return true;
}
-void msc_app_task(void)
-{
+void msc_app_task(void) {
if (!_cli) {
return;
}
int ch = board_getchar();
- if ( ch > 0 )
- {
- while( ch > 0 )
- {
- embeddedCliReceiveChar(_cli, (char) ch);
+ if (ch > 0) {
+ while (ch > 0) {
+ embeddedCliReceiveChar(_cli, (char)ch);
ch = board_getchar();
}
embeddedCliProcess(_cli);
@@ -105,185 +108,172 @@ void msc_app_task(void)
//
//--------------------------------------------------------------------+
-static bool inquiry_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data) {
- msc_cbw_t const* cbw = cb_data->cbw;
- msc_csw_t const* csw = cb_data->csw;
+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)
- {
+ if (csw->status != 0) {
printf("Inquiry failed\r\n");
return false;
}
// Print out Vendor ID, Product ID and Rev
- printf("%.8s %.16s rev %.4s\r\n", scsi_resp.inquiry.vendor_id, scsi_resp.inquiry.product_id, scsi_resp.inquiry.product_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
- uint32_t const block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
- uint32_t const block_size = tuh_msc_get_block_size(dev_addr, cbw->lun);
+ 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 " MB\r\n", block_count / ((1024*1024)/block_size));
- // printf("Block Count = %lu, Block Size: %lu\r\n", block_count, block_size);
+ 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
- uint8_t const drive_num = dev_addr-1;
- char drive_path[3] = "0:";
+ const uint8_t drive_num = dev_addr - 1;
+ char drive_path[3] = "0:";
drive_path[0] += drive_num;
- if ( f_mount(&fatfs[drive_num], drive_path, 1) != FR_OK )
- {
- puts("mount failed");
+ if (f_mount(&fatfs[drive_num], drive_path, 1) != FR_OK) {
+ printf("mount failed\r\n");
+ return true;
}
// change to newly mounted drive
- f_chdir(drive_path);
+ f_chdrive(drive_path);
+ FRESULT rc = f_chdir("/");
+ if (rc != FR_OK) {
+ printf("chdir failed: %d\r\n", rc);
+ }
// print the drive label
-// char label[34];
-// if ( FR_OK == f_getlabel(drive_path, label, NULL) )
-// {
-// puts(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 is mounted\r\n");
+void tuh_msc_mount_cb(uint8_t dev_addr) {
+ printf("A MassStorage device (addr = %u) is mounted\r\n", dev_addr);
- uint8_t const lun = 0;
+ 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)
-{
+void tuh_msc_umount_cb(uint8_t dev_addr) {
printf("A MassStorage device is unmounted\r\n");
- uint8_t const drive_num = dev_addr-1;
- char drive_path[3] = "0:";
+ const uint8_t drive_num = dev_addr - 1;
+ char drive_path[3] = "0:";
drive_path[0] += drive_num;
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
-// }
-// }
-// }
+ // 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])
- {
+static void wait_for_disk_io(BYTE pdrv) {
+ while (_disk_busy[pdrv]) {
tuh_task();
}
}
-static bool disk_io_complete(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data)
-{
- (void) dev_addr; (void) cb_data;
- _disk_busy[dev_addr-1] = false;
+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 */
-)
-{
+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
+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 */
-)
-{
- uint8_t const dev_addr = pdrv + 1;
- uint8_t const lun = 0;
+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);
+ _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;
+ 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 */
-)
-{
- uint8_t const dev_addr = pdrv + 1;
- uint8_t const lun = 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);
+ _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;
+ 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 */
-)
-{
- uint8_t const dev_addr = pdrv + 1;
- uint8_t const lun = 0;
- switch ( cmd )
- {
+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) = (WORD) tuh_msc_get_block_count(dev_addr, lun);
+ *((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);
+ *((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
+ *((DWORD *)buff) = 1; // erase block size in units of sector size
return RES_OK;
default:
return RES_PARERR;
}
-
- return RES_OK;
}
//--------------------------------------------------------------------+
@@ -293,6 +283,7 @@ DRESULT disk_ioctl (
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);
@@ -300,12 +291,11 @@ 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);
+ (void)cli;
+ putchar((int)c);
}
-bool cli_init(void)
-{
+bool cli_init(void) {
EmbeddedCliConfig *config = embeddedCliDefaultConfig();
config->cliBuffer = cli_buffer;
config->cliBufferSize = CLI_BUFFER_SIZE;
@@ -321,230 +311,239 @@ bool cli_init(void)
_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){"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){"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){"cp", "Usage: cp SOURCE DEST\r\n\tCopy SOURCE to DEST.", true, NULL,
+ cli_cmd_cp});
- 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){"dd", "Usage: dd [COUNT]\r\n\t" "Read COUNT sectors (default 1024) and report speed.", true, NULL,
+ cli_cmd_dd});
- embeddedCliAddBinding(_cli, (CliCommandBinding) {
- "pwd",
- "Usage: pwd\r\n\tPrint the name of the current working directory.",
- true,
- NULL,
- cli_cmd_pwd
- });
+ 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) {
- "mkdir",
- "Usage: mkdir DIR...\r\n\tCreate the DIRECTORY(ies), if they do not already exist..",
- true,
- NULL,
- cli_cmd_mkdir
- });
+ embeddedCliAddBinding(_cli,
+ (CliCommandBinding){"pwd", "Usage: pwd\r\n\tPrint the name of the current working directory.",
+ true, NULL, cli_cmd_pwd});
- embeddedCliAddBinding(_cli, (CliCommandBinding) {
- "mv",
- "Usage: mv SOURCE DEST...\r\n\tRename SOURCE to DEST.",
- true,
- NULL,
- cli_cmd_mv
- });
+ 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) {
- "rm",
- "Usage: rm [FILE]...\r\n\tRemove (unlink) the FILE(s).",
- true,
- NULL,
- cli_cmd_rm
- });
+ 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_cat(EmbeddedCli *cli, char *args, void *context)
-{
- (void) cli; (void) context;
+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;
+
+ 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;
+ tuh_msc_read10(dev_addr, lun, rw_buf, i, n, disk_io_complete, 0);
+ wait_for_disk_io(pdrv);
+ }
+
+ 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 )
- {
+ if (argc == 0) {
printf("invalid arguments\r\n");
return;
}
- for(uint16_t i=0; i<argc; i++)
- {
- FIL fi;
- const char* fpath = embeddedCliGetToken(args, i+1); // token count from 1
+ 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) )
- {
+ if (FR_OK != f_open(fi, fpath, FA_READ)) {
printf("%s: No such file or directory\r\n", fpath);
- }else
- {
- uint8_t buf[512];
+ } else {
UINT count = 0;
- while ( (FR_OK == f_read(&fi, buf, sizeof(buf), &count)) && (count > 0) )
- {
- for(UINT c = 0; c < count; c++)
- {
- const uint8_t ch = buf[c];
- if (isprint(ch) || iscntrl(ch))
- {
+ 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
- {
+ } else {
putchar('.');
}
}
}
}
- f_close(&fi);
+ f_close(fi);
}
}
-void cli_cmd_cd(EmbeddedCli *cli, char *args, void *context)
-{
- (void) cli; (void) context;
+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 )
- {
+ if (argc != 1) {
printf("invalid arguments\r\n");
return;
}
// default is current directory
- const char* dpath = args;
+ const char *dpath = args;
- if ( FR_OK != f_chdir(dpath) )
- {
+ 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;
+void cli_cmd_cp(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
uint16_t argc = embeddedCliGetTokenCount(args);
- if ( argc != 2 )
- {
+ 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);
+ const char *src = embeddedCliGetToken(args, 1);
+ const char *dst = embeddedCliGetToken(args, 2);
- FIL f_src;
- FIL f_dst;
+ FIL *f_src = &file1;
+ FIL *f_dst = &file2;
- if ( FR_OK != f_open(&f_src, src, FA_READ) )
- {
+ 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) )
- {
+ if (FR_OK != f_open(f_dst, dst, FA_WRITE | FA_CREATE_ALWAYS)) {
printf("cannot create '%s'\r\n", dst);
return;
- }else
- {
- uint8_t buf[512];
+ } else {
UINT rd_count = 0;
- while ( (FR_OK == f_read(&f_src, buf, sizeof(buf), &rd_count)) && (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, buf, rd_count, &wr_count) )
- {
+ 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);
+ f_close(f_src);
+ f_close(f_dst);
}
-void cli_cmd_ls(EmbeddedCli *cli, char *args, void *context)
-{
- (void) cli; (void) context;
+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 )
- {
+ if (argc > 1) {
printf("invalid arguments\r\n");
return;
}
// default is current directory
- const char* dpath = ".";
- if (argc) dpath = args;
+ const char *dpath = ".";
+ if (argc) {
+ dpath = args;
+ }
DIR dir;
- if ( FR_OK != f_opendir(&dir, dpath) )
- {
+ 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
+ while ((f_readdir(&dir, &fno) == FR_OK) && (fno.fname[0] != 0)) {
+ if (fno.fname[0] != '.') // ignore . and .. entry
{
- if ( fno.fattrib & AM_DIR )
- {
+ if (fno.fattrib & AM_DIR) {
// directory
printf("/%s\r\n", fno.fname);
- }else
- {
+ } else {
printf("%-40s", fno.fname);
- if (fno.fsize < 1024)
- {
+ if (fno.fsize < 1024) {
printf("%" PRIu32 " B\r\n", fno.fsize);
- }else
- {
+ } else {
printf("%" PRIu32 " KB\r\n", fno.fsize / 1024);
}
}
@@ -554,90 +553,81 @@ void cli_cmd_ls(EmbeddedCli *cli, char *args, void *context)
f_closedir(&dir);
}
-void cli_cmd_pwd(EmbeddedCli *cli, char *args, void *context)
-{
- (void) cli; (void) context;
+void cli_cmd_pwd(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
uint16_t argc = embeddedCliGetTokenCount(args);
- if (argc != 0)
- {
+ if (argc != 0) {
printf("invalid arguments\r\n");
return;
}
char path[256];
- if (FR_OK != f_getcwd(path, sizeof(path)))
- {
+ if (FR_OK != f_getcwd(path, sizeof(path))) {
printf("cannot get current working directory\r\n");
}
puts(path);
}
-void cli_cmd_mkdir(EmbeddedCli *cli, char *args, void *context)
-{
- (void) cli; (void) context;
+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 )
- {
+ if (argc != 1) {
printf("invalid arguments\r\n");
return;
}
// default is current directory
- const char* dpath = args;
+ const char *dpath = args;
- if ( FR_OK != f_mkdir(dpath) )
- {
+ 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;
+void cli_cmd_mv(EmbeddedCli *cli, char *args, void *context) {
+ (void)cli;
+ (void)context;
uint16_t argc = embeddedCliGetTokenCount(args);
- if ( argc != 2 )
- {
+ 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);
+ const char *src = embeddedCliGetToken(args, 1);
+ const char *dst = embeddedCliGetToken(args, 2);
- if ( FR_OK != f_rename(src, dst) )
- {
+ 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;
+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 )
- {
+ 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
+ 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) )
- {
+ 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/src/msc_app.h b/examples/host/msc_file_explorer/src/msc_app.h
index 3ba03d0dc..a99da5b5c 100644
--- a/examples/host/msc_file_explorer/src/msc_app.h
+++ b/examples/host/msc_file_explorer/src/msc_app.h
@@ -27,6 +27,7 @@
#define MSC_APP_H
#include <stdbool.h>
+#include <stdio.h>
bool msc_app_init(void);
void msc_app_task(void);
diff --git a/examples/host/msc_file_explorer/src/tusb_config.h b/examples/host/msc_file_explorer/src/tusb_config.h
index c1829c300..a9d24c89f 100644
--- a/examples/host/msc_file_explorer/src/tusb_config.h
+++ b/examples/host/msc_file_explorer/src/tusb_config.h
@@ -23,8 +23,8 @@
*
*/
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
#ifdef __cplusplus
extern "C" {
@@ -118,4 +118,4 @@
}
#endif
-#endif /* _TUSB_CONFIG_H_ */
+#endif /* TUSB_CONFIG_H_ */
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..a8c9bea2a
--- /dev/null
+++ b/examples/host/msc_file_explorer_freertos/skip.txt
@@ -0,0 +1,4 @@
+mcu:CH32F20X
+board:lpcxpresso54114
+mcu:FT90X
+board:stm32h7s3nucleo
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_ */