summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-03-20 18:24:42 +0700
committerhathach <[email protected]>2026-03-20 18:57:02 +0700
commit99d70e2990a04ca7874fe6dc3ff129cfe67c9cc7 (patch)
tree4b5887dd17f9e0ae5127e641b188d7b6526c4468
parentf7d1c10b73e2f549d9727b4a8a11c1ca58906881 (diff)
Add dd command to MSC file explorer for sector read and speed reporting, adjust CLI configuration and HIL tests
-rw-r--r--examples/host/msc_file_explorer/src/msc_app.c75
-rw-r--r--hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.cmake1
-rwxr-xr-xtest/hil/hil_test.py60
3 files changed, 117 insertions, 19 deletions
diff --git a/examples/host/msc_file_explorer/src/msc_app.c b/examples/host/msc_file_explorer/src/msc_app.c
index 238af5431..21ff42726 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 {
@@ -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;
diff --git a/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.cmake b/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.cmake
index ecb8b378f..819a1ba1e 100644
--- a/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.cmake
+++ b/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.cmake
@@ -9,5 +9,6 @@ function(update_board TARGET)
target_compile_definitions(${TARGET} PUBLIC
SYSCLK_FREQ_144MHz_HSI=144000000
CFG_EXAMPLE_MSC_DUAL_READONLY
+ CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE=1024
)
endfunction()
diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py
index b3458e59d..f8b43430d 100755
--- a/test/hil/hil_test.py
+++ b/test/hil/hil_test.py
@@ -538,28 +538,28 @@ def test_host_cdc_msc_hid(board):
def rand_ascii(length):
return "".join(random.choices(string.ascii_letters + string.digits, k=length)).encode("ascii")
- sizes = [8, 32, 64, 128]
- for size in sizes:
- test_data = rand_ascii(size)
- ser.reset_input_buffer()
+ packet_size = 64
- # Write byte-by-byte with delay to avoid UART overrun
- for b in test_data:
- ser.write(bytes([b]))
- ser.flush()
- time.sleep(0.001)
-
- # Read echo back with timeout
+ # Echo test: 1KB random data, write random 1-packet_size chunks, only write next once echo matched
+ echo_len = 1024
+ echo_data = rand_ascii(echo_len)
+ ser.reset_input_buffer()
+ offset = 0
+ while offset < echo_len:
+ chunk_size = min(random.randint(1, packet_size), echo_len - offset)
+ ser.write(echo_data[offset:offset + chunk_size])
+ ser.flush()
+ # wait until this chunk is echoed back
echo = b''
t = 5.0
- while t > 0 and len(echo) < size:
- rd = ser.read(max(1, ser.in_waiting))
+ while t > 0 and len(echo) < chunk_size:
+ rd = ser.read(chunk_size - len(echo))
if rd:
echo += rd
- time.sleep(0.05)
- t -= 0.05
- assert echo == test_data, (f'CDC echo wrong data ({size} bytes):\n'
- f' expected: {test_data}\n received: {echo}')
+ expected = echo_data[offset:offset + chunk_size]
+ assert echo == expected, (f'CDC echo mismatch at offset {offset} ({chunk_size} bytes):\n'
+ f' expected: {expected}\n received: {echo}')
+ offset += chunk_size
ser.close()
@@ -620,6 +620,32 @@ def test_host_msc_file_explorer(board):
f' received: {resp_text}')
print('README.TXT matched ', end='')
+ # MSC throughput test: send dd command to read sectors
+ time.sleep(0.5)
+ ser.reset_input_buffer()
+ for ch in 'dd 1024\r':
+ ser.write(ch.encode())
+ ser.flush()
+ time.sleep(0.002)
+
+ # Read dd output until prompt
+ resp = b''
+ t = 30.0
+ while t > 0:
+ rd = ser.read(max(1, ser.in_waiting))
+ if rd:
+ resp += rd
+ if b'KB/s' in resp and b'>' in resp:
+ break
+ time.sleep(0.05)
+ t -= 0.05
+
+ resp_text = resp.decode('utf-8', errors='ignore')
+ for line in resp_text.splitlines():
+ if 'KB/s' in line:
+ print(f'{line.strip()} ', end='')
+ break
+
ser.close()