USB Memory control program using USBHost library

Dependencies:   USBHOST

see /users/kenjiArai/notebook/usb-interface--usbhost-and-usbdevice/

Revision:
0:a1ca40de3f45
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FatFs_Mon/mon.cpp	Sat Jan 04 23:51:21 2020 +0000
@@ -0,0 +1,1234 @@
+/*
+ * mbed Application program for the mbed
+ *  FatFs Check program / monitor part
+ *
+ * Copyright (c) 2015,'18,'19 Kenji Arai / JH1PJL
+ *  http://www.page.sannet.ne.jp/kenjia/index.html
+ *  https://os.mbed.com/users/kenjiArai/
+ *      Created:    May        5th, 2015
+ *      Revised:    June      14th, 2015
+ *      Revised:    April      7th, 2018
+ *      Revised:    December  24th, 2019
+ */
+
+/*
+ *---------------- REFERENCE ---------------------------------------------------
+ * Original Source Information
+ * FatFs sample program
+ *      ChaN FatFs  http://elm-chan.org/
+ *      http://elm-chan.org/fsw/ff/00index_e.html
+ */
+/*----------------------------------------------------------------------*/
+/* FAT file system sample project for FatFs            (C)ChaN, 2016    */
+/*----------------------------------------------------------------------*/
+
+//  Include --------------------------------------------------------------------
+#include "mbed.h"
+#if (MBED_MAJOR_VERSION == 2)
+#include "SDFileSystem.h"
+#elif (MBED_MAJOR_VERSION == 5)
+#include "FATFileSystem.h"
+#endif
+#include "ff.h"
+#include "ffconf.h"
+#include "diskio.h"
+#include "mon.h"
+
+//  Definition -----------------------------------------------------------------
+#define DO_DEBUG    0
+
+#if DO_DEBUG
+#define DEBUG_LINE  pc.printf("line:%d\r\n", __LINE__);
+#else
+#define DEBUG_LINE  {;}
+#endif
+
+// Com
+#if  1
+#define BAUD(x)     pc.baud(x)
+#define GETC(x)     pc.getc(x)
+#define PUTC(x)     pc.putc(x)
+#define PUTS(x)     pc.puts(x)
+#define PRINTF(...) pc.printf(__VA_ARGS__)
+#define READABLE(x) pc.readable(x)
+#else
+#define BAUD(x)     {;}
+#define GETC(x)     {;}
+#define PUTC(x)     {;}
+#define PRINTF(...) {;}
+#define READABLE(x) {;}
+#endif
+
+#define UTC_JST_OFFSET  (32400) // +9 hours
+
+// from ffconf.h
+#define _VOLUMES    1
+
+#if !defined(FF_FS_RPATH)
+#define FF_FS_RPATH 0
+#endif
+
+#define DW_CHAR         sizeof(char)
+#define DW_SHORT        sizeof(short)
+#define DW_LONG         sizeof(long)
+
+/* These types must be 16-bit, 32-bit or larger integer */
+typedef int             INT;
+typedef unsigned int    UINT;
+
+/* These types must be 8-bit integer */
+typedef char            CHAR;
+typedef unsigned char   UCHAR;
+typedef unsigned char   BYTE;
+
+/* These types must be 16-bit integer */
+typedef short           SHORT;
+typedef unsigned short  USHORT;
+typedef unsigned short  WORD;
+typedef unsigned short  WCHAR;
+
+/* These types must be 32-bit integer */
+typedef long            LONG;
+typedef unsigned long   ULONG;
+typedef unsigned long   DWORD;
+/*  by Kenji Arai / JH1PJL  September 10th, 2012  */
+typedef unsigned long long  DDWORD;
+
+//  RAM ------------------------------------------------------------------------
+BYTE Buff[4096];
+char Linebuf[128];          // Console input buffer
+FATFS Fatfs[_VOLUMES];      // File system object for each logical drive
+FIL File1, File2;           // File objects
+FATFS_DIR* Dirx;
+FILINFO Finfo;
+DWORD AccSize;              // Work register for fs command
+WORD AccFiles, AccDirs;
+
+//  ROM / Constant data --------------------------------------------------------
+const char *const monmsg0 = "Start monitor program for FatFs File System\r\n";
+const char *const monmsg1 = " <Please press any key to start the monitor>";
+
+static const char HelpMsg0[] =
+    "dir   <full_pass>\r\n"
+    "type  <file_name>\r\n"
+    "vol\r\n"
+    "ren   <org_file_name> <new_file_name>\r\n"
+    "copy  <file_name> <file_name>\r\n"
+    "mkdir <dir_name>\r\n"
+    "cd    <dir_name>\r\n"
+    "x     extend commands mode\r\n"
+    "q     Return to main\r\n"
+    "t     Show current time or Adjust time\r\n"
+    "      e.g. t 19 12 24 10 11 12 -> 2019-12-24 10:11:12\r\n"
+    "?     Help/You know the command\r\n"
+    "\r\n";
+
+static const char HelpMsg1[] =
+    "[File system controls]\r\n"
+    " fi <ld#> [<mount>]- Force initialized the volume\r\n"
+    " fs [<path>] - Show volume status\r\n"
+    " fl [<path>] - Show a directory\r\n"
+    " fo <mode> <file> - Open a file\r\n"
+    "    <mode>  Read=1, Write=2\r\n"
+    " fc - Close the file\r\n"
+    " fe <ofs> - Move fp in normal seek\r\n"
+    " fd <len> - Read and dump the file\r\n"
+    " fr <len> - Read the file\r\n"
+    " fw <len> <val> - Write to the file\r\n"
+    " fn <org.name> <new.name> - Rename an object\r\n"
+    " fu <name> - Unlink an object\r\n"
+    " fv - Truncate the file at current fp\r\n"
+    " fk <name> - Create a directory\r\n"
+    " fa <atrr> <mask> <object name> - Change attribute of an object\r\n"
+    " ft <year> <month> <day> <hour> <min> <sec> <name>"
+    " - Change timestamp of an object\r\n"
+    " fx <src.file> <dst.file> - Copy a file\r\n"
+    " fg <path> - Change current directory\r\n"
+    " fq - Show current directory\r\n"
+    " fb <name> - Set volume label\r\n"
+    " fm <ld#> <type> <csize> - Create file system\r\n"
+    " fz [<len>] - Change/Show R/W length for fr/fw/fx command\r\n"
+    "[Disk contorls]\r\n"
+    " di <pd#> - Initialize disk\r\n"
+    " dd [<pd#> <lba>] - Dump a secrtor\r\n"
+    " ds <pd#> - Show disk status\r\n"
+    "[Buffer controls]\r\n"
+    " bd <ofs> - Dump working buffer\r\n"
+    " be <ofs> [<data>] ... - Edit working buffer\r\n"
+    " br <pd#> <lba> [<count>] - Read disk into working buffer\r\n"
+    " bw <pd#> <lba> [<count>] - Write working buffer into disk\r\n"
+    " bf <val> - Fill working buffer\r\n"
+    "[Misc commands]\r\n"
+    " q Return\r\n"
+    " ? Help\r\n"
+    "\r\n";
+
+//  Function prototypes --------------------------------------------------------
+#if (MBED_MAJOR_VERSION == 2)
+extern SDFileSystem fs;
+#elif (MBED_MAJOR_VERSION == 5)
+extern HeapBlockDevice bd;
+extern FATFileSystem fs;
+#endif
+
+static void extended_mon( char *ptr );
+static void v_next( char *ptr );
+static void d_next( char *ptr );
+static void c_next( char *ptr );
+static void m_next( char *ptr );
+static void r_next( char *ptr );
+static void t_next( char *ptr );
+static void memory_inf(char *ptr);
+static void disk_inf(char *ptr);
+
+static void crlf( void );
+static FRESULT scan_files( char* path );
+static void put_rc( FRESULT rc );
+static void file_inf( char *ptr );
+static void put_dump( void* buff, unsigned long addr, int len, int width );
+static void chk_and_set_time(char *ptr);
+static int xatoi ( char **str, long *res );
+
+void get_line (char *buff, int len);
+
+//  Object ---------------------------------------------------------------------
+extern  Serial pc;
+Timer   t;
+
+//------------------------------------------------------------------------------
+//  Control Program
+//------------------------------------------------------------------------------
+// Monitor program for File control
+void mon ()
+{
+    char *ptr;
+
+    Dirx = new FATFS_DIR;
+    /* Open Uart to communicate with Host PC */
+    PUTS(monmsg0);
+    PUTS(monmsg1);
+    crlf();
+    for (;;) {
+        DEBUG_LINE
+        PUTC('>');
+        ptr = Linebuf;
+        get_line( ptr, sizeof(Linebuf) );
+        switch ( *ptr++ ) {
+                // vol
+            case 'v' :
+                v_next(ptr);
+                break;
+                // dir
+            case 'd' :
+                d_next(ptr);
+                break;
+                // cd, copy
+            case 'c' :
+                c_next(ptr);
+                break;
+                // mkdir
+            case 'm' :
+                m_next(ptr);
+                break;
+                // ren
+            case 'r' :
+                r_next(ptr);
+                break;
+            case 't' :
+                t_next(ptr);
+                break;
+            case 'x' :
+                extended_mon(ptr);
+                break;
+                // Help
+            case '?' :
+                PUTS(HelpMsg0);
+                break;
+                // Exit monitor (return to main())
+            case 'q' :
+                PUTS("Return to main\r\n");
+                return;
+                // Not a command
+            default:
+                PUTS("? [HELP]=?");
+                crlf();
+                break;
+        }
+    }
+}
+
+uint32_t get_disk_freespace(void)
+{
+    long  p1;
+    UINT   s1, s2;
+    FATFS  *fs;
+    BYTE res;
+
+    if (Dirx == NULL){
+        Dirx = new FATFS_DIR;
+    }
+    char p = NULL;
+    res = f_opendir(Dirx, &p);
+    if (res) {
+        return 0;
+    }
+    p1 = s1 = s2 = 0;
+    for(;;) {
+        res = f_readdir(Dirx, &Finfo);
+        if ((res != FR_OK) || !Finfo.fname[0]) break;
+        if (Finfo.fattrib & AM_DIR) {
+            s2++;
+        } else {
+            s1++;
+            p1 += Finfo.fsize;
+        }
+    }
+    res = f_getfree(&p, (DWORD*)&p1, &fs);
+    uint32_t size = p1 * fs->csize * 512;
+    if (res == FR_OK) {
+        return size;
+    } else {
+        return 0;
+    }
+}
+
+uint32_t get_data_file_size(const char *const file_name)
+{
+    BYTE res;
+    //const char *file_name ="acc_data.txt";
+ 
+    if (Dirx == NULL){
+        Dirx = new FATFS_DIR;
+    }
+    char p = NULL;
+    res = f_opendir(Dirx, &p);
+    if (res) {
+        put_rc((FRESULT)res);
+        return 0;
+    }
+    for(;;) {
+        res = f_readdir(Dirx, &Finfo);
+        if ((res != FR_OK) || !Finfo.fname[0]) break;
+        if (strcmp(Finfo.fname, file_name) == 0){
+            return Finfo.fsize;
+        }
+    }
+    return 0;
+}
+
+static void extended_mon( char *ptr )
+{
+    PUTS(HelpMsg1);
+    while(true) {
+        PUTS("e>");
+        ptr = Linebuf;
+        get_line( ptr, sizeof(Linebuf) );
+        switch ( *ptr++ ) {
+            case 'f' :
+                DEBUG_LINE;
+                file_inf(ptr);
+                break;
+            case 'd' :
+                DEBUG_LINE;
+                disk_inf(ptr);
+                break;
+            case 'm' :
+                DEBUG_LINE;
+                memory_inf(ptr);
+                break;
+            case '?' :
+                DEBUG_LINE;
+                PUTS(HelpMsg1);
+                break;
+            case 'q' :
+                DEBUG_LINE;
+                return;
+            default:
+                PUTS( "?\r\n" );
+        }
+    }
+}
+
+//------------------------------------------------------------------------------
+// General monitor functions
+static void v_next( char *ptr )
+{
+    switch ( *ptr++ ) {
+        case 'o' :
+            if (*ptr == 'l') {
+                *ptr = 's';
+                file_inf(ptr);  // fs [<path>] - Show volume status
+            }
+            break;
+        default:
+            PUTS( "?\r\n" );
+    }
+}
+
+static void d_next(char *ptr)
+{
+    switch ( *ptr++ ) {
+        case 'i' :
+            if (*ptr == 'r') {
+                *ptr = 'l';
+                file_inf(ptr);  // fl [<path>] - Directory listing
+            }
+            break;
+        default:
+            PUTS( "?\r\n" );
+    }
+}
+
+static void c_next(char *ptr)
+{
+    switch ( *ptr++ ) {
+        case 'o' :
+            if ((*ptr == 'p') && (*(ptr + 1) == 'y')) {
+                ptr++;
+                *ptr = 'x';
+                file_inf(ptr);  // fx <src_name> <dst_name> - Copy file
+            }
+            break;
+        case 'd' :
+            *ptr = 'g';
+            file_inf(ptr);  // fx <src_name> <dst_name> - Copy file
+            break;
+        default:
+            PUTS( "?\r\n" );
+    }
+}
+
+static void m_next(char *ptr)
+{
+    switch ( *ptr++ ) {
+        case 'k' :
+            if ((*ptr == 'd') && (*(ptr + 1) == 'i') && (*(ptr + 2) == 'r')) {
+                ptr += 2;
+                *ptr = 'k';
+                file_inf(ptr);  // fk <name> - Create a directory
+            }
+            break;
+        default:
+            PUTS("?\r\n");
+    }
+}
+
+static void r_next(char *ptr)
+{
+    switch (*ptr++) {
+        case 'e' :
+            if (*ptr == 'n') {
+                // fn <old_name> <new_name> - Change file/dir name
+                file_inf(ptr);
+            }
+            break;
+        default:
+            PUTS("?\r\n");
+    }
+}
+
+static void t_next(char *ptr)
+{
+    switch (*ptr++) {
+        case ' ' :
+        case 0x0d:
+            chk_and_set_time(ptr);
+        case 'y' :
+            if ((*ptr == 'p') && (*(ptr + 1) == 'e')) {
+                ptr++;
+                *ptr = '&';
+                file_inf(ptr);
+            }
+            break;
+        default:
+            PUTS("?\r\n");
+    }
+}
+
+static FRESULT scan_files (
+    char* path      /* Pointer to the path name working buffer */
+)
+{
+    FATFS_DIR  dirs;
+    FRESULT res;
+    BYTE i;
+    char *fn;
+
+    if ((res = f_opendir(&dirs, path)) == FR_OK) {
+        i = strlen(path);
+        PRINTF("path: %s, n=%u\r\n", path, i);
+        while (((res = f_readdir(&dirs, &Finfo)) == FR_OK) && Finfo.fname[0]) {
+            if (FF_FS_RPATH && Finfo.fname[0] == '.') {
+                continue;
+            }
+            fn = Finfo.fname;
+            if (Finfo.fattrib & AM_DIR) {
+                AccDirs++;
+                *(path+i) = '/';
+                strcpy(path+i+1, fn);
+                res = scan_files(path);
+                *(path+i) = '\0';
+                if (res != FR_OK) break;
+            } else {
+                PRINTF("%s/%s\r\n", path, fn);
+                AccFiles++;
+                AccSize += Finfo.fsize;
+            }
+        }
+    }
+    return res;
+}
+
+static void put_rc (FRESULT rc)
+{
+    const char *str =
+        "OK\0" "DISK_ERR\0" "INT_ERR\0" "NOT_READY\0" "NO_FILE\0" "NO_PATH\0"
+        "INVALID_NAME\0" "DENIED\0" "EXIST\0" "INVALID_OBJECT\0"
+        "WRITE_PROTECTED\0" "INVALID_DRIVE\0" "NOT_ENABLED\0"
+        "NO_FILE_SYSTEM\0" "MKFS_ABORTED\0" "TIMEOUT\0"
+        "LOCKED\0" "NOT_ENOUGH_CORE\0" "TOO_MANY_OPEN_FILES\0";
+    int i;
+
+    for ( i = 0; i != rc && *str; i++ ) {
+        while ( *str++ ) {
+            ;
+        }
+    }
+    PRINTF( "rc=%u FR_%s\r\n", (UINT)rc, str );
+}
+
+static void file_inf(char *ptr)
+{
+    long  p1, p2, p3;
+    CHAR   *ptr2;
+    BYTE   f_res;
+    UINT   s1, s2, cnt, blen = sizeof Buff;
+    FATFS  *fs;
+    static const BYTE ft[] = {0, 12, 16, 32};
+    BYTE res;
+    DWORD ofs = 0;
+    uint32_t tim;
+
+    switch (*ptr++) {
+        case '&' :
+            DEBUG_LINE;
+            while (*ptr == ' ') ptr++;
+            /* Open a file */
+            f_res = f_open(&File1, ptr, FA_READ);
+            if ( f_res ) {
+                put_rc((FRESULT)f_res);
+                break;
+            }
+            DEBUG_LINE;
+            /* Read all lines and display it */
+            while(true) {
+                f_res = f_read(&File1, (TCHAR*)Buff, blen, &cnt);
+                if ( f_res ) {
+                    put_rc((FRESULT)f_res);
+                    break;
+                }
+                for (s1 = 0; s1 < cnt; s1++) {
+                    PUTC(Buff[s1]);
+                }
+                if (cnt != blen) {
+                    break;
+                }
+            }
+            DEBUG_LINE;
+            /* Close the file */
+            f_close(&File1);
+            break;
+
+        case 'i' :  /* fi [<opt>]- Initialize logical drive */
+            if ( !xatoi(&ptr, &p1) ) {
+                break;
+            }
+            if (!xatoi(&ptr, &p2)) p2 = 0;
+            put_rc(f_mount(&Fatfs[p1], (const TCHAR*)p1, 0));
+            break;
+
+        case 's' :  /* fs [<path>] - Show volume status */
+            f_res = f_getfree( ptr, (DWORD*)&p2, &fs );
+            if ( f_res ) {
+                put_rc((FRESULT)f_res);
+                break;
+            }
+            PRINTF
+            (
+                "\rFAT type = FAT%u\r\nBytes/Cluster"
+                " = %lu\r\nNumber of FATs = %u\r\n"
+                "Root DIR entries = %u\r\n"
+                "Sectors/FAT = %lu\r\n"
+                "Number of clusters = %lu\r\n"
+                "FAT start (lba) = %lu\r\n"
+                "DIR start (lba,clustor) = %lu\r\n"
+                "Data start (lba) = %lu\r\n",
+                ft[fs->fs_type & 3], (DWORD)fs->csize * 512, fs->n_fats,
+                fs->n_rootdir, fs->fsize, (DWORD)fs->n_fatent - 2,
+                fs->fatbase, fs->dirbase, fs->database
+            );
+            AccSize = AccFiles = AccDirs = 0;
+            break;
+        case 'l' :  /* fl [<path>] - Directory listing */
+            while (*ptr == ' ') ptr++;
+            res = f_opendir(Dirx, ptr);
+            if (res) {
+                put_rc((FRESULT)res);
+                break;
+            }
+            p1 = s1 = s2 = 0;
+            for(;;) {
+                res = f_readdir(Dirx, &Finfo);
+                if ((res != FR_OK) || !Finfo.fname[0]) break;
+                if (Finfo.fattrib & AM_DIR) {
+                    s2++;
+                } else {
+                    s1++;
+                    p1 += Finfo.fsize;
+                }
+                PRINTF("%c%c%c%c%c %u/%02u/%02u %02u:%02u %9lu  %s\r\n",
+                       (Finfo.fattrib & AM_DIR) ? 'D' : '-',
+                       (Finfo.fattrib & AM_RDO) ? 'R' : '-',
+                       (Finfo.fattrib & AM_HID) ? 'H' : '-',
+                       (Finfo.fattrib & AM_SYS) ? 'S' : '-',
+                       (Finfo.fattrib & AM_ARC) ? 'A' : '-',
+                       (Finfo.fdate >> 9) + 1980, (Finfo.fdate >> 5) & 15,
+                       Finfo.fdate & 31,
+                       (Finfo.ftime >> 11), (Finfo.ftime >> 5) & 63,
+                       Finfo.fsize, Finfo.fname);
+            }
+#if 0   // f_getfree cannnot count under Dir, subdirectory area
+            PRINTF("%4u File(s),%10lu bytes total\r\n%4u Dir(s)", s1, p1, s2);
+            res = f_getfree(ptr, (DWORD*)&p1, &fs);
+            if (res == FR_OK)
+                PRINTF(", %10lu bytes free\r\n", p1 * fs->csize * 512);
+            else
+                put_rc((FRESULT)res);
+#else
+            PRINTF("%4u File(s) = %10lu bytes total, %4u Dir(s)\r\n",
+                   s1, p1, s2);
+#endif
+            break;
+
+        case 'o' :  /* fo <mode> <file> - Open a file */
+            if (!xatoi(&ptr, &p1)) break;
+            while (*ptr == ' ') ptr++;
+            put_rc(f_open(&File1, ptr, (BYTE)p1));
+#if 0
+            put_rc(f_open(&File1, "savedata.txt", 1));
+            PRINTF("Open savedata.txt as read mode\r\n");
+#endif
+            break;
+
+        case 'c' :  /* fc - Close a file */
+            put_rc(f_close(&File1));
+            break;
+
+        case 'e' :  /* fe - Seek file pointer */
+            if (!xatoi(&ptr, &p1)) break;
+            res = f_lseek(&File1, p1);
+            put_rc((FRESULT)res);
+            if (res == FR_OK)
+                PRINTF("fptr=%lu(0x%lX)\r\n", File1.fptr, File1.fptr);
+            break;
+
+        case 'd' :  /* fd <len> - read and dump file from current fp */
+            if (!xatoi(&ptr, &p1)) break;
+            ofs = File1.fptr;
+            while (p1) {
+                if ((UINT)p1 >= 16) {
+                    cnt = 16;
+                    p1 -= 16;
+                } else                {
+                    cnt = p1;
+                    p1 = 0;
+                }
+                res = f_read(&File1, Buff, cnt, &cnt);
+                if (res != FR_OK) {
+                    put_rc((FRESULT)res);
+                    break;
+                }
+                if (!cnt) break;
+                put_dump(Buff, ofs, cnt, DW_CHAR);
+                ofs += 16;
+            }
+            break;
+
+        case 'r' :  /* fr <len> - read file */
+            if (!xatoi(&ptr, &p1)) break;
+            p2 = 0;
+            t.reset();
+            t.start();
+            while (p1) {
+                if ((UINT)p1 >= blen) {
+                    cnt = blen;
+                    p1 -= blen;
+                } else {
+                    cnt = p1;
+                    p1 = 0;
+                }
+                res = f_read(&File1, Buff, cnt, &s2);
+                if (res != FR_OK) {
+                    put_rc((FRESULT)res);
+                    break;
+                }
+                p2 += s2;
+                if (cnt != s2) break;
+            }
+            tim = t.read_ms();
+            PRINTF("%lu bytes read with %lu kB/sec.\r\n",
+                   p2, tim ? (p2 / tim) : 0);
+            break;
+
+        case 'w' :  /* fw <len> <val> - write file */
+            if (!xatoi(&ptr, &p1) || !xatoi(&ptr, &p2)) break;
+            memset(Buff, (BYTE)p2, blen);
+            p2 = 0;
+            t.reset();
+            t.start();
+            while (p1) {
+                if ((UINT)p1 >= blen) {
+                    cnt = blen;
+                    p1 -= blen;
+                } else {
+                    cnt = p1;
+                    p1 = 0;
+                }
+                res = f_write(&File1, Buff, cnt, &s2);
+                if (res != FR_OK) {
+                    put_rc((FRESULT)res);
+                    break;
+                }
+                p2 += s2;
+                if (cnt != s2) break;
+            }
+            tim = t.read_ms();
+            PRINTF("%lu bytes written with %lu kB/sec.\r\n",
+                   p2, tim ? (p2 / tim) : 0);
+            break;
+
+        case 'n' :  /* fn <org.name> <new.name> - Change name of an object */
+            while (*ptr == ' ') ptr++;
+            ptr2 = strchr(ptr, ' ');
+            if (!ptr2) break;
+            *ptr2++ = 0;
+            while (*ptr2 == ' ') ptr2++;
+            put_rc(f_rename(ptr, ptr2));
+            break;
+
+        case 'u' :  /* fu <name> - Unlink an object */
+            while (*ptr == ' ') ptr++;
+            put_rc(f_unlink(ptr));
+            break;
+
+        case 'v' :  /* fv - Truncate file */
+            put_rc(f_truncate(&File1));
+            break;
+
+        case 'k' :  /* fk <name> - Create a directory */
+            while (*ptr == ' ') ptr++;
+            put_rc(f_mkdir(ptr));
+            break;
+#if 0
+        case 'a' :  /* fa <atrr> <mask> <name> - Change attribute of an object */
+            if (!xatoi(&ptr, &p1) || !xatoi(&ptr, &p2)) break;
+            while (*ptr == ' ') ptr++;
+            put_rc(f_chmod(ptr, p1, p2));
+            break;
+#endif
+#if 0
+            /* ft <year> <month> <day> <hour> <min> <sec> <name>
+                                                 - Change timestamp of an object */
+        case 't' :
+            if (!xatoi(&ptr, &p1) || !xatoi(&ptr, &p2) || !xatoi(&ptr, &p3)) {
+                break;
+            }
+            Finfo.fdate = ((p1 - 1980) << 9) | ((p2 & 15) << 5) | (p3 & 31);
+            if (!xatoi(&ptr, &p1) || !xatoi(&ptr, &p2) || !xatoi(&ptr, &p3)) {
+                break;
+            }
+            Finfo.ftime =
+                ((p1 & 31) << 11) | ((p2 & 63) << 5) | ((p3 >> 1) & 31);
+            put_rc(f_utime(ptr, &Finfo));
+            break;
+#endif
+#if FILCPY_NOTUSE == 0
+        case 'x' : /* fx <src_name> <dst_name> - Copy file */
+            while ( *ptr == ' ' ) {
+                ptr++;
+            }
+            ptr2 = strchr( ptr, ' ' );
+            if ( !ptr2 ) {
+                break;
+            }
+            *ptr2++ = 0;
+            while ( *ptr2 == ' ' ) {
+                ptr2++;
+            }
+            f_res = f_open( &File1, ptr, FA_OPEN_EXISTING | FA_READ );
+            PRINTF("Opening %s \r\n", ptr);
+            if ( f_res ) {
+                put_rc( (FRESULT)f_res );
+                break;
+            }
+            f_res = f_open( &File2, ptr2, FA_CREATE_ALWAYS | FA_WRITE );
+            PRINTF(" Creating %s \r\n", ptr2);
+            if ( f_res ) {
+                put_rc( (FRESULT)f_res );
+                f_close( &File1 );
+                break;
+            }
+            PRINTF("Copying file...");
+            p1 = 0;
+            for ( ;; ) {
+                f_res = f_read( &File1, Buff, blen, &s1 );
+                if ( f_res || s1 == 0 ) {
+                    break;   /* error or eof */
+                }
+                f_res = f_write( &File2, Buff, s1, &s2 );
+                p1 += s2;
+                if ( f_res || s2 < s1 ) {
+                    break;   /* error or disk full */
+                }
+            }
+            f_close( &File1 );
+            f_close( &File2 );
+            crlf();
+            break;
+#endif
+#if 0
+        case 'x' : /* fx <src.name> <dst.name> - Copy a file */
+            while (*ptr == ' ') ptr++;
+            ptr2 = strchr(ptr, ' ');
+            if (!ptr2) break;
+            *ptr2++ = 0;
+            while (*ptr2 == ' ') ptr2++;
+            PRINTF("Opening \"%s\"", ptr);
+            res = f_open(&File1, ptr, FA_OPEN_EXISTING | FA_READ);
+            PUTS("\r\n");
+            if (res) {
+                put_rc((FRESULT)res);
+                break;
+            }
+            PRINTF("Creating \"%s\"", ptr2);
+            res = f_open(&File1, ptr2, FA_CREATE_ALWAYS | FA_WRITE);
+            PUTS("\r\n");
+            if (res) {
+                put_rc((FRESULT)res);
+                f_close(&File1);
+                break;
+            }
+            PRINTF("Copying file...");
+            t.reset();
+            t.start();
+            p1 = 0;
+            for (;;) {
+                res = f_read(&File1, Buff, blen, &s1);
+                if (res || s1 == 0) break;   /* error or eof */
+                res = f_write(&File2, Buff, s1, &s2);
+                p1 += s2;
+                if (res || s2 < s1) break;   /* error or disk full */
+            }
+            tim = t.read_ms();
+            PRINTF("\r\n%lu bytes copied with %lu kB/sec.\r\n",
+                   p1, tim ? (p1 / tim) : 0);
+            f_close(&File1);
+            f_close(&File2);
+            break;
+#endif
+#if FF_FS_RPATH
+        case 'g' :  /* fg <path> - Change current directory */
+            while (*ptr == ' ') ptr++;
+            put_rc(f_chdir(ptr));
+            break;
+#if FF_FS_RPATH >= 2
+        case 'q' :  /* fq - Show current dir path */
+            res = f_getcwd(Linebuf, sizeof Linebuf);
+            if (res)
+                put_rc(res);
+            else
+                PRINTF("%s\r\n", Linebuf);
+            break;
+#endif
+#endif
+#if FF_USE_LABEL
+        case 'b' :  /* fb <name> - Set volume label */
+            while (*ptr == ' ') ptr++;
+            put_rc(f_setlabel(ptr));
+            break;
+#endif  /* FF_USE_LABEL */
+#if FF_USE_MKFS
+        case 'm' :  /* fm <type> <csize> - Create file system */
+            if (!xatoi(&ptr, &p2) || !xatoi(&ptr, &p3)) break;
+            PRINTF("The volume will be formatted. Are you sure? (Y/n)=");
+            get_line(Linebuf, sizeof Linebuf);
+            if (Linebuf[0] == 'Y')
+                put_rc(f_mkfs("", (BYTE)p2, (DWORD)p3, Buff, sizeof Buff));
+            break;
+#endif  /* FF_USE_MKFS */
+            /* fz [<size>] - Change/Show R/W length for fr/fw/fx command */
+        case 'z' :
+            if (xatoi(&ptr, &p1) && p1 >= 1 && p1 <= (long)sizeof Buff)
+                blen = p1;
+            PRINTF("blen=%u\r\n", blen);
+            break;
+    }
+}
+
+static void memory_inf(char *ptr)
+{
+    long  p1, p2, p3;
+
+    switch (*ptr++) {
+        case 'd' :  /* md[b|h|w] <address> [<count>] - Dump memory */
+            switch (*ptr++) {
+                case 'w':
+                    p3 = DW_LONG;
+                    break;
+                case 'h':
+                    p3 = DW_SHORT;
+                    break;
+                default:
+                    p3 = DW_CHAR;
+            }
+            if (!xatoi(&ptr, &p1)) break;
+            if (!xatoi(&ptr, &p2)) p2 = 128 / p3;
+            for (ptr = (char*)p1; p2 >= 16 / p3; ptr += 16, p2 -= 16 / p3)
+                put_dump(ptr, (DWORD)ptr, 16 / p3, p3);
+            if (p2) put_dump((BYTE*)ptr, (UINT)ptr, p2, p3);
+            break;
+        case 'f' :  /* mf <address> <value> <count> - Fill memory */
+            if (!xatoi(&ptr, &p1) || !xatoi(&ptr, &p2) || !xatoi(&ptr, &p3)) {
+                break;
+            }
+            while (p3--) {
+                *(BYTE*)p1 = (BYTE)p2;
+                p1++;
+            }
+            break;
+        case 'e' :  /* me[b|h|w] <address> [<value> ...] - Edit memory */
+            switch (*ptr++) {   /* Get data width */
+                case 'w':
+                    p3 = DW_LONG;
+                    break;
+                case 'h':
+                    p3 = DW_SHORT;
+                    break;
+                default:
+                    p3 = DW_CHAR;
+            }
+            if (!xatoi(&ptr, &p1)) break;   /* Get start address */
+            if (xatoi(&ptr, &p2)) { /* 2nd parameter is given (direct mode) */
+                do {
+                    switch (p3) {
+                        case DW_LONG:
+                            *(DWORD*)p1 = (DWORD)p2;
+                            break;
+                        case DW_SHORT:
+                            *(WORD*)p1 = (WORD)p2;
+                            break;
+                        default:
+                            *(BYTE*)p1 = (BYTE)p2;
+                    }
+                    p1 += p3;
+                } while (xatoi(&ptr, &p2)); /* Get next value */
+                break;
+            }
+            for (;;) {  /* 2nd parameter is not given (interactive mode) */
+                switch (p3) {
+                    case DW_LONG:
+                        PRINTF("%08X 0x%08X-", (unsigned int)p1, *(unsigned int*)p1);
+                        break;
+                    case DW_SHORT:
+                        PRINTF("%08X 0x%04X-", (unsigned int)p1, *(WORD*)p1);
+                        break;
+                    default:
+                        PRINTF("%08X 0x%02X-", (unsigned int)p1, *(BYTE*)p1);
+                }
+                ptr = Linebuf;
+                get_line(ptr, sizeof Linebuf);
+                if (*ptr == '.') break;
+                if ((BYTE)*ptr >= ' ') {
+                    if (!xatoi(&ptr, &p2)) continue;
+                    switch (p3) {
+                        case DW_LONG:
+                            *(DWORD*)p1 = (DWORD)p2;
+                            break;
+                        case DW_SHORT:
+                            *(WORD*)p1 = (WORD)p2;
+                            break;
+                        default:
+                            *(BYTE*)p1 = (BYTE)p2;
+                    }
+                }
+                p1 += p3;
+            }
+    }
+}
+
+static void disk_inf(char *ptr)
+{
+    long  p1, p2;
+    UINT   s1;
+    BYTE res, b, drv = 0;
+    DWORD ofs = 0, sect = 0, blk[2];
+
+    switch (*ptr++) {
+        case 'd' :  /* dd [<pd#> <sect>] - Dump secrtor */
+            if (!xatoi(&ptr, &p1)) {
+                p1 = drv;
+                p2 = sect;
+            } else {
+                if (!xatoi(&ptr, &p2)) break;
+            }
+            drv = (BYTE)p1;
+            sect = p2;
+            res = disk_read(drv, Buff, sect, 1);
+            if (res) {
+                PRINTF("rc=%d\r\n", (WORD)res);
+                break;
+            }
+            PRINTF("PD#:%u LBA:%lu\r\n", drv, sect++);
+            for (ptr=(char*)Buff, ofs = 0; ofs < 0x200; ptr += 16, ofs += 16)
+                put_dump((BYTE*)ptr, ofs, 16, DW_CHAR);
+            break;
+
+        case 'i' :  /* di <pd#> - Initialize disk */
+            if (!xatoi(&ptr, &p1)) break;
+            PRINTF("rc=%d\r\n", (WORD)disk_initialize((BYTE)p1));
+            break;
+
+        case 's' :  /* ds <pd#> - Show disk status */
+            if (!xatoi(&ptr, &p1)) break;
+            if (disk_ioctl((BYTE)p1, GET_SECTOR_COUNT, &p2) == RES_OK) {
+                PRINTF("Drive size: %lu sectors\r\n", p2);
+            }
+            if (disk_ioctl((BYTE)p1, GET_BLOCK_SIZE, &p2) == RES_OK) {
+                PRINTF("Block size: %lu sectors\r\n", p2);
+            }
+            if (disk_ioctl((BYTE)p1, MMC_GET_TYPE, &b) == RES_OK) {
+                PRINTF("Media type: %u\r\n", b);
+            }
+            if (disk_ioctl((BYTE)p1, MMC_GET_CSD, Buff) == RES_OK) {
+                PUTS("CSD:\r\n");
+                put_dump(Buff, 0, 16, DW_CHAR);
+            }
+            if (disk_ioctl((BYTE)p1, MMC_GET_CID, Buff) == RES_OK) {
+                PUTS("CID:\r\n");
+                put_dump(Buff, 0, 16, DW_CHAR);
+            }
+            if (disk_ioctl((BYTE)p1, MMC_GET_OCR, Buff) == RES_OK) {
+                PUTS("OCR:\r\n");
+                put_dump(Buff, 0, 4, DW_CHAR);
+            }
+            if (disk_ioctl((BYTE)p1, MMC_GET_SDSTAT, Buff) == RES_OK) {
+                PUTS("SD Status:\r\n");
+                for (s1 = 0; s1 < 64; s1 += 16) {
+                    put_dump(Buff+s1, s1, 16, DW_CHAR);
+                }
+            }
+            break;
+
+        case 'c' :  /* Disk ioctl */
+            switch (*ptr++) {
+                case 's' :  /* dcs <pd#> - CTRL_SYNC */
+                    if (!xatoi(&ptr, &p1)) break;
+                    PRINTF("rc=%d\r\n", disk_ioctl((BYTE)p1, CTRL_SYNC, 0));
+                    break;
+                case 'e' :  /* dce <pd#> <s.lba> <e.lba> - CTRL_TRIM */
+                    if (!xatoi(&ptr, &p1) ||
+                            !xatoi(&ptr, (long*)&blk[0]) ||
+                            !xatoi(&ptr, (long*)&blk[1])) {
+                        break;
+                    }
+                    PRINTF("rc=%d\r\n", disk_ioctl((BYTE)p1, CTRL_TRIM, blk));
+                    break;
+            }
+    }
+}
+
+void put_dump (
+    void* buff,             /* Pointer to the array to be dumped */
+    unsigned long addr,     /* Heading address value */
+    int len,                /* Number of items to be dumped */
+    int width               /* Size of the items (DW_CHAR, DW_SHORT, DW_LONG) */
+)
+{
+    int i;
+    unsigned char *bp;
+    unsigned short *sp;
+    unsigned long *lp;
+
+    PRINTF( "%08lx ", addr );      /* address */
+    switch ( width )  {
+        case DW_CHAR:
+            bp = (unsigned char *)buff;
+            for ( i = 0; i < len; i++ ) {       /* Hexdecimal dump */
+                PRINTF( " %02x", bp[i] );
+            }
+            PUTC(' ');
+            for ( i = 0; i < len; i++ ) {       /* ASCII dump */
+                PUTC( (bp[i] >= ' ' && bp[i] <= '~') ? bp[i] : '.' );
+            }
+            break;
+        case DW_SHORT:
+            sp = (unsigned short *)buff;
+            do {                            /* Hexdecimal dump */
+                PRINTF( " %04x", *sp++ );
+            } while ( --len );
+            break;
+        case DW_LONG:
+            lp = (unsigned long *)buff;
+            do {                            /* Hexdecimal dump */
+                PRINTF( " %08lx", *lp++ );
+            } while ( --len );
+            break;
+    }
+    PUTS( "\r\n" );
+}
+
+// RTC related subroutines
+void chk_and_set_time(char *ptr)
+{
+    char buf[64];
+
+    long p1;
+    struct tm t;
+    time_t seconds;
+
+    if (xatoi(&ptr, &p1)) {
+        t.tm_year       = (uint8_t)p1 + 100;
+        pc.printf("Year:%ld ",p1);
+        xatoi( &ptr, &p1 );
+        t.tm_mon        = (uint8_t)p1 - 1;
+        pc.printf("Month:%ld ",p1);
+        xatoi( &ptr, &p1 );
+        t.tm_mday       = (uint8_t)p1;
+        pc.printf("Day:%ld ",p1);
+        xatoi( &ptr, &p1 );
+        t.tm_hour       = (uint8_t)p1;
+        pc.printf("Hour:%ld ",p1);
+        xatoi( &ptr, &p1 );
+        t.tm_min        = (uint8_t)p1;
+        pc.printf("Min:%ld ",p1);
+        xatoi( &ptr, &p1 );
+        t.tm_sec        = (uint8_t)p1;
+        pc.printf("Sec: %ld \r\n",p1);
+        seconds = mktime(&t);
+        set_time(seconds);
+    } else {
+        seconds = time(NULL);
+    }
+    strftime(buf, 50, " %B %d,'%y, %H:%M:%S\r\n", localtime(&seconds));
+    pc.printf("[Time] %s", buf);
+}
+
+//  Get key input data
+void get_line (char *buff, int len)
+{
+    char c;
+    int idx = 0;
+
+    for (;;) {
+        c = GETC();
+        //    Added by Kenji Arai / JH1PJL   May 9th, 2010
+        if (c == '\r') {
+            buff[idx++] = c;
+            break;
+        }
+        if ((c == '\b') && idx) {
+            idx--;
+            PUTC(c);
+            PUTC(' ');
+            PUTC(c);
+        }
+        if (((uint8_t)c >= ' ') && (idx < len - 1)) {
+            buff[idx++] = c;
+            PUTC(c);
+        }
+    }
+    buff[idx] = 0;
+    PUTS("\r\n");
+}
+
+/*  Outpur LF & CR */
+void crlf( void )
+{
+    PRINTF( "\r\n" );
+}
+
+/*  Check key input */
+unsigned int check_hit_key (void)
+{
+    return ( READABLE() );
+}
+
+/*----------------------------------------------*/
+/* Get a value of the string                    */
+/*----------------------------------------------*/
+/*  "123 -5   0x3ff 0b1111 0377  w "
+        ^                           1st call returns 123 and next ptr
+           ^                        2nd call returns -5 and next ptr
+                   ^                3rd call returns 1023 and next ptr
+                          ^         4th call returns 15 and next ptr
+                               ^    5th call returns 255 and next ptr
+                                  ^ 6th call fails and returns 0
+*/
+int xatoi (         /* 0:Failed, 1:Successful */
+    char **str,     /* Pointer to pointer to the string */
+    long *res       /* Pointer to the valiable to store the value */
+)
+{
+    unsigned long val;
+    unsigned char c, r, s = 0;
+
+    *res = 0;
+    while ( (c = **str) == ' ' ) {
+        (*str)++;   /* Skip leading spaces */
+    }
+    if ( c == '-' ) {       /* negative? */
+        s = 1;
+        c = *(++(*str));
+    }
+    if ( c == '0' ) {
+        c = *(++(*str));
+        switch (c) {
+            case 'x':       /* hexdecimal */
+                r = 16;
+                c = *(++(*str));
+                break;
+            case 'b':       /* binary */
+                r = 2;
+                c = *(++(*str));
+                break;
+            default:
+                if ( c <= ' ' ) return 1;   /* single zero */
+                if ( c < '0' || c > '9' ) return 0; /* invalid char */
+                r = 8;      /* octal */
+        }
+    } else {
+        if ( c < '0' || c > '9' ) return 0; /* EOL or invalid char */
+        r = 10;         /* decimal */
+    }
+    val = 0;
+    while ( c > ' ' ) {
+        if ( c >= 'a' ) {
+            c -= 0x20;
+        }
+        c -= '0';
+        if ( c >= 17 ) {
+            c -= 7;
+            if ( c <= 9 ) {
+                return 0;   /* invalid char */
+            }
+        }
+        if ( c >= r ) {
+            return 0;   /* invalid char for current radix */
+        }
+        val = val * r + c;
+        c = *(++(*str));
+    }
+    if (s) val = 0 - val;           /* apply sign if needed */
+    *res = val;
+    return 1;
+}