/* ###################################################################
**     This component module is generated by Processor Expert. Do not modify it.
**     Filename    : McuFatFS.h
**     Project     : FRDM-K64F_Generator
**     Processor   : MK64FN1M0VLL12
**     Component   : FAT_FileSystem
**     Version     : Component 01.214, Driver 01.00, CPU db: 3.00.000
**     Compiler    : GNU C Compiler
**     Date/Time   : 2021-05-13, 11:07, # CodeGen: 738
**     Abstract    :
**
**     Settings    :
**          Component name                                 : McuFatFS
**          FatFs Version                                  : R0.12 (Patch 3, 29-April-2016)
**          Tiny                                           : no
**          Volumes                                        : 1
**          Drives                                         : 1
**            Drive0                                       : SDCard
**          FS_MINIMIZE                                    : 0
**          Maximum Sector Size                            : 512
**          Relative Path                                  : Enabled with f_getcwd()
**          Code Page                                      : U.S. (OEM)
**          File Sharing                                   : 0
**          Multipartion                                   : no
**          Fast Seek                                      : yes
**          Use Find                                       : Disable (0)
**          String Functions                               : enable
**          LFN                                            : Long File Name Support
**            Use LFN                                      : Disable
**            exFAT                                        : no
**            Max LFN Length                               : 255
**            LFN Unicode                                  : no
**          Write enabled                                  : Enabled
**            Use TimeDate                                 : Enabled
**              Realtime clock                             : McuTimeDate
**          Reentrant                                      : Enabled
**            Timeout ticks                                : 1000
**            Handle                                       : HANDLE
**            User Sync Functions                          : no
**            RTOS                                         : Enabled
**              RTOS                                       : McuRTOS
**              Use dynamic heap                           : yes
**          Utility                                        : McuUtility
**          Wait                                           : McuWait
**          SDK                                            : McuLib
**          Shell                                          : Enabled
**            Shell                                        : McuShell
**            Card Insert Delay (ms)                       : 100
**            File print buffer size                       : 32
**            File copy buffer size                        : 32
**     Contents    :
**         open              - FRESULT McuFatFS_open(FIL *fp, const XCHAR *path, BYTE mode);
**         close             - FRESULT McuFatFS_close(FIL *fp);
**         read              - FRESULT McuFatFS_read(FIL *fp, void *buff, UINT btr, UINT *br);
**         write             - FRESULT McuFatFS_write(FIL *fp, const *void buff, UINT btw, UINT *bw);
**         opendir           - FRESULT McuFatFS_opendir(DIR *dj, const XCHAR *path);
**         readdir           - FRESULT McuFatFS_readdir(DIR *dj, FILINFO *fno);
**         lseek             - FRESULT McuFatFS_lseek(FIL *fp, DWORD ofs);
**         unlink            - FRESULT McuFatFS_unlink(const XCHAR *path);
**         mount             - FRESULT McuFatFS_mount(FATFS *fs, const TCHAR* path, byte opt);
**         getfree           - FRESULT McuFatFS_getfree(const XCHAR *path, dword *nclst, FATFS **fatfs);
**         sync              - FRESULT McuFatFS_sync(FIL *fp);
**         rename            - FRESULT McuFatFS_rename(const XCHAR *path_old, const XCHAR *path_new);
**         isWriteProtected  - bool McuFatFS_isWriteProtected(uint8_t *drvStr);
**         isDiskPresent     - bool McuFatFS_isDiskPresent(uint8_t *drvStr);
**         mkdir             - FRESULT McuFatFS_mkdir(const XCHAR *path);
**         chmod             - FRESULT McuFatFS_chmod(const TCHAR* FileName, uint8_t Attribute, uint8_t...
**         truncate          - FRESULT McuFatFS_truncate(FIL *FileObject);
**         stat              - FRESULT McuFatFS_stat(const TCHAR* FileName, FILINFO* FileInfo);
**         utime             - FRESULT McuFatFS_utime(const TCHAR* FileName, const FILINFO* TimeDate);
**         mkfs              - FRESULT McuFatFS_mkfs(byte drive, uint8_t PartitioningRule, UINT  AllocSize);
**         chdir             - FRESULT McuFatFS_chdir(const TCHAR* Path);
**         chdrive           - FRESULT McuFatFS_chdrive(uint8_t Drive);
**         getcwd            - FRESULT McuFatFS_getcwd(TCHAR* Buffer, UINT BufferLen);
**         errFResultMsg     - char* McuFatFS_errFResultMsg(int errNo);
**         errDResultMsg     - char* McuFatFS_errDResultMsg(int errNo);
**         f_gets            - McuFatFS_CHARP McuFatFS_f_gets(TCHAR* buff, int len, FIL *fil);
**         f_puts            - McuFatFS_INT McuFatFS_f_puts(const TCHAR* str, FIL *fil);
**         f_putc            - McuFatFS_INT McuFatFS_f_putc(TCHAR c, FIL *fil);
**         f_printf          - McuFatFS_INT McuFatFS_f_printf(FIL* fil, const TCHAR* str, ...);
**         f_eof             - byte McuFatFS_f_eof(FIL *fil);
**         f_error           - uint8_t McuFatFS_f_error(FIL *fil);
**         f_tell            - dword McuFatFS_f_tell(FIL *fil);
**         f_size            - dword McuFatFS_f_size(FIL *fil);
**         f_getlabel        - FRESULT McuFatFS_f_getlabel(const TCHAR* path, TCHAR* label, DWORD* vsn);
**         f_setlabel        - FRESULT McuFatFS_f_setlabel(const TCHAR* label);
**         f_expand          - FRESULT McuFatFS_f_expand(FIL* fp, FSIZE_t fsz, BYTE opt);
**         f_findfirst       - FRESULT McuFatFS_f_findfirst(DIR* dp, FILINFO* fno, const TCHAR* path, const...
**         f_findnext        - FRESULT McuFatFS_f_findnext(DIR* dp, FILINFO* fno);
**         f_opendir         - FRESULT McuFatFS_f_opendir(DIR* dp, const TCHAR* path);
**         f_readdir         - FRESULT McuFatFS_f_readdir(DIR *dj, FILINFO *fno);
**         f_closedir        - FRESULT McuFatFS_f_closedir(DIR* dp);
**         get_fattime       - uint32_t McuFatFS_get_fattime(void);
**         ParseCommand      - uint8_t McuFatFS_ParseCommand(const unsigned char *cmd, bool *handled, const...
**         StrToDriveNumber  - uint8_t McuFatFS_StrToDriveNumber(uint8_t *drvStr);
**         CheckCardPresence - uint8_t McuFatFS_CheckCardPresence(bool *cardMounted, uint8_t *drive, FATFS...
**         MountFileSystem   - uint8_t McuFatFS_MountFileSystem(FATFS *fileSystemObject, uint8_t...
**         UnMountFileSystem - uint8_t McuFatFS_UnMountFileSystem(uint8_t *logicalDrive, const...
**         PrintDirectory    - uint8_t McuFatFS_PrintDirectory(const uint8_t *dirName, const...
**         CopyFile          - uint8_t McuFatFS_CopyFile(const uint8_t*srcFileName, const uint8_t...
**         DeleteFile        - uint8_t McuFatFS_DeleteFile(const uint8_t *fileName, const...
**         CreateFile        - uint8_t McuFatFS_CreateFile(const uint8_t *fileName, const...
**         PrintFile         - uint8_t McuFatFS_PrintFile(const uint8_t *fileName, const...
**         PrintHexFile      - uint8_t McuFatFS_PrintHexFile(const uint8_t *fileName, const...
**         MakeDirectory     - uint8_t McuFatFS_MakeDirectory(const uint8_t *dirName, const...
**         ChangeDirectory   - uint8_t McuFatFS_ChangeDirectory(const uint8_t *dirName, const...
**         RenameFile        - uint8_t McuFatFS_RenameFile(const uint8_t *srcFileName, const uint8_t...
**         PrintSector       - uint8_t McuFatFS_PrintSector(uint8_t drive, uint32_t sectorNo, const...
**         PrintDiskInfo     - uint8_t McuFatFS_PrintDiskInfo(uint8_t *drive, const...
**         Benchmark         - uint8_t McuFatFS_Benchmark(const %@Shell@'ModuleName'%.StdIOType *io);
**         Deinit            - uint8_t McuFatFS_Deinit(void);
**         Init              - uint8_t McuFatFS_Init(void);
**
** Copyright (c) 2014-2021,  Erich Styger
** Web: http://mcuoneclipse.com/
** SourceForge: https://sourceforge.net/projects/mcuoneclipse
** Git: https://github.com/ErichStyger/McuOnEclipse_PEx
** All rights reserved.
** 
** Redistribution and use in source and binary forms, with or without modification,
** are permitted provided that the following conditions are met:
** - Redistributions of source code must retain the above copyright notice, this list
**   of conditions and the following disclaimer.
** - Redistributions in binary form must reproduce the above copyright notice, this
**   list of conditions and the following disclaimer in the documentation and/or
**   other materials provided with the distribution.
** 
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
** ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
** ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
** ###################################################################*/
/*!
** @file McuFatFS.h
** @version 01.00
** @brief
**
*/         
/*!
**  @addtogroup McuFatFS_module McuFatFS module documentation
**  @{
*/         

/* MODULE McuFatFS. */

#include "ff.h"
#include "McuFatFS.h"

#if McuLib_CONFIG_SDK_VERSION_USED == McuLib_CONFIG_SDK_PROCESSOR_EXPERT
#include "SDCard.h"
#endif


/*-----------------------------------------------------------------------*/
#if McuLib_CONFIG_SDK_VERSION_USED == McuLib_CONFIG_SDK_PROCESSOR_EXPERT
/* Initialize a Drive                                                    */
DSTATUS disk_initialize (
        uint8_t drv                     /* Physical drive number (0..) */
)
{
  switch(drv) {
    case 0:
      return SDCard_disk_initialize(drv);
    default:
      break;
  } /* switch */
  return (DSTATUS)RES_PARERR;
}
#endif
/*-----------------------------------------------------------------------*/
#if McuLib_CONFIG_SDK_VERSION_USED == McuLib_CONFIG_SDK_PROCESSOR_EXPERT
/* Return Disk Status                                                    */
DSTATUS disk_status (
        uint8_t drv                     /* Physical drive number (0..) */
)
{
  switch(drv) {
    case 0:
      return SDCard_disk_status(drv);
    default:
      break;
  } /* switch */
  return (DSTATUS)RES_PARERR;
}
#endif
/*-----------------------------------------------------------------------*/
#if McuLib_CONFIG_SDK_VERSION_USED == McuLib_CONFIG_SDK_PROCESSOR_EXPERT
/* Read Sector(s)                                                        */
DRESULT disk_read (
        uint8_t drv,                    /* Physical drive number (0..) */
        uint8_t *buff,                  /* Data buffer to store read data */
        uint32_t sector,                /* Sector address (LBA) */
        unsigned int count              /* Number of sectors to read */
)
{
  switch(drv) {
    case 0:
      return SDCard_disk_read(drv, buff, sector, count);
    default:
      break;
  } /* switch */
  return RES_PARERR;
}
#endif
/*-----------------------------------------------------------------------*/
#if McuLib_CONFIG_SDK_VERSION_USED == McuLib_CONFIG_SDK_PROCESSOR_EXPERT
/* Write Sector(s)                                                       */
#if _READONLY == 0
DRESULT disk_write (
        uint8_t drv,                    /* Physical drive number (0..) */
        const uint8_t *buff,            /* Data to be written */
        uint32_t sector,                /* Sector address (LBA) */
        unsigned int count              /* Number of sectors to write */
)
{
  switch(drv) {
    case 0:
      return SDCard_disk_write(drv, buff, sector, count);
    default:
      break;
  } /* switch */
  return RES_PARERR;
}
#endif /* _READONLY == 0 */
#endif
/*-----------------------------------------------------------------------*/
#if McuLib_CONFIG_SDK_VERSION_USED == McuLib_CONFIG_SDK_PROCESSOR_EXPERT
DRESULT disk_ioctl (
        uint8_t drv,                    /* Physical drive number (0..) */
        uint8_t ctrl,                   /* Control code */
        void *buff                      /* Buffer to send/receive control data */
)
{
  switch(drv) {
    case 0:
      return SDCard_disk_ioctl(drv, ctrl, buff);
    default:
      break;
  } /* switch */
  return RES_PARERR;
}
#endif
/*-----------------------------------------------------------------------*/
#define McuFatFS_8_3_SIZE       sizeof("0:/12345678.txt") /* length of a 8.3 file name (13 including the zero byte) including the optional drive letter */

#if McuFatFS_USE_LFN == 0               /* No LFN */
#define McuFatFS_DEF_NAMEBUF(name)      uint8_t name[McuFatFS_8_3_SIZE]
#define McuFatFS_PTR_NAMEBUF(name)      &name[0]
#define McuFatFS_SIZE_NAMEBUF(name)     sizeof(name)
#define McuFatFS_INIT_NAMEBUF(name)
#define McuFatFS_FREE_NAMEBUF(name)

#elif McuFatFS_USE_LFN == 1             /* LFN with static LFN working buffer */
static TCHAR McuFatFS_FileName[McuFatFS_MAX_LFN+1];
#define McuFatFS_DEF_NAMEBUF(name)
#define McuFatFS_PTR_NAMEBUF(name)      &McuFatFS_FileName[0]
#define McuFatFS_SIZE_NAMEBUF(name)     sizeof(McuFatFS_FileName)
#define McuFatFS_INIT_NAMEBUF(name)
#define McuFatFS_FREE_NAMEBUF(name)

