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
5151static EmbeddedCli * _cli ;
5252static 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
5656static volatile bool _disk_busy [CFG_TUH_DEVICE_MAX ];
5757
5858static CFG_TUH_MEM_SECTION FIL file1 , file2 ;
59- static CFG_TUH_MEM_SECTION uint8_t rw_buf [512 ];
59+
60+ #ifndef CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE
61+ #define CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE 4096
62+ #endif
63+ static CFG_TUH_MEM_SECTION uint8_t rw_buf [CFG_EXAMPLE_MSC_FILE_EXPLORER_RW_BUFSIZE ];
6064
6165// define the buffer to be place in USB/DMA memory with correct alignment/cache line size
6266CFG_TUH_MEM_SECTION static struct {
@@ -114,15 +118,15 @@ static bool inquiry_complete_cb(uint8_t dev_addr, const tuh_msc_complete_data_t
114118 }
115119
116120 // Print out Vendor ID, Product ID and Rev
117- printf ("%.8s %.16s rev %.4s\r\n" , scsi_resp .inquiry .vendor_id , scsi_resp .inquiry .product_id ,
121+ printf ("%.8s %.16s %.4s\r\n" , scsi_resp .inquiry .vendor_id , scsi_resp .inquiry .product_id ,
118122 scsi_resp .inquiry .product_rev );
119123
120124 // Get capacity of device
121125 const uint32_t block_count = tuh_msc_get_block_count (dev_addr , cbw -> lun );
122126 const uint32_t block_size = tuh_msc_get_block_size (dev_addr , cbw -> lun );
123127
124- printf ("Disk Size: %" PRIu32 " MB\r\n" , block_count / (( 1024 * 1024 ) / block_size ));
125- // printf("Block Count = %lu, Block Size: %lu\r\n", block_count, block_size);
128+ printf ("Disk Size: %" PRIu32 " %" PRIu32 "-byte blocks: %" PRIu32 " MB\r\n" ,
129+ block_count , block_size , block_count / (( 1024 * 1024 ) / block_size ) );
126130
127131 // For simplicity: we only mount 1 LUN per device
128132 const uint8_t drive_num = dev_addr - 1 ;
@@ -279,6 +283,7 @@ DRESULT disk_ioctl(BYTE pdrv, /* Physical drive nmuber (0..) */
279283void cli_cmd_cat (EmbeddedCli * cli , char * args , void * context );
280284void cli_cmd_cd (EmbeddedCli * cli , char * args , void * context );
281285void cli_cmd_cp (EmbeddedCli * cli , char * args , void * context );
286+ void cli_cmd_dd (EmbeddedCli * cli , char * args , void * context );
282287void cli_cmd_ls (EmbeddedCli * cli , char * args , void * context );
283288void cli_cmd_pwd (EmbeddedCli * cli , char * args , void * context );
284289void 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+
342413void cli_cmd_cat (EmbeddedCli * cli , char * args , void * context ) {
343414 (void )cli ;
344415 (void )context ;
0 commit comments