Skip to content

Commit 9d8de08

Browse files
committed
Add dd command to MSC file explorer for sector read and speed reporting, adjust CLI configuration and HIL tests
1 parent f7d1c10 commit 9d8de08

File tree

2 files changed

+116
-19
lines changed

2 files changed

+116
-19
lines changed

examples/host/msc_file_explorer/src/msc_app.c

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
#define CLI_RX_BUFFER_SIZE 16
4747
#define CLI_CMD_BUFFER_SIZE 64
4848
#define CLI_HISTORY_SIZE 32
49-
#define CLI_BINDING_COUNT 8
49+
#define CLI_BINDING_COUNT 9
5050

5151
static EmbeddedCli *_cli;
5252
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
5656
static volatile bool _disk_busy[CFG_TUH_DEVICE_MAX];
5757

5858
static CFG_TUH_MEM_SECTION FIL file1, file2;
59-
static CFG_TUH_MEM_SECTION uint8_t rw_buf[512];
59+
60+
#ifndef CFG_EXAMPLE_MSC_RW_BUFSIZE
61+
#define CFG_EXAMPLE_MSC_RW_BUFSIZE 4096
62+
#endif
63+
static CFG_TUH_MEM_SECTION uint8_t rw_buf[CFG_EXAMPLE_MSC_RW_BUFSIZE];
6064