#elif McuFatFS_USE_LFN == 2             /* LFN with dynamic LFN working buffer on the stack */
#define McuFatFS_DEF_NAMEBUF(name)      uint8_t name[McuFatFS_MAX_LFN+1]
#define McuFatFS_PTR_NAMEBUF(name)      &name[0]
#define McuFatFS_SIZE_NAMEBUF(name)     sizeof(name)
#define McuFatFS_INIT_NAMEBUF(name)
#define McuFatFS_FREE_NAMEBUF(name)

#elif McuFatFS_USE_LFN == 3             /* LFN with dynamic LFN working buffer on the heap */
#define McuFatFS_DEF_NAMEBUF(name)      uint8_t *name
#define McuFatFS_PTR_NAMEBUF(name)      name
#define McuFatFS_SIZE_NAMEBUF(name)     (McuFatFS_MAX_LFN+1)
#define McuFatFS_INIT_NAMEBUF(name)     { name = ff_memalloc(McuFatFS_MAX_LFN+1); \
                                          if (!name) { \
                                            McuShell_SendStr((unsigned char*)"ff_memalloc failed!", io->stdErr); \
                                            return ERR_FAILED; \
                                          } \
                                        }
#define McuFatFS_FREE_NAMEBUF(name)     ff_memfree(name)

#else
  #error Wrong LFN configuration.
#endif

static void FatFsFResultMsg(uint8_t *msg, McuFatFS_FRESULT errNo, const McuShell_StdIOType *io) {
  unsigned char buf[sizeof("1234")];

  McuShell_SendStr((unsigned char*)"ERROR: ", io->stdErr);
  McuShell_SendStr(msg, io->stdErr);
  McuShell_SendStr((unsigned char*)": (", io->stdErr);
  McuUtility_Num16sToStr(buf, sizeof(buf), (int16_t)errNo);
  McuShell_SendStr(buf, io->stdErr);
  McuShell_SendStr((unsigned char*)") ", io->stdErr);
  McuShell_SendStr((unsigned char*)McuFatFS_errFResultMsg((int)errNo), io->stdErr);
  McuShell_SendStr((unsigned char*)"\r\n", io->stdErr);
}

static void FatFsDResultMsg(uint8_t *msg, McuFatFS_DRESULT errNo, const McuShell_StdIOType *io) {
  unsigned char buf[sizeof("1234")];

  McuShell_SendStr((unsigned char*)"ERROR: ", io->stdErr);
  McuShell_SendStr(msg, io->stdErr);
  McuShell_SendStr((unsigned char*)": (", io->stdErr);
  McuUtility_Num16sToStr(buf, sizeof(buf), (int16_t)errNo);
  McuShell_SendStr(buf, io->stdErr);
  McuShell_SendStr((unsigned char*)") ", io->stdErr);
  McuShell_SendStr((unsigned char*)McuFatFS_errDResultMsg((int)errNo), io->stdErr);
  McuShell_SendStr((unsigned char*)"\r\n", io->stdErr);
}

static void CmdUsageError(const unsigned char *cmd, uint8_t *usage, const McuShell_StdIOType *io) {
  McuShell_SendStr((unsigned char*)"*** error while reading command: ", io->stdErr);
  McuShell_SendStr(cmd, io->stdErr);
  McuShell_SendStr((unsigned char*)"\r\n*** Usage: ", io->stdErr);
  McuShell_SendStr((unsigned char*)usage, io->stdErr);
  McuShell_SendStr((unsigned char*)"\r\n", io->stdErr);
}

static void PrintInASCII(uint8_t *buf, size_t bufSize, size_t LineLen, uint8_t nonASCIIchar, uint8_t fillChar, const McuShell_StdIOType *io) {
  unsigned int i;
  uint8_t ch;

  /* print in ASCII */
  McuShell_SendCh(' ', io->stdOut);
  for (i=0; i<bufSize; i++) {
    ch = buf[i];
    if (ch >= ' ' && ch <= 0x7f) {
      McuShell_SendCh(ch, io->stdOut);
    } else {
      McuShell_SendCh(nonASCIIchar, io->stdOut); /* place holder */
    }
  }
  for (/*empty*/; i<LineLen; i++) { /* fill up line */
    McuShell_SendCh(fillChar, io->stdOut);
  }
}

/*!
 * \brief Writes a directory listing of the given path
 * \param[in] dirPathPtr Pointer to a directory path string. Pass e.g. "0:/" for the root directory
 * \param[in] io Callback to write directory output
 * \return Error code, otherwise ERR_OK
 */
static uint8_t PrintDir(const uint8_t *dirPathPtr, const McuShell_StdIOType *io) {
  McuFatFS_FILINFO fInfo;
  McuFatFS_FRESULT fres;
  uint32_t p1;
  UINT s1, s2;
  McuFatFS_DIR dir;  /* Directory object */
  uint8_t buf[sizeof("yyyy-mm-dd hh:ss")+1];
#if !McuFatFS_FS_READONLY
  McuFatFS_FATFS *fs;
#endif

  McuShell_SendStr((unsigned char*)"Directory of ", io->stdOut);
  McuShell_SendStr(dirPathPtr, io->stdOut);
  McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
  fres = McuFatFS_f_opendir(&dir, (const TCHAR*)dirPathPtr);
  if (fres != FR_OK) {
    FatFsFResultMsg((unsigned char*)"opendir failed", fres, io);
    return ERR_FAULT;
  }
  p1 = s1 = s2 = 0;
  for(;;) {
    fres = McuFatFS_readdir(&dir, &fInfo);
    if (fres != FR_OK) {
      FatFsFResultMsg((unsigned char*)"readdir failed", fres, io);
      break;
    }
    if (!fInfo.fname[0]) { /* end of directory list */
      break;
    }
    /* file attributes */
    if (fInfo.fattrib & AM_DIR) { /* directory */
      s2++;
      io->stdOut('D');
    } else {
      s1++;
      p1 += fInfo.fsize;
      io->stdOut('-');
    }
    if (fInfo.fattrib & AM_RDO) { /* read only */
      io->stdOut('R');
    } else {
      io->stdOut('-');
    }
    if (fInfo.fattrib & AM_HID) { /* hidden */
      io->stdOut('H');
    } else {
      io->stdOut('-');
    }
    if (fInfo.fattrib & AM_SYS) { /* system */
      io->stdOut('S');
    } else {
      io->stdOut('-');
    }
    if (fInfo.fattrib & AM_ARC) { /* archive */
      io->stdOut('A');
    } else {
      io->stdOut('-');
    }
    io->stdOut(' ');
    /* file date & time */
    buf[0] = '\0';
    McuUtility_strcatNum16sFormatted(buf, sizeof(buf), (int16_t)((fInfo.fdate >> 9) + 1980), ' ', 4); /* year */
    McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"-");
    McuUtility_strcatNum16sFormatted(buf, sizeof(buf), (int16_t)((fInfo.fdate >> 5) & 15), '0', 2); /* month */
    McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"-");
    McuUtility_strcatNum16sFormatted(buf, sizeof(buf), (int16_t)(fInfo.fdate & 31), '0', 2); /* day */
    McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" ");
    McuUtility_strcatNum16sFormatted(buf, sizeof(buf), (int16_t)((fInfo.ftime >> 11)), '0', 2); /* hour */
    McuUtility_strcat(buf, sizeof(buf), (unsigned char*)":");
    McuUtility_strcatNum16sFormatted(buf, sizeof(buf), (int16_t)((fInfo.ftime >> 5) & 63), '0', 2); /* minute */
    McuShell_SendStr(buf, io->stdOut);
    io->stdOut(' ');
    buf[0] = '\0';
    /* file size */
    McuUtility_strcatNum32uFormatted(buf, sizeof(buf), fInfo.fsize, ' ', 10); /* size */
    McuShell_SendStr(buf, io->stdOut);
    /* file name */
    io->stdOut(' ');
    McuShell_SendStr((unsigned char*)fInfo.fname, io->stdOut);
#if McuFatFS_USE_LFN
    io->stdOut(' ');
    McuShell_SendStr((unsigned char*)fInfo.altname, io->stdOut);
#endif
    io->stdOut('\r');
    io->stdOut('\n');
  }
  /* number of files and bytes used */
  buf[0] = '\0';
  McuUtility_strcatNum16u(buf, sizeof(buf), (uint16_t)s1);
  McuShell_SendStr(buf, io->stdOut);
  McuShell_SendStr((unsigned char*)" File(s), ", io->stdOut);
  buf[0] = '\0';
  McuUtility_strcatNum32u(buf, sizeof(buf), p1);
  McuShell_SendStr(buf, io->stdOut);
  McuShell_SendStr((unsigned char*)" bytes total\r\n", io->stdOut);
  /* number of directories and number of free bytes */
  buf[0] = '\0';
  McuUtility_strcatNum16u(buf, sizeof(buf), (uint16_t)s2);
  McuShell_SendStr(buf, io->stdOut);
  McuShell_SendStr((unsigned char*)" Dir(s)", io->stdOut);
#if !McuFatFS_FS_READONLY
  /* number of free bytes */
  fres = McuFatFS_getfree((const TCHAR*)dirPathPtr, &p1, &fs);
  if (fres != FR_OK) {
    FatFsFResultMsg((unsigned char*)"getfree failed", fres, io);
  } else {
    io->stdOut(',');
    io->stdOut(' ');
    buf[0] = '\0';
    McuUtility_strcatNum32s(buf, sizeof(buf), (long)(p1*fs->csize/2));
    McuShell_SendStr(buf, io->stdOut);
    McuShell_SendStr((unsigned char*)" KBytes free", io->stdOut);
  }
#endif
  (void)McuFatFS_f_closedir(&dir); /* close directory */
  io->stdOut('\r');
  io->stdOut('\n');
  return ERR_OK;
}

static uint8_t DirCmd(const unsigned char *cmd, const McuShell_ConstStdIOType *io) {
  /* precondition: cmd starts with "dir" */
  uint8_t res = ERR_OK;
  McuFatFS_DEF_NAMEBUF(fileName);

  McuFatFS_INIT_NAMEBUF(fileName);
  if (*(cmd+sizeof("dir")-1)== ' ') { /* space after "dir": read name */
    if (McuUtility_ReadEscapedName(cmd+sizeof("dir"), (uint8_t*)McuFatFS_PTR_NAMEBUF(fileName),
          McuFatFS_SIZE_NAMEBUF(fileName), NULL, NULL, NULL)==ERR_OK
       )
    {
      /* ok, have now directory name */
    } else {
      McuShell_SendStr((unsigned char*)"reading directory name failed!\r\n", io->stdErr);
      res = ERR_FAILED;
    }
  } else { /* use current directory */
#if McuFatFS_FS_RPATH >= 2
    McuFatFS_FRESULT fres;
    fres = McuFatFS_getcwd((TCHAR*)McuFatFS_PTR_NAMEBUF(fileName), McuFatFS_SIZE_NAMEBUF(fileName));
    if(fres!=FR_OK) {
      FatFsFResultMsg((unsigned char*)"getcwd failed", fres, io);
      res = ERR_FAILED;
    } else {
      /* ok, have now directory name */
    }
#else
    McuUtility_strcpy(McuFatFS_PTR_NAMEBUF(fileName), McuFatFS_SIZE_NAMEBUF(fileName), (unsigned char*)McuFatFS_CONFIG_DEFAULT_DRIVE_STRING); /* use root */
#endif
  }
  if (res == ERR_OK) {
    res = McuFatFS_PrintDirectory((const uint8_t*)McuFatFS_PTR_NAMEBUF(fileName), io);
  }
  McuFatFS_FREE_NAMEBUF(fileName);
  return res;
}

static uint8_t CopyCmd(const unsigned char *cmd, const McuShell_ConstStdIOType *io) {
  /* precondition: cmd starts with "copy" */
  uint8_t res = ERR_OK;
  size_t lenRead;
  McuFatFS_DEF_NAMEBUF(fileName);
  McuFatFS_DEF_NAMEBUF(fileName2);

  McuFatFS_INIT_NAMEBUF(fileName);
  McuFatFS_INIT_NAMEBUF(fileName2);
  if (   (McuUtility_ReadEscapedName(cmd+sizeof("copy"), (uint8_t*)McuFatFS_PTR_NAMEBUF(fileName),
          McuFatFS_SIZE_NAMEBUF(fileName), &lenRead, NULL, NULL)==ERR_OK)
      && *(cmd+sizeof("copy")+lenRead)==' '
      && (McuUtility_ReadEscapedName(cmd+sizeof("copy")+lenRead+1, (uint8_t*)McuFatFS_PTR_NAMEBUF(fileName2),
          McuFatFS_SIZE_NAMEBUF(fileName2), NULL, NULL, NULL)==ERR_OK)
     )
  {
    res = McuFatFS_CopyFile((uint8_t*)McuFatFS_PTR_NAMEBUF(fileName), McuFatFS_PTR_NAMEBUF(fileName2), io);
  } else {
    CmdUsageError(cmd, (unsigned char*)"copy srcFileName dstFileName", io);
    res = ERR_FAILED;
  }
  McuFatFS_FREE_NAMEBUF(fileName);
  McuFatFS_FREE_NAMEBUF(fileName2);
  return res;
}

static uint8_t DeleteCmd(const unsigned char *cmd, const McuShell_ConstStdIOType *io) {
  /* precondition: cmd starts with "delete" */
  uint8_t res = ERR_OK;
  McuFatFS_DEF_NAMEBUF(fileName);

  McuFatFS_INIT_NAMEBUF(fileName);
  if (McuUtility_ReadEscapedName(cmd+sizeof("delete"), (uint8_t*)McuFatFS_PTR_NAMEBUF(fileName),
        McuFatFS_SIZE_NAMEBUF(fileName), NULL, NULL, NULL)==ERR_OK
     )
  {
    res = McuFatFS_DeleteFile((uint8_t*)McuFatFS_PTR_NAMEBUF(fileName), io);
  } else {
    CmdUsageError(cmd, (unsigned char*)"delete fileName", io);
    res = ERR_FAILED;
  }
  McuFatFS_FREE_NAMEBUF(fileName);
  return res;
}

