summaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorHa Thach <[email protected]>2026-04-02 11:31:11 +0700
committerGitHub <[email protected]>2026-04-02 11:31:11 +0700
commit871b843245666786857f54b7ffc1454d4acad7a0 (patch)
treefa1d515702aab8de32a957f85c5b38bf1d6bf0dc /examples
parenteae25b531dff32636aecb21ca4a8a3ac8e391fc3 (diff)
parentb6f04f8643e5ca1efe2b8f9754427f5e5a0303e7 (diff)
Merge pull request #3561 from hathach/rp2040-hcd-epx
rp2 hcd rework: epx for non-interrupt and ping-pong double buffered
Diffstat (limited to 'examples')
-rw-r--r--examples/host/cdc_msc_hid/src/msc_app.c6
-rw-r--r--examples/host/cdc_msc_hid_freertos/src/msc_app.c6
-rw-r--r--examples/host/msc_file_explorer/README.md105
-rw-r--r--examples/host/msc_file_explorer/src/msc_app.c81
4 files changed, 187 insertions, 11 deletions
diff --git a/examples/host/cdc_msc_hid/src/msc_app.c b/examples/host/cdc_msc_hid/src/msc_app.c
index dd4e22d7f..8181bdcb9 100644
--- a/examples/host/cdc_msc_hid/src/msc_app.c
+++ b/examples/host/cdc_msc_hid/src/msc_app.c
@@ -41,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_freertos/src/msc_app.c b/examples/host/cdc_msc_hid_freertos/src/msc_app.c
index fa864c364..17df11951 100644
--- a/examples/host/cdc_msc_hid_freertos/src/msc_app.c
+++ b/examples/host/cdc_msc_hid_freertos/src/msc_app.c
@@ -47,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/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/src/msc_app.c b/examples/host/msc_file_explorer/src/msc_app.c
index 238af5431..c7cc366b5 100644
--- a/examples/host/msc_file_explorer/src/msc_app.c
+++ b/examples/host/msc_file_explorer/src/msc_app.c
@@ -46,7 +46,7 @@
#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)];
@@ -56,7 +56,11 @@ static CFG_TUH_MEM_SECTION FATFS fatfs[CFG_TUH_DEVICE_MAX]; // for simplicity on
static volatile bool _disk_busy[CFG_TUH_DEVICE_MAX];
static CFG_TUH_MEM_SECTION FIL file1, file2;
-static CFG_TUH_MEM_SECTION uint8_t rw_buf[512];
+
+#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 {
@@ -114,15 +118,15 @@ static bool inquiry_complete_cb(uint8_t dev_addr, const tuh_msc_complete_data_t
}
// 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,
+ 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 " 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
const uint8_t drive_num = dev_addr - 1;
@@ -279,6 +283,7 @@ DRESULT disk_ioctl(BYTE pdrv, /* Physical drive nmuber (0..) */
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);
@@ -316,6 +321,9 @@ bool cli_init(void) {
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).",
@@ -339,6 +347,69 @@ bool cli_init(void) {
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;
+
+ 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;