6165
// define the buffer to be place in USB/DMA memory with correct alignment/cache line size
6266
CFG_TUH_MEM_SECTION static struct {
@@ -279,6 +283,7 @@ DRESULT disk_ioctl(BYTE pdrv, /* Physical drive nmuber (0..) */
279283
void cli_cmd_cat(EmbeddedCli *cli, char *args, void *context);
280284
void cli_cmd_cd(EmbeddedCli *cli, char *args, void *context);
281285
void cli_cmd_cp(EmbeddedCli *cli, char *args, void *context);
286+
void cli_cmd_dd(EmbeddedCli *cli, char *args, void *context);
282287
void cli_cmd_ls(EmbeddedCli *cli, char *args, void *context);
283288
void cli_cmd_pwd(EmbeddedCli *cli, char *args, void *context);
284289
void cli_cmd_mkdir(EmbeddedCli *cli, char *args, void *context);
@@ -316,6 +321,9 @@ bool cli_init(void) {
316321
embeddedCliAddBinding(_cli, (CliCommandBinding){"cp", "Usage: cp SOURCE DEST\r\n\tCopy SOURCE to DEST.", true, NULL,
317322
cli_cmd_cp});
318323

324+
embeddedCliAddBinding(_cli, (CliCommandBinding){"dd", "Usage: dd [COUNT]\r\n\t" "Read COUNT sectors (default 1024) and report speed.", true, NULL,
325+
cli_cmd_dd});
326+
319327
embeddedCliAddBinding(_cli, (CliCommandBinding){"ls",
320328
"Usage: ls [DIR]...\r\n\tList information about the FILEs (the "
321329
"current directory by default).",
@@ -339,6 +347,69 @@ bool cli_init(void) {
339347
return true;
340348
}
341349

350+
void cli_cmd_dd(EmbeddedCli *cli, char *args, void *context) {
351+
(void)cli;
352+
(void)context;
353+
354+
uint32_t count = 1024; // default sectors to read
355+
if (embeddedCliGetTokenCount(args) >= 1) {
356+
count = (uint32_t)atoi(embeddedCliGetToken(args, 1));
357+
if (count == 0) {
358+
count = 1024;
359+
}
360+
}
361+
362+
// find first mounted MSC device
363+
uint8_t dev_addr = 0;
364+
for (uint8_t i = 1; i <= CFG_TUH_DEVICE_MAX; i++) {
365+
if (tuh_msc_mounted(i)) {
366+
dev_addr = i;
367+
break;
368+
}
369+
}
370+
if (dev_addr == 0) {
371+
printf("no MSC device mounted\r\n");
372+
return;
373+
}
374+
375+
const uint8_t lun = 0;
376+
const uint32_t block_size = tuh_msc_get_block_size(dev_addr, lun);
377+
const uint32_t block_count = tuh_msc_get_block_count(dev_addr, lun);
378+
if (count > block_count) {
379+
count = block_count;
380+
}
381+
382+
const uint16_t sectors_per_xfer = (uint16_t)(sizeof(rw_buf) / block_size);
383+
const uint32_t xfer_count = (count + sectors_per_xfer - 1) / sectors_per_xfer;
384+
385+
printf("dd: reading %" PRIu32 " sectors (%" PRIu32 " bytes), %u sectors/xfer ...\r\n",
386+
count, count * block_size, sectors_per_xfer);
387+
388+
const uint32_t start_ms = tusb_time_millis_api();
389+
const uint8_t pdrv = dev_addr - 1;
390+
391+
for (uint32_t i = 0; i < count; i += sectors_per_xfer) {
392+
const uint16_t n = (uint16_t)((count - i < sectors_per_xfer) ? (count - i) : sectors_per_xfer);
393+
_disk_busy[pdrv] = true;
394+
tuh_msc_read10(dev_addr, lun, rw_buf, i, n, disk_io_complete, 0);
395+
wait_for_disk_io(pdrv);
396+
}
397+
398+
const uint32_t elapsed_ms = tusb_time_millis_api() - start_ms;
399+
const uint32_t total_data = count * block_size;
400+
// each SCSI transaction has 31-byte CBW + data + 13-byte CSW
401+
const uint32_t total_bus = total_data + xfer_count * (31 + 13);
402+
403+
if (elapsed_ms > 0) {
404+
const uint32_t data_kbs = total_data / elapsed_ms; // KB/s (bytes/ms = KB/s)
405+
const uint32_t bus_kbs = total_bus / elapsed_ms;
406+
printf("dd: %" PRIu32 " bytes in %" PRIu32 " ms = %" PRIu32 " KB/s (bus %" PRIu32 " KB/s)\r\n",
407+
total_data, elapsed_ms, data_kbs, bus_kbs);
408+
} else {
409+
printf("dd: %" PRIu32 " bytes in <1 ms\r\n", total_data);
410+
}
411+
}
412+
342413
void cli_cmd_cat(EmbeddedCli *cli, char *args, void *context) {
343414
(void)cli;
344415
(void)context;

test/hil/hil_test.py

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -538,28 +538,28 @@ def test_host_cdc_msc_hid(board):
538538
def rand_ascii(length):
539539
return "".join(random.choices(string.ascii_letters + string.digits, k=length)).encode("ascii")
540540

541-
sizes = [8, 32, 64, 128]
542-
for size in sizes:
543-
test_data = rand_ascii(size)
544-
ser.reset_input_buffer()
541+
packet_size = 64
545542

546-
# Write byte-by-byte with delay to avoid UART overrun
547-
for b in test_data:
548-
ser.write(bytes([b]))
549-
ser.flush()
550-
time.sleep(0.001)
551-
552-
# Read echo back with timeout
543+
# Echo test: 1KB random data, write random 1-packet_size chunks, only write next once echo matched
544+
echo_len = 1024
545+
echo_data = rand_ascii(echo_len)
546+
ser.reset_input_buffer()
547+
offset = 0
548+
while offset < echo_len:
549+
chunk_size = min(random.randint(1, packet_size), echo_len - offset)
550+
ser.write(echo_data[offset:offset + chunk_size])
551+
ser.flush()
552+
# wait until this chunk is echoed back
553553
echo = b''
554554
t = 5.0
555-
while t > 0 and len(echo) < size:
556-
rd = ser.read(max(1, ser.in_waiting))
555+
while t > 0 and len(echo) < chunk_size:
556+
rd = ser.read(chunk_size - len(echo))
557557
if rd:
558558
echo += rd
559-
time.sleep(0.05)
560-
t -= 0.05
561-
assert echo == test_data, (f'CDC echo wrong data ({size} bytes):\n'
562-
f' expected: {test_data}\n received: {echo}')
559+
expected = echo_data[offset:offset + chunk_size]
560+
assert echo == expected, (f'CDC echo mismatch at offset {offset} ({chunk_size} bytes):\n'
561+
f' expected: {expected}\n received: {echo}')
562+
offset += chunk_size
563563

564564
ser.close()
565565

@@ -620,6 +620,32 @@ def test_host_msc_file_explorer(board):
620620
f' received: {resp_text}')
621621
print('README.TXT matched ', end='')
622622

623+
# MSC throughput test: send dd command to read sectors
624+
time.sleep(0.5)
625+
ser.reset_input_buffer()
626+
for ch in 'dd 1024\r':
627+
ser.write(ch.encode())
628+
ser.flush()
629+
time.sleep(0.002)
630+
631+
# Read dd output until prompt
632+
resp = b''
633+
t = 30.0
634+
while t > 0:
635+
rd = ser.read(max(1, ser.in_waiting))
636+
if rd:
637+
resp += rd
638+
if b'KB/s' in resp and b'>' in resp:
639+
break
640+
time.sleep(0.05)
641+
t -= 0.05
642+
643+
resp_text = resp.decode('utf-8', errors='ignore')
644+
for line in resp_text.splitlines():
645+
if 'KB/s' in line:
646+
print(f'{line.strip()} ', end='')
647+
break
648+
623649
ser.close()
624650

625651

0 commit comments

Comments
 (0)