static uint8_t MkdirCmd(const unsigned char *cmd, const McuShell_ConstStdIOType *io) {
  /* precondition: cmd starts with "mkdir" */
  uint8_t res = ERR_OK;
  McuFatFS_DEF_NAMEBUF(fileName);

  McuFatFS_INIT_NAMEBUF(fileName);
  if (McuUtility_ReadEscapedName(cmd+sizeof("mkdir"), (uint8_t*)McuFatFS_PTR_NAMEBUF(fileName),
        McuFatFS_SIZE_NAMEBUF(fileName), NULL, NULL, NULL)==ERR_OK
     )
  {
    res = McuFatFS_MakeDirectory((uint8_t*)McuFatFS_PTR_NAMEBUF(fileName), io);
  } else {
    CmdUsageError(cmd, (unsigned char*)"mkdir directoryName", io);
    res = ERR_FAILED;
  }
  McuFatFS_FREE_NAMEBUF(fileName);
  return res;
}

static uint8_t RenameCmd(const unsigned char *cmd, const McuShell_ConstStdIOType *io) {
  /* precondition: cmd starts with "rename" */
  uint8_t res = ERR_OK;
  size_t lenRead;
  McuFatFS_DEF_NAMEBUF(fileName);
  McuFatFS_DEF_NAMEBUF(fileName2);

  McuFatFS_INIT_NAMEBUF(fileName);
  McuFatFS_INIT_NAMEBUF(fileName2);
  if (   (McuUtility_ReadEscapedName(cmd+sizeof("rename"), (uint8_t*)McuFatFS_PTR_NAMEBUF(fileName),
          McuFatFS_SIZE_NAMEBUF(fileName), &lenRead, NULL, NULL)==ERR_OK)
      && *(cmd+sizeof("rename")+lenRead)==' '
      && (McuUtility_ReadEscapedName(cmd+sizeof("rename")+lenRead+1,
          (uint8_t*)McuFatFS_PTR_NAMEBUF(fileName2),
          McuFatFS_SIZE_NAMEBUF(fileName2), NULL, NULL, NULL)==ERR_OK)
     )
  {
    res = McuFatFS_RenameFile((unsigned char*)McuFatFS_PTR_NAMEBUF(fileName), (unsigned char*)McuFatFS_PTR_NAMEBUF(fileName2), io);
  } else {
    CmdUsageError(cmd, (unsigned char*)"rename srcFileName dstFileName", io);
    res = ERR_FAILED;
  }
  McuFatFS_FREE_NAMEBUF(fileName);
  McuFatFS_FREE_NAMEBUF(fileName2);
  return res;
}

static uint8_t SectorCmd(const unsigned char *cmd, const McuShell_ConstStdIOType *io) {
  /* precondition: cmd starts with "sector" */
  uint8_t res;
  uint32_t sectorNo;
  const unsigned char *p = cmd+sizeof("printsector");

  res = McuUtility_ScanDecimal32uNumber(&p, &sectorNo);
  if (res == ERR_OK) { /* format fine */
    res = McuFatFS_PrintSector(0, sectorNo, io);
    if (res!=ERR_OK) {
      return res;
    }
  } else {
    CmdUsageError(cmd, (unsigned char*)"printsector <number>", io);
    return ERR_FAILED;
  }
  return ERR_OK;
}

static uint8_t PrintCmd(const unsigned char *cmd, const McuShell_ConstStdIOType *io) {
  /* precondition: cmd starts with "print" */
  uint8_t res = ERR_OK;
  McuFatFS_DEF_NAMEBUF(fileName);

  McuFatFS_INIT_NAMEBUF(fileName);
  if (McuUtility_ReadEscapedName(cmd+sizeof("print"), (uint8_t*)McuFatFS_PTR_NAMEBUF(fileName),
        McuFatFS_SIZE_NAMEBUF(fileName), NULL, NULL, NULL)==ERR_OK
     )
  {
    res = McuFatFS_PrintFile((uint8_t*)McuFatFS_PTR_NAMEBUF(fileName), io);
  } else {
    CmdUsageError(cmd, (unsigned char*)"print fileName", io);
    res = ERR_FAILED;
  }
  McuFatFS_FREE_NAMEBUF(fileName);
  return res;
}

static uint8_t PrintHexCmd(const unsigned char *cmd, const McuShell_ConstStdIOType *io) {
  /* precondition: cmd starts with "printhex" */
  uint8_t res = ERR_OK;
  McuFatFS_DEF_NAMEBUF(fileName);

  McuFatFS_INIT_NAMEBUF(fileName);
  if (McuUtility_ReadEscapedName(cmd+sizeof("printhex"), (uint8_t*)McuFatFS_PTR_NAMEBUF(fileName),
        McuFatFS_SIZE_NAMEBUF(fileName), NULL, NULL, NULL)==ERR_OK
     )
  {
    res = McuFatFS_PrintHexFile((uint8_t*)McuFatFS_PTR_NAMEBUF(fileName), io);
  } else {
    CmdUsageError(cmd, (unsigned char*)"printhex fileName", io);
    res = ERR_FAILED;
  }
  McuFatFS_FREE_NAMEBUF(fileName);
  return res;
}

static uint8_t CdCmd(const unsigned char *cmd, const McuShell_ConstStdIOType *io) {
  /* precondition: cmd starts with "cd" */
#if McuFatFS_FS_RPATH > 0
  uint8_t res = ERR_OK;
  McuFatFS_DEF_NAMEBUF(fileName);

  McuFatFS_INIT_NAMEBUF(fileName);
  if (*(cmd+sizeof("cd")-1)== ' ') { /* space after "cd": read name */
    if (McuUtility_ReadEscapedName(cmd+sizeof("cd"), (uint8_t*)McuFatFS_PTR_NAMEBUF(fileName),
          McuFatFS_SIZE_NAMEBUF(fileName), NULL, NULL, NULL)==ERR_OK
       )
    {
      res = McuFatFS_ChangeDirectory((uint8_t*)McuFatFS_PTR_NAMEBUF(fileName), io);
    } else {
      McuShell_SendStr((unsigned char*)"reading directory name failed!\r\n", io->stdErr);
      res = ERR_FAILED;
    }
  } else { /* print current directory */
#if McuFatFS_FS_RPATH >= 2
    McuFatFS_FRESULT fres;

    fres = McuFatFS_getcwd((TCHAR*)McuFatFS_PTR_NAMEBUF(fileName), McuFatFS_SIZE_NAMEBUF(fileName));
    if(fres!=FR_OK) {
      FatFsFResultMsg((unsigned char*)"getcwd failed", fres, io);
      res = ERR_FAILED;
    } else {
      McuShell_SendStr((uint8_t*)McuFatFS_PTR_NAMEBUF(fileName), io->stdOut);
      McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
    }
#else
    McuUtility_strcpy(McuFatFS_PTR_NAMEBUF(fileName), McuFatFS_SIZE_NAMEBUF(fileName), (unsigned char*)McuFatFS_CONFIG_DEFAULT_DRIVE_STRING); /* use root */
#endif
  }
  McuFatFS_FREE_NAMEBUF(fileName);
  return res;
#else
  (void)cmd;
  #warning "relative directories not enabled in FatFS!"
  McuShell_SendStr((unsigned char*)"relative directories not available in file system\r\n", io->stdErr);
  return ERR_FAILED;
#endif
}


static uint8_t CreateCmd(const unsigned char *cmd, const McuShell_ConstStdIOType *io) {
  /* precondition: cmd starts with "create" */
  uint8_t res = ERR_OK;
  McuFatFS_DEF_NAMEBUF(fileName);

  McuFatFS_INIT_NAMEBUF(fileName);
  if (McuUtility_ReadEscapedName(cmd+sizeof("create"), (uint8_t*)McuFatFS_PTR_NAMEBUF(fileName),
        McuFatFS_SIZE_NAMEBUF(fileName), NULL, NULL, NULL)==ERR_OK
     )
  {
    res = McuFatFS_CreateFile((uint8_t*)McuFatFS_PTR_NAMEBUF(fileName), io);
  } else {
    CmdUsageError(cmd, (unsigned char*)"create fileName", io);
    res = ERR_FAILED;
  }
  McuFatFS_FREE_NAMEBUF(fileName);
  return res;
}

static uint8_t PrintStatus(const McuShell_StdIOType *io) {
  McuShell_SendStatusStr((unsigned char*)"McuFatFS", (unsigned char*)"FatFs status\r\n", io->stdOut);
  McuShell_SendStatusStr((unsigned char*)"  present", McuFatFS_isDiskPresent((uint8_t*)"0")?(unsigned char*)"drive0: yes\r\n":(unsigned char*)"drive0: no\r\n", io->stdOut);
  McuShell_SendStatusStr((unsigned char*)"  protected", McuFatFS_isWriteProtected((uint8_t*)"0")?(unsigned char*)"drive0: yes\r\n":(unsigned char*)"drive0: no\r\n", io->stdOut);
  McuShell_SendStatusStr((unsigned char*)"  default drv", (unsigned char*)McuFatFS_CONFIG_DEFAULT_DRIVE_STRING "\r\n", io->stdOut);
  return ERR_OK;
}

static uint8_t PrintHelp(const McuShell_StdIOType *io) {
  McuShell_SendHelpStr((unsigned char*)"McuFatFS", (unsigned char*)"Group of McuFatFS commands\r\n", io->stdOut);
  McuShell_SendHelpStr((unsigned char*)"  help|status", (unsigned char*)"Print help or status information\r\n", io->stdOut);
  McuShell_SendHelpStr((unsigned char*)"  cd [<directoryName>]", (const unsigned char*)"Change the current directory or display the name of the current directory\r\n", io->stdOut);
  McuShell_SendHelpStr((unsigned char*)"  dir [<directoryName>]", (const unsigned char*)"Prints a directory\r\n", io->stdOut);
  McuShell_SendHelpStr((unsigned char*)"  copy <src> <dst>", (const unsigned char*)"Copy a file\r\n", io->stdOut);
  McuShell_SendHelpStr((unsigned char*)"  delete <filename>", (const unsigned char*)"Delete a file\r\n", io->stdOut);
  McuShell_SendHelpStr((unsigned char*)"  create <filename>", (const unsigned char*)"Create a file\r\n", io->stdOut);
  McuShell_SendHelpStr((unsigned char*)"  mkdir <directory>", (const unsigned char*)"Create a directory\r\n", io->stdOut);
  McuShell_SendHelpStr((unsigned char*)"  rename <src> <dst>", (const unsigned char*)"Rename a file\r\n", io->stdOut);
  McuShell_SendHelpStr((unsigned char*)"  print <filename>", (const unsigned char*)"Print a file\r\n", io->stdOut);
  McuShell_SendHelpStr((unsigned char*)"  printhex <filename>", (const unsigned char*)"Print a file as hexdump\r\n", io->stdOut);
  McuShell_SendHelpStr((unsigned char*)"  printsector <number>", (const unsigned char*)"Print disk sector\r\n", io->stdOut);
  McuShell_SendHelpStr((unsigned char*)"  diskinfo", (const unsigned char*)"Print disk information\r\n", io->stdOut);
  McuShell_SendHelpStr((unsigned char*)"  benchmark", (const unsigned char*)"Run disk benchmark\r\n", io->stdOut);
  return ERR_OK;
}


/* Unicode support functions */
#if McuFatFS_USE_LFN  /* Unicode - OEM code conversion */
#if McuFatFS_USE_LFN == 3   /* Memory functions */
void *ff_memalloc(UINT size) { /* Allocate memory block */
  /* FreeRTOS */
  return McuRTOS_pvPortMalloc(size);
}

void ff_memfree (void* ptr) {  /* Free memory block */
  /* FreeRTOS */
  McuRTOS_vPortFree(ptr);
}
#endif
#endif

#if McuFatFS_FS_REENTRANT
/*!
* \brief Create a Synchronization Object
* This function is called in f_mount function to create a new
* synchronization object, such as semaphore and mutex. When a FALSE is
* returned, the f_mount function fails with FR_INT_ERR.
* \param[in] vol Corresponding logical drive being processed
* \param[out] sobj Pointer to return the created sync object
* \return TRUE: Function succeeded, FALSE: Could not create due to any error
*/
#if configSUPPORT_STATIC_ALLOCATION
  static StaticSemaphore_t xMutexBuffer[FF_VOLUMES];
#endif

int ff_cre_syncobj(uint8_t vol, McuFatFS_SYNC_t *sobj) {
  (void)vol; /* argument not used */
#if configSUPPORT_STATIC_ALLOCATION
  *sobj = xSemaphoreCreateMutexStatic(&xMutexBuffer[vol]);
#else
  *sobj = xSemaphoreCreateMutex(); /* create semaphore */
#endif
  if (*sobj!=NULL) {
    vQueueAddToRegistry(*sobj, "McuFatFS_Mutex");
  }
  return (*sobj != NULL) ? TRUE : FALSE;
}

/*!
* \brief Delete a Synchronization Object
* This function is called in f_mount function to delete a synchronization
* object that created with ff_cre_syncobj function. When a FALSE is
* returned, the f_mount function fails with FR_INT_ERR.
* \param[out] sobj Sync object tied to the logical drive to be deleted
* \return TRUE: Function succeeded, FALSE: Could not create due to any error
*/
int ff_del_syncobj(McuFatFS_SYNC_t sobj) {
  vQueueUnregisterQueue(sobj);
  McuRTOS_vSemaphoreDelete(sobj); /* FreeRTOS: free up memory for semaphore */
  return TRUE; /* everything ok */
}

