1st version for FatFs monitor

Dependents:   DISCO-F469NI_USB_Disk

mon.cpp

Committer:
kenjiArai
Date:
2018-04-30
Revision:
0:6f52c89729de

File content as of revision 0:6f52c89729de:

/*
 * mbed Application program for the mbed
 *  FatFs Check program / monitor part
 *
 * Copyright (c) 2015,'18 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     29th, 2018
 */

/*
 *---------------- 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
#define FF_USE_LFN  0

#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;
#if FF_USE_LFN
//inside of FILINFO
char Lfname[512];
#endif
DWORD AccSize;              // Work register for fs command
WORD AccFiles, AccDirs;

//  ROM / Constant data --------------------------------------------------------
char *const monmsg0 = "Start monitor program for FatFs File System\r\n";
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 18 3 28 14 48 20 -> 2018-03-28 14:48:20\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;
static 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();
#if FF_USE_LFN
    // no needs because FILINFO structure is changed
    Finfo.lfname = Lfname;
    Finfo.lfsize = sizeof Lfname;
#endif
    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);
    DWORD tot_sect = (fs->n_fatent - 2) * fs->csize;
    DWORD fre_sect = p1 * fs->csize;
#if 0
    PRINTF("%lu KB total drive space.\n"
           "%lu KB available.\n",
           tot_sect / 2, fre_sect / 2);
#endif
    uint32_t size = fre_sect / 2;
//    uint64_t size = p1 * fs->csize * 512;
    if (res == FR_OK) {
        return size;
    } else {
        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;
            }
#if FF_USE_LFN
            //fn = *Finfo.lfname ? Finfo.lfname : Finfo.fname;
            if (Finfo.altname[0] == 0) {
                fn = Finfo.fname;
            } else {
                fn = Finfo.altname;
            }
#else
            fn = Finfo.fname;
#endif
            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-", p1, *(DWORD*)p1);
                        break;
                    case DW_SHORT:
                        PRINTF("%08X 0x%04X-", p1, *(WORD*)p1);
                        break;
                    default:
                        PRINTF("%08X 0x%02X-", 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:%d ",p1);
        xatoi( &ptr, &p1 );
        t.tm_mon        = (uint8_t)p1 - 1;
        pc.printf("Month:%d ",p1);
        xatoi( &ptr, &p1 );
        t.tm_mday       = (uint8_t)p1;
        pc.printf("Day:%d ",p1);
        xatoi( &ptr, &p1 );
        t.tm_hour       = (uint8_t)p1;
        pc.printf("Hour:%d ",p1);
        xatoi( &ptr, &p1 );
        t.tm_min        = (uint8_t)p1;
        pc.printf("Min:%d ",p1);
        xatoi( &ptr, &p1 );
        t.tm_sec        = (uint8_t)p1;
        pc.printf("Sec: %d \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;
}