/*!
* \brief Request Grant to Access the Volume
* This function is called on entering file functions to lock the volume.
* When a FALSE is returned, the file function fails with FR_TIMEOUT.
* \param[in] sobj Sync object to wait
* \return TRUE: Function succeeded, FALSE: Could not create due to any error
*/
int ff_req_grant (McuFatFS_SYNC_t sobj) {
  if (McuRTOS_xSemaphoreTake(sobj, McuFatFS_FS_TIMEOUT) == pdTRUE) {
    return TRUE; /* success */
  } else {  /* failed to get the sync object? */
    return FALSE; /* failure */
  }
}

/*!
* \brief Release Grant to Access the Volume
* This function is called on leaving file functions to unlock the volume.
* \param[in] sobj Sync object to be signaled
*/
void ff_rel_grant (McuFatFS_SYNC_t sobj) {
  (void)McuRTOS_xSemaphoreGive(sobj); /* FreeRTOS */
}
#endif /* McuFatFS_FS_REENTRANT */

/*
** ===================================================================
**     Method      :  open (component FAT_FileSystem)
**
**     Description :
**         Open/Create a file
**     Parameters  :
**         NAME            - DESCRIPTION
**         fp              - Pointer to the blank file object structure
**         path            - Pointer to a null-terminated string that
**                           specifies the file name to create or open.
**         mode            - Specifies the type of access and open
**                           method for the file. It is specified by a
**                           combination of following flags.
**                           FA_READ: Specifies read access to the
**                           object. Data can be read from the file.
**                           Combine with FA_WRITE for read-write access.
**                           FA_WRITE: Specifies write access to the
**                           object. Data can be written to the file.
**                           Combine with FA_READ for read-write access.
**                           FA_OPEN_EXISTING: Opens the file. The
**                           function fails if the file is not existing.
**                           (Default)
**                           FA_OPEN_ALWAYS: Opens the file if it is
**                           existing. If not, a new file is created. To
**                           append data to the file, use f_lseek
**                           function after file open in this method.
**                           FA_CREATE_NEW: Creates a new file. The
**                           function fails if the file is already
**                           existing.
**                           FA_CREATE_ALWAYS: Creates a new file. If
**                           the file is existing, it is truncated and
**                           overwritten.
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_open(FIL *fp, const XCHAR *path, BYTE mode)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  close (component FAT_FileSystem)
**
**     Description :
**         Close a file
**     Parameters  :
**         NAME            - DESCRIPTION
**         fp              - Pointer to the open file object structure to
**                           be closed.
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_close(FIL *fp)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  read (component FAT_FileSystem)
**
**     Description :
**         Read file
**     Parameters  :
**         NAME            - DESCRIPTION
**         fp              - Pointer to the file object
**         buff            - Pointer to the data buffer
**         btr             - Number of bytes to read
**         br              - Pointer to the number of bytes read
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_read(FIL *fp, void *buff, UINT btr, UINT *br)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  write (component FAT_FileSystem)
**
**     Description :
**         Write to a file
**     Parameters  :
**         NAME            - DESCRIPTION
**         fp              - Pointer to the file object structure 
**         buff            - Pointer to the data to be written
**         btw             - Number of bytes to write
**         bw              - Pointer to the variable to return number of
**                           bytes written
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_write(FIL *fp, const *void buff, UINT btw, UINT *bw)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  opendir (component FAT_FileSystem)
**
**     Description :
**         Open a directory
**     Parameters  :
**         NAME            - DESCRIPTION
**         dj              - Pointer to the blank directory object to be
**                           created.
**         path            - Pointer to the null-terminated string
**                           that specifies the directory name to be
**                           opened.
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_opendir(DIR *dj, const XCHAR *path)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  readdir (component FAT_FileSystem)
**
**     Description :
**         Read a directory item
**     Parameters  :
**         NAME            - DESCRIPTION
**         dir             - Pointer to the open directory object
**         fno             - Pointer to the file information structure
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_readdir(DIR *dj, FILINFO *fno)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  lseek (component FAT_FileSystem)
**
**     Description :
**         The f_lseek function moves the file read/write pointer of an
**         open file object. It can also be used to increase the file
**         size (cluster pre-allocation).
**     Parameters  :
**         NAME            - DESCRIPTION
**         fp              - Pointer to the file object
**         ofs             - File pointer from top of file
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_lseek(FIL *fp, DWORD ofs)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  unlink (component FAT_FileSystem)
**
**     Description :
**         Remove a file or directory
**     Parameters  :
**         NAME            - DESCRIPTION
**         path            - Pointer to the object name
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_unlink(const XCHAR *path)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  mount (component FAT_FileSystem)
**
**     Description :
**         Mount/unmount a logical drive
**     Parameters  :
**         NAME            - DESCRIPTION
**         fs              - pointer to the new file system object (NULL
**                           for unmount)
**       * path            - Logical drive number to be
**                           mounted/unmounted
**         opt             - options: 0:Do not mount (delayed mount), 1:
**                           Mount immediately
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_mount(FATFS *fs, const TCHAR* path, uint8_t opt)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  getfree (component FAT_FileSystem)
**
**     Description :
**         Get Number of Free Clusters
**     Parameters  :
**         NAME            - DESCRIPTION
**         path            - Pointer to the logical drive number
**                           (root dir)
**       * nclst           - Pointer to the variable to return
**                           number of free clusters
**         fatfs           - Pointer to pointer to file system
**                           object
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_getfree(const XCHAR *path, dword *nclst, FATFS **fatfs)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  sync (component FAT_FileSystem)
**
**     Description :
**         Flush cached data of a writing file
**     Parameters  :
**         NAME            - DESCRIPTION
**         fp              - Pointer to the file object
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_sync(FIL *fp)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  rename (component FAT_FileSystem)
**
**     Description :
**         Delete a file or directory
**     Parameters  :
**         NAME            - DESCRIPTION
**         path_old        - Pointer to old object name
**         path_new        - Pointer to new object name
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_rename(const XCHAR *path_old, const XCHAR *path_new)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  mkdir (component FAT_FileSystem)
**
**     Description :
**         Creates a directory
**     Parameters  :
**         NAME            - DESCRIPTION
**         path            - Name of directory to be created
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_mkdir(const XCHAR *path)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  chmod (component FAT_FileSystem)
**
**     Description :
**         Changes the attribute of a file or directory
**         Following attribute flags to be set in one or more
**         combination of the following flags. The specified flags are
**         set and others are cleared.
**         AM_RDO	Read only
**         AM_ARC	Archive
**         AM_SYS	System
**         AM_HID	Hidden
**     Parameters  :
**         NAME            - DESCRIPTION
**         FileName        - Pointer to the file or directory
**         Attribute       - Attribute flags. Attribute
**                           flags to be set in one or more combination
**                           of the following flags. The specified flags
**                           are set and others are cleard.
**         AttributeMask   - Attribute mask.
**                           Attribute mask that specifies which
**                           attribute is changed. The specified
**                           aattributes are set or cleard.
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_chmod(const TCHAR* FileName, uint8_t Attribute, uint8_t AttributeMask)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  truncate (component FAT_FileSystem)
**
**     Description :
**         Truncates the file size.
**         The truncate() function truncates the file size to the
**         current file read/write point. This function has no effect
**         if the file read/write pointer is already pointing end of
**         the file.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * FileObject      - Pointer to the file object
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_truncate(FIL *FileObject)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  stat (component FAT_FileSystem)
**
**     Description :
**         The f_stat gets the information of a file or directory. For
**         details of the infomation, refer to the FILINFO structure
**         and f_readdir function. This function is not supported in
**         minimization level of >= 1.
**     Parameters  :
**         NAME            - DESCRIPTION
**         FileName        - Pointer to the file or directory
**                           path
**       * FileInfo        - Pointer to the FILINFO structure
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_stat(const TCHAR* FileName, FILINFO* FileInfo)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  utime (component FAT_FileSystem)
**
**     Description :
**         The f_utime function changes the timestamp of a file or
**         directory
**     Parameters  :
**         NAME            - DESCRIPTION
**       * FileName        - Pointer to the file or directory
**                           path
**       * TimeDate        - Pointer to time and data to be
**                           set
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_utime(const TCHAR* FileName, const FILINFO* TimeDate)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  mkfs (component FAT_FileSystem)
**
**     Description :
**         The f_mkfs fucntion creates a file system on the drive.
**     Parameters  :
**         NAME            - DESCRIPTION
**         drive           - Logical drive number
**         PartitioningRule - When 0 is given,
**                           a partition table is created into the
**                           master boot record and a primary DOS
**                           partition is created and then an FAT volume
**                           is created on the partition. This is called
**                           FDISK format and used for harddisk and
**                           memory cards. When 1 is given, the FAT
**                           volume starts from the first sector on the
**                           drive without partition table. This is
**                           called SFD format and used for floppy disk
**                           and most optical disk
**         AllocSize       - Size of the allocation unit.
**                           Force the allocation unit (cluster) size in
**                           unit of byte. The value must be power of 2
**                           and between the sector size and 128 times
**                           sector size. When invalid value is
**                           specified, the cluster size is determined
**                           depends on the volume size.
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_mkfs(uint8_t drive, uint8_t PartitioningRule, UINT  AllocSize)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  chdir (component FAT_FileSystem)
**
**     Description :
**         The f_chdir function changes the current directory of the
**         logical drive. The current directory of a drive is
**         initialized to the root directory when the drive is
**         auto-mounted. Note that the current directory is retained in
**         the each file system object so that it also affects other
**         tasks that using the drive.
**     Parameters  :
**         NAME            - DESCRIPTION
**         Path            - Pointer to the path name
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_chdir(const TCHAR* Path)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  chdrive (component FAT_FileSystem)
**
**     Description :
**         The f_chdrive function changes the current drive. The
**         initial value of the current drive number is 0. Note that
**         the current drive is retained in a static variable so that
**         it also affects other tasks that using the file functions.
**     Parameters  :
**         NAME            - DESCRIPTION
**         Drive           - Logical drive number
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_chdrive(uint8_t Drive)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  getcwd (component FAT_FileSystem)
**
**     Description :
**         The f_getcwd function retrieves the current directory of the
**         current drive in full path string including drive number.
**     Parameters  :
**         NAME            - DESCRIPTION
**         Buffer          - Pointer to the buffer to receive the
**                           current directory string.
**         BufferLen       - Size of the buffer in unit of
**                           TCHAR.
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_getcwd(TCHAR* Buffer, UINT BufferLen)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  errFResultMsg (component FAT_FileSystem)
**
**     Description :
**         Returns for a given FatFs FRESULT error code a string
**     Parameters  :
**         NAME            - DESCRIPTION
**         errNo           - FatFs error code
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
char* McuFatFS_errFResultMsg(int errNo)
{
  switch(errNo) {
    case FR_OK:                  return (char*)"Succeeded";
    case FR_DISK_ERR:            return (char*)"A hard error occurred in the low level disk I/O layer";
    case FR_INT_ERR:             return (char*)"Assertion failed";
    case FR_NOT_READY:           return (char*)"The physical drive cannot work";
    case FR_NO_FILE:             return (char*)"Could not find the file";
    case FR_NO_PATH:             return (char*)"Could not find the path";
    case FR_INVALID_NAME:        return (char*)"The path name format is invalid";
    case FR_DENIED:              return (char*)"Access denied due to prohibited access or directory full";
    case FR_EXIST:               return (char*)"Access denied due to prohibited access";
    case FR_INVALID_OBJECT:      return (char*)"The file/directory object is invalid";
    case FR_WRITE_PROTECTED:     return (char*)"The physical drive is write protected";
    case FR_INVALID_DRIVE:       return (char*)"The logical drive number is invalid";
    case FR_NOT_ENABLED:         return (char*)"The volume has no work area";
    case FR_NO_FILESYSTEM:       return (char*)"There is no valid FAT volume on the physical drive";
    case FR_MKFS_ABORTED:        return (char*)"The f_mkfs() aborted due to any parameter error";
    case FR_TIMEOUT:             return (char*)"Could not get a grant to access the volume within defined period";
    case FR_LOCKED:              return (char*)"The operation is rejected according to the file sharing policy";
    case FR_NOT_ENOUGH_CORE:     return (char*)"LFN working buffer could not be allocated";
    case FR_TOO_MANY_OPEN_FILES: return (char*)"Number of open files > _FS_SHARE";
    /*default:                     return (char*)"unknown";*/
  } /* switch */
  return (char*)"*error*";
}

/*
** ===================================================================
**     Method      :  errDResultMsg (component FAT_FileSystem)
**
**     Description :
**         Returns for a given FatFs DRESULT error code a string
**     Parameters  :
**         NAME            - DESCRIPTION
**         errNo           - FatFs error code
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
char* McuFatFS_errDResultMsg(int errNo)
{
  switch(errNo) {
    case RES_OK:     return (char*)"Successful";
    case RES_ERROR:  return (char*)"R/W Error";
    case RES_WRPRT:  return (char*)"Write Protected";
    case RES_NOTRDY: return (char*)"Not Ready";
    case RES_PARERR: return (char*)"Invalid Parameter";
    /*default:         return (char*)"unknown";*/
  } /* switch */
  return (char*)"*error*";
}

/*
** ===================================================================
**     Method      :  isWriteProtected (component FAT_FileSystem)
**
**     Description :
**         Determines if the file system is write protected.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * drvStr          - Pointer to drive string, e.g. "" or
**                           "0"
**     Returns     :
**         ---             - TRUE if file system is write protected
** ===================================================================
*/
bool McuFatFS_isWriteProtected(uint8_t *drvStr)
{
#if McuLib_CONFIG_SDK_VERSION_USED == McuLib_CONFIG_SDK_PROCESSOR_EXPERT
  uint8_t drv;

  drv = McuFatFS_StrToDriveNumber(drvStr);
  switch(drv) {
    case 0:
      return SDCard_isWriteProtected();
    default:
      break;
  } /* switch */
  return TRUE;
#else
  return McuFatFS_CONFIG_IS_WRITE_PROTECTED_CALLBACK(drvStr);
#endif
}

/*
** ===================================================================
**     Method      :  isDiskPresent (component FAT_FileSystem)
**
**     Description :
**         Determines if the disk is present or not (e.g. disk inserted).
**     Parameters  :
**         NAME            - DESCRIPTION
**       * drvStr          - drive string, "" or "0" or "1"
**     Returns     :
**         ---             - TRUE if file system is write protected
** ===================================================================
*/
bool McuFatFS_isDiskPresent(uint8_t *drvStr)
{
#if McuLib_CONFIG_SDK_VERSION_USED == McuLib_CONFIG_SDK_PROCESSOR_EXPERT
  uint8_t drv;

  drv = McuFatFS_StrToDriveNumber(drvStr);
  switch(drv) {
    case 0:
      return SDCard_CardPresent();
    default:
      break;
  } /* switch */
  return FALSE;
#else
  return McuFatFS_CONFIG_IS_DISK_PRESENT_CALLBACK(drvStr);
#endif
}

/*
** ===================================================================
**     Method      :  f_gets (component FAT_FileSystem)
**
**     Description :
**         Get a string from the file
**     Parameters  :
**         NAME            - DESCRIPTION
**       * buff            - Pointer to the string buffer to read 
**         len             - Size of string buffer (characters)
**         fil             - Pointer to the file object
**     Returns     :
**         ---             - zero (NULL) if failed, otherwise a string
**                           to the buffer is returned.
** ===================================================================
*/
/*
McuFatFS_CHARP McuFatFS_f_gets(TCHAR* buff, int len, FIL *fil)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  f_puts (component FAT_FileSystem)
**
**     Description :
**         Put a string to the file
**     Parameters  :
**         NAME            - DESCRIPTION
**       * buff            - A character to be output
**         fil             - Pointer to the file object
**     Returns     :
**         ---             - number of characters written.
** ===================================================================
*/
/*
McuFatFS_INT McuFatFS_f_puts(const TCHAR* str, FIL *fil)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  f_putc (component FAT_FileSystem)
**
**     Description :
**         Put a character to the file
**     Parameters  :
**         NAME            - DESCRIPTION
**       * buff            - A character to be output
**         fil             - Pointer to the file object
**     Returns     :
**         ---             - 1 if ok, EOF otherwise
** ===================================================================
*/
/*
McuFatFS_INT McuFatFS_f_putc(TCHAR c, FIL *fil)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  f_printf (component FAT_FileSystem)
**
**     Description :
**         Put a formatted string to the file
**     Parameters  :
**         NAME            - DESCRIPTION
**         Variable_1      - Pointer to the file object
**         str             - Pointer to the format string
**         Variable_2      - Optional arguments...
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
McuFatFS_INT McuFatFS_f_printf(FIL* fil, const TCHAR* str, ...)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  f_eof (component FAT_FileSystem)
**
**     Description :
**         Wrapper to to the f_eof() macro. Returns 1 if at the end of
**         the file, 0 otherwise.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * fp              - Pointer to file object
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
uint8_t McuFatFS_f_eof(FIL *fil)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  f_error (component FAT_FileSystem)
**
**     Description :
**         Wrapper to to the f_eof() macro. Returns 1 if at the end of
**         the file, 0 otherwise.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * fp              - Pointer to file object
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
uint8_t McuFatFS_f_error(FIL *fil)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  f_tell (component FAT_FileSystem)
**
**     Description :
**         Wrapper to to the f_tell() macro. Returns the file
**         read/write pointer (0 on file open).
**     Parameters  :
**         NAME            - DESCRIPTION
**       * fp              - Pointer to file object
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
dword McuFatFS_f_tell(FIL *fil)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  f_size (component FAT_FileSystem)
**
**     Description :
**         Wrapper to to the f_size() macro. Returns the file size.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * fp              - Pointer to file object
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
dword McuFatFS_f_size(FIL *fil)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  Init (component FAT_FileSystem)
**
**     Description :
**         Initializes the device driver.
**     Parameters  : None
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuFatFS_Init(void)
{
  uint8_t res = ERR_OK;
#if McuLib_CONFIG_SDK_VERSION_USED == McuLib_CONFIG_SDK_PROCESSOR_EXPERT
  if (SDCard_Init(NULL)!=ERR_OK) {
    res = ERR_FAILED;
  }
#endif
  return res;
}

/*
** ===================================================================
**     Method      :  Deinit (component FAT_FileSystem)
**
**     Description :
**         Deinitializes the driver.
**     Parameters  : None
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuFatFS_Deinit(void)
{
  uint8_t res = ERR_OK;
#if McuLib_CONFIG_SDK_VERSION_USED == McuLib_CONFIG_SDK_PROCESSOR_EXPERT
  if (SDCard_Deinit(NULL)!=ERR_OK) {
    res = ERR_FAILED;
  }
#endif
  return res;
}


#if !McuFatFS_FS_READONLY
/*
** ===================================================================
**     Method      :  get_fattime (component FAT_FileSystem)
**
**     Description :
**         Returns the current time
**     Parameters  : None
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint32_t McuFatFS_get_fattime(void)
{
  /* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */
  /* 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */
  /* Pack date and time into a uint32_t variable */
  TIMEREC time;
  DATEREC date;
  uint8_t res;

  res = McuTimeDate_GetTime(&time);     /* get time information */
  if (res!=ERR_OK) {
    return 0; /* failed */
  }
  res = McuTimeDate_GetDate(&date);     /* get date information */
  if (res!=ERR_OK) {
    return 0; /* failed */
  }
  return ((uint32_t)(date.Year - 1980) << 25)
       | ((uint32_t)date.Month << 21)
       | ((uint32_t)date.Day << 16)
       | ((uint32_t)time.Hour << 11)
       | ((uint32_t)time.Min << 5)
       | ((uint32_t)time.Sec);
}

uint32_t get_fattime(void) {
  return McuFatFS_get_fattime();
}
#endif /* !McuFatFS_FS_READONLY */


/*
** ===================================================================
**     Method      :  ParseCommand (component FAT_FileSystem)
**
**     Description :
**         Shell Command Line parser. This method is enabled/disabled
**         depending on if you have the Shell enabled/disabled in the
**         properties.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * cmd             - Pointer to command string
**       * handled         - Pointer to variable which tells if
**                           the command has been handled or not
**       * io              - Pointer to I/O structure
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuFatFS_ParseCommand(const unsigned char *cmd, bool *handled, const McuShell_StdIOType *io)
{
  if (McuUtility_strcmp((char*)cmd, McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, "McuFatFS help")==0) {
    *handled = TRUE;
    return PrintHelp(io);
  } else if ((McuUtility_strcmp((char*)cmd, McuShell_CMD_STATUS)==0) || (McuUtility_strcmp((char*)cmd, "McuFatFS status")==0)) {
    *handled = TRUE;
    return PrintStatus(io);
  } else if (McuUtility_strncmp((char*)cmd, "McuFatFS cd", sizeof("McuFatFS cd")-1)==0) {
    *handled = TRUE;
    return CdCmd(cmd+sizeof("McuFatFS"), io);
  } else if (McuUtility_strncmp((char*)cmd, "McuFatFS dir", sizeof("McuFatFS dir")-1)==0) {
    *handled = TRUE;
    return DirCmd(cmd+sizeof("McuFatFS"), io);
  } else if (McuUtility_strncmp((char*)cmd, "McuFatFS copy ", sizeof("McuFatFS copy ")-1)==0) {
    *handled = TRUE;
    return CopyCmd(cmd+sizeof("McuFatFS"), io);
  } else if (McuUtility_strncmp((char*)cmd, "McuFatFS delete ", sizeof("McuFatFS delete ")-1)==0) {
    *handled = TRUE;
    return DeleteCmd(cmd+sizeof("McuFatFS"), io);
  } else if (McuUtility_strncmp((char*)cmd, "McuFatFS create ", sizeof("McuFatFS create ")-1)==0) {
    *handled = TRUE;
    return CreateCmd(cmd+sizeof("McuFatFS"), io);
  } else if (McuUtility_strncmp((char*)cmd, "McuFatFS mkdir ", sizeof("McuFatFS mkdir ")-1)==0) {
    *handled = TRUE;
    return MkdirCmd(cmd+sizeof("McuFatFS"), io);
  } else if (McuUtility_strncmp((char*)cmd, "McuFatFS rename ", sizeof("McuFatFS rename ")-1)==0) {
    *handled = TRUE;
    return RenameCmd(cmd+sizeof("McuFatFS"), io);
  } else if (McuUtility_strncmp((char*)cmd, "McuFatFS printhex ", sizeof("McuFatFS printhex ")-1)==0) {
    *handled = TRUE;
    return PrintHexCmd(cmd+sizeof("McuFatFS"), io);
  } else if (McuUtility_strncmp((char*)cmd, "McuFatFS printsector ", sizeof("McuFatFS printsector ")-1)==0) {
    *handled = TRUE;
    return SectorCmd(cmd+sizeof("McuFatFS"), io);
  } else if (McuUtility_strncmp((char*)cmd, "McuFatFS print ", sizeof("McuFatFS print ")-1)==0) {
    *handled = TRUE;
    return PrintCmd(cmd+sizeof("McuFatFS"), io);
  } else if (McuUtility_strcmp((char*)cmd, "McuFatFS diskinfo")==0) {
    *handled = TRUE;
    return McuFatFS_PrintDiskInfo((unsigned char*)McuFatFS_CONFIG_DEFAULT_DRIVE_STRING, io);
  } else if (McuUtility_strcmp((char*)cmd, "McuFatFS benchmark")==0) {
    *handled = TRUE;
    return McuFatFS_Benchmark(io);
  }
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  CheckCardPresence (component FAT_FileSystem)
**
**     Description :
**         This method checks if card has been inserted or removed and
**         mounts or unmounts the file system.
**     Parameters  :
**         NAME            - DESCRIPTION
**       * cardMounted     - Pointer to initialize this
**                           variable to FALSE on the caller side the
**                           first time.
**       * drive           - drive string, or ""
**       * fileSystemObject - Pointer to file
**                           system object
**         io              - Pointer to io handler to be used. Can be
**                           NULL, then no messages are written.
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuFatFS_CheckCardPresence(bool *cardMounted, uint8_t *drive, FATFS *fileSystemObject, const McuShell_StdIOType *io)
{
  if (drive==NULL) { /* backward compatibility with drive numbers before (which could be zero or NULL) */
    drive = (unsigned char*)McuFatFS_CONFIG_DEFAULT_DRIVE_STRING;
  }
  if (!(*cardMounted) && McuFatFS_isDiskPresent(drive)) {
    /* card inserted */
    #if McuFatFS_CONFIG_CARD_INSERT_DELAY_TIME_MS>0
        McuWait_WaitOSms(McuFatFS_CONFIG_CARD_INSERT_DELAY_TIME_MS); /* give card some time to power up */
    #endif
    if (McuFatFS_MountFileSystem(fileSystemObject, drive, io)==ERR_OK) {
      *cardMounted = TRUE;
      if (io!=NULL) {
        McuShell_SendStr((unsigned char*)"File System mounted\r\n", io->stdOut);
      }
  #if (McuFatFS_FS_RPATH >= 1U && McuFatFS_VOLUMES >= 2)
      if (f_chdrive((char const *)drive)!=FR_OK) { /* change default drive */
        return ERR_FAILED;
      }
  #endif
    } else {
      return ERR_FAILED;
    }
  } else if (*cardMounted && !McuFatFS_isDiskPresent(drive)) {
    /* card removed */
    if (McuFatFS_UnMountFileSystem(drive, io)==ERR_OK) {
      *cardMounted = FALSE;
      if (io!=NULL) {
        McuShell_SendStr((unsigned char*)"File System unmounted\r\n", io->stdOut);
      }
    } else {
      return ERR_FAILED;
    }
  }
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  MountFileSystem (component FAT_FileSystem)
**
**     Description :
**         Mounts a file system
**     Parameters  :
**         NAME            - DESCRIPTION
**       * fileSystemObject - Pointer to a
**                           file system object
**       * logicalDrive    - Pointer to the drive
**                           string, or ""
**       * io              - Pointer to I/O handler
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*!
 * \brief Mounts the file system for a drive
 * \param[in] fileSystemObject Pointer to the file system object
 * \param[in] logicalDrive The drive number to be used
 * \param[in] io IO handler for output
 * \return Error code, ERR_OK for success.
 */
uint8_t McuFatFS_MountFileSystem(FATFS *fileSystemObject, uint8_t *logicalDrive, const McuShell_StdIOType *io)
{
  McuFatFS_FRESULT fres;

  fres = McuFatFS_mount(fileSystemObject, (const TCHAR*)logicalDrive, 1 /* 1: mount immediately; 0: delayed mount */);
  if (fres != FR_OK) {
    if (io!=NULL) {
      FatFsFResultMsg((unsigned char*)"mount failed", fres, io);
    }
    return ERR_FAILED;
  }
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  UnMountFileSystem (component FAT_FileSystem)
**
**     Description :
**         Mounts a file system
**     Parameters  :
**         NAME            - DESCRIPTION
**       * logicalDrive    - Pointer to the drive
**                           string, or ""
**       * io              - Pointer to I/O handler
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*!
 * \brief Unmounts the file system for a drive
 * \param[in] logicalDrive The drive number to be used
 * \param[in] io IO handler for output
 * \return Error code, ERR_OK for success.
 */
uint8_t McuFatFS_UnMountFileSystem(uint8_t *logicalDrive, const McuShell_StdIOType *io)
{
  McuFatFS_FRESULT fres;

  fres = McuFatFS_mount(NULL, (const TCHAR*)logicalDrive, 0);
  if (fres != FR_OK) {
    if (io!=NULL) {
      FatFsFResultMsg((unsigned char*)"unmount failed", fres, io);
    }
    return ERR_FAILED;
  }
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  PrintDirectory (component FAT_FileSystem)
**
**     Description :
**         Prints a directory
**     Parameters  :
**         NAME            - DESCRIPTION
**         dirName         - Directory folder to be printed
**         io              - Pointer to I/O handler
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*!
 * \brief Prints a directory listing
 * \param[in] dirName Directory to be used
 * \param[in] io IO handler for output
 * \return Error code, ERR_OK for success.
 */
uint8_t McuFatFS_PrintDirectory(const uint8_t *dirName, const McuShell_StdIOType *io)
{
  uint8_t res;

  res = PrintDir(dirName, io);
  if (res != ERR_OK) {
    McuShell_SendStr((unsigned char*)"PrintDir failed\r\n", io->stdErr);
    return res;
  }
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  CopyFile (component FAT_FileSystem)
**
**     Description :
**         Copy a file
**     Parameters  :
**         NAME            - DESCRIPTION
**         srcFileName     - Source file name
**         dstFileName     - Destination file name
**         io              - Pointer to I/O handler
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*!
 * \brief Copy the source file to a destination file
 * \param[in] srcFileName Source file name
 * \param[in] dstFileName Destination file name
 * \param[in] io IO handler for output
 * \return Error code, ERR_OK for success.
 */
uint8_t McuFatFS_CopyFile(const uint8_t*srcFileName, const uint8_t *dstFileName, const McuShell_StdIOType *io)
{
#if !McuFatFS_FS_READONLY
  McuFatFS_FIL fsrc, fdst;  /* file objects */
  McuFatFS_FRESULT fres;
  uint8_t buffer[32];   /* copy buffer */
  UINT br, bw;          /* file read/write counters */
  uint8_t res =  ERR_OK;

  if (McuFatFS_isWriteProtected((uint8_t*)McuFatFS_CONFIG_DEFAULT_DRIVE_STRING)) {
    McuShell_SendStr((unsigned char*)"disk is write protected!\r\n", io->stdErr);
    return ERR_FAILED;
  }
  /* open source file */
  fres = McuFatFS_open(&fsrc, (const TCHAR*)srcFileName, FA_OPEN_EXISTING | FA_READ);
  if (fres != FR_OK) {
    FatFsFResultMsg((unsigned char*)"open source file failed", fres, io);
    return ERR_FAILED;
  }
  /* create destination file */
  fres = McuFatFS_open(&fdst, (const TCHAR*)dstFileName, FA_CREATE_ALWAYS | FA_WRITE);
  if (fres != FR_OK) {
    (void)McuFatFS_close(&fsrc); /* close the source file which we have open */
    FatFsFResultMsg((unsigned char*)"open destination file failed", fres, io);
    return ERR_FAILED;
  }
  /* now copy source to destination */
  for (;;) {
    fres = McuFatFS_read(&fsrc, buffer, sizeof(buffer), &br);
    if (fres != FR_OK) {
      FatFsFResultMsg((unsigned char*)"reading source file failed", fres, io);
      res = ERR_FAILED;
      break;
    }
    if (br == 0) { /* EOF */
      break; /* get out of loop */
    }
    fres = McuFatFS_write(&fdst, buffer, br, &bw);
    if (fres != FR_OK) {
      FatFsFResultMsg((unsigned char*)"writing destination file failed", fres, io);
      res = ERR_FAILED;
      break;
    }
    if (bw < br) {
      McuShell_SendStr((unsigned char*)"failed writing destination file, or disk full\r\n", io->stdErr);
      res = ERR_FAILED;
      break;
    }
  } /* for */
  /* close all files */
  fres = McuFatFS_close(&fsrc);
  if (fres != FR_OK) {
    FatFsFResultMsg((unsigned char*)"closing source file failed", fres, io);
    res = ERR_FAILED;
  }
  fres = McuFatFS_close(&fdst);
  if (fres != FR_OK) {
    FatFsFResultMsg((unsigned char*)"closing destination file failed", fres, io);
    res = ERR_FAILED;
  }
  return res;
#else
  (void)srcFileName; /* unused argument */
  (void)dstFileName; /* unused argument */
  McuShell_SendStr((unsigned char*)"File System is in Read-Only mode\r\n", io->stdErr);
  return ERR_FAILED;
#endif
}

/*
** ===================================================================
**     Method      :  DeleteFile (component FAT_FileSystem)
**
**     Description :
**         Deletes a file
**     Parameters  :
**         NAME            - DESCRIPTION
**         fileName        - Filename of file to be deleted
**         io              - Pointer to I/O handler
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*!
 * \brief Deletes a file
 * \param[in] fileName Name of file to be deleted
 * \param[in] io IO handler for output
 * \return Error code, ERR_OK for success.
 */
uint8_t McuFatFS_DeleteFile(const uint8_t *fileName, const McuShell_StdIOType *io)
{
  McuFatFS_FRESULT fres;

  if (McuFatFS_isWriteProtected((uint8_t*)McuFatFS_CONFIG_DEFAULT_DRIVE_STRING)) {
    McuShell_SendStr((unsigned char*)"disk is write protected!\r\n", io->stdErr);
    return ERR_FAILED;
  }
  fres = McuFatFS_unlink((const TCHAR*)fileName);
  if (fres != FR_OK) {
    FatFsFResultMsg((unsigned char*)"unlink failed", fres, io);
    return ERR_FAILED;
  }
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  CreateFile (component FAT_FileSystem)
**
**     Description :
**         Creates an empty file
**     Parameters  :
**         NAME            - DESCRIPTION
**         fileName        - Filename of file to be created
**         io              - Pointer to I/O handler
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuFatFS_CreateFile(const uint8_t *fileName, const McuShell_StdIOType *io)
{
  McuFatFS_FRESULT fres;
  FIL fp;

  if (McuFatFS_isWriteProtected((uint8_t*)McuFatFS_CONFIG_DEFAULT_DRIVE_STRING)) {
    McuShell_SendStr((unsigned char*)"disk is write protected!\r\n", io->stdErr);
    return ERR_FAILED;
  }
  fres = McuFatFS_open(&fp, (const TCHAR*)fileName, FA_CREATE_NEW);
  if (fres != FR_OK) {
    FatFsFResultMsg((unsigned char*)"creating new file failed", fres, io);
    return ERR_FAILED;
  }
  fres = McuFatFS_close(&fp);
  if (fres != FR_OK) {
    FatFsFResultMsg((unsigned char*)"closing file failed", fres, io);
    return ERR_FAILED;
  }
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  PrintFile (component FAT_FileSystem)
**
**     Description :
**         Prints the content of a file
**     Parameters  :
**         NAME            - DESCRIPTION
**         fileName        - Name of file to be printed
**         io              - Pointer to I/O handler
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*!
 * \brief Print the content of a file
 * \param[in] fileName Name of file to be printed
 * \param[in] io IO handler for output
 * \return Error code, ERR_OK for success.
 */
uint8_t McuFatFS_PrintFile(const uint8_t *fileName, const McuShell_StdIOType *io)
{
  McuFatFS_FIL file;
  McuFatFS_FRESULT fres;
  UINT nofRead = 0;
  uint8_t buf[32];
  uint8_t res = ERR_OK;

  fres = McuFatFS_open(&file, (const TCHAR *)fileName, FA_READ);
  if (fres == FR_OK) {
    do {
      nofRead = 0;
      fres=McuFatFS_read(&file, buf, sizeof(buf)-1, &nofRead); /* read one byte less for zero byte */
      if (fres != FR_OK) {
        McuShell_SendStr((unsigned char*)"fread failed\r\n", io->stdErr);
        res = ERR_FAILED;
      } else {
        buf[nofRead] = '\0'; /* terminate buffer */
        McuShell_SendStr(buf, io->stdOut);
      }
    } while(nofRead>0 && fres==FR_OK);
    fres=McuFatFS_close(&file);
    if (fres != FR_OK) {
      McuShell_SendStr((unsigned char*)"fclose failed\r\n", io->stdErr);
      res = ERR_FAILED;
    }
  } else {
    FatFsFResultMsg((unsigned char*)"open file failed", fres, io);
    res = ERR_FAILED;
  }
  McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
  return res;
}

/*
** ===================================================================
**     Method      :  PrintHexFile (component FAT_FileSystem)
**
**     Description :
**         Prints the content of a file in hexadecimal format, useful
**         for binary files.
**     Parameters  :
**         NAME            - DESCRIPTION
**         fileName        - Name of file to be printed
**         io              - Pointer to I/O handler
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuFatFS_PrintHexFile(const uint8_t *fileName, const McuShell_StdIOType *io)
{
  McuFatFS_FIL file;
  McuFatFS_FRESULT fres;
  UINT nofRead = 0;
  #define PRINT_HEX_NOF_BYTES_PER_LINE 16
  uint8_t filebuf[PRINT_HEX_NOF_BYTES_PER_LINE];
  uint8_t buf[16];
  uint8_t res = ERR_OK;
  uint32_t address;
  UINT i;

  fres = McuFatFS_open(&file, (const TCHAR *)fileName, FA_READ);
  if (fres == FR_OK) {
    address = 0;
    do {
      nofRead = 0;
      fres=McuFatFS_read(&file, filebuf, sizeof(filebuf), &nofRead); /* read one byte less for zero byte */
      if (fres != FR_OK) {
        McuShell_SendStr((unsigned char*)"fread failed\r\n", io->stdErr);
        res = ERR_FAILED;
      } else if (nofRead>0) {
        for(i=0; i<nofRead; i++) {
          if ((i%PRINT_HEX_NOF_BYTES_PER_LINE)==0) { /* new line to make things readable */
            McuShell_SendStr((unsigned char*)"0x", io->stdOut);
            buf[0] = '\0';
            McuUtility_strcatNum32Hex(buf, sizeof(buf), address);
            McuShell_SendStr(buf, io->stdOut);
            McuShell_SendStr((unsigned char*)": ", io->stdOut);
            address += PRINT_HEX_NOF_BYTES_PER_LINE;
          }
          buf[0] = '\0';
          McuUtility_strcatNum8Hex(buf, sizeof(buf), filebuf[i]);
          McuShell_SendStr(buf, io->stdOut);
          McuShell_SendStr((unsigned char*)" ", io->stdOut);
        }
        /* fill up line if not a full line */
        for (/*empty*/; i<PRINT_HEX_NOF_BYTES_PER_LINE; i++) {
          McuShell_SendStr((unsigned char*)"-- ", io->stdOut);
        }
        /* print in ASCII */
        PrintInASCII(&filebuf[0], nofRead, PRINT_HEX_NOF_BYTES_PER_LINE, '.', '-', io);
        McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
      }
    } while(nofRead>0 && fres==FR_OK);
    fres=McuFatFS_close(&file);
    if (fres != FR_OK) {
      McuShell_SendStr((unsigned char*)"fclose failed\r\n", io->stdErr);
      res = ERR_FAILED;
    }
  } else {
    FatFsFResultMsg((unsigned char*)"open file failed\r\n", fres, io);
    res = ERR_FAILED;
  }
  return res;
}

/*
** ===================================================================
**     Method      :  MakeDirectory (component FAT_FileSystem)
**
**     Description :
**         Creates a directory
**     Parameters  :
**         NAME            - DESCRIPTION
**         dirName         - Directory name
**         io              - Pointer to I/O handler
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*!
 * \brief Creates a new directory
 * \param[in] dirName Name of the directory to be created
 * \param[in] io IO handler for output
 * \return Error code, ERR_OK for success.
 */
uint8_t McuFatFS_MakeDirectory(const uint8_t *dirName, const McuShell_StdIOType *io)
{
  McuFatFS_FRESULT fres;

  if (McuFatFS_isWriteProtected((uint8_t*)McuFatFS_CONFIG_DEFAULT_DRIVE_STRING)) {
    McuShell_SendStr((unsigned char*)"disk is write protected!\r\n", io->stdErr);
    return ERR_FAILED;
  }
  fres = McuFatFS_mkdir((const TCHAR*)dirName);
  if(fres!=FR_OK) {
    FatFsFResultMsg((unsigned char*)"mkdir failed", fres, io);
    return ERR_FAILED;
  }
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  ChangeDirectory (component FAT_FileSystem)
**
**     Description :
**         Changes to a directory
**     Parameters  :
**         NAME            - DESCRIPTION
**         dirName         - Directory name
**         io              - Pointer to I/O handler
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuFatFS_ChangeDirectory(const uint8_t *dirName, const McuShell_StdIOType *io)
{
#if McuFatFS_FS_RPATH > 0
  McuFatFS_FRESULT fres;

  if ((fres=McuFatFS_chdir((const TCHAR*)dirName)) != FR_OK) {
    FatFsFResultMsg((unsigned char*)"chdir failed", fres, io);
    return ERR_FAILED;
  }
  return ERR_OK;
#else
  (void)dirName;
  #warning "relative directories not enabled in FatFS!"
  McuShell_SendStr((unsigned char*)"cd command not available as relative directories not enable in file system\r\n", io->stdErr);
  return ERR_FAILED;
#endif
}

/*
** ===================================================================
**     Method      :  RenameFile (component FAT_FileSystem)
**
**     Description :
**         Renames a file
**     Parameters  :
**         NAME            - DESCRIPTION
**         srcFileName     - Source file name
**         dstFileName     - Destination file name
**         io              - Pointer to I/O handler
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*!
 * \brief Renames a file
 * \param[in] srcFileName Source/existing file name
 * \param[in] dstFileName Destination/new file name
 * \param[in] io IO handler for output
 * \return Error code, ERR_OK for success.
 */
uint8_t McuFatFS_RenameFile(const uint8_t *srcFileName, const uint8_t *dstFileName, const McuShell_StdIOType *io)
{
  McuFatFS_FRESULT fres;

  if (McuFatFS_isWriteProtected((uint8_t*)McuFatFS_CONFIG_DEFAULT_DRIVE_STRING)) {
    McuShell_SendStr((unsigned char*)"disk is write protected!\r\n", io->stdErr);
    return ERR_FAILED;
  }
  fres = McuFatFS_rename((const TCHAR*)srcFileName, (const TCHAR*)dstFileName);
  if(fres!=FR_OK) {
    FatFsFResultMsg((unsigned char*)"rename failed", fres, io);
    return ERR_FAILED;
  }
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  PrintSector (component FAT_FileSystem)
**
**     Description :
**         Prints information about the current disk
**     Parameters  :
**         NAME            - DESCRIPTION
**         drive           - drive number, starting with zero
**         sectorNo        - sector number
**         io              - Pointer to I/O handler
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
#if McuFatFS_USE_RTOS_DYNAMIC_MEMORY
#if McuLib_CONFIG_SDK_VERSION_USED == McuLib_CONFIG_SDK_PROCESSOR_EXPERT
  #define SECTOR_BUF_SIZE0   SDCard_BLOCK_SIZE
#else
  #define SECTOR_BUF_SIZE0   512
#endif
#else
#if McuLib_CONFIG_SDK_VERSION_USED == McuLib_CONFIG_SDK_PROCESSOR_EXPERT
  static uint8_t print_buf0[SDCard_BLOCK_SIZE];
  #define SECTOR_BUF_SIZE0   sizeof(print_buf0)
#else
  static uint8_t print_buf0[512];
  #define SECTOR_BUF_SIZE0   sizeof(print_buf0)
#endif
#endif
uint8_t McuFatFS_PrintSector(uint8_t drive, uint32_t sectorNo, const McuShell_StdIOType *io)
{
  #define PRINT_SECTOR_NOF_BYTES_PER_LINE  16
  uint16_t i;
  unsigned char buf[8];
  McuFatFS_DRESULT dres;
#if McuFatFS_USE_RTOS_DYNAMIC_MEMORY
  #if configFRTOS_MEMORY_SCHEME==1 /* this scheme does not allow deallocation of memory */
    static unsigned char *print_buf0=NULL; /* use global buffer pointer, allocated only once, and not deallocated! */
  #else
    unsigned char *print_buf0;
  #endif
#endif

  if ((disk_initialize(drive)&STA_NOINIT)!=(DSTATUS)RES_OK) {
    McuShell_SendStr((unsigned char*)"disk initialize failed\r\n", io->stdErr);
    return ERR_FAILED;
  }
#if McuFatFS_USE_RTOS_DYNAMIC_MEMORY
  #if configFRTOS_MEMORY_SCHEME==1 /* this scheme does not allow deallocation of memory */
  if (print_buf0==NULL) { /* only if not allocated yet */
    print_buf0 = pvPortMalloc(SECTOR_BUF_SIZE0);
  }
  #else
  print_buf0 = pvPortMalloc(SECTOR_BUF_SIZE0);
  #endif
  if (print_buf0 == NULL) {
    McuShell_SendStr((unsigned char*)"allocating memory failed\r\n", io->stdErr);
    return ERR_FAILED;
  }
#endif
  dres = disk_read(drive, &print_buf0[0], sectorNo, 1);
  if (dres==RES_OK) { /* read one sector */
    McuShell_SendStr((unsigned char*)"dumping disk sector: 0x", io->stdOut);
    buf[0] = '\0';
    McuUtility_strcatNum32Hex(buf, sizeof(buf), sectorNo);
    McuShell_SendStr(buf, io->stdOut);
    McuShell_SendStr((unsigned char*)", sector size ", io->stdOut);
    McuShell_SendNum32u(SECTOR_BUF_SIZE0, io->stdOut);
    for(i=0; i<SECTOR_BUF_SIZE0; i++) {
      if ((i%PRINT_SECTOR_NOF_BYTES_PER_LINE)==0) { /* new line to make things readable */
        if (i!=0) { /* not for first line */
          PrintInASCII(&print_buf0[i-PRINT_SECTOR_NOF_BYTES_PER_LINE], PRINT_SECTOR_NOF_BYTES_PER_LINE, PRINT_SECTOR_NOF_BYTES_PER_LINE, '.', '-', io);
        }
        McuShell_SendStr((unsigned char*)"\r\n0x", io->stdOut);
        buf[0] = '\0';
        McuUtility_strcatNum16Hex(buf, sizeof(buf), i);
        McuShell_SendStr(buf, io->stdOut);
        McuShell_SendStr((unsigned char*)": ", io->stdOut);
      }
      buf[0] = '\0';
      McuUtility_strcatNum8Hex(buf, sizeof(buf), (uint8_t)print_buf0[i]);
      McuShell_SendStr(buf, io->stdOut);
      McuShell_SendStr((unsigned char*)" ", io->stdOut);
    }
    PrintInASCII(&print_buf0[i-PRINT_SECTOR_NOF_BYTES_PER_LINE], PRINT_SECTOR_NOF_BYTES_PER_LINE, PRINT_SECTOR_NOF_BYTES_PER_LINE, '.', '-', io);
    McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
  } else {
    FatFsDResultMsg((unsigned char*)"disk_read failed", dres, io);
  }
#if McuFatFS_USE_RTOS_DYNAMIC_MEMORY
  #if configFRTOS_MEMORY_SCHEME!=1 /* this scheme does not allow deallocation of memory */
    vPortFree(print_buf0);
  #endif
#endif
  if (dres==RES_OK) {
    return ERR_OK;
  } else {
    return ERR_FAILED;
  }
}

/*
** ===================================================================
**     Method      :  PrintDiskInfo (component FAT_FileSystem)
**
**     Description :
**         Prints information about the current disk
**     Parameters  :
**         NAME            - DESCRIPTION
**       * drive           - Drive string, can be NULL or e.g.
**                           pointing to "0"
**         io              - Pointer to I/O handler
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*!
 * \brief Prints information about the disk
 * \param[in] drive Disk drive number, starting with zero
 * \param[in] io IO handler for output
 * \return Error code, ERR_OK for success.
 */
uint8_t McuFatFS_PrintDiskInfo(uint8_t *drvStr, const McuShell_StdIOType *io)
{
  /* see for details:
   * http://www.retroleum.co.uk/electronics-articles/basic-mmc-card-access/
   */
  uint8_t buf[8];
  int32_t val32;
  int16_t val16;
  int8_t val8;
  uint8_t buff[64];
  uint8_t driverVersion; /* 0: SPI, 1: SDHC_LDD */
  uint8_t driveNr;

  driveNr = McuFatFS_StrToDriveNumber(drvStr);
  if (!McuFatFS_isDiskPresent(drvStr)) {
    McuShell_SendStr((unsigned char*)"disk not present!\r\n", io->stdErr);
    return ERR_FAILED;
  }
  if ((disk_initialize(McuFatFS_StrToDriveNumber(drvStr))&STA_NOINIT)!=0) {
    McuShell_SendStr((unsigned char*)"disk initialize failed!\r\n", io->stdErr);
    return ERR_FAILED;
  }
  if (disk_ioctl(driveNr, MMC_GET_DRIVER_VERSION, &driverVersion)!=RES_OK) {
    McuShell_SendStr((unsigned char*)"failed identification of driver version\r\n", io->stdErr);
    return ERR_FAILED;
  }
  McuShell_SendStatusStr((unsigned char*)"Card type", (unsigned char*)"", io->stdOut);
  if (disk_ioctl(driveNr, MMC_GET_TYPE, &val8)==RES_OK) {
    if (val8&CT_SD1) {
      McuShell_SendStr((unsigned char*)"SD1 ", io->stdOut);
    }
    if (val8&CT_SD2) {
      McuShell_SendStr((unsigned char*)"SD2 ", io->stdOut);
    }
    if (val8&CT_BLOCK) {
      McuShell_SendStr((unsigned char*)"BLOCK ", io->stdOut);
    }
    if (val8&CT_MMC) {
      McuShell_SendStr((unsigned char*)"MMC ", io->stdOut);
    }
    if (val8&CT_SDC) {
      McuShell_SendStr((unsigned char*)"SDC ", io->stdOut);
    }
    if (val8&CT_ATA) {
      McuShell_SendStr((unsigned char*)"ATA ", io->stdOut);
    }
    McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
  } else {
    McuShell_SendStr((unsigned char*)"ERROR\r\n", io->stdOut);
  }
  if (driverVersion==0) { /* only SPI cards implement this */
    McuShell_SendStatusStr((unsigned char*)"SDC version", (unsigned char*)"", io->stdOut);
    if (disk_ioctl(driveNr, MMC_GET_SDC_VERSION, &val8)==RES_OK) {
      if (val8==1) {
        McuShell_SendStr((unsigned char*)"SDC ver 1.XX or MMC\r\n", io->stdOut);
      } else if (val8==2) {
        McuShell_SendStr((unsigned char*)"SDC ver 2.00\r\n", io->stdOut);
      } else {
        McuShell_SendStr((unsigned char*)"unknown\r\n", io->stdOut);
      }
    } else {
      McuShell_SendStr((unsigned char*)"ERROR\r\n", io->stdOut);
    }
  }
  McuShell_SendStatusStr((unsigned char*)"Sector count", (unsigned char*)"", io->stdOut);
  if (disk_ioctl(driveNr, GET_SECTOR_COUNT, &val32)==RES_OK) {
    McuUtility_Num32sToStr(buf, sizeof(buf), val32);
    McuShell_SendStr(buf, io->stdOut);
    McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
  } else {
    McuShell_SendStr((unsigned char*)"ERROR\r\n", io->stdOut);
  }
  McuShell_SendStatusStr((unsigned char*)"Sector size", (unsigned char*)"", io->stdOut);
  if (disk_ioctl(driveNr, GET_SECTOR_SIZE, &val16)==RES_OK) {
    McuUtility_Num16sToStr(buf, sizeof(buf), val16);
    McuShell_SendStr(buf, io->stdOut);
    McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
  } else {
    McuShell_SendStr((unsigned char*)"ERROR\r\n", io->stdOut);
  }
  McuShell_SendStatusStr((unsigned char*)"READ_BL_LEN", (unsigned char*)"", io->stdOut);
  if (disk_ioctl(driveNr, MMC_GET_READ_BL_LEN, &val16)==RES_OK) {
    McuUtility_Num16sToStr(buf, sizeof(buf), val16);
    McuShell_SendStr(buf, io->stdOut);
    McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
  } else {
    McuShell_SendStr((unsigned char*)"ERROR\r\n", io->stdOut);
  }
  if (driverVersion==0) { /* only SPI cards implement this */
    McuShell_SendStatusStr((unsigned char*)"Block size", (unsigned char*)"", io->stdOut);
    if (disk_ioctl(driveNr, GET_BLOCK_SIZE, &val32)==RES_OK) {
      McuUtility_Num32sToStr(buf, sizeof(buf), val32);
      McuShell_SendStr(buf, io->stdOut);
      McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
    } else {
      McuShell_SendStr((unsigned char*)"ERROR\r\n", io->stdOut);
    }
    McuShell_SendStatusStr((unsigned char*)"CSD", (unsigned char*)"", io->stdOut);
    if (disk_ioctl(driveNr, MMC_GET_CSD, &buff[0])==RES_OK) {
      for(val8=0; val8<16; val8++) {
        buf[0] = '\0';
        McuUtility_strcatNum8Hex(buf, sizeof(buf), (uint8_t)buff[val8]);
        McuShell_SendStr(buf, io->stdOut);
        McuShell_SendStr((unsigned char*)" ", io->stdOut);
      }
      McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
    } else {
      McuShell_SendStr((unsigned char*)"ERROR\r\n", io->stdOut);
    }
    McuShell_SendStatusStr((unsigned char*)"CID", (unsigned char*)"", io->stdOut);
    if (disk_ioctl(driveNr, MMC_GET_CID, &buff[0])==RES_OK) {
      for(val8=0; val8<16; val8++) {
        buf[0] = '\0';
        McuUtility_strcatNum8Hex(buf, sizeof(buf), (uint8_t)buff[val8]);
        McuShell_SendStr(buf, io->stdOut);
        McuShell_SendStr((unsigned char*)" ", io->stdOut);
      }
      McuShell_SendStr((unsigned char*)"\r\n               Manufacturer ", io->stdOut);
      for(val8=3; val8<=8; val8++) {
        io->stdOut(buff[val8]);
      }
      McuShell_SendStr((unsigned char*)"\r\n               Serial Number ", io->stdOut);
      buf[0] = '\0';
      McuUtility_strcatNum32Hex(buf, sizeof(buf), (uint32_t)((buff[0xa]<<24)|(buff[0xb]<<16)|(buff[0xc]<<8)|buff[0xd]));
      McuShell_SendStr(buf, io->stdOut);
      McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
    } else {
      McuShell_SendStr((unsigned char*)"ERROR\r\n", io->stdOut);
    }
    McuShell_SendStatusStr((unsigned char*)"OCR", (unsigned char*)"", io->stdOut);
    if (disk_ioctl(driveNr, MMC_GET_OCR, &buff[0])==RES_OK) {
      for(val8=0; val8<4; val8++) {
        buf[0] = '\0';
        McuUtility_strcatNum8Hex(buf, sizeof(buf), (uint8_t)buff[val8]);
        McuShell_SendStr(buf, io->stdOut);
        McuShell_SendStr((unsigned char*)" ", io->stdOut);
      }
      McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
    } else {
      McuShell_SendStr((unsigned char*)"ERROR\r\n", io->stdOut);
    }
    McuShell_SendStatusStr((unsigned char*)"SD Status", (unsigned char*)"", io->stdOut);
    if (disk_ioctl(driveNr, MMC_GET_SDSTAT, &buff[0])==RES_OK) {
      for(val8=0; val8<64; val8++) {
        buf[0] = '\0';
        if (val8!=0 && (val8%16)==0) { /* new line to make things readable */
          McuShell_SendStr((unsigned char*)"\r\n               ", io->stdOut);
        }
        McuUtility_strcatNum8Hex(buf, sizeof(buf), (uint8_t)buff[val8]);
       McuShell_SendStr(buf, io->stdOut);
       McuShell_SendStr((unsigned char*)" ", io->stdOut);
      }
      McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
    } else {
      McuShell_SendStr((unsigned char*)"ERROR\r\n", io->stdOut);
    }
  }
  if (driverVersion==1) { /* only LLD_SDHD cards implement this */
    buff[1] = sizeof(buf)-2; /* size of buffer */
    buff[0] = MMC_GET_LLD_CMD_HIGH_CAPACITY; /* cmd */
    if (disk_ioctl(driveNr, MMC_GET_LLD_INFO, &buff[0])==RES_OK) {
      McuShell_SendStatusStr((unsigned char*)"High capacity", (unsigned char*)(buff[2]!=0?"yes\r\n":"no\r\n"), io->stdOut);
    }
    buff[0] = MMC_GET_LLD_CMD_HIGH_SPEED; /* cmd */
    if (disk_ioctl(driveNr, MMC_GET_LLD_INFO, &buff[0])==RES_OK) {
      McuShell_SendStatusStr((unsigned char*)"High speed", (unsigned char*)(buff[2]!=0?"yes\r\n":"no\r\n"), io->stdOut);
    }
    buff[0] = MMC_GET_LLD_CMD_LOW_VOLTAGE; /* cmd */
    if (disk_ioctl(driveNr, MMC_GET_LLD_INFO, &buff[0])==RES_OK) {
      McuShell_SendStatusStr((unsigned char*)"Low voltage", (unsigned char*)(buff[2]!=0?"yes\r\n":"no\r\n"), io->stdOut);
    }
    buff[0] = MMC_GET_LLD_CMD_DATA_WIDTHS; /* cmd */
    if (disk_ioctl(driveNr, MMC_GET_LLD_INFO, &buff[0])==RES_OK) {
      McuShell_SendStatusStr((unsigned char*)"Data widths", (unsigned char*)"", io->stdOut);
      if (buff[2]&0x1) {
        McuShell_SendStr((unsigned char*)"1 ", io->stdOut);
      }
      if (buff[2]&0x2) {
        McuShell_SendStr((unsigned char*)"4 ", io->stdOut);
      }
      if (buff[2]&0x4) {
        McuShell_SendStr((unsigned char*)"8 ", io->stdOut);
      }
      McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
    }
    buff[0] = MMC_GET_LLD_CMD_OPERATIONS; /* cmd */
    if (disk_ioctl(driveNr, MMC_GET_LLD_INFO, &buff[0])==RES_OK) {
      McuShell_SendStatusStr((unsigned char*)"Operations", (unsigned char*)"", io->stdOut);
      if (buff[2]&0x1) {
        McuShell_SendStr((unsigned char*)"BlockRead ", io->stdOut);
      }
      if (buff[2]&0x2) {
        McuShell_SendStr((unsigned char*)"BlockWrite ", io->stdOut);
      }
      if (buff[2]&0x4) {
        McuShell_SendStr((unsigned char*)"BlockErase ", io->stdOut);
      }
      if (buff[2]&0x8) {
        McuShell_SendStr((unsigned char*)"WriteProtect ", io->stdOut);
      }
      if (buff[2]&0x10) {
        McuShell_SendStr((unsigned char*)"I/O ", io->stdOut);
      }
      McuShell_SendStr((unsigned char*)"\r\n", io->stdOut);
    }
  }
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  Benchmark (component FAT_FileSystem)
**
**     Description :
**         Performs a disk benchmark
**     Parameters  :
**         NAME            - DESCRIPTION
**         io              - Pointer to I/O handler
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*! \brief Simple benchmark function: first we are going to write a file, then we will copy it */
uint8_t McuFatFS_Benchmark(const McuShell_StdIOType *io)
{
  static FIL fp;
  uint16_t i;
  UINT bw;
  uint8_t read_buf[10];
  TIMEREC time, startTime;
  int32_t start_mseconds, mseconds;

  if (McuFatFS_isWriteProtected((uint8_t*)McuFatFS_CONFIG_DEFAULT_DRIVE_STRING)) {
    McuShell_SendStr((unsigned char*)"disk is write protected!\r\n", io->stdErr);
    return ERR_FAILED;
  }
  /* write benchmark */
  McuShell_SendStr((const unsigned char*)"Benchmark: write/copy/read a 100kB file:\r\n", io->stdOut);
  McuShell_SendStr((const unsigned char*)"Delete existing benchmark files...\r\n", io->stdOut);
  (void)McuFatFS_DeleteFile((const unsigned char*)"./bench.txt", io);
  (void)McuFatFS_DeleteFile((const unsigned char*)"./copy.txt", io);

  McuShell_SendStr((const unsigned char*)"Create benchmark file...\r\n", io->stdOut);
  (void)McuTimeDate_GetTime(&startTime);
  if (McuFatFS_open(&fp, (const TCHAR*)"./bench.txt", FA_CREATE_ALWAYS|FA_WRITE)!=FR_OK) {
    McuShell_SendStr((const unsigned char*)"*** Failed opening benchmark file!\r\n", io->stdErr);
    return ERR_FAILED;
  }
  for(i=0;i<10240;i++) {
    if (McuFatFS_write(&fp, "benchmark ", sizeof("benchmark ")-1, &bw)!=FR_OK) {
      McuShell_SendStr((const unsigned char*)"*** Failed writing file!\r\n", io->stdErr);
      (void)McuFatFS_close(&fp);
      return ERR_FAILED;
    }
  }
  (void)McuFatFS_close(&fp);
  (void)McuTimeDate_GetTime(&time);
  start_mseconds = startTime.Hour*60*60*1000 + startTime.Min*60*1000 + startTime.Sec*1000
#if McuTimeDate_HAS_SEC100_IN_TIMEREC
  + startTime.Sec100*10
#endif
  ;
  mseconds = time.Hour*60*60*1000 + time.Min*60*1000 + time.Sec*1000
#if McuTimeDate_HAS_SEC100_IN_TIMEREC
  + time.Sec100*10
#endif
  - start_mseconds;
  McuShell_SendNum32s(mseconds, io->stdOut);
  McuShell_SendStr((const unsigned char*)" ms for writing (", io->stdOut);
  McuShell_SendNum32s((100*1000)/mseconds, io->stdOut);
  McuShell_SendStr((const unsigned char*)" kB/s)\r\n", io->stdOut);

  /* read benchmark */
  McuShell_SendStr((const unsigned char*)"Read 100kB benchmark file...\r\n", io->stdOut);
  (void)McuTimeDate_GetTime(&startTime);
  if (McuFatFS_open(&fp, (const TCHAR*)"./bench.txt", FA_READ)!=FR_OK) {
    McuShell_SendStr((const unsigned char*)"*** Failed opening benchmark file!\r\n", io->stdErr);
    return ERR_FAILED;
  }
  for(i=0;i<10240;i++) {
    if (McuFatFS_read(&fp, &read_buf[0], sizeof(read_buf), &bw)!=FR_OK) {
      McuShell_SendStr((const unsigned char*)"*** Failed reading file!\r\n", io->stdErr);
      (void)McuFatFS_close(&fp);
      return ERR_FAILED;
    }
  }
  (void)McuFatFS_close(&fp);
  (void)McuTimeDate_GetTime(&time);
  start_mseconds = startTime.Hour*60*60*1000 + startTime.Min*60*1000 + startTime.Sec*1000
#if McuTimeDate_HAS_SEC100_IN_TIMEREC
  + startTime.Sec100*10
#endif
  ;
  mseconds = time.Hour*60*60*1000 + time.Min*60*1000 + time.Sec*1000
#if McuTimeDate_HAS_SEC100_IN_TIMEREC
  + time.Sec100*10
#endif
  - start_mseconds;
  McuShell_SendNum32s(mseconds, io->stdOut);
  McuShell_SendStr((const unsigned char*)" ms for reading (", io->stdOut);
  McuShell_SendNum32s((100*1000)/mseconds, io->stdOut);
  McuShell_SendStr((const unsigned char*)" kB/s)\r\n", io->stdOut);

  /* copy benchmark */
  McuShell_SendStr((const unsigned char*)"Copy 100kB file...\r\n", io->stdOut);
  (void)McuTimeDate_GetTime(&startTime);
  (void)McuFatFS_CopyFile((const unsigned char*)"./bench.txt", (const unsigned char*)"./copy.txt", io);
  (void)McuTimeDate_GetTime(&time);
  start_mseconds = startTime.Hour*60*60*1000 + startTime.Min*60*1000 + startTime.Sec*1000
#if McuTimeDate_HAS_SEC100_IN_TIMEREC
  + startTime.Sec100*10
#endif
  ;
  mseconds = time.Hour*60*60*1000 + time.Min*60*1000 + time.Sec*1000
#if McuTimeDate_HAS_SEC100_IN_TIMEREC
  + time.Sec100*10
#endif
  - start_mseconds;
  McuShell_SendNum32s(mseconds, io->stdOut);
  McuShell_SendStr((const unsigned char*)" ms for copy (", io->stdOut);
  McuShell_SendNum32s((100*1000)/mseconds, io->stdOut);
  McuShell_SendStr((const unsigned char*)" kB/s)\r\n", io->stdOut);
  McuShell_SendStr((const unsigned char*)"done!\r\n", io->stdOut);
  return ERR_OK;
}

/*
** ===================================================================
**     Method      :  f_getlabel (component FAT_FileSystem)
**
**     Description :
**         Get volume label
**     Parameters  :
**         NAME            - DESCRIPTION
**       * path            - Pointer to path name of the logical
**                           drive number
**       * label           - Pointer to a buffer to return the
**                           volume label
**         vsn             - 
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_f_getlabel(const TCHAR* path, TCHAR* label, DWORD* vsn)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  f_setlabel (component FAT_FileSystem)
**
**     Description :
**         Set Volume Label 
**     Parameters  :
**         NAME            - DESCRIPTION
**       * label           - Pointer to the volume label to set
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_f_setlabel(const TCHAR* label)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  f_expand (component FAT_FileSystem)
**
**     Description :
**         Allocate a Contiguous Blocks to the File
**     Parameters  :
**         NAME            - DESCRIPTION
**       * fp              - Pointer to the file object
**         fsz             - File size to be expanded to
**         opt             - Operation mode 0:Find and prepare or 1:
**                           Find and allocate
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_f_expand(FIL* fp, FSIZE_t fsz, BYTE opt)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  f_findfirst (component FAT_FileSystem)
**
**     Description :
**         Find FirstFile
**     Parameters  :
**         NAME            - DESCRIPTION
**         dp              - Pointer to the open directory object
**         fno             - Pointer to the file information structure
**         path            - Pointer to the directory to open
**         pattern         - Pointer to the matching pattern
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_f_findfirst(DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  f_findnext (component FAT_FileSystem)
**
**     Description :
**         Find Next File
**     Parameters  :
**         NAME            - DESCRIPTION
**         dp              - Pointer to the open directory object
**         fno             - Pointer to the file information structure
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_f_findnext(DIR* dp, FILINFO* fno)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  f_opendir (component FAT_FileSystem)
**
**     Description :
**         Open a directory
**     Parameters  :
**         NAME            - DESCRIPTION
**         dp              - Pointer to the open directory object
**         path            - path of directory
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_f_opendir(DIR* dp, const TCHAR* path)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  f_closedir (component FAT_FileSystem)
**
**     Description :
**         Close a directory
**     Parameters  :
**         NAME            - DESCRIPTION
**         dp              - Pointer to the open directory object
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_f_closedir(DIR* dp)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  f_readdir (component FAT_FileSystem)
**
**     Description :
**         Read a directory item
**     Parameters  :
**         NAME            - DESCRIPTION
**         dir             - Pointer to the open directory object
**         fno             - Pointer to the file information structure
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
/*
FRESULT McuFatFS_f_readdir(DIR *dj, FILINFO *fno)
{
  *** method is implemented as macro in the header file
}
*/

/*
** ===================================================================
**     Method      :  StrToDriveNumber (component FAT_FileSystem)
**
**     Description :
**         Transforms a drive string ("0:/") into a drive number (0)
**     Parameters  :
**         NAME            - DESCRIPTION
**       * drvStr          - Pointer to drive string, e.g. "0:/"
**     Returns     :
**         ---             - Error code
** ===================================================================
*/
uint8_t McuFatFS_StrToDriveNumber(uint8_t *drvStr)
{
  uint8_t drv = 0;
  const unsigned char *p;

  if (drvStr==NULL || *drvStr=='\0') { /* default, "" */
    drv = 0;
  } else {
    p = drvStr;
    if (McuUtility_ScanDecimal8uNumber(&p, &drv)!=ERR_OK) { /* "0", "1", ... */
      drv = 0; /* error, use default number */
    }
  }
  return drv;
}

/* END McuFatFS. */

/*!
** @}
*/
