瀏覽代碼

Merge branch 'master' of https://79.135.245.84:43000/ebalbekova/bt-67xx_universal_hw

KoKiii 5 年之前
父節點
當前提交
a38e13f1b5
共有 53 個文件被更改,包括 12373 次插入718 次删除
  1. 50 0
      .vscode/c_cpp_properties.json
  2. 3 1
      .vscode/settings.json
  3. 6 3
      Makefile
  4. 1 1
      config/board_bt6703.h
  5. 1 1
      config/board_bt6709.h
  6. 4 0
      config/common_config.h
  7. 9 0
      docs/bt6709/Changelog.md
  8. 123 0
      iap/FATFS/00readme.txt
  9. 140 0
      iap/FATFS/diskio.c
  10. 80 0
      iap/FATFS/diskio.h
  11. 4606 0
      iap/FATFS/ff.c
  12. 342 0
      iap/FATFS/ff.h
  13. 228 0
      iap/FATFS/ffconf.h
  14. 33 0
      iap/FATFS/integer.h
  15. 4 0
      iap/Makefile
  16. 861 475
      iap/Modules/Ethernet/httpserver.c
  17. 19 0
      iap/Modules/Ethernet/httpserver.h
  18. 2 2
      iap/Modules/Ethernet/lwipopts.h
  19. 46 56
      iap/Modules/Ethernet/netconf.c
  20. 0 1
      iap/Modules/Ethernet/netconf.h
  21. 3238 0
      iap/Modules/SD_Card/sdio_sd.c
  22. 409 0
      iap/Modules/SD_Card/sdio_sd.h
  23. 15 2
      iap/Modules/crc.c
  24. 1 1
      iap/Modules/crc.h
  25. 16 0
      iap/Modules/rng.c
  26. 16 0
      iap/Modules/rng.h
  27. 18 0
      iap/Modules/systick.c
  28. 5 0
      iap/Modules/systick.h
  29. 23 0
      iap/User/conf.h
  30. 230 121
      iap/User/main.c
  31. 633 0
      iap/lcd/drivers/jlx12864.c
  32. 22 0
      iap/lcd/drivers/jlx12864.h
  33. 182 0
      iap/lcd/fonts.h
  34. 77 0
      iap/lcd/lcd.c
  35. 25 0
      iap/lcd/lcd.h
  36. 36 0
      iap/lcd/pins.h
  37. 53 0
      iap/stm32sprog/serial.c
  38. 46 0
      iap/stm32sprog/serial.h
  39. 503 0
      iap/stm32sprog/stm32sprog.c
  40. 78 0
      iap/stm32sprog/stm32sprog.h
  41. 38 20
      modules/HTTP_Server/http_server.c
  42. 29 0
      modules/HTTP_Server/my_ssl_server.c
  43. 2 0
      modules/HTTP_Server/my_ssl_server.h
  44. 7 1
      modules/Makefile
  45. 21 30
      modules/MegaTec/megatec.c
  46. 38 3
      modules/parameters.c
  47. 11 0
      modules/parameters.h
  48. 1 0
      modules/settings_api.c
  49. 6 0
      modules/settings_api.h
  50. 8 0
      modules/settings_api_bt6703.c
  51. 8 0
      modules/settings_api_bt6707.c
  52. 12 0
      modules/settings_api_bt6709.c
  53. 8 0
      modules/settings_api_bt6710.c

+ 50 - 0
.vscode/c_cpp_properties.json

@@ -1,5 +1,31 @@
 {
     "configurations": [
+        {
+            "name": "Mac",
+            "includePath": [
+                "/usr/include",
+                "/usr/local/include",
+                "${workspaceFolder}"
+            ],
+            "defines": [],
+            "intelliSenseMode": "clang-x64",
+            "browse": {
+                "path": [
+                    "/usr/include",
+                    "/usr/local/include",
+                    "${workspaceFolder}"
+                ],
+                "limitSymbolsToIncludedHeaders": true,
+                "databaseFilename": ""
+            },
+            "macFrameworkPath": [
+                "/System/Library/Frameworks",
+                "/Library/Frameworks"
+            ],
+            "compilerPath": "/usr/bin/gcc",
+            "cStandard": "c11",
+            "cppStandard": "c++17"
+        },
         {
             "name": "Linux",
             "includePath": [
@@ -35,6 +61,30 @@
             "compilerPath": "/usr/bin/gcc",
             "cStandard": "c11",
             "cppStandard": "c++17"
+        },
+        {
+            "name": "Win32",
+            "includePath": [
+                "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include",
+                "${workspaceFolder}"
+            ],
+            "defines": [
+                "_DEBUG",
+                "UNICODE",
+                "_UNICODE"
+            ],
+            "intelliSenseMode": "msvc-x64",
+            "browse": {
+                "path": [
+                    "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/include/*",
+                    "${workspaceFolder}"
+                ],
+                "limitSymbolsToIncludedHeaders": true,
+                "databaseFilename": ""
+            },
+            "compilerPath": "/usr/bin/gcc",
+            "cStandard": "c11",
+            "cppStandard": "c++17"
         }
     ],
     "version": 4

+ 3 - 1
.vscode/settings.json

@@ -39,6 +39,8 @@
         "stm32f4xx_dma.h": "c",
         "stm32f4xx.h": "c",
         "thread": "c",
-        "usart.h": "c"
+        "usart.h": "c",
+        "fr_timers.h": "c",
+        "string.h": "c"
     }
 }

+ 6 - 3
Makefile

@@ -1,4 +1,4 @@
-TARGETS = projects/gcc/tools iap modules service_hw 
+TARGETS = projects/gcc/tools modules service_hw #iap
 BUILDDIR = build
 
 .PHONY: all config $(TARGETS) tools distclean
@@ -68,8 +68,11 @@ release_6703:
 release_6707:
 	$(MAKE) -C modules HARDWARE=bt6707 VERBOSE=1 DEBUG=0 PRINTF=custom MAC=EC-4C-4D-00-80-0A release
 
-release_6709:
-	$(MAKE) -C modules HARDWARE=bt6709 VERBOSE=1 DEBUG=0 PRINTF=custom MAC=EC-4C-4D-00-80-0A release
+release_6709_mts:
+	$(MAKE) -C modules HARDWARE=bt6709 ORDER=mts VERBOSE=1 DEBUG=0 PRINTF=custom MAC=EC-4C-4D-00-80-0A release
+
+release_6709_beeline:
+	$(MAKE) -C modules HARDWARE=bt6709 ORDER=beeline VERBOSE=1 DEBUG=0 PRINTF=custom MAC=EC-4C-4D-00-80-0A release
 
 release_6710:
 	$(MAKE) -C modules HARDWARE=bt6710 VERBOSE=1 DEBUG=0 PRINTF=custom MAC=EC-4C-4D-00-80-0A release

+ 1 - 1
config/board_bt6703.h

@@ -61,7 +61,7 @@ WDG_PIN(X)
 /**
   * @brief  Версия прошивки
   */
-#define VERSION                         "1.0"
+#define VERSION                         "1.0a"
 
 #define RTC_ENABLE                  //RTC clock
 #define LED_ENABLE                  //LED индикация

+ 1 - 1
config/board_bt6709.h

@@ -52,7 +52,7 @@ WDG_PIN(X)
 /**
   * @brief  Версия прошивки
   */
-#define VERSION                         "1.2"
+#define VERSION                         "1.3"
 
 /**
   * brief  Текст сообщения при запуске CLI

+ 4 - 0
config/common_config.h

@@ -16,11 +16,15 @@
 
 #include "board.h"
 
+#define HW_REV_UPPER                    "NONE"
 
 #define HW_REV_LEN                      16
 
 #define HW_REV_OFFSET                   0x1F0
 
+#define DAUGHTER_FW_NAME                "NONE"
+#define MAIN_FW_NAME                    "ANY"
+
 
 /**
   * @brief  Адрес сектора настроек

+ 9 - 0
docs/bt6709/Changelog.md

@@ -1,3 +1,12 @@
+# 1.3
+### Добавлено
+[CLI] Вывод серийного номера ИБП в ответе на команду info
+
+### Исправлено
+[SNMP] Запуск отправки trap-сообщений
+[NTP] Запуск синхронизации времени
+
+
 # 1.2
 ### Добавлено
 1. Поддержка SSH.

+ 123 - 0
iap/FATFS/00readme.txt

@@ -0,0 +1,123 @@
+FatFs Module Source Files R0.08a                       (C)ChaN, 2010
+
+
+FILES
+
+  ffconf.h   Configuration file for FatFs module.
+  ff.h       Common include file for FatFs and application module.
+  ff.c       FatFs module.
+  diskio.h   Common include file for FatFs and disk I/O module.
+  integer.h  Alternative type definitions for integer variables.
+  option     Optional external functions.
+
+  Low level disk I/O module is not included in this archive because the FatFs
+  module is only a generic file system layer and not depend on any specific
+  storage device. You have to provide a low level disk I/O module that written
+  to control your storage device.
+
+
+
+AGREEMENTS
+
+ FatFs module is an open source software to implement FAT file system to
+ small embedded systems. This is a free software and is opened for education,
+ research and commercial developments under license policy of following trems.
+
+  Copyright (C) 2010, ChaN, all right reserved.
+
+ * The FatFs module is a free software and there is NO WARRANTY.
+ * No restriction on use. You can use, modify and redistribute it for
+   personal, non-profit or commercial product UNDER YOUR RESPONSIBILITY.
+ * Redistributions of source code must retain the above copyright notice.
+
+
+
+REVISION HISTORY
+
+  Feb 26, 2006  R0.00  Prototype
+
+  Apr 29, 2006  R0.01  First release.
+
+  Jun 01, 2006  R0.02  Added FAT12.
+                       Removed unbuffered mode.
+                       Fixed a problem on small (<32M) patition.
+
+  Jun 10, 2006  R0.02a Added a configuration option _FS_MINIMUM.
+
+  Sep 22, 2006  R0.03  Added f_rename.
+                       Changed option _FS_MINIMUM to _FS_MINIMIZE.
+
+  Dec 11, 2006  R0.03a Improved cluster scan algolithm to write files fast.
+                       Fixed f_mkdir creates incorrect directory on FAT32.
+
+  Feb 04, 2007  R0.04  Supported multiple drive system. (FatFs)
+                       Changed some APIs for multiple drive system.
+                       Added f_mkfs. (FatFs)
+                       Added _USE_FAT32 option. (Tiny-FatFs)
+
+  Apr 01, 2007  R0.04a Supported multiple partitions on a plysical drive. (FatFs)
+                       Fixed an endian sensitive code in f_mkfs. (FatFs)
+                       Added a capability of extending the file size to f_lseek.
+                       Added minimization level 3.
+                       Fixed a problem that can collapse a sector when recreate an
+                       existing file in any sub-directory at non FAT32 cfg. (Tiny-FatFs)
+
+  May 05, 2007  R0.04b Added _USE_NTFLAG option.
+                       Added FSInfo support.
+                       Fixed some problems corresponds to FAT32. (Tiny-FatFs)
+                       Fixed DBCS name can result FR_INVALID_NAME.
+                       Fixed short seek (0 < ofs <= csize) collapses the file object.
+
+  Aug 25, 2007  R0.05  Changed arguments of f_read, f_write.
+                       Changed arguments of f_mkfs. (FatFs)
+                       Fixed f_mkfs on FAT32 creates incorrect FSInfo. (FatFs)
+                       Fixed f_mkdir on FAT32 creates incorrect directory. (FatFs)
+
+  Feb 03, 2008  R0.05a Added f_truncate().
+                       Added f_utime().
+                       Fixed off by one error at FAT sub-type determination.
+                       Fixed btr in f_read() can be mistruncated.
+                       Fixed cached sector is not flushed when create and close without write.
+
+  Apr 01, 2008  R0.06  Added f_forward(). (Tiny-FatFs)
+                       Added string functions: fputc(), fputs(), fprintf() and fgets().
+                       Improved performance of f_lseek() on move to the same or following cluster.
+
+  Apr 01, 2009, R0.07  Merged Tiny-FatFs as a buffer configuration option.
+                       Added long file name support.
+                       Added multiple code page support.
+                       Added re-entrancy for multitask operation.
+                       Added auto cluster size selection to f_mkfs().
+                       Added rewind option to f_readdir().
+                       Changed result code of critical errors.
+                       Renamed string functions to avoid name collision.
+
+  Apr 14, 2009, R0.07a Separated out OS dependent code on reentrant cfg.
+                       Added multiple sector size support.
+
+  Jun 21, 2009, R0.07c Fixed f_unlink() may return FR_OK on error.
+                       Fixed wrong cache control in f_lseek().
+                       Added relative path feature.
+                       Added f_chdir().
+                       Added f_chdrive().
+                       Added proper case conversion for extended characters.
+
+  Nov 03, 2009 R0.07e  Separated out configuration options from ff.h to ffconf.h.
+                       Added a configuration option, _LFN_UNICODE.
+                       Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH.
+                       Fixed name matching error on the 13 char boundary.
+                       Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.
+
+  May 15, 2010, R0.08  Added a memory configuration option. (_USE_LFN)
+                       Added file lock feature. (_FS_SHARE)
+                       Added fast seek feature. (_USE_FASTSEEK)
+                       Changed some types on the API, XCHAR->TCHAR.
+                       Changed fname member in the FILINFO structure on Unicode cfg.
+                       String functions support UTF-8 encoding files on Unicode cfg.
+
+  Aug 16,'10 R0.08a    Added f_getcwd(). (_FS_RPATH = 2)
+                       Added sector erase feature. (_USE_ERASE)
+                       Moved file lock semaphore table from fs object to the bss.
+                       Fixed a wrong directory entry is created on non-LFN cfg when the given name contains ';'.
+                       Fixed f_mkfs() creates wrong FAT32 volume.
+

+ 140 - 0
iap/FATFS/diskio.c

@@ -0,0 +1,140 @@
+/*-----------------------------------------------------------------------*/
+/* Low level disk I/O module skeleton for FatFs     (C)ChaN, 2013        */
+/*-----------------------------------------------------------------------*/
+/* If a working storage control module is available, it should be        */
+/* attached to the FatFs via a glue function rather than modifying it.   */
+/* This is an example of glue functions to attach various exsisting      */
+/* storage control module to the FatFs module with a defined API.        */
+/*-----------------------------------------------------------------------*/
+
+#include "diskio.h"		/* FatFs lower layer API */
+#include <string.h> // memcpy
+
+#include "stm32f4xx.h"
+#include "../SD_Card/sdio_sd.h"
+
+#define BLOCK_SIZE            512 /* Block Size in Bytes */
+
+/* Definitions of physical drive number for each media */
+#define ATA		0
+#define MMC		1
+#define USB		2
+
+
+/*-----------------------------------------------------------------------*/
+/* Inidialize a Drive                                                    */
+/*-----------------------------------------------------------------------*/
+
+DSTATUS disk_initialize (
+	BYTE pdrv				/* Physical drive nmuber (0..) */
+)
+{
+	SD_Error  Status;
+
+	/* Supports only single drive */
+	if (pdrv) return STA_NOINIT;
+	/*-------------------------- SD Init ----------------------------- */
+	Status = SD_Init();
+	if (Status!=SD_OK ) return STA_NOINIT;
+	else return RES_OK;
+}
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get Disk Status                                                       */
+/*-----------------------------------------------------------------------*/
+
+DSTATUS disk_status (
+	BYTE pdrv		/* Physical drive nmuber (0..) */
+)
+{
+	DSTATUS stat = 0;
+
+	if (SD_Detect() != SD_PRESENT)
+		stat |= STA_NODISK;
+
+	// STA_NOTINIT - Subsystem not initailized
+	// STA_PROTECTED - Write protected, MMC/SD switch if available
+
+	return(stat);
+}
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read Sector(s)                                                        */
+/*-----------------------------------------------------------------------*/
+
+DRESULT disk_read (
+	BYTE pdrv,		/* Physical drive nmuber (0..) */
+	BYTE *buff,		/* Data buffer to store read data */
+	DWORD sector,	/* Sector address (LBA) */
+	UINT count		/* Number of sectors to read (1..128) */
+)
+{
+	SDTransferState state;
+	DRESULT res = RES_OK;
+
+	if (SD_Detect() != SD_PRESENT) return RES_NOTRDY;
+
+	if (count == 0) return RES_PARERR;
+
+	//SD_ReadMultiBlocks ( buff, sector, 512, 1 );
+	SD_ReadMultiBlocksFIXED (buff, sector, 512, count);
+
+	SD_WaitReadOperation();
+    do {
+      state = SD_GetStatus();
+      if(state == SD_TRANSFER_ERROR) {
+        res = RES_ERROR;
+        break;
+      }
+	} while (state != SD_TRANSFER_OK);
+
+	return res;
+}
+
+/*-----------------------------------------------------------------------*/
+/* Write Sector(s)                                                       */
+/*-----------------------------------------------------------------------*/
+
+#if _USE_WRITE
+DRESULT disk_write (
+	BYTE pdrv,			/* Physical drive nmuber (0..) */
+	const BYTE *buff,	/* Data to be written */
+	DWORD sector,		/* Sector address (LBA) */
+	UINT count			/* Number of sectors to write (1..128) */
+)
+{
+	//SDTransferState state;
+	//  DRESULT res = RES_OK;
+
+	if (SD_Detect( ) != SD_PRESENT) return RES_NOTRDY;
+
+	if (count == 0) return RES_PARERR;
+
+	//SD_WriteMultiBlocks ( (uint8_t *)buff, sector, 512, count );
+	SD_WriteMultiBlocksFIXED ((uint8_t *)buff, sector, 512, count);
+	SD_WaitWriteOperation();
+	while (SD_GetStatus() != SD_TRANSFER_OK);
+
+	return RES_OK;
+}
+#endif
+
+
+/*-----------------------------------------------------------------------*/
+/* Miscellaneous Functions                                               */
+/*-----------------------------------------------------------------------*/
+
+#if _USE_IOCTL
+DRESULT disk_ioctl (
+	BYTE pdrv,		/* Physical drive nmuber (0..) */
+	BYTE cmd,		/* Control code */
+	void *buff		/* Buffer to send/receive control data */
+)
+{
+	return RES_OK;
+}
+#endif

+ 80 - 0
iap/FATFS/diskio.h

@@ -0,0 +1,80 @@
+/*-----------------------------------------------------------------------/
+/  Low level disk interface modlue include file   (C)ChaN, 2013          /
+/-----------------------------------------------------------------------*/
+
+#ifndef _DISKIO_DEFINED
+#define _DISKIO_DEFINED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define _USE_WRITE	1	/* 1: Enable disk_write function */
+#define _USE_IOCTL	1	/* 1: Enable disk_ioctl fucntion */
+
+#include "integer.h"
+
+
+/* Status of Disk Functions */
+typedef BYTE	DSTATUS;
+
+/* Results of Disk Functions */
+typedef enum {
+	RES_OK = 0,		/* 0: Successful */
+	RES_ERROR,		/* 1: R/W Error */
+	RES_WRPRT,		/* 2: Write Protected */
+	RES_NOTRDY,		/* 3: Not Ready */
+	RES_PARERR		/* 4: Invalid Parameter */
+} DRESULT;
+
+
+/*---------------------------------------*/
+/* Prototypes for disk control functions */
+
+
+DSTATUS disk_initialize (BYTE pdrv);
+DSTATUS disk_status (BYTE pdrv);
+DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
+DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
+DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
+
+
+/* Disk Status Bits (DSTATUS) */
+
+#define STA_NOINIT		0x01	/* Drive not initialized */
+#define STA_NODISK		0x02	/* No medium in the drive */
+#define STA_PROTECT		0x04	/* Write protected */
+
+
+/* Command code for disk_ioctrl fucntion */
+
+/* Generic command (used by FatFs) */
+#define CTRL_SYNC			0	/* Flush disk cache (for write functions) */
+#define GET_SECTOR_COUNT	1	/* Get media size (for only f_mkfs()) */
+#define GET_SECTOR_SIZE		2	/* Get sector size (for multiple sector size (_MAX_SS >= 1024)) */
+#define GET_BLOCK_SIZE		3	/* Get erase block size (for only f_mkfs()) */
+#define CTRL_ERASE_SECTOR	4	/* Force erased a block of sectors (for only _USE_ERASE) */
+
+/* Generic command (not used by FatFs) */
+#define CTRL_POWER			5	/* Get/Set power status */
+#define CTRL_LOCK			6	/* Lock/Unlock media removal */
+#define CTRL_EJECT			7	/* Eject media */
+#define CTRL_FORMAT			8	/* Create physical format on the media */
+
+/* MMC/SDC specific ioctl command */
+#define MMC_GET_TYPE		10	/* Get card type */
+#define MMC_GET_CSD			11	/* Get CSD */
+#define MMC_GET_CID			12	/* Get CID */
+#define MMC_GET_OCR			13	/* Get OCR */
+#define MMC_GET_SDSTAT		14	/* Get SD status */
+
+/* ATA/CF specific ioctl command */
+#define ATA_GET_REV			20	/* Get F/W revision */
+#define ATA_GET_MODEL		21	/* Get model name */
+#define ATA_GET_SN			22	/* Get serial number */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 4606 - 0
iap/FATFS/ff.c

@@ -0,0 +1,4606 @@
+/*----------------------------------------------------------------------------/
+/  FatFs - FAT file system module  R0.10b                (C)ChaN, 2014
+/-----------------------------------------------------------------------------/
+/ FatFs module is a generic FAT file system module for small embedded systems.
+/ This is a free software that opened for education, research and commercial
+/ developments under license policy of following terms.
+/
+/  Copyright (C) 2014, ChaN, all right reserved.
+/
+/ * The FatFs module is a free software and there is NO WARRANTY.
+/ * No restriction on use. You can use, modify and redistribute it for
+/   personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
+/ * Redistributions of source code must retain the above copyright notice.
+/
+/-----------------------------------------------------------------------------/
+/ Feb 26,'06 R0.00  Prototype.
+/
+/ Apr 29,'06 R0.01  First stable version.
+/
+/ Jun 01,'06 R0.02  Added FAT12 support.
+/                   Removed unbuffered mode.
+/                   Fixed a problem on small (<32M) partition.
+/ Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM).
+/
+/ Sep 22,'06 R0.03  Added f_rename().
+/                   Changed option _FS_MINIMUM to _FS_MINIMIZE.
+/ Dec 11,'06 R0.03a Improved cluster scan algorithm to write files fast.
+/                   Fixed f_mkdir() creates incorrect directory on FAT32.
+/
+/ Feb 04,'07 R0.04  Supported multiple drive system.
+/                   Changed some interfaces for multiple drive system.
+/                   Changed f_mountdrv() to f_mount().
+/                   Added f_mkfs().
+/ Apr 01,'07 R0.04a Supported multiple partitions on a physical drive.
+/                   Added a capability of extending file size to f_lseek().
+/                   Added minimization level 3.
+/                   Fixed an endian sensitive code in f_mkfs().
+/ May 05,'07 R0.04b Added a configuration option _USE_NTFLAG.
+/                   Added FSINFO support.
+/                   Fixed DBCS name can result FR_INVALID_NAME.
+/                   Fixed short seek (<= csize) collapses the file object.
+/
+/ Aug 25,'07 R0.05  Changed arguments of f_read(), f_write() and f_mkfs().
+/                   Fixed f_mkfs() on FAT32 creates incorrect FSINFO.
+/                   Fixed f_mkdir() on FAT32 creates incorrect directory.
+/ Feb 03,'08 R0.05a Added f_truncate() and f_utime().
+/                   Fixed off by one error at FAT sub-type determination.
+/                   Fixed btr in f_read() can be mistruncated.
+/                   Fixed cached sector is not flushed when create and close without write.
+/
+/ Apr 01,'08 R0.06  Added fputc(), fputs(), fprintf() and fgets().
+/                   Improved performance of f_lseek() on moving to the same or following cluster.
+/
+/ Apr 01,'09 R0.07  Merged Tiny-FatFs as a configuration option. (_FS_TINY)
+/                   Added long file name feature.
+/                   Added multiple code page feature.
+/                   Added re-entrancy for multitask operation.
+/                   Added auto cluster size selection to f_mkfs().
+/                   Added rewind option to f_readdir().
+/                   Changed result code of critical errors.
+/                   Renamed string functions to avoid name collision.
+/ Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg.
+/                   Added multiple sector size feature.
+/ Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error.
+/                   Fixed wrong cache control in f_lseek().
+/                   Added relative path feature.
+/                   Added f_chdir() and f_chdrive().
+/                   Added proper case conversion to extended character.
+/ Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h.
+/                   Fixed f_unlink() fails to remove a sub-directory on _FS_RPATH.
+/                   Fixed name matching error on the 13 character boundary.
+/                   Added a configuration option, _LFN_UNICODE.
+/                   Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.
+/
+/ May 15,'10 R0.08  Added a memory configuration option. (_USE_LFN = 3)
+/                   Added file lock feature. (_FS_SHARE)
+/                   Added fast seek feature. (_USE_FASTSEEK)
+/                   Changed some types on the API, XCHAR->TCHAR.
+/                   Changed .fname in the FILINFO structure on Unicode cfg.
+/                   String functions support UTF-8 encoding files on Unicode cfg.
+/ Aug 16,'10 R0.08a Added f_getcwd().
+/                   Added sector erase feature. (_USE_ERASE)
+/                   Moved file lock semaphore table from fs object to the bss.
+/                   Fixed a wrong directory entry is created on non-LFN cfg when the given name contains ';'.
+/                   Fixed f_mkfs() creates wrong FAT32 volume.
+/ Jan 15,'11 R0.08b Fast seek feature is also applied to f_read() and f_write().
+/                   f_lseek() reports required table size on creating CLMP.
+/                   Extended format syntax of f_printf().
+/                   Ignores duplicated directory separators in given path name.
+/
+/ Sep 06,'11 R0.09  f_mkfs() supports multiple partition to complete the multiple partition feature.
+/                   Added f_fdisk().
+/ Aug 27,'12 R0.09a Changed f_open() and f_opendir() reject null object pointer to avoid crash.
+/                   Changed option name _FS_SHARE to _FS_LOCK.
+/                   Fixed assertion failure due to OS/2 EA on FAT12/16 volume.
+/ Jan 24,'13 R0.09b Added f_setlabel() and f_getlabel().
+/
+/ Oct 02,'13 R0.10  Added selection of character encoding on the file. (_STRF_ENCODE)
+/                   Added f_closedir().
+/                   Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO)
+/                   Added forced mount feature with changes of f_mount().
+/                   Improved behavior of volume auto detection.
+/                   Improved write throughput of f_puts() and f_printf().
+/                   Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write().
+/                   Fixed f_write() can be truncated when the file size is close to 4GB.
+/                   Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect error code.
+/ Jan 15,'14 R0.10a Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID)
+/                   Added a configuration option of minimum sector size. (_MIN_SS)
+/                   2nd argument of f_rename() can have a drive number and it will be ignored.
+/                   Fixed f_mount() with forced mount fails when drive number is >= 1.
+/                   Fixed f_close() invalidates the file object without volume lock.
+/                   Fixed f_closedir() returns but the volume lock is left acquired.
+/                   Fixed creation of an entry with LFN fails on too many SFN collisions.
+/ May 19,'14 R0.10b Fixed a hard error in the disk I/O layer can collapse the directory entry.
+/                   Fixed LFN entry is not deleted on delete/rename an object with lossy converted SFN.
+/---------------------------------------------------------------------------*/
+
+#include "ff.h"			/* Declarations of FatFs API */
+#include "diskio.h"		/* Declarations of disk I/O functions */
+
+#if _FS_REENTRANT
+#include "rtos.h"
+#include "semphr.h"
+extern volatile xSemaphoreHandle	Mutex_fatfs;
+#endif
+
+/*--------------------------------------------------------------------------
+
+   Module Private Definitions
+
+---------------------------------------------------------------------------*/
+
+#if _FATFS != 8051	/* Revision ID */
+#error Wrong include file (ff.h).
+#endif
+
+
+/* Reentrancy related */
+#if _FS_REENTRANT
+#if _USE_LFN == 1
+#error Static LFN work area cannot be used at thread-safe configuration.
+#endif
+#define	ENTER_FF(fs)		{ if (!lock_fs(fs)) return FR_TIMEOUT; }
+#define	LEAVE_FF(fs, res)	{ unlock_fs(fs, res); return res; }
+#else
+#define	ENTER_FF(fs)
+#define LEAVE_FF(fs, res)	return res
+#endif
+
+#define	ABORT(fs, res)		{ fp->err = (BYTE)(res); LEAVE_FF(fs, res); }
+
+
+/* Definitions of sector size */
+#if (_MAX_SS < _MIN_SS) || (_MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096) || (_MIN_SS != 512 && _MIN_SS != 1024 && _MIN_SS != 2048 && _MIN_SS != 4096)
+#error Wrong sector size configuration.
+#endif
+#if _MAX_SS == _MIN_SS
+#define	SS(fs)	((UINT)_MAX_SS)	/* Fixed sector size */
+#else
+#define	SS(fs)	((fs)->ssize)	/* Variable sector size */
+#endif
+
+
+/* File access control feature */
+#if _FS_LOCK
+#if _FS_READONLY
+#error _FS_LOCK must be 0 at read-only cfg.
+#endif
+typedef struct {
+	FATFS *fs;		/* Object ID 1, volume (NULL:blank entry) */
+	DWORD clu;		/* Object ID 2, directory (0:root) */
+	WORD idx;		/* Object ID 3, directory index */
+	WORD ctr;		/* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */
+} FILESEM;
+#endif
+
+
+
+/* DBCS code ranges and SBCS extend character conversion table */
+
+#if _CODE_PAGE == 932	/* Japanese Shift-JIS */
+#define _DF1S	0x81	/* DBC 1st byte range 1 start */
+#define _DF1E	0x9F	/* DBC 1st byte range 1 end */
+#define _DF2S	0xE0	/* DBC 1st byte range 2 start */
+#define _DF2E	0xFC	/* DBC 1st byte range 2 end */
+#define _DS1S	0x40	/* DBC 2nd byte range 1 start */
+#define _DS1E	0x7E	/* DBC 2nd byte range 1 end */
+#define _DS2S	0x80	/* DBC 2nd byte range 2 start */
+#define _DS2E	0xFC	/* DBC 2nd byte range 2 end */
+
+#elif _CODE_PAGE == 936	/* Simplified Chinese GBK */
+#define _DF1S	0x81
+#define _DF1E	0xFE
+#define _DS1S	0x40
+#define _DS1E	0x7E
+#define _DS2S	0x80
+#define _DS2E	0xFE
+
+#elif _CODE_PAGE == 949	/* Korean */
+#define _DF1S	0x81
+#define _DF1E	0xFE
+#define _DS1S	0x41
+#define _DS1E	0x5A
+#define _DS2S	0x61
+#define _DS2E	0x7A
+#define _DS3S	0x81
+#define _DS3E	0xFE
+
+#elif _CODE_PAGE == 950	/* Traditional Chinese Big5 */
+#define _DF1S	0x81
+#define _DF1E	0xFE
+#define _DS1S	0x40
+#define _DS1E	0x7E
+#define _DS2S	0xA1
+#define _DS2E	0xFE
+
+#elif _CODE_PAGE == 437	/* U.S. (OEM) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+		0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+		0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 720	/* Arabic (OEM) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+		0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+		0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 737	/* Greek (OEM) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \
+		0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+		0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 775	/* Baltic (OEM) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+		0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+		0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 850	/* Multilingual Latin 1 (OEM) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+		0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+		0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 852	/* Latin 2 (OEM) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \
+		0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+		0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}
+
+#elif _CODE_PAGE == 855	/* Cyrillic (OEM) */
+#define _DF1S	0
+#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \
+		0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \
+		0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 857	/* Turkish (OEM) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \
+		0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+		0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 858	/* Multilingual Latin 1 + Euro (OEM) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+		0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+		0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 862	/* Hebrew (OEM) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+		0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+		0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 866	/* Russian (OEM) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+		0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+		0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 874	/* Thai (OEM, Windows) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+		0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+		0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 1250 /* Central Europe (Windows) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \
+		0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}
+
+#elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \
+		0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF}
+
+#elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \
+		0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}
+
+#elif _CODE_PAGE == 1253 /* Greek (Windows) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+		0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \
+		0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF}
+
+#elif _CODE_PAGE == 1254 /* Turkish (Windows) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \
+		0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}
+
+#elif _CODE_PAGE == 1255 /* Hebrew (Windows) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+		0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+		0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 1256 /* Arabic (Windows) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \
+		0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+		0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 1257 /* Baltic (Windows) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+		0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}
+
+#elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */
+#define _DF1S	0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \
+		0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+		0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F}
+
+#elif _CODE_PAGE == 1	/* ASCII (for only non-LFN cfg) */
+#if _USE_LFN
+#error Cannot use LFN feature without valid code page.
+#endif
+#define _DF1S	0
+
+#else
+#error Unknown code page
+
+#endif
+
+
+/* Character code support macros */
+#define IsUpper(c)	(((c)>='A')&&((c)<='Z'))
+#define IsLower(c)	(((c)>='a')&&((c)<='z'))
+#define IsDigit(c)	(((c)>='0')&&((c)<='9'))
+
+#if _DF1S		/* Code page is DBCS */
+
+#ifdef _DF2S	/* Two 1st byte areas */
+#define IsDBCS1(c)	(((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E))
+#else			/* One 1st byte area */
+#define IsDBCS1(c)	((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E)
+#endif
+
+#ifdef _DS3S	/* Three 2nd byte areas */
+#define IsDBCS2(c)	(((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E))
+#else			/* Two 2nd byte areas */
+#define IsDBCS2(c)	(((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E))
+#endif
+
+#else			/* Code page is SBCS */
+
+#define IsDBCS1(c)	0
+#define IsDBCS2(c)	0
+
+#endif /* _DF1S */
+
+
+/* Name status flags */
+#define NS			11		/* Index of name status byte in fn[] */
+#define NS_LOSS		0x01	/* Out of 8.3 format */
+#define NS_LFN		0x02	/* Force to create LFN entry */
+#define NS_LAST		0x04	/* Last segment */
+#define NS_BODY		0x08	/* Lower case flag (body) */
+#define NS_EXT		0x10	/* Lower case flag (ext) */
+#define NS_DOT		0x20	/* Dot entry */
+
+
+/* FAT sub-type boundaries */
+#define MIN_FAT16	4086U	/* Minimum number of clusters for FAT16 */
+#define	MIN_FAT32	65526U	/* Minimum number of clusters for FAT32 */
+
+
+/* FatFs refers the members in the FAT structures as byte array instead of
+/ structure member because the structure is not binary compatible between
+/ different platforms */
+
+#define BS_jmpBoot			0		/* Jump instruction (3) */
+#define BS_OEMName			3		/* OEM name (8) */
+#define BPB_BytsPerSec		11		/* Sector size [byte] (2) */
+#define BPB_SecPerClus		13		/* Cluster size [sector] (1) */
+#define BPB_RsvdSecCnt		14		/* Size of reserved area [sector] (2) */
+#define BPB_NumFATs			16		/* Number of FAT copies (1) */
+#define BPB_RootEntCnt		17		/* Number of root directory entries for FAT12/16 (2) */
+#define BPB_TotSec16		19		/* Volume size [sector] (2) */
+#define BPB_Media			21		/* Media descriptor (1) */
+#define BPB_FATSz16			22		/* FAT size [sector] (2) */
+#define BPB_SecPerTrk		24		/* Track size [sector] (2) */
+#define BPB_NumHeads		26		/* Number of heads (2) */
+#define BPB_HiddSec			28		/* Number of special hidden sectors (4) */
+#define BPB_TotSec32		32		/* Volume size [sector] (4) */
+#define BS_DrvNum			36		/* Physical drive number (2) */
+#define BS_BootSig			38		/* Extended boot signature (1) */
+#define BS_VolID			39		/* Volume serial number (4) */
+#define BS_VolLab			43		/* Volume label (8) */
+#define BS_FilSysType		54		/* File system type (1) */
+#define BPB_FATSz32			36		/* FAT size [sector] (4) */
+#define BPB_ExtFlags		40		/* Extended flags (2) */
+#define BPB_FSVer			42		/* File system version (2) */
+#define BPB_RootClus		44		/* Root directory first cluster (4) */
+#define BPB_FSInfo			48		/* Offset of FSINFO sector (2) */
+#define BPB_BkBootSec		50		/* Offset of backup boot sector (2) */
+#define BS_DrvNum32			64		/* Physical drive number (2) */
+#define BS_BootSig32		66		/* Extended boot signature (1) */
+#define BS_VolID32			67		/* Volume serial number (4) */
+#define BS_VolLab32			71		/* Volume label (8) */
+#define BS_FilSysType32		82		/* File system type (1) */
+#define	FSI_LeadSig			0		/* FSI: Leading signature (4) */
+#define	FSI_StrucSig		484		/* FSI: Structure signature (4) */
+#define	FSI_Free_Count		488		/* FSI: Number of free clusters (4) */
+#define	FSI_Nxt_Free		492		/* FSI: Last allocated cluster (4) */
+#define MBR_Table			446		/* MBR: Partition table offset (2) */
+#define	SZ_PTE				16		/* MBR: Size of a partition table entry */
+#define BS_55AA				510		/* Signature word (2) */
+
+#define	DIR_Name			0		/* Short file name (11) */
+#define	DIR_Attr			11		/* Attribute (1) */
+#define	DIR_NTres			12		/* NT flag (1) */
+#define DIR_CrtTimeTenth	13		/* Created time sub-second (1) */
+#define	DIR_CrtTime			14		/* Created time (2) */
+#define	DIR_CrtDate			16		/* Created date (2) */
+#define DIR_LstAccDate		18		/* Last accessed date (2) */
+#define	DIR_FstClusHI		20		/* Higher 16-bit of first cluster (2) */
+#define	DIR_WrtTime			22		/* Modified time (2) */
+#define	DIR_WrtDate			24		/* Modified date (2) */
+#define	DIR_FstClusLO		26		/* Lower 16-bit of first cluster (2) */
+#define	DIR_FileSize		28		/* File size (4) */
+#define	LDIR_Ord			0		/* LFN entry order and LLE flag (1) */
+#define	LDIR_Attr			11		/* LFN attribute (1) */
+#define	LDIR_Type			12		/* LFN type (1) */
+#define	LDIR_Chksum			13		/* Sum of corresponding SFN entry */
+#define	LDIR_FstClusLO		26		/* Filled by zero (0) */
+#define	SZ_DIR				32		/* Size of a directory entry */
+#define	LLE					0x40	/* Last long entry flag in LDIR_Ord */
+#define	DDE					0xE5	/* Deleted directory entry mark in DIR_Name[0] */
+#define	NDDE				0x05	/* Replacement of the character collides with DDE */
+
+
+
+
+/*------------------------------------------------------------*/
+/* Module private work area                                   */
+/*------------------------------------------------------------*/
+/* Note that uninitialized variables with static duration are
+/  guaranteed zero/null as initial value. If not, either the
+/  linker or start-up routine is out of ANSI-C standard.
+ */
+
+#if _VOLUMES >= 1 || _VOLUMES <= 10
+static
+FATFS *FatFs[_VOLUMES];		/* Pointer to the file system objects (logical drives) */
+#else
+#error Number of volumes must be 1 to 10.
+#endif
+
+static
+WORD Fsid;					/* File system mount ID */
+
+#if _FS_RPATH && _VOLUMES >= 2
+static
+BYTE CurrVol;				/* Current drive */
+#endif
+
+#if _FS_LOCK
+static
+FILESEM	Files[_FS_LOCK];	/* Open object lock semaphores */
+#endif
+
+#if _USE_LFN == 0			/* No LFN feature */
+#define	DEF_NAMEBUF			BYTE sfn[12]
+#define INIT_BUF(dobj)		(dobj).fn = sfn
+#define	FREE_BUF()
+
+#elif _USE_LFN == 1			/* LFN feature with static working buffer */
+static
+WCHAR LfnBuf[_MAX_LFN+1];
+#define	DEF_NAMEBUF			BYTE sfn[12]
+#define INIT_BUF(dobj)		{ (dobj).fn = sfn; (dobj).lfn = LfnBuf; }
+#define	FREE_BUF()
+
+#elif _USE_LFN == 2 		/* LFN feature with dynamic working buffer on the stack */
+#define	DEF_NAMEBUF			BYTE sfn[12]; WCHAR lbuf[_MAX_LFN+1]
+#define INIT_BUF(dobj)		{ (dobj).fn = sfn; (dobj).lfn = lbuf; }
+#define	FREE_BUF()
+
+#elif _USE_LFN == 3 		/* LFN feature with dynamic working buffer on the heap */
+#define	DEF_NAMEBUF			BYTE sfn[12]; WCHAR *lfn
+#define INIT_BUF(dobj)		{ lfn = ff_memalloc((_MAX_LFN + 1) * 2); \
+		if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); \
+		(dobj).lfn = lfn;	(dobj).fn = sfn; }
+#define	FREE_BUF()			ff_memfree(lfn)
+
+#else
+#error Wrong LFN configuration.
+#endif
+
+
+#ifdef _EXCVT
+static
+const BYTE ExCvt[] = _EXCVT;	/* Upper conversion table for extended characters */
+#endif
+
+
+
+
+
+
+/*--------------------------------------------------------------------------
+
+   Module Private Functions
+
+---------------------------------------------------------------------------*/
+
+DWORD get_fattime ()
+{
+	return	/*((DWORD)(DateTime.date.RTC_Year - 1980) << 25)
+			| ((DWORD)DateTime.date.RTC_Month << 21)
+			| ((DWORD)DateTime.date.RTC_Date << 16)
+			| ((DWORD)DateTime.time.RTC_Hours << 11)
+			| ((DWORD)DateTime.time.RTC_Minutes << 5)
+			| ((DWORD)DateTime.time.RTC_Seconds >> 1);*/
+			((DWORD)(2000 - 1980) << 25)
+			| ((DWORD)1 << 21)
+			| ((DWORD)1 << 16)
+			| ((DWORD)1 << 11)
+			| ((DWORD)1 << 5)
+			| ((DWORD)1 >> 1);
+}
+
+/*-----------------------------------------------------------------------*/
+/* String functions                                                      */
+/*-----------------------------------------------------------------------*/
+
+/* Copy memory to memory */
+static
+void mem_cpy (void* dst, const void* src, UINT cnt) {
+	BYTE *d = (BYTE*)dst;
+	const BYTE *s = (const BYTE*)src;
+
+#if _WORD_ACCESS == 1
+	while (cnt >= sizeof (int)) {
+		*(int*)d = *(int*)s;
+		d += sizeof (int); s += sizeof (int);
+		cnt -= sizeof (int);
+	}
+#endif
+	while (cnt--)
+		*d++ = *s++;
+}
+
+/* Fill memory */
+static
+void mem_set (void* dst, int val, UINT cnt) {
+	BYTE *d = (BYTE*)dst;
+
+	while (cnt--)
+		*d++ = (BYTE)val;
+}
+
+/* Compare memory to memory */
+static
+int mem_cmp (const void* dst, const void* src, UINT cnt) {
+	const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src;
+	int r = 0;
+
+	while (cnt-- && (r = *d++ - *s++) == 0) ;
+	return r;
+}
+
+/* Check if chr is contained in the string */
+static
+int chk_chr (const char* str, int chr) {
+	while (*str && *str != chr) str++;
+	return *str;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Request/Release grant to access the volume                            */
+/*-----------------------------------------------------------------------*/
+#if _FS_REENTRANT
+static
+int lock_fs (
+		FATFS* fs		/* File system object */
+)
+{
+	return ff_req_grant(fs->sobj);
+}
+
+
+static
+void unlock_fs (
+		FATFS* fs,		/* File system object */
+		FRESULT res		/* Result code to be returned */
+)
+{
+	if (fs &&
+			res != FR_NOT_ENABLED &&
+			res != FR_INVALID_DRIVE &&
+			res != FR_INVALID_OBJECT &&
+			res != FR_TIMEOUT) {
+		ff_rel_grant(fs->sobj);
+	}
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* File lock control functions                                           */
+/*-----------------------------------------------------------------------*/
+#if _FS_LOCK
+
+static
+FRESULT chk_lock (	/* Check if the file can be accessed */
+		DIR* dp,		/* Directory object pointing the file to be checked */
+		int acc			/* Desired access type (0:Read, 1:Write, 2:Delete/Rename) */
+)
+{
+	UINT i, be;
+
+	/* Search file semaphore table */
+	for (i = be = 0; i < _FS_LOCK; i++) {
+		if (Files[i].fs) {	/* Existing entry */
+			if (Files[i].fs == dp->fs &&	 	/* Check if the object matched with an open object */
+					Files[i].clu == dp->sclust &&
+					Files[i].idx == dp->index) break;
+		} else {			/* Blank entry */
+			be = 1;
+		}
+	}
+	if (i == _FS_LOCK)	/* The object is not opened */
+		return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES;	/* Is there a blank entry for new object? */
+
+	/* The object has been opened. Reject any open against writing file and all write mode open */
+	return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK;
+}
+
+
+static
+int enq_lock (void)	/* Check if an entry is available for a new object */
+{
+	UINT i;
+
+	for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ;
+	return (i == _FS_LOCK) ? 0 : 1;
+}
+
+
+static
+UINT inc_lock (	/* Increment object open counter and returns its index (0:Internal error) */
+		DIR* dp,	/* Directory object pointing the file to register or increment */
+		int acc		/* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
+)
+{
+	UINT i;
+
+
+	for (i = 0; i < _FS_LOCK; i++) {	/* Find the object */
+		if (Files[i].fs == dp->fs &&
+				Files[i].clu == dp->sclust &&
+				Files[i].idx == dp->index) break;
+	}
+
+	if (i == _FS_LOCK) {				/* Not opened. Register it as new. */
+		for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ;
+		if (i == _FS_LOCK) return 0;	/* No free entry to register (int err) */
+		Files[i].fs = dp->fs;
+		Files[i].clu = dp->sclust;
+		Files[i].idx = dp->index;
+		Files[i].ctr = 0;
+	}
+
+	if (acc && Files[i].ctr) return 0;	/* Access violation (int err) */
+
+	Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1;	/* Set semaphore value */
+
+	return i + 1;
+}
+
+
+static
+FRESULT dec_lock (	/* Decrement object open counter */
+		UINT i			/* Semaphore index (1..) */
+)
+{
+	WORD n;
+	FRESULT res;
+
+
+	if (--i < _FS_LOCK) {	/* Shift index number origin from 0 */
+		n = Files[i].ctr;
+		if (n == 0x100) n = 0;		/* If write mode open, delete the entry */
+		if (n) n--;					/* Decrement read mode open count */
+		Files[i].ctr = n;
+		if (!n) Files[i].fs = 0;	/* Delete the entry if open count gets zero */
+		res = FR_OK;
+	} else {
+		res = FR_INT_ERR;			/* Invalid index nunber */
+	}
+	return res;
+}
+
+
+static
+void clear_lock (	/* Clear lock entries of the volume */
+		FATFS *fs
+)
+{
+	UINT i;
+
+	for (i = 0; i < _FS_LOCK; i++) {
+		if (Files[i].fs == fs) Files[i].fs = 0;
+	}
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Move/Flush disk access window in the file system object               */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT sync_window (
+		FATFS* fs		/* File system object */
+)
+{
+	DWORD wsect;
+	UINT nf;
+
+
+	if (fs->wflag) {	/* Write back the sector if it is dirty */
+		wsect = fs->winsect;	/* Current sector number */
+		if (disk_write(fs->drv, fs->win, wsect, 1))
+			return FR_DISK_ERR;
+		fs->wflag = 0;
+		if (wsect - fs->fatbase < fs->fsize) {		/* Is it in the FAT area? */
+			for (nf = fs->n_fats; nf >= 2; nf--) {	/* Reflect the change to all FAT copies */
+				wsect += fs->fsize;
+				disk_write(fs->drv, fs->win, wsect, 1);
+			}
+		}
+	}
+	return FR_OK;
+}
+#endif
+
+
+static
+FRESULT move_window (
+		FATFS* fs,		/* File system object */
+		DWORD sector	/* Sector number to make appearance in the fs->win[] */
+)
+{
+	if (sector != fs->winsect) {	/* Changed current window */
+#if !_FS_READONLY
+		if (sync_window(fs) != FR_OK)
+			return FR_DISK_ERR;
+#endif
+		if (disk_read(fs->drv, fs->win, sector, 1))
+			return FR_DISK_ERR;
+		fs->winsect = sector;
+	}
+
+	return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Synchronize file system and strage device                             */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT sync_fs (	/* FR_OK: successful, FR_DISK_ERR: failed */
+		FATFS* fs		/* File system object */
+)
+{
+	FRESULT res;
+
+
+	res = sync_window(fs);
+	if (res == FR_OK) {
+		/* Update FSINFO sector if needed */
+		if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) {
+			/* Create FSINFO structure */
+			mem_set(fs->win, 0, SS(fs));
+			ST_WORD(fs->win+BS_55AA, 0xAA55);
+			ST_DWORD(fs->win+FSI_LeadSig, 0x41615252);
+			ST_DWORD(fs->win+FSI_StrucSig, 0x61417272);
+			ST_DWORD(fs->win+FSI_Free_Count, fs->free_clust);
+			ST_DWORD(fs->win+FSI_Nxt_Free, fs->last_clust);
+			/* Write it into the FSINFO sector */
+			fs->winsect = fs->volbase + 1;
+			disk_write(fs->drv, fs->win, fs->winsect, 1);
+			fs->fsi_flag = 0;
+		}
+		/* Make sure that no pending write process in the physical drive */
+		if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK)
+			res = FR_DISK_ERR;
+	}
+
+	return res;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get sector# from cluster#                                             */
+/*-----------------------------------------------------------------------*/
+
+
+DWORD clust2sect (	/* !=0: Sector number, 0: Failed - invalid cluster# */
+		FATFS* fs,		/* File system object */
+		DWORD clst		/* Cluster# to be converted */
+)
+{
+	clst -= 2;
+	if (clst >= (fs->n_fatent - 2)) return 0;		/* Invalid cluster# */
+	return clst * fs->csize + fs->database;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT access - Read value of a FAT entry                                */
+/*-----------------------------------------------------------------------*/
+
+
+DWORD get_fat (	/* 0xFFFFFFFF:Disk error, 1:Internal error, Else:Cluster status */
+		FATFS* fs,	/* File system object */
+		DWORD clst	/* Cluster# to get the link information */
+)
+{
+	UINT wc, bc;
+	BYTE *p;
+
+
+	if (clst < 2 || clst >= fs->n_fatent)	/* Check range */
+		return 1;
+
+	switch (fs->fs_type) {
+	case FS_FAT12 :
+		bc = (UINT)clst; bc += bc / 2;
+		if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break;
+		wc = fs->win[bc % SS(fs)]; bc++;
+		if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break;
+		wc |= fs->win[bc % SS(fs)] << 8;
+		return clst & 1 ? wc >> 4 : (wc & 0xFFF);
+
+	case FS_FAT16 :
+		if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)))) break;
+		p = &fs->win[clst * 2 % SS(fs)];
+		return LD_WORD(p);
+
+	case FS_FAT32 :
+		if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)))) break;
+		p = &fs->win[clst * 4 % SS(fs)];
+		return LD_DWORD(p) & 0x0FFFFFFF;
+
+	default:
+		return 1;
+	}
+
+	return 0xFFFFFFFF;	/* An error occurred at the disk I/O layer */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT access - Change value of a FAT entry                              */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+
+FRESULT put_fat (
+		FATFS* fs,	/* File system object */
+		DWORD clst,	/* Cluster# to be changed in range of 2 to fs->n_fatent - 1 */
+		DWORD val	/* New value to mark the cluster */
+)
+{
+	UINT bc;
+	BYTE *p;
+	FRESULT res;
+
+
+	if (clst < 2 || clst >= fs->n_fatent) {	/* Check range */
+		res = FR_INT_ERR;
+
+	} else {
+		switch (fs->fs_type) {
+		case FS_FAT12 :
+			bc = (UINT)clst; bc += bc / 2;
+			res = move_window(fs, fs->fatbase + (bc / SS(fs)));
+			if (res != FR_OK) break;
+			p = &fs->win[bc % SS(fs)];
+			*p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;
+			bc++;
+			fs->wflag = 1;
+			res = move_window(fs, fs->fatbase + (bc / SS(fs)));
+			if (res != FR_OK) break;
+			p = &fs->win[bc % SS(fs)];
+			*p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));
+			break;
+
+		case FS_FAT16 :
+			res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)));
+			if (res != FR_OK) break;
+			p = &fs->win[clst * 2 % SS(fs)];
+			ST_WORD(p, (WORD)val);
+			break;
+
+		case FS_FAT32 :
+			res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));
+			if (res != FR_OK) break;
+			p = &fs->win[clst * 4 % SS(fs)];
+			val |= LD_DWORD(p) & 0xF0000000;
+			ST_DWORD(p, val);
+			break;
+
+		default :
+			res = FR_INT_ERR;
+		}
+		fs->wflag = 1;
+	}
+
+	return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Remove a cluster chain                                 */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT remove_chain (
+		FATFS* fs,			/* File system object */
+		DWORD clst			/* Cluster# to remove a chain from */
+)
+{
+	FRESULT res;
+	DWORD nxt;
+#if _USE_ERASE
+	DWORD scl = clst, ecl = clst, rt[2];
+#endif
+
+	if (clst < 2 || clst >= fs->n_fatent) {	/* Check range */
+		res = FR_INT_ERR;
+
+	} else {
+		res = FR_OK;
+		while (clst < fs->n_fatent) {			/* Not a last link? */
+			nxt = get_fat(fs, clst);			/* Get cluster status */
+			if (nxt == 0) break;				/* Empty cluster? */
+			if (nxt == 1) { res = FR_INT_ERR; break; }	/* Internal error? */
+			if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }	/* Disk error? */
+			res = put_fat(fs, clst, 0);			/* Mark the cluster "empty" */
+			if (res != FR_OK) break;
+			if (fs->free_clust != 0xFFFFFFFF) {	/* Update FSINFO */
+				fs->free_clust++;
+				fs->fsi_flag |= 1;
+			}
+#if _USE_ERASE
+			if (ecl + 1 == nxt) {	/* Is next cluster contiguous? */
+				ecl = nxt;
+			} else {				/* End of contiguous clusters */ 
+				rt[0] = clust2sect(fs, scl);					/* Start sector */
+				rt[1] = clust2sect(fs, ecl) + fs->csize - 1;	/* End sector */
+				disk_ioctl(fs->drv, CTRL_ERASE_SECTOR, rt);		/* Erase the block */
+				scl = ecl = nxt;
+			}
+#endif
+			clst = nxt;	/* Next cluster */
+		}
+	}
+
+	return res;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Stretch or Create a cluster chain                      */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+DWORD create_chain (	/* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
+		FATFS* fs,			/* File system object */
+		DWORD clst			/* Cluster# to stretch. 0 means create a new chain. */
+)
+{
+	DWORD cs, ncl, scl;
+	FRESULT res;
+
+
+	if (clst == 0) {		/* Create a new chain */
+		scl = fs->last_clust;			/* Get suggested start point */
+		if (!scl || scl >= fs->n_fatent) scl = 1;
+	}
+	else {					/* Stretch the current chain */
+		cs = get_fat(fs, clst);			/* Check the cluster status */
+		if (cs < 2) return 1;			/* Invalid value */
+		if (cs == 0xFFFFFFFF) return cs;	/* A disk error occurred */
+		if (cs < fs->n_fatent) return cs;	/* It is already followed by next cluster */
+		scl = clst;
+	}
+
+	ncl = scl;				/* Start cluster */
+	for (;;) {
+		ncl++;							/* Next cluster */
+		if (ncl >= fs->n_fatent) {		/* Check wrap around */
+			ncl = 2;
+			if (ncl > scl) return 0;	/* No free cluster */
+		}
+		cs = get_fat(fs, ncl);			/* Get the cluster status */
+		if (cs == 0) break;				/* Found a free cluster */
+		if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */
+			return cs;
+		if (ncl == scl) return 0;		/* No free cluster */
+	}
+
+	res = put_fat(fs, ncl, 0x0FFFFFFF);	/* Mark the new cluster "last link" */
+	if (res == FR_OK && clst != 0) {
+		res = put_fat(fs, clst, ncl);	/* Link it to the previous one if needed */
+	}
+	if (res == FR_OK) {
+		fs->last_clust = ncl;			/* Update FSINFO */
+		if (fs->free_clust != 0xFFFFFFFF) {
+			fs->free_clust--;
+			fs->fsi_flag |= 1;
+		}
+	} else {
+		ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1;
+	}
+
+	return ncl;		/* Return new cluster number or error code */
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Convert offset into cluster with link map table        */
+/*-----------------------------------------------------------------------*/
+
+#if _USE_FASTSEEK
+static
+DWORD clmt_clust (	/* <2:Error, >=2:Cluster number */
+		FIL* fp,		/* Pointer to the file object */
+		DWORD ofs		/* File offset to be converted to cluster# */
+)
+{
+	DWORD cl, ncl, *tbl;
+
+
+	tbl = fp->cltbl + 1;	/* Top of CLMT */
+	cl = ofs / SS(fp->fs) / fp->fs->csize;	/* Cluster order from top of the file */
+	for (;;) {
+		ncl = *tbl++;			/* Number of cluters in the fragment */
+		if (!ncl) return 0;		/* End of table? (error) */
+		if (cl < ncl) break;	/* In this fragment? */
+		cl -= ncl; tbl++;		/* Next fragment */
+	}
+	return cl + *tbl;	/* Return the cluster number */
+}
+#endif	/* _USE_FASTSEEK */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Set directory index                              */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_sdi (
+		DIR* dp,		/* Pointer to directory object */
+		UINT idx		/* Index of directory table */
+)
+{
+	DWORD clst, sect;
+	UINT ic;
+
+
+	dp->index = (WORD)idx;	/* Current index */
+	clst = dp->sclust;		/* Table start cluster (0:root) */
+	if (clst == 1 || clst >= dp->fs->n_fatent)	/* Check start cluster range */
+		return FR_INT_ERR;
+	if (!clst && dp->fs->fs_type == FS_FAT32)	/* Replace cluster# 0 with root cluster# if in FAT32 */
+		clst = dp->fs->dirbase;
+
+	if (clst == 0) {	/* Static table (root-directory in FAT12/16) */
+		if (idx >= dp->fs->n_rootdir)	/* Is index out of range? */
+			return FR_INT_ERR;
+		sect = dp->fs->dirbase;
+	}
+	else {				/* Dynamic table (root-directory in FAT32 or sub-directory) */
+		ic = SS(dp->fs) / SZ_DIR * dp->fs->csize;	/* Entries per cluster */
+		while (idx >= ic) {	/* Follow cluster chain */
+			clst = get_fat(dp->fs, clst);				/* Get next cluster */
+			if (clst == 0xFFFFFFFF) return FR_DISK_ERR;	/* Disk error */
+			if (clst < 2 || clst >= dp->fs->n_fatent)	/* Reached to end of table or internal error */
+				return FR_INT_ERR;
+			idx -= ic;
+		}
+		sect = clust2sect(dp->fs, clst);
+	}
+	dp->clust = clst;	/* Current cluster# */
+	if (!sect) return FR_INT_ERR;
+	dp->sect = sect + idx / (SS(dp->fs) / SZ_DIR);					/* Sector# of the directory entry */
+	dp->dir = dp->fs->win + (idx % (SS(dp->fs) / SZ_DIR)) * SZ_DIR;	/* Ptr to the entry in the sector */
+
+	return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Move directory table index next                  */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_next (	/* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */
+		DIR* dp,		/* Pointer to the directory object */
+		int stretch		/* 0: Do not stretch table, 1: Stretch table if needed */
+)
+{
+	DWORD clst;
+	UINT i;
+
+
+	i = dp->index + 1;
+	if (!(i & 0xFFFF) || !dp->sect)	/* Report EOT when index has reached 65535 */
+		return FR_NO_FILE;
+
+	if (!(i % (SS(dp->fs) / SZ_DIR))) {	/* Sector changed? */
+		dp->sect++;					/* Next sector */
+
+		if (!dp->clust) {		/* Static table */
+			if (i >= dp->fs->n_rootdir)	/* Report EOT if it reached end of static table */
+				return FR_NO_FILE;
+		}
+		else {					/* Dynamic table */
+			if (((i / (SS(dp->fs) / SZ_DIR)) & (dp->fs->csize - 1)) == 0) {	/* Cluster changed? */
+				clst = get_fat(dp->fs, dp->clust);				/* Get next cluster */
+				if (clst <= 1) return FR_INT_ERR;
+				if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
+				if (clst >= dp->fs->n_fatent) {					/* If it reached end of dynamic table, */
+#if !_FS_READONLY
+					UINT c;
+					if (!stretch) return FR_NO_FILE;			/* If do not stretch, report EOT */
+					clst = create_chain(dp->fs, dp->clust);		/* Stretch cluster chain */
+					if (clst == 0) return FR_DENIED;			/* No free cluster */
+					if (clst == 1) return FR_INT_ERR;
+					if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
+					/* Clean-up stretched table */
+					if (sync_window(dp->fs)) return FR_DISK_ERR;/* Flush disk access window */
+					mem_set(dp->fs->win, 0, SS(dp->fs));		/* Clear window buffer */
+					dp->fs->winsect = clust2sect(dp->fs, clst);	/* Cluster start sector */
+					for (c = 0; c < dp->fs->csize; c++) {		/* Fill the new cluster with 0 */
+						dp->fs->wflag = 1;
+						if (sync_window(dp->fs)) return FR_DISK_ERR;
+						dp->fs->winsect++;
+					}
+					dp->fs->winsect -= c;						/* Rewind window offset */
+#else
+					if (!stretch) return FR_NO_FILE;			/* If do not stretch, report EOT (this is to suppress warning) */
+					return FR_NO_FILE;							/* Report EOT */
+#endif
+				}
+				dp->clust = clst;				/* Initialize data for new cluster */
+				dp->sect = clust2sect(dp->fs, clst);
+			}
+		}
+	}
+
+	dp->index = (WORD)i;	/* Current index */
+	dp->dir = dp->fs->win + (i % (SS(dp->fs) / SZ_DIR)) * SZ_DIR;	/* Current entry in the window */
+
+	return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Reserve directory entry                          */
+/*-----------------------------------------------------------------------*/
+
+#if !_FS_READONLY
+static
+FRESULT dir_alloc (
+		DIR* dp,	/* Pointer to the directory object */
+		UINT nent	/* Number of contiguous entries to allocate (1-21) */
+)
+{
+	FRESULT res;
+	UINT n;
+
+
+	res = dir_sdi(dp, 0);
+	if (res == FR_OK) {
+		n = 0;
+		do {
+			res = move_window(dp->fs, dp->sect);
+			if (res != FR_OK) break;
+			if (dp->dir[0] == DDE || dp->dir[0] == 0) {	/* Is it a blank entry? */
+				if (++n == nent) break;	/* A block of contiguous entries is found */
+			} else {
+				n = 0;					/* Not a blank entry. Restart to search */
+			}
+			res = dir_next(dp, 1);		/* Next entry with table stretch enabled */
+		} while (res == FR_OK);
+	}
+	if (res == FR_NO_FILE) res = FR_DENIED;	/* No directory entry to allocate */
+	return res;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Load/Store start cluster number                  */
+/*-----------------------------------------------------------------------*/
+
+static
+DWORD ld_clust (
+		FATFS* fs,	/* Pointer to the fs object */
+		BYTE* dir	/* Pointer to the directory entry */
+)
+{
+	DWORD cl;
+
+	cl = LD_WORD(dir+DIR_FstClusLO);
+	if (fs->fs_type == FS_FAT32)
+		cl |= (DWORD)LD_WORD(dir+DIR_FstClusHI) << 16;
+
+	return cl;
+}
+
+
+#if !_FS_READONLY
+static
+void st_clust (
+		BYTE* dir,	/* Pointer to the directory entry */
+		DWORD cl	/* Value to be set */
+)
+{
+	ST_WORD(dir+DIR_FstClusLO, cl);
+	ST_WORD(dir+DIR_FstClusHI, cl >> 16);
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry   */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+static
+const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30};	/* Offset of LFN characters in the directory entry */
+
+
+static
+int cmp_lfn (			/* 1:Matched, 0:Not matched */
+		WCHAR* lfnbuf,		/* Pointer to the LFN to be compared */
+		BYTE* dir			/* Pointer to the directory entry containing a part of LFN */
+)
+{
+	UINT i, s;
+	WCHAR wc, uc;
+
+
+	i = ((dir[LDIR_Ord] & ~LLE) - 1) * 13;	/* Get offset in the LFN buffer */
+	s = 0; wc = 1;
+	do {
+		uc = LD_WORD(dir+LfnOfs[s]);	/* Pick an LFN character from the entry */
+		if (wc) {	/* Last character has not been processed */
+			wc = ff_wtoupper(uc);		/* Convert it to upper case */
+			if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++]))	/* Compare it */
+				return 0;				/* Not matched */
+		} else {
+			if (uc != 0xFFFF) return 0;	/* Check filler */
+		}
+	} while (++s < 13);				/* Repeat until all characters in the entry are checked */
+
+	if ((dir[LDIR_Ord] & LLE) && wc && lfnbuf[i])	/* Last segment matched but different length */
+		return 0;
+
+	return 1;						/* The part of LFN matched */
+}
+
+
+
+static
+int pick_lfn (			/* 1:Succeeded, 0:Buffer overflow */
+		WCHAR* lfnbuf,		/* Pointer to the Unicode-LFN buffer */
+		BYTE* dir			/* Pointer to the directory entry */
+)
+{
+	UINT i, s;
+	WCHAR wc, uc;
+
+
+	i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13;	/* Offset in the LFN buffer */
+
+	s = 0; wc = 1;
+	do {
+		uc = LD_WORD(dir+LfnOfs[s]);		/* Pick an LFN character from the entry */
+		if (wc) {	/* Last character has not been processed */
+			if (i >= _MAX_LFN) return 0;	/* Buffer overflow? */
+			lfnbuf[i++] = wc = uc;			/* Store it */
+		} else {
+			if (uc != 0xFFFF) return 0;		/* Check filler */
+		}
+	} while (++s < 13);						/* Read all character in the entry */
+
+	if (dir[LDIR_Ord] & LLE) {				/* Put terminator if it is the last LFN part */
+		if (i >= _MAX_LFN) return 0;		/* Buffer overflow? */
+		lfnbuf[i] = 0;
+	}
+
+	return 1;
+}
+
+
+#if !_FS_READONLY
+static
+void fit_lfn (
+		const WCHAR* lfnbuf,	/* Pointer to the LFN buffer */
+		BYTE* dir,				/* Pointer to the directory entry */
+		BYTE ord,				/* LFN order (1-20) */
+		BYTE sum				/* SFN sum */
+)
+{
+	UINT i, s;
+	WCHAR wc;
+
+
+	dir[LDIR_Chksum] = sum;			/* Set check sum */
+	dir[LDIR_Attr] = AM_LFN;		/* Set attribute. LFN entry */
+	dir[LDIR_Type] = 0;
+	ST_WORD(dir+LDIR_FstClusLO, 0);
+
+	i = (ord - 1) * 13;				/* Get offset in the LFN buffer */
+	s = wc = 0;
+	do {
+		if (wc != 0xFFFF) wc = lfnbuf[i++];	/* Get an effective character */
+		ST_WORD(dir+LfnOfs[s], wc);	/* Put it */
+		if (!wc) wc = 0xFFFF;		/* Padding characters following last character */
+	} while (++s < 13);
+	if (wc == 0xFFFF || !lfnbuf[i]) ord |= LLE;	/* Bottom LFN part is the start of LFN sequence */
+	dir[LDIR_Ord] = ord;			/* Set the LFN order */
+}
+
+#endif
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create numbered name                                                  */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+static
+void gen_numname (
+		BYTE* dst,			/* Pointer to the buffer to store numbered SFN */
+		const BYTE* src,	/* Pointer to SFN */
+		const WCHAR* lfn,	/* Pointer to LFN */
+		UINT seq			/* Sequence number */
+)
+{
+	BYTE ns[8], c;
+	UINT i, j;
+
+
+	mem_cpy(dst, src, 11);
+
+	if (seq > 5) {	/* On many collisions, generate a hash number instead of sequential number */
+		WCHAR wc;
+		DWORD sr = seq;
+
+		while (*lfn) {	/* Create a CRC */
+			wc = *lfn++;
+			for (i = 0; i < 16; i++) {
+				sr = (sr << 1) + (wc & 1);
+				wc >>= 1;
+				if (sr & 0x10000) sr ^= 0x11021;
+			}
+		}
+		seq = (UINT)sr;
+	}
+
+	/* itoa (hexdecimal) */
+	i = 7;
+	do {
+		c = (seq % 16) + '0';
+		if (c > '9') c += 7;
+		ns[i--] = c;
+		seq /= 16;
+	} while (seq);
+	ns[i] = '~';
+
+	/* Append the number */
+	for (j = 0; j < i && dst[j] != ' '; j++) {
+		if (IsDBCS1(dst[j])) {
+			if (j == i - 1) break;
+			j++;
+		}
+	}
+	do {
+		dst[j++] = (i < 8) ? ns[i++] : ' ';
+	} while (j < 8);
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Calculate sum of an SFN                                               */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+static
+BYTE sum_sfn (
+		const BYTE* dir		/* Pointer to the SFN entry */
+)
+{
+	BYTE sum = 0;
+	UINT n = 11;
+
+	do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n);
+	return sum;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Find an object in the directory                  */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_find (
+		DIR* dp			/* Pointer to the directory object linked to the file name */
+)
+{
+	FRESULT res;
+	BYTE c, *dir;
+#if _USE_LFN
+	BYTE a, ord, sum;
+#endif
+
+	res = dir_sdi(dp, 0);			/* Rewind directory object */
+	if (res != FR_OK) return res;
+
+#if _USE_LFN
+	ord = sum = 0xFF; dp->lfn_idx = 0xFFFF;	/* Reset LFN sequence */
+#endif
+	do {
+		res = move_window(dp->fs, dp->sect);
+		if (res != FR_OK) break;
+		dir = dp->dir;					/* Ptr to the directory entry of current index */
+		c = dir[DIR_Name];
+		if (c == 0) { res = FR_NO_FILE; break; }	/* Reached to end of table */
+#if _USE_LFN	/* LFN configuration */
+		a = dir[DIR_Attr] & AM_MASK;
+		if (c == DDE || ((a & AM_VOL) && a != AM_LFN)) {	/* An entry without valid data */
+			ord = 0xFF; dp->lfn_idx = 0xFFFF;	/* Reset LFN sequence */
+		} else {
+			if (a == AM_LFN) {			/* An LFN entry is found */
+				if (dp->lfn) {
+					if (c & LLE) {		/* Is it start of LFN sequence? */
+						sum = dir[LDIR_Chksum];
+						c &= ~LLE; ord = c;	/* LFN start order */
+						dp->lfn_idx = dp->index;	/* Start index of LFN */
+					}
+					/* Check validity of the LFN entry and compare it with given name */
+					ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dp->lfn, dir)) ? ord - 1 : 0xFF;
+				}
+			} else {					/* An SFN entry is found */
+				if (!ord && sum == sum_sfn(dir)) break;	/* LFN matched? */
+				if (!(dp->fn[NS] & NS_LOSS) && !mem_cmp(dir, dp->fn, 11)) break;	/* SFN matched? */
+				ord = 0xFF; dp->lfn_idx = 0xFFFF;	/* Reset LFN sequence */
+			}
+		}
+#else		/* Non LFN configuration */
+		if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dp->fn, 11)) /* Is it a valid entry? */
+			break;
+#endif
+		res = dir_next(dp, 0);		/* Next entry */
+	} while (res == FR_OK);
+
+	return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read an object from the directory                                     */
+/*-----------------------------------------------------------------------*/
+#if _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2
+static
+FRESULT dir_read (
+		DIR* dp,		/* Pointer to the directory object */
+		int vol			/* Filtered by 0:file/directory or 1:volume label */
+)
+{
+	FRESULT res;
+	BYTE a, c, *dir;
+#if _USE_LFN
+	BYTE ord = 0xFF, sum = 0xFF;
+#endif
+
+	res = FR_NO_FILE;
+	while (dp->sect) {
+		res = move_window(dp->fs, dp->sect);
+		if (res != FR_OK) break;
+		dir = dp->dir;					/* Ptr to the directory entry of current index */
+		c = dir[DIR_Name];
+		if (c == 0) { res = FR_NO_FILE; break; }	/* Reached to end of table */
+		a = dir[DIR_Attr] & AM_MASK;
+#if _USE_LFN	/* LFN configuration */
+		if (c == DDE || (!_FS_RPATH && c == '.') || (int)(a == AM_VOL) != vol) {	/* An entry without valid data */
+			ord = 0xFF;
+		} else {
+			if (a == AM_LFN) {			/* An LFN entry is found */
+				if (c & LLE) {			/* Is it start of LFN sequence? */
+					sum = dir[LDIR_Chksum];
+					c &= ~LLE; ord = c;
+					dp->lfn_idx = dp->index;
+				}
+				/* Check LFN validity and capture it */
+				ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dp->lfn, dir)) ? ord - 1 : 0xFF;
+			} else {					/* An SFN entry is found */
+				if (ord || sum != sum_sfn(dir))	/* Is there a valid LFN? */
+					dp->lfn_idx = 0xFFFF;		/* It has no LFN. */
+				break;
+			}
+		}
+#else		/* Non LFN configuration */
+		if (c != DDE && (_FS_RPATH || c != '.') && a != AM_LFN && (int)(a == AM_VOL) == vol)	/* Is it a valid entry? */
+			break;
+#endif
+		res = dir_next(dp, 0);				/* Next entry */
+		if (res != FR_OK) break;
+	}
+
+	if (res != FR_OK) dp->sect = 0;
+
+	return res;
+}
+#endif	/* _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Register an object to the directory                                   */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT dir_register (	/* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */
+		DIR* dp				/* Target directory with object name to be created */
+)
+{
+	FRESULT res;
+#if _USE_LFN	/* LFN configuration */
+	UINT n, nent;
+	BYTE sn[12], *fn, sum;
+	WCHAR *lfn;
+
+
+	fn = dp->fn; lfn = dp->lfn;
+	mem_cpy(sn, fn, 12);
+
+	if (_FS_RPATH && (sn[NS] & NS_DOT))		/* Cannot create dot entry */
+		return FR_INVALID_NAME;
+
+	if (sn[NS] & NS_LOSS) {			/* When LFN is out of 8.3 format, generate a numbered name */
+		fn[NS] = 0; dp->lfn = 0;			/* Find only SFN */
+		for (n = 1; n < 100; n++) {
+			gen_numname(fn, sn, lfn, n);	/* Generate a numbered name */
+			res = dir_find(dp);				/* Check if the name collides with existing SFN */
+			if (res != FR_OK) break;
+		}
+		if (n == 100) return FR_DENIED;		/* Abort if too many collisions */
+		if (res != FR_NO_FILE) return res;	/* Abort if the result is other than 'not collided' */
+		fn[NS] = sn[NS]; dp->lfn = lfn;
+	}
+
+	if (sn[NS] & NS_LFN) {			/* When LFN is to be created, allocate entries for an SFN + LFNs. */
+		for (n = 0; lfn[n]; n++) ;
+		nent = (n + 25) / 13;
+	} else {						/* Otherwise allocate an entry for an SFN  */
+		nent = 1;
+	}
+	res = dir_alloc(dp, nent);		/* Allocate entries */
+
+	if (res == FR_OK && --nent) {	/* Set LFN entry if needed */
+		res = dir_sdi(dp, dp->index - nent);
+		if (res == FR_OK) {
+			sum = sum_sfn(dp->fn);	/* Sum value of the SFN tied to the LFN */
+			do {					/* Store LFN entries in bottom first */
+				res = move_window(dp->fs, dp->sect);
+				if (res != FR_OK) break;
+				fit_lfn(dp->lfn, dp->dir, (BYTE)nent, sum);
+				dp->fs->wflag = 1;
+				res = dir_next(dp, 0);	/* Next entry */
+			} while (res == FR_OK && --nent);
+		}
+	}
+#else	/* Non LFN configuration */
+	res = dir_alloc(dp, 1);		/* Allocate an entry for SFN */
+#endif
+
+	if (res == FR_OK) {				/* Set SFN entry */
+		res = move_window(dp->fs, dp->sect);
+		if (res == FR_OK) {
+			mem_set(dp->dir, 0, SZ_DIR);	/* Clean the entry */
+			mem_cpy(dp->dir, dp->fn, 11);	/* Put SFN */
+#if _USE_LFN
+			dp->dir[DIR_NTres] = dp->fn[NS] & (NS_BODY | NS_EXT);	/* Put NT flag */
+#endif
+			dp->fs->wflag = 1;
+		}
+	}
+
+	return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Remove an object from the directory                                   */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY && !_FS_MINIMIZE
+static
+FRESULT dir_remove (	/* FR_OK: Successful, FR_DISK_ERR: A disk error */
+		DIR* dp				/* Directory object pointing the entry to be removed */
+)
+{
+	FRESULT res;
+#if _USE_LFN	/* LFN configuration */
+	UINT i;
+
+	i = dp->index;	/* SFN index */
+	res = dir_sdi(dp, (dp->lfn_idx == 0xFFFF) ? i : dp->lfn_idx);	/* Goto the SFN or top of the LFN entries */
+	if (res == FR_OK) {
+		do {
+			res = move_window(dp->fs, dp->sect);
+			if (res != FR_OK) break;
+			mem_set(dp->dir, 0, SZ_DIR);	/* Clear and mark the entry "deleted" */
+			*dp->dir = DDE;
+			dp->fs->wflag = 1;
+			if (dp->index >= i) break;	/* When reached SFN, all entries of the object has been deleted. */
+			res = dir_next(dp, 0);		/* Next entry */
+		} while (res == FR_OK);
+		if (res == FR_NO_FILE) res = FR_INT_ERR;
+	}
+
+#else			/* Non LFN configuration */
+	res = dir_sdi(dp, dp->index);
+	if (res == FR_OK) {
+		res = move_window(dp->fs, dp->sect);
+		if (res == FR_OK) {
+			mem_set(dp->dir, 0, SZ_DIR);	/* Clear and mark the entry "deleted" */
+			*dp->dir = DDE;
+			dp->fs->wflag = 1;
+		}
+	}
+#endif
+
+	return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get file information from directory entry                             */
+/*-----------------------------------------------------------------------*/
+#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2
+static
+void get_fileinfo (		/* No return code */
+		DIR* dp,			/* Pointer to the directory object */
+		FILINFO* fno	 	/* Pointer to the file information to be filled */
+)
+{
+	UINT i;
+	TCHAR *p, c;
+
+
+	p = fno->fname;
+	if (dp->sect) {		/* Get SFN */
+		BYTE *dir = dp->dir;
+
+		i = 0;
+		while (i < 11) {		/* Copy name body and extension */
+			c = (TCHAR)dir[i++];
+			if (c == ' ') continue;			/* Skip padding spaces */
+			if (c == NDDE) c = (TCHAR)DDE;	/* Restore replaced DDE character */
+			if (i == 9) *p++ = '.';			/* Insert a . if extension is exist */
+#if _USE_LFN
+			if (IsUpper(c) && (dir[DIR_NTres] & (i >= 9 ? NS_EXT : NS_BODY)))
+				c += 0x20;			/* To lower */
+#if _LFN_UNICODE
+			if (IsDBCS1(c) && i != 8 && i != 11 && IsDBCS2(dir[i]))
+				c = c << 8 | dir[i++];
+			c = ff_convert(c, 1);	/* OEM -> Unicode */
+			if (!c) c = '?';
+#endif
+#endif
+			*p++ = c;
+		}
+		fno->fattrib = dir[DIR_Attr];				/* Attribute */
+		fno->fsize = LD_DWORD(dir+DIR_FileSize);	/* Size */
+		fno->fdate = LD_WORD(dir+DIR_WrtDate);		/* Date */
+		fno->ftime = LD_WORD(dir+DIR_WrtTime);		/* Time */
+	}
+	*p = 0;		/* Terminate SFN string by a \0 */
+
+#if _USE_LFN
+	if (fno->lfname) {
+		WCHAR w, *lfn;
+
+		i = 0; p = fno->lfname;
+		if (dp->sect && fno->lfsize && dp->lfn_idx != 0xFFFF) {	/* Get LFN if available */
+			lfn = dp->lfn;
+			while ((w = *lfn++) != 0) {		/* Get an LFN character */
+#if !_LFN_UNICODE
+				w = ff_convert(w, 0);		/* Unicode -> OEM */
+				if (!w) { i = 0; break; }	/* No LFN if it could not be converted */
+				if (_DF1S && w >= 0x100)	/* Put 1st byte if it is a DBC (always false on SBCS cfg) */
+					p[i++] = (TCHAR)(w >> 8);
+#endif
+				if (i >= fno->lfsize - 1) { i = 0; break; }	/* No LFN if buffer overflow */
+				p[i++] = (TCHAR)w;
+			}
+		}
+		p[i] = 0;	/* Terminate LFN string by a \0 */
+	}
+#endif
+}
+#endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2*/
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Pick a segment and create the object name in directory form           */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT create_name (
+		DIR* dp,			/* Pointer to the directory object */
+		const TCHAR** path	/* Pointer to pointer to the segment in the path string */
+)
+{
+#if _USE_LFN	/* LFN configuration */
+	BYTE b, cf;
+	WCHAR w, *lfn;
+	UINT i, ni, si, di;
+	const TCHAR *p;
+
+	/* Create LFN in Unicode */
+	for (p = *path; *p == '/' || *p == '\\'; p++) ;	/* Strip duplicated separator */
+	lfn = dp->lfn;
+	si = di = 0;
+	for (;;) {
+		w = p[si++];					/* Get a character */
+		if (w < ' ' || w == '/' || w == '\\') break;	/* Break on end of segment */
+		if (di >= _MAX_LFN)				/* Reject too long name */
+			return FR_INVALID_NAME;
+#if !_LFN_UNICODE
+		w &= 0xFF;
+		if (IsDBCS1(w)) {				/* Check if it is a DBC 1st byte (always false on SBCS cfg) */
+			b = (BYTE)p[si++];			/* Get 2nd byte */
+			if (!IsDBCS2(b))
+				return FR_INVALID_NAME;	/* Reject invalid sequence */
+			w = (w << 8) + b;			/* Create a DBC */
+		}
+		w = ff_convert(w, 1);			/* Convert ANSI/OEM to Unicode */
+		if (!w) return FR_INVALID_NAME;	/* Reject invalid code */
+#endif
+		if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal characters for LFN */
+			return FR_INVALID_NAME;
+		lfn[di++] = w;					/* Store the Unicode character */
+	}
+	*path = &p[si];						/* Return pointer to the next segment */
+	cf = (w < ' ') ? NS_LAST : 0;		/* Set last segment flag if end of path */
+#if _FS_RPATH
+	if ((di == 1 && lfn[di-1] == '.') || /* Is this a dot entry? */
+			(di == 2 && lfn[di-1] == '.' && lfn[di-2] == '.')) {
+		lfn[di] = 0;
+		for (i = 0; i < 11; i++)
+			dp->fn[i] = (i < di) ? '.' : ' ';
+		dp->fn[i] = cf | NS_DOT;		/* This is a dot entry */
+		return FR_OK;
+	}
+#endif
+	while (di) {						/* Strip trailing spaces and dots */
+		w = lfn[di-1];
+		if (w != ' ' && w != '.') break;
+		di--;
+	}
+	if (!di) return FR_INVALID_NAME;	/* Reject nul string */
+
+	lfn[di] = 0;						/* LFN is created */
+
+	/* Create SFN in directory form */
+	mem_set(dp->fn, ' ', 11);
+	for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ;	/* Strip leading spaces and dots */
+	if (si) cf |= NS_LOSS | NS_LFN;
+	while (di && lfn[di - 1] != '.') di--;	/* Find extension (di<=si: no extension) */
+
+	b = i = 0; ni = 8;
+	for (;;) {
+		w = lfn[si++];					/* Get an LFN character */
+		if (!w) break;					/* Break on end of the LFN */
+		if (w == ' ' || (w == '.' && si != di)) {	/* Remove spaces and dots */
+			cf |= NS_LOSS | NS_LFN; continue;
+		}
+
+		if (i >= ni || si == di) {		/* Extension or end of SFN */
+			if (ni == 11) {				/* Long extension */
+				cf |= NS_LOSS | NS_LFN; break;
+			}
+			if (si != di) cf |= NS_LOSS | NS_LFN;	/* Out of 8.3 format */
+			if (si > di) break;			/* No extension */
+			si = di; i = 8; ni = 11;	/* Enter extension section */
+			b <<= 2; continue;
+		}
+
+		if (w >= 0x80) {				/* Non ASCII character */
+#ifdef _EXCVT
+			w = ff_convert(w, 0);		/* Unicode -> OEM code */
+			if (w) w = ExCvt[w - 0x80];	/* Convert extended character to upper (SBCS) */
+#else
+			w = ff_convert(ff_wtoupper(w), 0);	/* Upper converted Unicode -> OEM code */
+#endif
+			cf |= NS_LFN;				/* Force create LFN entry */
+		}
+
+		if (_DF1S && w >= 0x100) {		/* Double byte character (always false on SBCS cfg) */
+			if (i >= ni - 1) {
+				cf |= NS_LOSS | NS_LFN; i = ni; continue;
+			}
+			dp->fn[i++] = (BYTE)(w >> 8);
+		} else {						/* Single byte character */
+			if (!w || chk_chr("+,;=[]", w)) {	/* Replace illegal characters for SFN */
+				w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */
+			} else {
+				if (IsUpper(w)) {		/* ASCII large capital */
+					b |= 2;
+				} else {
+					if (IsLower(w)) {	/* ASCII small capital */
+						b |= 1; w -= 0x20;
+					}
+				}
+			}
+		}
+		dp->fn[i++] = (BYTE)w;
+	}
+
+	if (dp->fn[0] == DDE) dp->fn[0] = NDDE;	/* If the first character collides with deleted mark, replace it with 0x05 */
+
+	if (ni == 8) b <<= 2;
+	if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03)	/* Create LFN entry when there are composite capitals */
+		cf |= NS_LFN;
+	if (!(cf & NS_LFN)) {						/* When LFN is in 8.3 format without extended character, NT flags are created */
+		if ((b & 0x03) == 0x01) cf |= NS_EXT;	/* NT flag (Extension has only small capital) */
+		if ((b & 0x0C) == 0x04) cf |= NS_BODY;	/* NT flag (Filename has only small capital) */
+	}
+
+	dp->fn[NS] = cf;	/* SFN is created */
+
+	return FR_OK;
+
+
+#else	/* Non-LFN configuration */
+	BYTE b, c, d, *sfn;
+	UINT ni, si, i;
+	const char *p;
+
+	/* Create file name in directory form */
+	for (p = *path; *p == '/' || *p == '\\'; p++) ;	/* Strip duplicated separator */
+	sfn = dp->fn;
+	mem_set(sfn, ' ', 11);
+	si = i = b = 0; ni = 8;
+#if _FS_RPATH
+	if (p[si] == '.') { /* Is this a dot entry? */
+		for (;;) {
+			c = (BYTE)p[si++];
+			if (c != '.' || si >= 3) break;
+			sfn[i++] = c;
+		}
+		if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME;
+		*path = &p[si];									/* Return pointer to the next segment */
+		sfn[NS] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT;	/* Set last segment flag if end of path */
+		return FR_OK;
+	}
+#endif
+	for (;;) {
+		c = (BYTE)p[si++];
+		if (c <= ' ' || c == '/' || c == '\\') break;	/* Break on end of segment */
+		if (c == '.' || i >= ni) {
+			if (ni != 8 || c != '.') return FR_INVALID_NAME;
+			i = 8; ni = 11;
+			b <<= 2; continue;
+		}
+		if (c >= 0x80) {				/* Extended character? */
+			b |= 3;						/* Eliminate NT flag */
+#ifdef _EXCVT
+			c = ExCvt[c - 0x80];		/* To upper extended characters (SBCS cfg) */
+#else
+#if !_DF1S
+			return FR_INVALID_NAME;		/* Reject extended characters (ASCII cfg) */
+#endif
+#endif
+		}
+		if (IsDBCS1(c)) {				/* Check if it is a DBC 1st byte (always false on SBCS cfg) */
+			d = (BYTE)p[si++];			/* Get 2nd byte */
+			if (!IsDBCS2(d) || i >= ni - 1)	/* Reject invalid DBC */
+				return FR_INVALID_NAME;
+			sfn[i++] = c;
+			sfn[i++] = d;
+		} else {						/* Single byte code */
+			if (chk_chr("\"*+,:;<=>\?[]|\x7F", c))	/* Reject illegal chrs for SFN */
+				return FR_INVALID_NAME;
+			if (IsUpper(c)) {			/* ASCII large capital? */
+				b |= 2;
+			} else {
+				if (IsLower(c)) {		/* ASCII small capital? */
+					b |= 1; c -= 0x20;
+				}
+			}
+			sfn[i++] = c;
+		}
+	}
+	*path = &p[si];						/* Return pointer to the next segment */
+	c = (c <= ' ') ? NS_LAST : 0;		/* Set last segment flag if end of path */
+
+	if (!i) return FR_INVALID_NAME;		/* Reject nul string */
+	if (sfn[0] == DDE) sfn[0] = NDDE;	/* When first character collides with DDE, replace it with 0x05 */
+
+	if (ni == 8) b <<= 2;
+	if ((b & 0x03) == 0x01) c |= NS_EXT;	/* NT flag (Name extension has only small capital) */
+	if ((b & 0x0C) == 0x04) c |= NS_BODY;	/* NT flag (Name body has only small capital) */
+
+	sfn[NS] = c;		/* Store NT flag, File name is created */
+
+	return FR_OK;
+#endif
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Follow a file path                                                    */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT follow_path (	/* FR_OK(0): successful, !=0: error code */
+		DIR* dp,			/* Directory object to return last directory and found object */
+		const TCHAR* path	/* Full-path string to find a file or directory */
+)
+{
+	FRESULT res;
+	BYTE *dir, ns;
+
+
+#if _FS_RPATH
+	if (*path == '/' || *path == '\\') {	/* There is a heading separator */
+		path++;	dp->sclust = 0;				/* Strip it and start from the root directory */
+	} else {								/* No heading separator */
+		dp->sclust = dp->fs->cdir;			/* Start from the current directory */
+	}
+#else
+	if (*path == '/' || *path == '\\')		/* Strip heading separator if exist */
+		path++;
+	dp->sclust = 0;							/* Always start from the root directory */
+#endif
+
+	if ((UINT)*path < ' ') {				/* Null path name is the origin directory itself */
+		res = dir_sdi(dp, 0);
+		dp->dir = 0;
+	} else {								/* Follow path */
+		for (;;) {
+			res = create_name(dp, &path);	/* Get a segment name of the path */
+			if (res != FR_OK) break;
+			res = dir_find(dp);				/* Find an object with the sagment name */
+			ns = dp->fn[NS];
+			if (res != FR_OK) {				/* Failed to find the object */
+				if (res == FR_NO_FILE) {	/* Object is not found */
+					if (_FS_RPATH && (ns & NS_DOT)) {	/* If dot entry is not exist, */
+						dp->sclust = 0; dp->dir = 0;	/* it is the root directory and stay there */
+						if (!(ns & NS_LAST)) continue;	/* Continue to follow if not last segment */
+						res = FR_OK;					/* Ended at the root directroy. Function completed. */
+					} else {							/* Could not find the object */
+						if (!(ns & NS_LAST)) res = FR_NO_PATH;	/* Adjust error code if not last segment */
+					}
+				}
+				break;
+			}
+			if (ns & NS_LAST) break;			/* Last segment matched. Function completed. */
+			dir = dp->dir;						/* Follow the sub-directory */
+			if (!(dir[DIR_Attr] & AM_DIR)) {	/* It is not a sub-directory and cannot follow */
+				res = FR_NO_PATH; break;
+			}
+			dp->sclust = ld_clust(dp->fs, dir);
+		}
+	}
+
+	return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get logical drive number from path name                               */
+/*-----------------------------------------------------------------------*/
+
+static
+int get_ldnumber (		/* Returns logical drive number (-1:invalid drive) */
+		const TCHAR** path	/* Pointer to pointer to the path name */
+)
+{
+	const TCHAR *tp, *tt;
+	UINT i;
+	int vol = -1;
+
+
+	if (*path) {	/* If the pointer is not a null */
+		for (tt = *path; (UINT)*tt >= (_USE_LFN ? ' ' : '!') && *tt != ':'; tt++) ;	/* Find ':' in the path */
+		if (*tt == ':') {	/* If a ':' is exist in the path name */
+			tp = *path;
+			i = *tp++ - '0'; 
+			if (i < 10 && tp == tt) {	/* Is there a numeric drive id? */
+				if (i < _VOLUMES) {	/* If a drive id is found, get the value and strip it */
+					vol = (int)i;
+					*path = ++tt;
+				}
+			} else {	/* No numeric drive number */
+#if _STR_VOLUME_ID		/* Find string drive id */
+				static const char* const str[] = {_VOLUME_STRS};
+				const char *sp;
+				char c;
+				TCHAR tc;
+
+				i = 0; tt++;
+				do {
+					sp = str[i]; tp = *path;
+					do {	/* Compare a string drive id with path name */
+						c = *sp++; tc = *tp++;
+						if (IsLower(tc)) tc -= 0x20;
+					} while (c && (TCHAR)c == tc);
+				} while ((c || tp != tt) && ++i < _VOLUMES);	/* Repeat for each id until pattern match */
+				if (i < _VOLUMES) {	/* If a drive id is found, get the value and strip it */
+					vol = (int)i;
+					*path = tt;
+				}
+#endif
+			}
+			return vol;
+		}
+#if _FS_RPATH && _VOLUMES >= 2
+		vol = CurrVol;	/* Current drive */
+#else
+		vol = 0;		/* Drive 0 */
+#endif
+	}
+	return vol;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Load a sector and check if it is an FAT boot sector                   */
+/*-----------------------------------------------------------------------*/
+
+static
+BYTE check_fs (	/* 0:FAT boor sector, 1:Valid boor sector but not FAT, 2:Not a boot sector, 3:Disk error */
+		FATFS* fs,	/* File system object */
+		DWORD sect	/* Sector# (lba) to check if it is an FAT boot record or not */
+)
+{
+	fs->wflag = 0; fs->winsect = 0xFFFFFFFF;	/* Invaidate window */
+	if (move_window(fs, sect) != FR_OK)			/* Load boot record */
+		return 3;
+
+	if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55)	/* Check boot record signature (always placed at offset 510 even if the sector size is >512) */
+		return 2;
+
+	if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146)		/* Check "FAT" string */
+		return 0;
+	if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146)	/* Check "FAT" string */
+		return 0;
+
+	return 1;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Find logical drive and check if the volume is mounted                 */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT find_volume (	/* FR_OK(0): successful, !=0: any error occurred */
+		FATFS** rfs,		/* Pointer to pointer to the found file system object */
+		const TCHAR** path,	/* Pointer to pointer to the path name (drive number) */
+		BYTE wmode			/* !=0: Check write protection for write access */
+)
+{
+	BYTE fmt;
+	int vol;
+	DSTATUS stat;
+	DWORD bsect, fasize, tsect, sysect, nclst, szbfat;
+	WORD nrsv;
+	FATFS *fs;
+
+
+	/* Get logical drive number from the path name */
+	*rfs = 0;
+	vol = get_ldnumber(path);
+	if (vol < 0) return FR_INVALID_DRIVE;
+
+	/* Check if the file system object is valid or not */
+	fs = FatFs[vol];					/* Get pointer to the file system object */
+	if (!fs) return FR_NOT_ENABLED;		/* Is the file system object available? */
+
+	ENTER_FF(fs);						/* Lock the volume */
+	*rfs = fs;							/* Return pointer to the file system object */
+
+	if (fs->fs_type) {					/* If the volume has been mounted */
+		stat = disk_status(fs->drv);
+		if (!(stat & STA_NOINIT)) {		/* and the physical drive is kept initialized */
+			if (!_FS_READONLY && wmode && (stat & STA_PROTECT))	/* Check write protection if needed */
+				return FR_WRITE_PROTECTED;
+			return FR_OK;				/* The file system object is valid */
+		}
+	}
+
+	/* The file system object is not valid. */
+	/* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */
+
+	fs->fs_type = 0;					/* Clear the file system object */
+	fs->drv = LD2PD(vol);				/* Bind the logical drive and a physical drive */
+	stat = disk_initialize(fs->drv);	/* Initialize the physical drive */
+	if (stat & STA_NOINIT)				/* Check if the initialization succeeded */
+		return FR_NOT_READY;			/* Failed to initialize due to no medium or hard error */
+	if (!_FS_READONLY && wmode && (stat & STA_PROTECT))	/* Check disk write protection if needed */
+		return FR_WRITE_PROTECTED;
+#if _MAX_SS != _MIN_SS						/* Get sector size (multiple sector size cfg only) */
+	if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK
+			|| SS(fs) < _MIN_SS || SS(fs) > _MAX_SS) return FR_DISK_ERR;
+#endif
+	/* Find an FAT partition on the drive. Supports only generic partitioning, FDISK and SFD. */
+	bsect = 0;
+	fmt = check_fs(fs, bsect);					/* Load sector 0 and check if it is an FAT boot sector as SFD */
+	if (fmt == 1 || (!fmt && (LD2PT(vol)))) {	/* Not an FAT boot sector or forced partition number */
+		UINT i;
+		DWORD br[4];
+
+		for (i = 0; i < 4; i++) {			/* Get partition offset */
+			BYTE *pt = fs->win+MBR_Table + i * SZ_PTE;
+			br[i] = pt[4] ? LD_DWORD(&pt[8]) : 0;
+		}
+		i = LD2PT(vol);						/* Partition number: 0:auto, 1-4:forced */
+		if (i) i--;
+		do {								/* Find an FAT volume */
+			bsect = br[i];
+			fmt = bsect ? check_fs(fs, bsect) : 2;	/* Check the partition */
+		} while (!LD2PT(vol) && fmt && ++i < 4);
+	}
+	if (fmt == 3) return FR_DISK_ERR;		/* An error occured in the disk I/O layer */
+	if (fmt) return FR_NO_FILESYSTEM;		/* No FAT volume is found */
+
+	/* An FAT volume is found. Following code initializes the file system object */
+
+	if (LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs))		/* (BPB_BytsPerSec must be equal to the physical sector size) */
+		return FR_NO_FILESYSTEM;
+
+	fasize = LD_WORD(fs->win+BPB_FATSz16);				/* Number of sectors per FAT */
+	if (!fasize) fasize = LD_DWORD(fs->win+BPB_FATSz32);
+	fs->fsize = fasize;
+
+	fs->n_fats = fs->win[BPB_NumFATs];					/* Number of FAT copies */
+	if (fs->n_fats != 1 && fs->n_fats != 2)				/* (Must be 1 or 2) */
+		return FR_NO_FILESYSTEM;
+	fasize *= fs->n_fats;								/* Number of sectors for FAT area */
+
+	fs->csize = fs->win[BPB_SecPerClus];				/* Number of sectors per cluster */
+	if (!fs->csize || (fs->csize & (fs->csize - 1)))	/* (Must be power of 2) */
+		return FR_NO_FILESYSTEM;
+
+	fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt);	/* Number of root directory entries */
+	if (fs->n_rootdir % (SS(fs) / SZ_DIR))				/* (Must be sector aligned) */
+		return FR_NO_FILESYSTEM;
+
+	tsect = LD_WORD(fs->win+BPB_TotSec16);				/* Number of sectors on the volume */
+	if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32);
+
+	nrsv = LD_WORD(fs->win+BPB_RsvdSecCnt);				/* Number of reserved sectors */
+	if (!nrsv) return FR_NO_FILESYSTEM;					/* (Must not be 0) */
+
+	/* Determine the FAT sub type */
+	sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZ_DIR);	/* RSV+FAT+DIR */
+	if (tsect < sysect) return FR_NO_FILESYSTEM;		/* (Invalid volume size) */
+	nclst = (tsect - sysect) / fs->csize;				/* Number of clusters */
+	if (!nclst) return FR_NO_FILESYSTEM;				/* (Invalid volume size) */
+	fmt = FS_FAT12;
+	if (nclst >= MIN_FAT16) fmt = FS_FAT16;
+	if (nclst >= MIN_FAT32) fmt = FS_FAT32;
+
+	/* Boundaries and Limits */
+	fs->n_fatent = nclst + 2;							/* Number of FAT entries */
+	fs->volbase = bsect;								/* Volume start sector */
+	fs->fatbase = bsect + nrsv; 						/* FAT start sector */
+	fs->database = bsect + sysect;						/* Data start sector */
+	if (fmt == FS_FAT32) {
+		if (fs->n_rootdir) return FR_NO_FILESYSTEM;		/* (BPB_RootEntCnt must be 0) */
+		fs->dirbase = LD_DWORD(fs->win+BPB_RootClus);	/* Root directory start cluster */
+		szbfat = fs->n_fatent * 4;						/* (Needed FAT size) */
+	} else {
+		if (!fs->n_rootdir)	return FR_NO_FILESYSTEM;	/* (BPB_RootEntCnt must not be 0) */
+		fs->dirbase = fs->fatbase + fasize;				/* Root directory start sector */
+		szbfat = (fmt == FS_FAT16) ?					/* (Needed FAT size) */
+				fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1);
+	}
+	if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs))	/* (BPB_FATSz must not be less than needed) */
+		return FR_NO_FILESYSTEM;
+
+#if !_FS_READONLY
+	/* Initialize cluster allocation information */
+	fs->last_clust = fs->free_clust = 0xFFFFFFFF;
+
+	/* Get fsinfo if available */
+	fs->fsi_flag = 0x80;
+#if (_FS_NOFSINFO & 3) != 3
+	if (fmt == FS_FAT32				/* Enable FSINFO only if FAT32 and BPB_FSInfo is 1 */
+			&& LD_WORD(fs->win+BPB_FSInfo) == 1
+			&& move_window(fs, bsect + 1) == FR_OK)
+	{
+		fs->fsi_flag = 0;
+		if (LD_WORD(fs->win+BS_55AA) == 0xAA55	/* Load FSINFO data if available */
+				&& LD_DWORD(fs->win+FSI_LeadSig) == 0x41615252
+				&& LD_DWORD(fs->win+FSI_StrucSig) == 0x61417272)
+		{
+#if (_FS_NOFSINFO & 1) == 0
+			fs->free_clust = LD_DWORD(fs->win+FSI_Free_Count);
+#endif
+#if (_FS_NOFSINFO & 2) == 0
+			fs->last_clust = LD_DWORD(fs->win+FSI_Nxt_Free);
+#endif
+		}
+	}
+#endif
+#endif
+	fs->fs_type = fmt;	/* FAT sub-type */
+	fs->id = ++Fsid;	/* File system mount ID */
+#if _FS_RPATH
+	fs->cdir = 0;		/* Set current directory to root */
+#endif
+#if _FS_LOCK			/* Clear file lock semaphores */
+	clear_lock(fs);
+#endif
+
+	return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Check if the file/directory object is valid or not                    */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT validate (	/* FR_OK(0): The object is valid, !=0: Invalid */
+		void* obj		/* Pointer to the object FIL/DIR to check validity */
+)
+{
+	FIL *fil = (FIL*)obj;	/* Assuming offset of .fs and .id in the FIL/DIR structure is identical */
+
+
+	if (!fil || !fil->fs || !fil->fs->fs_type || fil->fs->id != fil->id)
+		return FR_INVALID_OBJECT;
+
+	ENTER_FF(fil->fs);		/* Lock file system */
+
+	if (disk_status(fil->fs->drv) & STA_NOINIT)
+		return FR_NOT_READY;
+
+	return FR_OK;
+}
+
+
+
+
+/*--------------------------------------------------------------------------
+
+   Public Functions
+
+--------------------------------------------------------------------------*/
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Mount/Unmount a Logical Drive                                         */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mount (
+		FATFS* fs,			/* Pointer to the file system object (NULL:unmount)*/
+		const TCHAR* path,	/* Logical drive number to be mounted/unmounted */
+		BYTE opt			/* 0:Do not mount (delayed mount), 1:Mount immediately */
+)
+{
+	FATFS *cfs;
+	int vol;
+	FRESULT res;
+	const TCHAR *rp = path;
+
+
+	vol = get_ldnumber(&rp);
+	if (vol < 0) return FR_INVALID_DRIVE;
+	cfs = FatFs[vol];					/* Pointer to fs object */
+
+	if (cfs) {
+#if _FS_LOCK
+		clear_lock(cfs);
+#endif
+#if _FS_REENTRANT						/* Discard sync object of the current volume */
+		if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR;
+#endif
+		cfs->fs_type = 0;				/* Clear old fs object */
+	}
+
+	if (fs) {
+		fs->fs_type = 0;				/* Clear new fs object */
+#if _FS_REENTRANT						/* Create sync object for the new volume */
+		if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR;
+#endif
+	}
+	FatFs[vol] = fs;					/* Register new fs object */
+
+	if (!fs || opt != 1) return FR_OK;	/* Do not mount now, it will be mounted later */
+
+	res = find_volume(&fs, &path, 0);	/* Force mounted the volume */
+	LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Open or Create a File                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_open (
+		FIL* fp,			/* Pointer to the blank file object */
+		const TCHAR* path,	/* Pointer to the file name */
+		BYTE mode			/* Access mode and file open mode flags */
+)
+{
+	FRESULT res;
+	DIR dj;
+	BYTE *dir;
+	DEF_NAMEBUF;
+
+
+	if (!fp) return FR_INVALID_OBJECT;
+	fp->fs = 0;			/* Clear file object */
+
+	/* Get logical drive number */
+#if !_FS_READONLY
+	mode &= FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW;
+	res = find_volume(&dj.fs, &path, (BYTE)(mode & ~FA_READ));
+#else
+	mode &= FA_READ;
+	res = find_volume(&dj.fs, &path, 0);
+#endif
+	if (res == FR_OK) {
+		INIT_BUF(dj);
+		res = follow_path(&dj, path);	/* Follow the file path */
+		dir = dj.dir;
+#if !_FS_READONLY	/* R/W configuration */
+		if (res == FR_OK) {
+			if (!dir)	/* Default directory itself */
+				res = FR_INVALID_NAME;
+#if _FS_LOCK
+			else
+				res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0);
+#endif
+		}
+		/* Create or Open a file */
+		if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {
+			DWORD dw, cl;
+
+			if (res != FR_OK) {					/* No file, create new */
+				if (res == FR_NO_FILE)			/* There is no file to open, create a new entry */
+#if _FS_LOCK
+					res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES;
+#else
+				res = dir_register(&dj);
+#endif
+				mode |= FA_CREATE_ALWAYS;		/* File is created */
+				dir = dj.dir;					/* New entry */
+			}
+			else {								/* Any object is already existing */
+				if (dir[DIR_Attr] & (AM_RDO | AM_DIR)) {	/* Cannot overwrite it (R/O or DIR) */
+					res = FR_DENIED;
+				} else {
+					if (mode & FA_CREATE_NEW)	/* Cannot create as new file */
+						res = FR_EXIST;
+				}
+			}
+			if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) {	/* Truncate it if overwrite mode */
+				dw = get_fattime();				/* Created time */
+				ST_DWORD(dir+DIR_CrtTime, dw);
+				dir[DIR_Attr] = 0;				/* Reset attribute */
+				ST_DWORD(dir+DIR_FileSize, 0);	/* size = 0 */
+				cl = ld_clust(dj.fs, dir);		/* Get start cluster */
+				st_clust(dir, 0);				/* cluster = 0 */
+				dj.fs->wflag = 1;
+				if (cl) {						/* Remove the cluster chain if exist */
+					dw = dj.fs->winsect;
+					res = remove_chain(dj.fs, cl);
+					if (res == FR_OK) {
+						dj.fs->last_clust = cl - 1;	/* Reuse the cluster hole */
+						res = move_window(dj.fs, dw);
+					}
+				}
+			}
+		}
+		else {	/* Open an existing file */
+			if (res == FR_OK) {					/* Follow succeeded */
+				if (dir[DIR_Attr] & AM_DIR) {	/* It is a directory */
+					res = FR_NO_FILE;
+				} else {
+					if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */
+						res = FR_DENIED;
+				}
+			}
+		}
+		if (res == FR_OK) {
+			if (mode & FA_CREATE_ALWAYS)		/* Set file change flag if created or overwritten */
+				mode |= FA__WRITTEN;
+			fp->dir_sect = dj.fs->winsect;		/* Pointer to the directory entry */
+			fp->dir_ptr = dir;
+#if _FS_LOCK
+			fp->lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0);
+			if (!fp->lockid) res = FR_INT_ERR;
+#endif
+		}
+
+#else				/* R/O configuration */
+		if (res == FR_OK) {					/* Follow succeeded */
+			dir = dj.dir;
+			if (!dir) {						/* Current directory itself */
+				res = FR_INVALID_NAME;
+			} else {
+				if (dir[DIR_Attr] & AM_DIR)	/* It is a directory */
+					res = FR_NO_FILE;
+			}
+		}
+#endif
+		FREE_BUF();
+
+		if (res == FR_OK) {
+			fp->flag = mode;					/* File access mode */
+			fp->err = 0;						/* Clear error flag */
+			fp->sclust = ld_clust(dj.fs, dir);	/* File start cluster */
+			fp->fsize = LD_DWORD(dir+DIR_FileSize);	/* File size */
+			fp->fptr = 0;						/* File pointer */
+			fp->dsect = 0;
+#if _USE_FASTSEEK
+			fp->cltbl = 0;						/* Normal seek mode */
+#endif
+			fp->fs = dj.fs;	 					/* Validate file object */
+			fp->id = fp->fs->id;
+		}
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read File                                                             */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_read (
+		FIL* fp, 		/* Pointer to the file object */
+		void* buff,		/* Pointer to data buffer */
+		UINT btr,		/* Number of bytes to read */
+		UINT* br		/* Pointer to number of bytes read */
+)
+{
+	FRESULT res;
+	DWORD clst, sect, remain;
+	UINT rcnt, cc;
+	BYTE csect, *rbuff = (BYTE*)buff;
+
+
+	*br = 0;	/* Clear read byte counter */
+
+	res = validate(fp);							/* Check validity */
+	if (res != FR_OK) LEAVE_FF(fp->fs, res);
+	if (fp->err)								/* Check error */
+		LEAVE_FF(fp->fs, (FRESULT)fp->err);
+	if (!(fp->flag & FA_READ)) 					/* Check access mode */
+		LEAVE_FF(fp->fs, FR_DENIED);
+	remain = fp->fsize - fp->fptr;
+	if (btr > remain) btr = (UINT)remain;		/* Truncate btr by remaining bytes */
+
+	for ( ;  btr;								/* Repeat until all data read */
+			rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {
+		if ((fp->fptr % SS(fp->fs)) == 0) {		/* On the sector boundary? */
+			csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1));	/* Sector offset in the cluster */
+			if (!csect) {						/* On the cluster boundary? */
+				if (fp->fptr == 0) {			/* On the top of the file? */
+					clst = fp->sclust;			/* Follow from the origin */
+				} else {						/* Middle or end of the file */
+#if _USE_FASTSEEK
+					if (fp->cltbl)
+						clst = clmt_clust(fp, fp->fptr);	/* Get cluster# from the CLMT */
+					else
+#endif
+						clst = get_fat(fp->fs, fp->clust);	/* Follow cluster chain on the FAT */
+				}
+				if (clst < 2) ABORT(fp->fs, FR_INT_ERR);
+				if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+				fp->clust = clst;				/* Update current cluster */
+			}
+			sect = clust2sect(fp->fs, fp->clust);	/* Get current sector */
+			if (!sect) ABORT(fp->fs, FR_INT_ERR);
+			sect += csect;
+			cc = btr / SS(fp->fs);				/* When remaining bytes >= sector size, */
+			if (cc) {							/* Read maximum contiguous sectors directly */
+				if (csect + cc > fp->fs->csize)	/* Clip at cluster boundary */
+					cc = fp->fs->csize - csect;
+				if (disk_read(fp->fs->drv, rbuff, sect, cc))
+					ABORT(fp->fs, FR_DISK_ERR);
+#if !_FS_READONLY && _FS_MINIMIZE <= 2			/* Replace one of the read sectors with cached data if it contains a dirty sector */
+#if _FS_TINY
+				if (fp->fs->wflag && fp->fs->winsect - sect < cc)
+					mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs));
+#else
+				if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc)
+					mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs));
+#endif
+#endif
+				rcnt = SS(fp->fs) * cc;			/* Number of bytes transferred */
+				continue;
+			}
+#if !_FS_TINY
+			if (fp->dsect != sect) {			/* Load data sector if not in cache */
+#if !_FS_READONLY
+				if (fp->flag & FA__DIRTY) {		/* Write-back dirty sector cache */
+					if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1))
+						ABORT(fp->fs, FR_DISK_ERR);
+					fp->flag &= ~FA__DIRTY;
+				}
+#endif
+				if (disk_read(fp->fs->drv, fp->buf, sect, 1))	/* Fill sector cache */
+					ABORT(fp->fs, FR_DISK_ERR);
+			}
+#endif
+			fp->dsect = sect;
+		}
+		rcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs));	/* Get partial sector data from sector buffer */
+		if (rcnt > btr) rcnt = btr;
+#if _FS_TINY
+		if (move_window(fp->fs, fp->dsect))		/* Move sector window */
+			ABORT(fp->fs, FR_DISK_ERR);
+		mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt);	/* Pick partial sector */
+#else
+		mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt);	/* Pick partial sector */
+#endif
+	}
+
+	LEAVE_FF(fp->fs, FR_OK);
+}
+
+
+
+
+#if !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Write File                                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_write (
+		FIL* fp,			/* Pointer to the file object */
+		const void *buff,	/* Pointer to the data to be written */
+		UINT btw,			/* Number of bytes to write */
+		UINT* bw			/* Pointer to number of bytes written */
+)
+{
+	FRESULT res;
+	DWORD clst, sect;
+	UINT wcnt, cc;
+	const BYTE *wbuff = (const BYTE*)buff;
+	BYTE csect;
+
+
+	*bw = 0;	/* Clear write byte counter */
+
+	res = validate(fp);						/* Check validity */
+	if (res != FR_OK) LEAVE_FF(fp->fs, res);
+	if (fp->err)							/* Check error */
+		LEAVE_FF(fp->fs, (FRESULT)fp->err);
+	if (!(fp->flag & FA_WRITE))				/* Check access mode */
+		LEAVE_FF(fp->fs, FR_DENIED);
+	if (fp->fptr + btw < fp->fptr) btw = 0;	/* File size cannot reach 4GB */
+
+	for ( ;  btw;							/* Repeat until all data written */
+			wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) {
+		if ((fp->fptr % SS(fp->fs)) == 0) {	/* On the sector boundary? */
+			csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1));	/* Sector offset in the cluster */
+			if (!csect) {					/* On the cluster boundary? */
+				if (fp->fptr == 0) {		/* On the top of the file? */
+					clst = fp->sclust;		/* Follow from the origin */
+					if (clst == 0)			/* When no cluster is allocated, */
+						clst = create_chain(fp->fs, 0);	/* Create a new cluster chain */
+				} else {					/* Middle or end of the file */
+#if _USE_FASTSEEK
+					if (fp->cltbl)
+						clst = clmt_clust(fp, fp->fptr);	/* Get cluster# from the CLMT */
+					else
+#endif
+						clst = create_chain(fp->fs, fp->clust);	/* Follow or stretch cluster chain on the FAT */
+				}
+				if (clst == 0) break;		/* Could not allocate a new cluster (disk full) */
+				if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
+				if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+				fp->clust = clst;			/* Update current cluster */
+				if (fp->sclust == 0) fp->sclust = clst;	/* Set start cluster if the first write */
+			}
+#if _FS_TINY
+			if (fp->fs->winsect == fp->dsect && sync_window(fp->fs))	/* Write-back sector cache */
+				ABORT(fp->fs, FR_DISK_ERR);
+#else
+			if (fp->flag & FA__DIRTY) {		/* Write-back sector cache */
+				if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1))
+					ABORT(fp->fs, FR_DISK_ERR);
+				fp->flag &= ~FA__DIRTY;
+			}
+#endif
+			sect = clust2sect(fp->fs, fp->clust);	/* Get current sector */
+			if (!sect) ABORT(fp->fs, FR_INT_ERR);
+			sect += csect;
+			cc = btw / SS(fp->fs);			/* When remaining bytes >= sector size, */
+			if (cc) {						/* Write maximum contiguous sectors directly */
+				if (csect + cc > fp->fs->csize)	/* Clip at cluster boundary */
+					cc = fp->fs->csize - csect;
+				if (disk_write(fp->fs->drv, wbuff, sect, cc))
+					ABORT(fp->fs, FR_DISK_ERR);
+#if _FS_MINIMIZE <= 2
+#if _FS_TINY
+				if (fp->fs->winsect - sect < cc) {	/* Refill sector cache if it gets invalidated by the direct write */
+					mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs));
+					fp->fs->wflag = 0;
+				}
+#else
+				if (fp->dsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
+					mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs));
+					fp->flag &= ~FA__DIRTY;
+				}
+#endif
+#endif
+				wcnt = SS(fp->fs) * cc;		/* Number of bytes transferred */
+				continue;
+			}
+#if _FS_TINY
+			if (fp->fptr >= fp->fsize) {	/* Avoid silly cache filling at growing edge */
+				if (sync_window(fp->fs)) ABORT(fp->fs, FR_DISK_ERR);
+				fp->fs->winsect = sect;
+			}
+#else
+			if (fp->dsect != sect) {		/* Fill sector cache with file data */
+				if (fp->fptr < fp->fsize &&
+						disk_read(fp->fs->drv, fp->buf, sect, 1))
+					ABORT(fp->fs, FR_DISK_ERR);
+			}
+#endif
+			fp->dsect = sect;
+		}
+		wcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs));/* Put partial sector into file I/O buffer */
+		if (wcnt > btw) wcnt = btw;
+#if _FS_TINY
+		if (move_window(fp->fs, fp->dsect))	/* Move sector window */
+			ABORT(fp->fs, FR_DISK_ERR);
+		mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt);	/* Fit partial sector */
+		fp->fs->wflag = 1;
+#else
+		mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt);	/* Fit partial sector */
+		fp->flag |= FA__DIRTY;
+#endif
+	}
+
+	if (fp->fptr > fp->fsize) fp->fsize = fp->fptr;	/* Update file size if needed */
+	fp->flag |= FA__WRITTEN;						/* Set file change flag */
+
+	LEAVE_FF(fp->fs, FR_OK);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Synchronize the File                                                  */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_sync (
+		FIL* fp		/* Pointer to the file object */
+)
+{
+	FRESULT res;
+	DWORD tm;
+	BYTE *dir;
+
+
+	res = validate(fp);					/* Check validity of the object */
+	if (res == FR_OK) {
+		if (fp->flag & FA__WRITTEN) {	/* Has the file been written? */
+			/* Write-back dirty buffer */
+#if !_FS_TINY
+			if (fp->flag & FA__DIRTY) {
+				if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1))
+					LEAVE_FF(fp->fs, FR_DISK_ERR);
+				fp->flag &= ~FA__DIRTY;
+			}
+#endif
+			/* Update the directory entry */
+			res = move_window(fp->fs, fp->dir_sect);
+			if (res == FR_OK) {
+				dir = fp->dir_ptr;
+				dir[DIR_Attr] |= AM_ARC;					/* Set archive bit */
+				ST_DWORD(dir+DIR_FileSize, fp->fsize);		/* Update file size */
+				st_clust(dir, fp->sclust);					/* Update start cluster */
+				tm = get_fattime();							/* Update updated time */
+				ST_DWORD(dir+DIR_WrtTime, tm);
+				ST_WORD(dir+DIR_LstAccDate, 0);
+				fp->flag &= ~FA__WRITTEN;
+				fp->fs->wflag = 1;
+				res = sync_fs(fp->fs);
+			}
+		}
+	}
+
+	LEAVE_FF(fp->fs, res);
+}
+
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Close File                                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_close (
+		FIL *fp		/* Pointer to the file object to be closed */
+)
+{
+	FRESULT res;
+
+
+#if !_FS_READONLY
+	res = f_sync(fp);					/* Flush cached data */
+	if (res == FR_OK)
+#endif
+	{
+		res = validate(fp);				/* Lock volume */
+		if (res == FR_OK) {
+#if _FS_REENTRANT
+			FATFS *fs = fp->fs;
+#endif
+#if _FS_LOCK
+			res = dec_lock(fp->lockid);	/* Decrement file open counter */
+			if (res == FR_OK)
+#endif
+				fp->fs = 0;				/* Invalidate file object */
+#if _FS_REENTRANT
+			unlock_fs(fs, FR_OK);		/* Unlock volume */
+#endif
+		}
+	}
+	return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Current Directory or Current Drive, Get Current Directory      */
+/*-----------------------------------------------------------------------*/
+
+#if _FS_RPATH >= 1
+#if _VOLUMES >= 2
+FRESULT f_chdrive (
+		const TCHAR* path		/* Drive number */
+)
+{
+	int vol;
+
+
+	vol = get_ldnumber(&path);
+	if (vol < 0) return FR_INVALID_DRIVE;
+
+	CurrVol = (BYTE)vol;
+
+	return FR_OK;
+}
+#endif
+
+
+FRESULT f_chdir (
+		const TCHAR* path	/* Pointer to the directory path */
+)
+{
+	FRESULT res;
+	DIR dj;
+	DEF_NAMEBUF;
+
+
+	/* Get logical drive number */
+	res = find_volume(&dj.fs, &path, 0);
+	if (res == FR_OK) {
+		INIT_BUF(dj);
+		res = follow_path(&dj, path);		/* Follow the path */
+		FREE_BUF();
+		if (res == FR_OK) {					/* Follow completed */
+			if (!dj.dir) {
+				dj.fs->cdir = dj.sclust;	/* Start directory itself */
+			} else {
+				if (dj.dir[DIR_Attr] & AM_DIR)	/* Reached to the directory */
+					dj.fs->cdir = ld_clust(dj.fs, dj.dir);
+				else
+					res = FR_NO_PATH;		/* Reached but a file */
+			}
+		}
+		if (res == FR_NO_FILE) res = FR_NO_PATH;
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+
+
+#if _FS_RPATH >= 2
+FRESULT f_getcwd (
+		TCHAR* buff,	/* Pointer to the directory path */
+		UINT len		/* Size of path */
+)
+{
+	FRESULT res;
+	DIR dj;
+	UINT i, n;
+	DWORD ccl;
+	TCHAR *tp;
+	FILINFO fno;
+	DEF_NAMEBUF;
+
+
+	*buff = 0;
+	/* Get logical drive number */
+	res = find_volume(&dj.fs, (const TCHAR**)&buff, 0);	/* Get current volume */
+	if (res == FR_OK) {
+		INIT_BUF(dj);
+		i = len;			/* Bottom of buffer (directory stack base) */
+		dj.sclust = dj.fs->cdir;			/* Start to follow upper directory from current directory */
+		while ((ccl = dj.sclust) != 0) {	/* Repeat while current directory is a sub-directory */
+			res = dir_sdi(&dj, 1);			/* Get parent directory */
+			if (res != FR_OK) break;
+			res = dir_read(&dj, 0);
+			if (res != FR_OK) break;
+			dj.sclust = ld_clust(dj.fs, dj.dir);	/* Goto parent directory */
+			res = dir_sdi(&dj, 0);
+			if (res != FR_OK) break;
+			do {							/* Find the entry links to the child directory */
+				res = dir_read(&dj, 0);
+				if (res != FR_OK) break;
+				if (ccl == ld_clust(dj.fs, dj.dir)) break;	/* Found the entry */
+				res = dir_next(&dj, 0);	
+			} while (res == FR_OK);
+			if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */
+			if (res != FR_OK) break;
+#if _USE_LFN
+			fno.lfname = buff;
+			fno.lfsize = i;
+#endif
+			get_fileinfo(&dj, &fno);		/* Get the directory name and push it to the buffer */
+			tp = fno.fname;
+#if _USE_LFN
+			if (*buff) tp = buff;
+#endif
+			for (n = 0; tp[n]; n++) ;
+			if (i < n + 3) {
+				res = FR_NOT_ENOUGH_CORE; break;
+			}
+			while (n) buff[--i] = tp[--n];
+			buff[--i] = '/';
+		}
+		tp = buff;
+		if (res == FR_OK) {
+#if _VOLUMES >= 2
+			*tp++ = '0' + CurrVol;			/* Put drive number */
+			*tp++ = ':';
+#endif
+			if (i == len) {					/* Root-directory */
+				*tp++ = '/';
+			} else {						/* Sub-directroy */
+				do		/* Add stacked path str */
+					*tp++ = buff[i++];
+				while (i < len);
+			}
+		}
+		*tp = 0;
+		FREE_BUF();
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+#endif /* _FS_RPATH >= 2 */
+#endif /* _FS_RPATH >= 1 */
+
+
+
+#if _FS_MINIMIZE <= 2
+/*-----------------------------------------------------------------------*/
+/* Seek File R/W Pointer                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_lseek (
+		FIL* fp,		/* Pointer to the file object */
+		DWORD ofs		/* File pointer from top of file */
+)
+{
+	FRESULT res;
+
+
+	res = validate(fp);					/* Check validity of the object */
+	if (res != FR_OK) LEAVE_FF(fp->fs, res);
+	if (fp->err)						/* Check error */
+		LEAVE_FF(fp->fs, (FRESULT)fp->err);
+
+#if _USE_FASTSEEK
+	if (fp->cltbl) {	/* Fast seek */
+		DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl;
+
+		if (ofs == CREATE_LINKMAP) {	/* Create CLMT */
+			tbl = fp->cltbl;
+			tlen = *tbl++; ulen = 2;	/* Given table size and required table size */
+			cl = fp->sclust;			/* Top of the chain */
+			if (cl) {
+				do {
+					/* Get a fragment */
+					tcl = cl; ncl = 0; ulen += 2;	/* Top, length and used items */
+					do {
+						pcl = cl; ncl++;
+						cl = get_fat(fp->fs, cl);
+						if (cl <= 1) ABORT(fp->fs, FR_INT_ERR);
+						if (cl == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+					} while (cl == pcl + 1);
+					if (ulen <= tlen) {		/* Store the length and top of the fragment */
+						*tbl++ = ncl; *tbl++ = tcl;
+					}
+				} while (cl < fp->fs->n_fatent);	/* Repeat until end of chain */
+			}
+			*fp->cltbl = ulen;	/* Number of items used */
+			if (ulen <= tlen)
+				*tbl = 0;		/* Terminate table */
+			else
+				res = FR_NOT_ENOUGH_CORE;	/* Given table size is smaller than required */
+
+		} else {						/* Fast seek */
+			if (ofs > fp->fsize)		/* Clip offset at the file size */
+				ofs = fp->fsize;
+			fp->fptr = ofs;				/* Set file pointer */
+			if (ofs) {
+				fp->clust = clmt_clust(fp, ofs - 1);
+				dsc = clust2sect(fp->fs, fp->clust);
+				if (!dsc) ABORT(fp->fs, FR_INT_ERR);
+				dsc += (ofs - 1) / SS(fp->fs) & (fp->fs->csize - 1);
+				if (fp->fptr % SS(fp->fs) && dsc != fp->dsect) {	/* Refill sector cache if needed */
+#if !_FS_TINY
+#if !_FS_READONLY
+					if (fp->flag & FA__DIRTY) {		/* Write-back dirty sector cache */
+						if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1))
+							ABORT(fp->fs, FR_DISK_ERR);
+						fp->flag &= ~FA__DIRTY;
+					}
+#endif
+					if (disk_read(fp->fs->drv, fp->buf, dsc, 1))	/* Load current sector */
+						ABORT(fp->fs, FR_DISK_ERR);
+#endif
+					fp->dsect = dsc;
+				}
+			}
+		}
+	} else
+#endif
+
+		/* Normal Seek */
+	{
+		DWORD clst, bcs, nsect, ifptr;
+
+		if (ofs > fp->fsize					/* In read-only mode, clip offset with the file size */
+#if !_FS_READONLY
+				&& !(fp->flag & FA_WRITE)
+#endif
+		) ofs = fp->fsize;
+
+		ifptr = fp->fptr;
+		fp->fptr = nsect = 0;
+		if (ofs) {
+			bcs = (DWORD)fp->fs->csize * SS(fp->fs);	/* Cluster size (byte) */
+			if (ifptr > 0 &&
+					(ofs - 1) / bcs >= (ifptr - 1) / bcs) {	/* When seek to same or following cluster, */
+				fp->fptr = (ifptr - 1) & ~(bcs - 1);	/* start from the current cluster */
+				ofs -= fp->fptr;
+				clst = fp->clust;
+			} else {									/* When seek to back cluster, */
+				clst = fp->sclust;						/* start from the first cluster */
+#if !_FS_READONLY
+				if (clst == 0) {						/* If no cluster chain, create a new chain */
+					clst = create_chain(fp->fs, 0);
+					if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
+					if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+					fp->sclust = clst;
+				}
+#endif
+				fp->clust = clst;
+			}
+			if (clst != 0) {
+				while (ofs > bcs) {						/* Cluster following loop */
+#if !_FS_READONLY
+					if (fp->flag & FA_WRITE) {			/* Check if in write mode or not */
+						clst = create_chain(fp->fs, clst);	/* Force stretch if in write mode */
+						if (clst == 0) {				/* When disk gets full, clip file size */
+							ofs = bcs; break;
+						}
+					} else
+#endif
+						clst = get_fat(fp->fs, clst);	/* Follow cluster chain if not in write mode */
+					if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+					if (clst <= 1 || clst >= fp->fs->n_fatent) ABORT(fp->fs, FR_INT_ERR);
+					fp->clust = clst;
+					fp->fptr += bcs;
+					ofs -= bcs;
+				}
+				fp->fptr += ofs;
+				if (ofs % SS(fp->fs)) {
+					nsect = clust2sect(fp->fs, clst);	/* Current sector */
+					if (!nsect) ABORT(fp->fs, FR_INT_ERR);
+					nsect += ofs / SS(fp->fs);
+				}
+			}
+		}
+		if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) {	/* Fill sector cache if needed */
+#if !_FS_TINY
+#if !_FS_READONLY
+			if (fp->flag & FA__DIRTY) {			/* Write-back dirty sector cache */
+				if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1))
+					ABORT(fp->fs, FR_DISK_ERR);
+				fp->flag &= ~FA__DIRTY;
+			}
+#endif
+			if (disk_read(fp->fs->drv, fp->buf, nsect, 1))	/* Fill sector cache */
+				ABORT(fp->fs, FR_DISK_ERR);
+#endif
+			fp->dsect = nsect;
+		}
+#if !_FS_READONLY
+		if (fp->fptr > fp->fsize) {			/* Set file change flag if the file size is extended */
+			fp->fsize = fp->fptr;
+			fp->flag |= FA__WRITTEN;
+		}
+#endif
+	}
+
+	LEAVE_FF(fp->fs, res);
+}
+
+
+
+#if _FS_MINIMIZE <= 1
+/*-----------------------------------------------------------------------*/
+/* Create a Directory Object                                             */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_opendir (
+		DIR* dp,			/* Pointer to directory object to create */
+		const TCHAR* path	/* Pointer to the directory path */
+)
+{
+	FRESULT res;
+	FATFS* fs;
+	DEF_NAMEBUF;
+
+
+	if (!dp) return FR_INVALID_OBJECT;
+
+	/* Get logical drive number */
+	res = find_volume(&fs, &path, 0);
+	if (res == FR_OK) {
+		dp->fs = fs;
+		INIT_BUF(*dp);
+		res = follow_path(dp, path);			/* Follow the path to the directory */
+		FREE_BUF();
+		if (res == FR_OK) {						/* Follow completed */
+			if (dp->dir) {						/* It is not the origin directory itself */
+				if (dp->dir[DIR_Attr] & AM_DIR)	/* The object is a sub directory */
+					dp->sclust = ld_clust(fs, dp->dir);
+				else							/* The object is a file */
+					res = FR_NO_PATH;
+			}
+			if (res == FR_OK) {
+				dp->id = fs->id;
+				res = dir_sdi(dp, 0);			/* Rewind directory */
+#if _FS_LOCK
+				if (res == FR_OK) {
+					if (dp->sclust) {
+						dp->lockid = inc_lock(dp, 0);	/* Lock the sub directory */
+						if (!dp->lockid)
+							res = FR_TOO_MANY_OPEN_FILES;
+					} else {
+						dp->lockid = 0;	/* Root directory need not to be locked */
+					}
+				}
+#endif
+			}
+		}
+		if (res == FR_NO_FILE) res = FR_NO_PATH;
+	}
+	if (res != FR_OK) dp->fs = 0;		/* Invalidate the directory object if function faild */
+
+	LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Close Directory                                                       */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_closedir (
+		DIR *dp		/* Pointer to the directory object to be closed */
+)
+{
+	FRESULT res;
+
+
+	res = validate(dp);
+	if (res == FR_OK) {
+#if _FS_REENTRANT
+		FATFS *fs = dp->fs;
+#endif
+#if _FS_LOCK
+		if (dp->lockid)				/* Decrement sub-directory open counter */
+			res = dec_lock(dp->lockid);
+		if (res == FR_OK)
+#endif
+			dp->fs = 0;				/* Invalidate directory object */
+#if _FS_REENTRANT
+		unlock_fs(fs, FR_OK);		/* Unlock volume */
+#endif
+	}
+	return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read Directory Entries in Sequence                                    */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_readdir (
+		DIR* dp,			/* Pointer to the open directory object */
+		FILINFO* fno		/* Pointer to file information to return */
+)
+{
+	FRESULT res;
+	DEF_NAMEBUF;
+
+
+	res = validate(dp);						/* Check validity of the object */
+	if (res == FR_OK) {
+		if (!fno) {
+			res = dir_sdi(dp, 0);			/* Rewind the directory object */
+		} else {
+			INIT_BUF(*dp);
+			res = dir_read(dp, 0);			/* Read an item */
+			if (res == FR_NO_FILE) {		/* Reached end of directory */
+				dp->sect = 0;
+				res = FR_OK;
+			}
+			if (res == FR_OK) {				/* A valid entry is found */
+				get_fileinfo(dp, fno);		/* Get the object information */
+				res = dir_next(dp, 0);		/* Increment index for next */
+				if (res == FR_NO_FILE) {
+					dp->sect = 0;
+					res = FR_OK;
+				}
+			}
+			FREE_BUF();
+		}
+	}
+
+	LEAVE_FF(dp->fs, res);
+}
+
+
+
+#if _FS_MINIMIZE == 0
+/*-----------------------------------------------------------------------*/
+/* Get File Status                                                       */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_stat (
+		const TCHAR* path,	/* Pointer to the file path */
+		FILINFO* fno		/* Pointer to file information to return */
+)
+{
+	FRESULT res;
+	DIR dj;
+	DEF_NAMEBUF;
+
+
+	/* Get logical drive number */
+	res = find_volume(&dj.fs, &path, 0);
+	if (res == FR_OK) {
+		INIT_BUF(dj);
+		res = follow_path(&dj, path);	/* Follow the file path */
+		if (res == FR_OK) {				/* Follow completed */
+			if (dj.dir) {		/* Found an object */
+				if (fno) get_fileinfo(&dj, fno);
+			} else {			/* It is root directory */
+				res = FR_INVALID_NAME;
+			}
+		}
+		FREE_BUF();
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+
+
+
+#if !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Get Number of Free Clusters                                           */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_getfree (
+		const TCHAR* path,	/* Path name of the logical drive number */
+		DWORD* nclst,		/* Pointer to a variable to return number of free clusters */
+		FATFS** fatfs		/* Pointer to return pointer to corresponding file system object */
+)
+{
+	FRESULT res;
+	FATFS *fs;
+	DWORD n, clst, sect, stat;
+	UINT i;
+	BYTE fat, *p;
+
+
+	/* Get logical drive number */
+	res = find_volume(fatfs, &path, 0);
+	fs = *fatfs;
+	if (res == FR_OK) {
+		/* If free_clust is valid, return it without full cluster scan */
+		if (fs->free_clust <= fs->n_fatent - 2) {
+			*nclst = fs->free_clust;
+		} else {
+			/* Get number of free clusters */
+			fat = fs->fs_type;
+			n = 0;
+			if (fat == FS_FAT12) {
+				clst = 2;
+				do {
+					stat = get_fat(fs, clst);
+					if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }
+					if (stat == 1) { res = FR_INT_ERR; break; }
+					if (stat == 0) n++;
+				} while (++clst < fs->n_fatent);
+			} else {
+				clst = fs->n_fatent;
+				sect = fs->fatbase;
+				i = 0; p = 0;
+				do {
+					if (!i) {
+						res = move_window(fs, sect++);
+						if (res != FR_OK) break;
+						p = fs->win;
+						i = SS(fs);
+					}
+					if (fat == FS_FAT16) {
+						if (LD_WORD(p) == 0) n++;
+						p += 2; i -= 2;
+					} else {
+						if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++;
+						p += 4; i -= 4;
+					}
+				} while (--clst);
+			}
+			fs->free_clust = n;
+			fs->fsi_flag |= 1;
+			*nclst = n;
+		}
+	}
+	LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Truncate File                                                         */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_truncate (
+		FIL* fp		/* Pointer to the file object */
+)
+{
+	FRESULT res;
+	DWORD ncl;
+
+
+	res = validate(fp);						/* Check validity of the object */
+	if (res == FR_OK) {
+		if (fp->err) {						/* Check error */
+			res = (FRESULT)fp->err;
+		} else {
+			if (!(fp->flag & FA_WRITE))		/* Check access mode */
+				res = FR_DENIED;
+		}
+	}
+	if (res == FR_OK) {
+		if (fp->fsize > fp->fptr) {
+			fp->fsize = fp->fptr;	/* Set file size to current R/W point */
+			fp->flag |= FA__WRITTEN;
+			if (fp->fptr == 0) {	/* When set file size to zero, remove entire cluster chain */
+				res = remove_chain(fp->fs, fp->sclust);
+				fp->sclust = 0;
+			} else {				/* When truncate a part of the file, remove remaining clusters */
+				ncl = get_fat(fp->fs, fp->clust);
+				res = FR_OK;
+				if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;
+				if (ncl == 1) res = FR_INT_ERR;
+				if (res == FR_OK && ncl < fp->fs->n_fatent) {
+					res = put_fat(fp->fs, fp->clust, 0x0FFFFFFF);
+					if (res == FR_OK) res = remove_chain(fp->fs, ncl);
+				}
+			}
+#if !_FS_TINY
+			if (res == FR_OK && (fp->flag & FA__DIRTY)) {
+				if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1))
+					res = FR_DISK_ERR;
+				else
+					fp->flag &= ~FA__DIRTY;
+			}
+#endif
+		}
+		if (res != FR_OK) fp->err = (FRESULT)res;
+	}
+
+	LEAVE_FF(fp->fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Delete a File or Directory                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_unlink (
+		const TCHAR* path		/* Pointer to the file or directory path */
+)
+{
+	FRESULT res;
+	DIR dj, sdj;
+	BYTE *dir;
+	DWORD dclst;
+	DEF_NAMEBUF;
+
+
+	/* Get logical drive number */
+	res = find_volume(&dj.fs, &path, 1);
+	if (res == FR_OK) {
+		INIT_BUF(dj);
+		res = follow_path(&dj, path);		/* Follow the file path */
+		if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+			res = FR_INVALID_NAME;			/* Cannot remove dot entry */
+#if _FS_LOCK
+		if (res == FR_OK) res = chk_lock(&dj, 2);	/* Cannot remove open file */
+#endif
+		if (res == FR_OK) {					/* The object is accessible */
+			dir = dj.dir;
+			if (!dir) {
+				res = FR_INVALID_NAME;		/* Cannot remove the start directory */
+			} else {
+				if (dir[DIR_Attr] & AM_RDO)
+					res = FR_DENIED;		/* Cannot remove R/O object */
+			}
+			dclst = ld_clust(dj.fs, dir);
+			if (res == FR_OK && (dir[DIR_Attr] & AM_DIR)) {	/* Is it a sub-dir? */
+				if (dclst < 2) {
+					res = FR_INT_ERR;
+				} else {
+					mem_cpy(&sdj, &dj, sizeof (DIR));	/* Check if the sub-directory is empty or not */
+					sdj.sclust = dclst;
+					res = dir_sdi(&sdj, 2);		/* Exclude dot entries */
+					if (res == FR_OK) {
+						res = dir_read(&sdj, 0);	/* Read an item */
+						if (res == FR_OK		/* Not empty directory */
+#if _FS_RPATH
+								|| dclst == dj.fs->cdir	/* Current directory */
+#endif
+						) res = FR_DENIED;
+						if (res == FR_NO_FILE) res = FR_OK;	/* Empty */
+					}
+				}
+			}
+			if (res == FR_OK) {
+				res = dir_remove(&dj);		/* Remove the directory entry */
+				if (res == FR_OK) {
+					if (dclst)				/* Remove the cluster chain if exist */
+						res = remove_chain(dj.fs, dclst);
+					if (res == FR_OK) res = sync_fs(dj.fs);
+				}
+			}
+		}
+		FREE_BUF();
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create a Directory                                                    */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mkdir (
+		const TCHAR* path		/* Pointer to the directory path */
+)
+{
+	FRESULT res;
+	DIR dj;
+	BYTE *dir, n;
+	DWORD dsc, dcl, pcl, tm = get_fattime();
+	DEF_NAMEBUF;
+
+
+	/* Get logical drive number */
+	res = find_volume(&dj.fs, &path, 1);
+	if (res == FR_OK) {
+		INIT_BUF(dj);
+		res = follow_path(&dj, path);			/* Follow the file path */
+		if (res == FR_OK) res = FR_EXIST;		/* Any object with same name is already existing */
+		if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT))
+			res = FR_INVALID_NAME;
+		if (res == FR_NO_FILE) {				/* Can create a new directory */
+			dcl = create_chain(dj.fs, 0);		/* Allocate a cluster for the new directory table */
+			res = FR_OK;
+			if (dcl == 0) res = FR_DENIED;		/* No space to allocate a new cluster */
+			if (dcl == 1) res = FR_INT_ERR;
+			if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR;
+			if (res == FR_OK)					/* Flush FAT */
+				res = sync_window(dj.fs);
+			if (res == FR_OK) {					/* Initialize the new directory table */
+				dsc = clust2sect(dj.fs, dcl);
+				dir = dj.fs->win;
+				mem_set(dir, 0, SS(dj.fs));
+				mem_set(dir+DIR_Name, ' ', 11);	/* Create "." entry */
+				dir[DIR_Name] = '.';
+				dir[DIR_Attr] = AM_DIR;
+				ST_DWORD(dir+DIR_WrtTime, tm);
+				st_clust(dir, dcl);
+				mem_cpy(dir+SZ_DIR, dir, SZ_DIR); 	/* Create ".." entry */
+				dir[SZ_DIR+1] = '.'; pcl = dj.sclust;
+				if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase)
+					pcl = 0;
+				st_clust(dir+SZ_DIR, pcl);
+				for (n = dj.fs->csize; n; n--) {	/* Write dot entries and clear following sectors */
+					dj.fs->winsect = dsc++;
+					dj.fs->wflag = 1;
+					res = sync_window(dj.fs);
+					if (res != FR_OK) break;
+					mem_set(dir, 0, SS(dj.fs));
+				}
+			}
+			if (res == FR_OK) res = dir_register(&dj);	/* Register the object to the directoy */
+			if (res != FR_OK) {
+				remove_chain(dj.fs, dcl);			/* Could not register, remove cluster chain */
+			} else {
+				dir = dj.dir;
+				dir[DIR_Attr] = AM_DIR;				/* Attribute */
+				ST_DWORD(dir+DIR_WrtTime, tm);		/* Created time */
+				st_clust(dir, dcl);					/* Table start cluster */
+				dj.fs->wflag = 1;
+				res = sync_fs(dj.fs);
+			}
+		}
+		FREE_BUF();
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Attribute                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_chmod (
+		const TCHAR* path,	/* Pointer to the file path */
+		BYTE value,			/* Attribute bits */
+		BYTE mask			/* Attribute mask to change */
+)
+{
+	FRESULT res;
+	DIR dj;
+	BYTE *dir;
+	DEF_NAMEBUF;
+
+
+	/* Get logical drive number */
+	res = find_volume(&dj.fs, &path, 1);
+	if (res == FR_OK) {
+		INIT_BUF(dj);
+		res = follow_path(&dj, path);		/* Follow the file path */
+		FREE_BUF();
+		if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+			res = FR_INVALID_NAME;
+		if (res == FR_OK) {
+			dir = dj.dir;
+			if (!dir) {						/* Is it a root directory? */
+				res = FR_INVALID_NAME;
+			} else {						/* File or sub directory */
+				mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC;	/* Valid attribute mask */
+				dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask);	/* Apply attribute change */
+				dj.fs->wflag = 1;
+				res = sync_fs(dj.fs);
+			}
+		}
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Timestamp                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_utime (
+		const TCHAR* path,	/* Pointer to the file/directory name */
+		const FILINFO* fno	/* Pointer to the time stamp to be set */
+)
+{
+	FRESULT res;
+	DIR dj;
+	BYTE *dir;
+	DEF_NAMEBUF;
+
+
+	/* Get logical drive number */
+	res = find_volume(&dj.fs, &path, 1);
+	if (res == FR_OK) {
+		INIT_BUF(dj);
+		res = follow_path(&dj, path);	/* Follow the file path */
+		FREE_BUF();
+		if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+			res = FR_INVALID_NAME;
+		if (res == FR_OK) {
+			dir = dj.dir;
+			if (!dir) {					/* Root directory */
+				res = FR_INVALID_NAME;
+			} else {					/* File or sub-directory */
+				ST_WORD(dir+DIR_WrtTime, fno->ftime);
+				ST_WORD(dir+DIR_WrtDate, fno->fdate);
+				dj.fs->wflag = 1;
+				res = sync_fs(dj.fs);
+			}
+		}
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Rename File/Directory                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_rename (
+		const TCHAR* path_old,	/* Pointer to the object to be renamed */
+		const TCHAR* path_new	/* Pointer to the new name */
+)
+{
+	FRESULT res;
+	DIR djo, djn;
+	BYTE buf[21], *dir;
+	DWORD dw;
+	DEF_NAMEBUF;
+
+
+	/* Get logical drive number of the source object */
+	res = find_volume(&djo.fs, &path_old, 1);
+	if (res == FR_OK) {
+		djn.fs = djo.fs;
+		INIT_BUF(djo);
+		res = follow_path(&djo, path_old);		/* Check old object */
+		if (_FS_RPATH && res == FR_OK && (djo.fn[NS] & NS_DOT))
+			res = FR_INVALID_NAME;
+#if _FS_LOCK
+		if (res == FR_OK) res = chk_lock(&djo, 2);
+#endif
+		if (res == FR_OK) {						/* Old object is found */
+			if (!djo.dir) {						/* Is root dir? */
+				res = FR_NO_FILE;
+			} else {
+				mem_cpy(buf, djo.dir+DIR_Attr, 21);		/* Save the object information except name */
+				mem_cpy(&djn, &djo, sizeof (DIR));		/* Duplicate the directory object */
+				if (get_ldnumber(&path_new) >= 0)		/* Snip drive number off and ignore it */
+					res = follow_path(&djn, path_new);	/* and check if new object is exist */
+				else
+					res = FR_INVALID_DRIVE;
+				if (res == FR_OK) res = FR_EXIST;		/* The new object name is already existing */
+				if (res == FR_NO_FILE) { 				/* Is it a valid path and no name collision? */
+					/* Start critical section that any interruption can cause a cross-link */
+					res = dir_register(&djn);			/* Register the new entry */
+					if (res == FR_OK) {
+						dir = djn.dir;					/* Copy object information except name */
+						mem_cpy(dir+13, buf+2, 19);
+						dir[DIR_Attr] = buf[0] | AM_ARC;
+						djo.fs->wflag = 1;
+						if (djo.sclust != djn.sclust && (dir[DIR_Attr] & AM_DIR)) {		/* Update .. entry in the directory if needed */
+							dw = clust2sect(djo.fs, ld_clust(djo.fs, dir));
+							if (!dw) {
+								res = FR_INT_ERR;
+							} else {
+								res = move_window(djo.fs, dw);
+								dir = djo.fs->win+SZ_DIR;	/* .. entry */
+								if (res == FR_OK && dir[1] == '.') {
+									dw = (djo.fs->fs_type == FS_FAT32 && djn.sclust == djo.fs->dirbase) ? 0 : djn.sclust;
+									st_clust(dir, dw);
+									djo.fs->wflag = 1;
+								}
+							}
+						}
+						if (res == FR_OK) {
+							res = dir_remove(&djo);		/* Remove old entry */
+							if (res == FR_OK)
+								res = sync_fs(djo.fs);
+						}
+					}
+					/* End critical section */
+				}
+			}
+		}
+		FREE_BUF();
+	}
+
+	LEAVE_FF(djo.fs, res);
+}
+
+#endif /* !_FS_READONLY */
+#endif /* _FS_MINIMIZE == 0 */
+#endif /* _FS_MINIMIZE <= 1 */
+#endif /* _FS_MINIMIZE <= 2 */
+
+
+
+#if _USE_LABEL
+/*-----------------------------------------------------------------------*/
+/* Get volume label                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_getlabel (
+		const TCHAR* path,	/* Path name of the logical drive number */
+		TCHAR* label,		/* Pointer to a buffer to return the volume label */
+		DWORD* vsn			/* Pointer to a variable to return the volume serial number */
+)
+{
+	FRESULT res;
+	DIR dj;
+	UINT i, j;
+
+
+	/* Get logical drive number */
+	res = find_volume(&dj.fs, &path, 0);
+
+	/* Get volume label */
+	if (res == FR_OK && label) {
+		dj.sclust = 0;					/* Open root directory */
+		res = dir_sdi(&dj, 0);
+		if (res == FR_OK) {
+			res = dir_read(&dj, 1);		/* Get an entry with AM_VOL */
+			if (res == FR_OK) {			/* A volume label is exist */
+#if _USE_LFN && _LFN_UNICODE
+				WCHAR w;
+				i = j = 0;
+				do {
+					w = (i < 11) ? dj.dir[i++] : ' ';
+					if (IsDBCS1(w) && i < 11 && IsDBCS2(dj.dir[i]))
+						w = w << 8 | dj.dir[i++];
+					label[j++] = ff_convert(w, 1);	/* OEM -> Unicode */
+				} while (j < 11);
+#else
+				mem_cpy(label, dj.dir, 11);
+#endif
+				j = 11;
+				do {
+					label[j] = 0;
+					if (!j) break;
+				} while (label[--j] == ' ');
+			}
+			if (res == FR_NO_FILE) {	/* No label, return nul string */
+				label[0] = 0;
+				res = FR_OK;
+			}
+		}
+	}
+
+	/* Get volume serial number */
+	if (res == FR_OK && vsn) {
+		res = move_window(dj.fs, dj.fs->volbase);
+		if (res == FR_OK) {
+			i = dj.fs->fs_type == FS_FAT32 ? BS_VolID32 : BS_VolID;
+			*vsn = LD_DWORD(&dj.fs->win[i]);
+		}
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+
+
+
+#if !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Set volume label                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_setlabel (
+		const TCHAR* label	/* Pointer to the volume label to set */
+)
+{
+	FRESULT res;
+	DIR dj;
+	BYTE vn[11];
+	UINT i, j, sl;
+	WCHAR w;
+	DWORD tm;
+
+
+	/* Get logical drive number */
+	res = find_volume(&dj.fs, &label, 1);
+	if (res) LEAVE_FF(dj.fs, res);
+
+	/* Create a volume label in directory form */
+	vn[0] = 0;
+	for (sl = 0; label[sl]; sl++) ;				/* Get name length */
+	for ( ; sl && label[sl-1] == ' '; sl--) ;	/* Remove trailing spaces */
+	if (sl) {	/* Create volume label in directory form */
+		i = j = 0;
+		do {
+#if _USE_LFN && _LFN_UNICODE
+			w = ff_convert(ff_wtoupper(label[i++]), 0);
+#else
+			w = (BYTE)label[i++];
+			if (IsDBCS1(w))
+				w = (j < 10 && i < sl && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0;
+#if _USE_LFN
+			w = ff_convert(ff_wtoupper(ff_convert(w, 1)), 0);
+#else
+			if (IsLower(w)) w -= 0x20;			/* To upper ASCII characters */
+#ifdef _EXCVT
+			if (w >= 0x80) w = ExCvt[w - 0x80];	/* To upper extended characters (SBCS cfg) */
+#else
+			if (!_DF1S && w >= 0x80) w = 0;		/* Reject extended characters (ASCII cfg) */
+#endif
+#endif
+#endif
+			if (!w || chk_chr("\"*+,.:;<=>\?[]|\x7F", w) || j >= (UINT)((w >= 0x100) ? 10 : 11)) /* Reject invalid characters for volume label */
+				LEAVE_FF(dj.fs, FR_INVALID_NAME);
+			if (w >= 0x100) vn[j++] = (BYTE)(w >> 8);
+			vn[j++] = (BYTE)w;
+		} while (i < sl);
+		while (j < 11) vn[j++] = ' ';
+	}
+
+	/* Set volume label */
+	dj.sclust = 0;					/* Open root directory */
+	res = dir_sdi(&dj, 0);
+	if (res == FR_OK) {
+		res = dir_read(&dj, 1);		/* Get an entry with AM_VOL */
+		if (res == FR_OK) {			/* A volume label is found */
+			if (vn[0]) {
+				mem_cpy(dj.dir, vn, 11);	/* Change the volume label name */
+				tm = get_fattime();
+				ST_DWORD(dj.dir+DIR_WrtTime, tm);
+			} else {
+				dj.dir[0] = DDE;			/* Remove the volume label */
+			}
+			dj.fs->wflag = 1;
+			res = sync_fs(dj.fs);
+		} else {					/* No volume label is found or error */
+			if (res == FR_NO_FILE) {
+				res = FR_OK;
+				if (vn[0]) {				/* Create volume label as new */
+					res = dir_alloc(&dj, 1);	/* Allocate an entry for volume label */
+					if (res == FR_OK) {
+						mem_set(dj.dir, 0, SZ_DIR);	/* Set volume label */
+						mem_cpy(dj.dir, vn, 11);
+						dj.dir[DIR_Attr] = AM_VOL;
+						tm = get_fattime();
+						ST_DWORD(dj.dir+DIR_WrtTime, tm);
+						dj.fs->wflag = 1;
+						res = sync_fs(dj.fs);
+					}
+				}
+			}
+		}
+	}
+
+	LEAVE_FF(dj.fs, res);
+}
+
+#endif /* !_FS_READONLY */
+#endif /* _USE_LABEL */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Forward data to the stream directly (available on only tiny cfg)      */
+/*-----------------------------------------------------------------------*/
+#if _USE_FORWARD && _FS_TINY
+
+FRESULT f_forward (
+		FIL* fp, 						/* Pointer to the file object */
+		UINT (*func)(const BYTE*,UINT),	/* Pointer to the streaming function */
+		UINT btf,						/* Number of bytes to forward */
+		UINT* bf						/* Pointer to number of bytes forwarded */
+)
+{
+	FRESULT res;
+	DWORD remain, clst, sect;
+	UINT rcnt;
+	BYTE csect;
+
+
+	*bf = 0;	/* Clear transfer byte counter */
+
+	res = validate(fp);								/* Check validity of the object */
+	if (res != FR_OK) LEAVE_FF(fp->fs, res);
+	if (fp->err)									/* Check error */
+		LEAVE_FF(fp->fs, (FRESULT)fp->err);
+	if (!(fp->flag & FA_READ))						/* Check access mode */
+		LEAVE_FF(fp->fs, FR_DENIED);
+
+	remain = fp->fsize - fp->fptr;
+	if (btf > remain) btf = (UINT)remain;			/* Truncate btf by remaining bytes */
+
+	for ( ;  btf && (*func)(0, 0);					/* Repeat until all data transferred or stream becomes busy */
+			fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) {
+		csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1));	/* Sector offset in the cluster */
+		if ((fp->fptr % SS(fp->fs)) == 0) {			/* On the sector boundary? */
+			if (!csect) {							/* On the cluster boundary? */
+				clst = (fp->fptr == 0) ?			/* On the top of the file? */
+						fp->sclust : get_fat(fp->fs, fp->clust);
+				if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
+				if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+				fp->clust = clst;					/* Update current cluster */
+			}
+		}
+		sect = clust2sect(fp->fs, fp->clust);		/* Get current data sector */
+		if (!sect) ABORT(fp->fs, FR_INT_ERR);
+		sect += csect;
+		if (move_window(fp->fs, sect))				/* Move sector window */
+			ABORT(fp->fs, FR_DISK_ERR);
+		fp->dsect = sect;
+		rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs));	/* Forward data from sector window */
+		if (rcnt > btf) rcnt = btf;
+		rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt);
+		if (!rcnt) ABORT(fp->fs, FR_INT_ERR);
+	}
+
+	LEAVE_FF(fp->fs, FR_OK);
+}
+#endif /* _USE_FORWARD */
+
+
+
+#if _USE_MKFS && !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Create File System on the Drive                                       */
+/*-----------------------------------------------------------------------*/
+#define N_ROOTDIR	512		/* Number of root directory entries for FAT12/16 */
+#define N_FATS		1		/* Number of FAT copies (1 or 2) */
+
+
+FRESULT f_mkfs (
+		const TCHAR* path,	/* Logical drive number */
+		BYTE sfd,			/* Partitioning rule 0:FDISK, 1:SFD */
+		UINT au				/* Allocation unit [bytes] */
+)
+{
+	static const WORD vst[] = { 1024,   512,  256,  128,   64,    32,   16,    8,    4,    2,   0};
+	static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512};
+	int vol;
+	BYTE fmt, md, sys, *tbl, pdrv, part;
+	DWORD n_clst, vs, n, wsect;
+	UINT i;
+	DWORD b_vol, b_fat, b_dir, b_data;	/* LBA */
+	DWORD n_vol, n_rsv, n_fat, n_dir;	/* Size */
+	FATFS *fs;
+	DSTATUS stat;
+
+
+	/* Check mounted drive and clear work area */
+	vol = get_ldnumber(&path);
+	if (vol < 0) return FR_INVALID_DRIVE;
+	if (sfd > 1) return FR_INVALID_PARAMETER;
+	if (au & (au - 1)) return FR_INVALID_PARAMETER;
+	fs = FatFs[vol];
+	if (!fs) return FR_NOT_ENABLED;
+	fs->fs_type = 0;
+	pdrv = LD2PD(vol);	/* Physical drive */
+	part = LD2PT(vol);	/* Partition (0:auto detect, 1-4:get from partition table)*/
+
+	/* Get disk statics */
+	stat = disk_initialize(pdrv);
+	if (stat & STA_NOINIT) return FR_NOT_READY;
+	if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
+#if _MAX_SS != _MIN_SS		/* Get disk sector size */
+	if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS || SS(fs) < _MIN_SS)
+		return FR_DISK_ERR;
+#endif
+	if (_MULTI_PARTITION && part) {
+		/* Get partition information from partition table in the MBR */
+		if (disk_read(pdrv, fs->win, 0, 1)) return FR_DISK_ERR;
+		if (LD_WORD(fs->win+BS_55AA) != 0xAA55) return FR_MKFS_ABORTED;
+		tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE];
+		if (!tbl[4]) return FR_MKFS_ABORTED;	/* No partition? */
+		b_vol = LD_DWORD(tbl+8);	/* Volume start sector */
+		n_vol = LD_DWORD(tbl+12);	/* Volume size */
+	} else {
+		/* Create a partition in this function */
+		if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128)
+			return FR_DISK_ERR;
+		b_vol = (sfd) ? 0 : 63;		/* Volume start sector */
+		n_vol -= b_vol;				/* Volume size */
+	}
+
+	if (!au) {				/* AU auto selection */
+		vs = n_vol / (2000 / (SS(fs) / 512));
+		for (i = 0; vs < vst[i]; i++) ;
+		au = cst[i];
+	}
+	au /= SS(fs);		/* Number of sectors per cluster */
+	if (au == 0) au = 1;
+	if (au > 128) au = 128;
+
+	/* Pre-compute number of clusters and FAT sub-type */
+	n_clst = n_vol / au;
+	fmt = FS_FAT12;
+	if (n_clst >= MIN_FAT16) fmt = FS_FAT16;
+	if (n_clst >= MIN_FAT32) fmt = FS_FAT32;
+
+	/* Determine offset and size of FAT structure */
+	if (fmt == FS_FAT32) {
+		n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs);
+		n_rsv = 32;
+		n_dir = 0;
+	} else {
+		n_fat = (fmt == FS_FAT12) ? (n_clst * 3 + 1) / 2 + 3 : (n_clst * 2) + 4;
+		n_fat = (n_fat + SS(fs) - 1) / SS(fs);
+		n_rsv = 1;
+		n_dir = (DWORD)N_ROOTDIR * SZ_DIR / SS(fs);
+	}
+	b_fat = b_vol + n_rsv;				/* FAT area start sector */
+	b_dir = b_fat + n_fat * N_FATS;		/* Directory area start sector */
+	b_data = b_dir + n_dir;				/* Data area start sector */
+	if (n_vol < b_data + au - b_vol) return FR_MKFS_ABORTED;	/* Too small volume */
+
+	/* Align data start sector to erase block boundary (for flash memory media) */
+	if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &n) != RES_OK || !n || n > 32768) n = 1;
+	n = (b_data + n - 1) & ~(n - 1);	/* Next nearest erase block from current data start */
+	n = (n - b_data) / N_FATS;
+	if (fmt == FS_FAT32) {		/* FAT32: Move FAT offset */
+		n_rsv += n;
+		b_fat += n;
+	} else {					/* FAT12/16: Expand FAT size */
+		n_fat += n;
+	}
+
+	/* Determine number of clusters and final check of validity of the FAT sub-type */
+	n_clst = (n_vol - n_rsv - n_fat * N_FATS - n_dir) / au;
+	if (   (fmt == FS_FAT16 && n_clst < MIN_FAT16)
+			|| (fmt == FS_FAT32 && n_clst < MIN_FAT32))
+		return FR_MKFS_ABORTED;
+
+	/* Determine system ID in the partition table */
+	if (fmt == FS_FAT32) {
+		sys = 0x0C;		/* FAT32X */
+	} else {
+		if (fmt == FS_FAT12 && n_vol < 0x10000) {
+			sys = 0x01;	/* FAT12(<65536) */
+		} else {
+			sys = (n_vol < 0x10000) ? 0x04 : 0x06;	/* FAT16(<65536) : FAT12/16(>=65536) */
+		}
+	}
+
+	if (_MULTI_PARTITION && part) {
+		/* Update system ID in the partition table */
+		tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE];
+		tbl[4] = sys;
+		if (disk_write(pdrv, fs->win, 0, 1))	/* Write it to teh MBR */
+			return FR_DISK_ERR;
+		md = 0xF8;
+	} else {
+		if (sfd) {	/* No partition table (SFD) */
+			md = 0xF0;
+		} else {	/* Create partition table (FDISK) */
+			mem_set(fs->win, 0, SS(fs));
+			tbl = fs->win+MBR_Table;	/* Create partition table for single partition in the drive */
+			tbl[1] = 1;						/* Partition start head */
+			tbl[2] = 1;						/* Partition start sector */
+			tbl[3] = 0;						/* Partition start cylinder */
+			tbl[4] = sys;					/* System type */
+			tbl[5] = 254;					/* Partition end head */
+			n = (b_vol + n_vol) / 63 / 255;
+			tbl[6] = (BYTE)(n >> 2 | 63);	/* Partition end sector */
+			tbl[7] = (BYTE)n;				/* End cylinder */
+			ST_DWORD(tbl+8, 63);			/* Partition start in LBA */
+			ST_DWORD(tbl+12, n_vol);		/* Partition size in LBA */
+			ST_WORD(fs->win+BS_55AA, 0xAA55);	/* MBR signature */
+			if (disk_write(pdrv, fs->win, 0, 1))	/* Write it to the MBR */
+				return FR_DISK_ERR;
+			md = 0xF8;
+		}
+	}
+
+	/* Create BPB in the VBR */
+	tbl = fs->win;							/* Clear sector */
+	mem_set(tbl, 0, SS(fs));
+	mem_cpy(tbl, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code, OEM name */
+	i = SS(fs);								/* Sector size */
+	ST_WORD(tbl+BPB_BytsPerSec, i);
+	tbl[BPB_SecPerClus] = (BYTE)au;			/* Sectors per cluster */
+	ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv);		/* Reserved sectors */
+	tbl[BPB_NumFATs] = N_FATS;				/* Number of FATs */
+	i = (fmt == FS_FAT32) ? 0 : N_ROOTDIR;	/* Number of root directory entries */
+	ST_WORD(tbl+BPB_RootEntCnt, i);
+	if (n_vol < 0x10000) {					/* Number of total sectors */
+		ST_WORD(tbl+BPB_TotSec16, n_vol);
+	} else {
+		ST_DWORD(tbl+BPB_TotSec32, n_vol);
+	}
+	tbl[BPB_Media] = md;					/* Media descriptor */
+	ST_WORD(tbl+BPB_SecPerTrk, 63);			/* Number of sectors per track */
+	ST_WORD(tbl+BPB_NumHeads, 255);			/* Number of heads */
+	ST_DWORD(tbl+BPB_HiddSec, b_vol);		/* Hidden sectors */
+	n = get_fattime();						/* Use current time as VSN */
+	if (fmt == FS_FAT32) {
+		ST_DWORD(tbl+BS_VolID32, n);		/* VSN */
+		ST_DWORD(tbl+BPB_FATSz32, n_fat);	/* Number of sectors per FAT */
+		ST_DWORD(tbl+BPB_RootClus, 2);		/* Root directory start cluster (2) */
+		ST_WORD(tbl+BPB_FSInfo, 1);			/* FSINFO record offset (VBR+1) */
+		ST_WORD(tbl+BPB_BkBootSec, 6);		/* Backup boot record offset (VBR+6) */
+		tbl[BS_DrvNum32] = 0x80;			/* Drive number */
+		tbl[BS_BootSig32] = 0x29;			/* Extended boot signature */
+		mem_cpy(tbl+BS_VolLab32, "NO NAME    " "FAT32   ", 19);	/* Volume label, FAT signature */
+	} else {
+		ST_DWORD(tbl+BS_VolID, n);			/* VSN */
+		ST_WORD(tbl+BPB_FATSz16, n_fat);	/* Number of sectors per FAT */
+		tbl[BS_DrvNum] = 0x80;				/* Drive number */
+		tbl[BS_BootSig] = 0x29;				/* Extended boot signature */
+		mem_cpy(tbl+BS_VolLab, "NO NAME    " "FAT     ", 19);	/* Volume label, FAT signature */
+	}
+	ST_WORD(tbl+BS_55AA, 0xAA55);			/* Signature (Offset is fixed here regardless of sector size) */
+	if (disk_write(pdrv, tbl, b_vol, 1))	/* Write it to the VBR sector */
+		return FR_DISK_ERR;
+	if (fmt == FS_FAT32)					/* Write backup VBR if needed (VBR+6) */
+		disk_write(pdrv, tbl, b_vol + 6, 1);
+
+	/* Initialize FAT area */
+	wsect = b_fat;
+	for (i = 0; i < N_FATS; i++) {		/* Initialize each FAT copy */
+		mem_set(tbl, 0, SS(fs));			/* 1st sector of the FAT  */
+		n = md;								/* Media descriptor byte */
+		if (fmt != FS_FAT32) {
+			n |= (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00;
+			ST_DWORD(tbl+0, n);				/* Reserve cluster #0-1 (FAT12/16) */
+		} else {
+			n |= 0xFFFFFF00;
+			ST_DWORD(tbl+0, n);				/* Reserve cluster #0-1 (FAT32) */
+			ST_DWORD(tbl+4, 0xFFFFFFFF);
+			ST_DWORD(tbl+8, 0x0FFFFFFF);	/* Reserve cluster #2 for root directory */
+		}
+		if (disk_write(pdrv, tbl, wsect++, 1))
+			return FR_DISK_ERR;
+		mem_set(tbl, 0, SS(fs));			/* Fill following FAT entries with zero */
+		for (n = 1; n < n_fat; n++) {		/* This loop may take a time on FAT32 volume due to many single sector writes */
+			if (disk_write(pdrv, tbl, wsect++, 1))
+				return FR_DISK_ERR;
+		}
+	}
+
+	/* Initialize root directory */
+	i = (fmt == FS_FAT32) ? au : (UINT)n_dir;
+	do {
+		if (disk_write(pdrv, tbl, wsect++, 1))
+			return FR_DISK_ERR;
+	} while (--i);
+
+#if _USE_ERASE	/* Erase data area if needed */
+	{
+		DWORD eb[2];
+
+		eb[0] = wsect; eb[1] = wsect + (n_clst - ((fmt == FS_FAT32) ? 1 : 0)) * au - 1;
+		disk_ioctl(pdrv, CTRL_ERASE_SECTOR, eb);
+	}
+#endif
+
+	/* Create FSINFO if needed */
+	if (fmt == FS_FAT32) {
+		ST_DWORD(tbl+FSI_LeadSig, 0x41615252);
+		ST_DWORD(tbl+FSI_StrucSig, 0x61417272);
+		ST_DWORD(tbl+FSI_Free_Count, n_clst - 1);	/* Number of free clusters */
+		ST_DWORD(tbl+FSI_Nxt_Free, 2);				/* Last allocated cluster# */
+		ST_WORD(tbl+BS_55AA, 0xAA55);
+		disk_write(pdrv, tbl, b_vol + 1, 1);	/* Write original (VBR+1) */
+		disk_write(pdrv, tbl, b_vol + 7, 1);	/* Write backup (VBR+7) */
+	}
+
+	return (disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR;
+}
+
+
+
+#if _MULTI_PARTITION
+/*-----------------------------------------------------------------------*/
+/* Divide Physical Drive                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_fdisk (
+		BYTE pdrv,			/* Physical drive number */
+		const DWORD szt[],	/* Pointer to the size table for each partitions */
+		void* work			/* Pointer to the working buffer */
+)
+{
+	UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl;
+	BYTE s_hd, e_hd, *p, *buf = (BYTE*)work;
+	DSTATUS stat;
+	DWORD sz_disk, sz_part, s_part;
+
+
+	stat = disk_initialize(pdrv);
+	if (stat & STA_NOINIT) return FR_NOT_READY;
+	if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
+	if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR;
+
+	/* Determine CHS in the table regardless of the drive geometry */
+	for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ;
+	if (n == 256) n--;
+	e_hd = n - 1;
+	sz_cyl = 63 * n;
+	tot_cyl = sz_disk / sz_cyl;
+
+	/* Create partition table */
+	mem_set(buf, 0, _MAX_SS);
+	p = buf + MBR_Table; b_cyl = 0;
+	for (i = 0; i < 4; i++, p += SZ_PTE) {
+		p_cyl = (szt[i] <= 100U) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl;
+		if (!p_cyl) continue;
+		s_part = (DWORD)sz_cyl * b_cyl;
+		sz_part = (DWORD)sz_cyl * p_cyl;
+		if (i == 0) {	/* Exclude first track of cylinder 0 */
+			s_hd = 1;
+			s_part += 63; sz_part -= 63;
+		} else {
+			s_hd = 0;
+		}
+		e_cyl = b_cyl + p_cyl - 1;
+		if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER;
+
+		/* Set partition table */
+		p[1] = s_hd;						/* Start head */
+		p[2] = (BYTE)((b_cyl >> 2) + 1);	/* Start sector */
+		p[3] = (BYTE)b_cyl;					/* Start cylinder */
+		p[4] = 0x06;						/* System type (temporary setting) */
+		p[5] = e_hd;						/* End head */
+		p[6] = (BYTE)((e_cyl >> 2) + 63);	/* End sector */
+		p[7] = (BYTE)e_cyl;					/* End cylinder */
+		ST_DWORD(p + 8, s_part);			/* Start sector in LBA */
+		ST_DWORD(p + 12, sz_part);			/* Partition size */
+
+		/* Next partition */
+		b_cyl += p_cyl;
+	}
+	ST_WORD(p, 0xAA55);
+
+	/* Write it to the MBR */
+	return (disk_write(pdrv, buf, 0, 1) || disk_ioctl(pdrv, CTRL_SYNC, 0)) ? FR_DISK_ERR : FR_OK;
+}
+
+
+#endif /* _MULTI_PARTITION */
+#endif /* _USE_MKFS && !_FS_READONLY */
+
+
+
+
+#if _USE_STRFUNC
+/*-----------------------------------------------------------------------*/
+/* Get a string from the file                                            */
+/*-----------------------------------------------------------------------*/
+
+TCHAR* f_gets (
+		TCHAR* buff,	/* Pointer to the string buffer to read */
+		int len,		/* Size of string buffer (characters) */
+		FIL* fp			/* Pointer to the file object */
+)
+{
+	int n = 0;
+	TCHAR c, *p = buff;
+	BYTE s[2];
+	UINT rc;
+
+
+	while (n < len - 1) {	/* Read characters until buffer gets filled */
+#if _USE_LFN && _LFN_UNICODE
+#if _STRF_ENCODE == 3		/* Read a character in UTF-8 */
+		f_read(fp, s, 1, &rc);
+		if (rc != 1) break;
+		c = s[0];
+		if (c >= 0x80) {
+			if (c < 0xC0) continue;	/* Skip stray trailer */
+			if (c < 0xE0) {			/* Two-byte sequence */
+				f_read(fp, s, 1, &rc);
+				if (rc != 1) break;
+				c = (c & 0x1F) << 6 | (s[0] & 0x3F);
+				if (c < 0x80) c = '?';
+			} else {
+				if (c < 0xF0) {		/* Three-byte sequence */
+					f_read(fp, s, 2, &rc);
+					if (rc != 2) break;
+					c = c << 12 | (s[0] & 0x3F) << 6 | (s[1] & 0x3F);
+					if (c < 0x800) c = '?';
+				} else {			/* Reject four-byte sequence */
+					c = '?';
+				}
+			}
+		}
+#elif _STRF_ENCODE == 2		/* Read a character in UTF-16BE */
+		f_read(fp, s, 2, &rc);
+		if (rc != 2) break;
+		c = s[1] + (s[0] << 8);
+#elif _STRF_ENCODE == 1		/* Read a character in UTF-16LE */
+		f_read(fp, s, 2, &rc);
+		if (rc != 2) break;
+		c = s[0] + (s[1] << 8);
+#else						/* Read a character in ANSI/OEM */
+		f_read(fp, s, 1, &rc);
+		if (rc != 1) break;
+		c = s[0];
+		if (IsDBCS1(c)) {
+			f_read(fp, s, 1, &rc);
+			if (rc != 1) break;
+			c = (c << 8) + s[0];
+		}
+		c = ff_convert(c, 1);	/* OEM -> Unicode */
+		if (!c) c = '?';
+#endif
+#else						/* Read a character without conversion */
+		f_read(fp, s, 1, &rc);
+		if (rc != 1) break;
+		c = s[0];
+#endif
+		if (_USE_STRFUNC == 2 && c == '\r') continue;	/* Strip '\r' */
+		*p++ = c;
+		n++;
+		if (c == '\n') break;		/* Break on EOL */
+	}
+	*p = 0;
+	return n ? buff : 0;			/* When no data read (eof or error), return with error. */
+}
+
+
+
+#if !_FS_READONLY
+#include <stdarg.h>
+/*-----------------------------------------------------------------------*/
+/* Put a character to the file                                           */
+/*-----------------------------------------------------------------------*/
+
+typedef struct {
+	FIL* fp;
+	int idx, nchr;
+	BYTE buf[64];
+} putbuff;
+
+
+static
+void putc_bfd (
+		putbuff* pb,
+		TCHAR c
+)
+{
+	UINT bw;
+	int i;
+
+
+	if (_USE_STRFUNC == 2 && c == '\n')	 /* LF -> CRLF conversion */
+		putc_bfd(pb, '\r');
+
+	i = pb->idx;	/* Buffer write index (-1:error) */
+	if (i < 0) return;
+
+#if _USE_LFN && _LFN_UNICODE
+#if _STRF_ENCODE == 3			/* Write a character in UTF-8 */
+	if (c < 0x80) {				/* 7-bit */
+		pb->buf[i++] = (BYTE)c;
+	} else {
+		if (c < 0x800) {		/* 11-bit */
+			pb->buf[i++] = (BYTE)(0xC0 | c >> 6);
+		} else {				/* 16-bit */
+			pb->buf[i++] = (BYTE)(0xE0 | c >> 12);
+			pb->buf[i++] = (BYTE)(0x80 | (c >> 6 & 0x3F));
+		}
+		pb->buf[i++] = (BYTE)(0x80 | (c & 0x3F));
+	}
+#elif _STRF_ENCODE == 2			/* Write a character in UTF-16BE */
+	pb->buf[i++] = (BYTE)(c >> 8);
+	pb->buf[i++] = (BYTE)c;
+#elif _STRF_ENCODE == 1			/* Write a character in UTF-16LE */
+	pb->buf[i++] = (BYTE)c;
+	pb->buf[i++] = (BYTE)(c >> 8);
+#else							/* Write a character in ANSI/OEM */
+	c = ff_convert(c, 0);	/* Unicode -> OEM */
+	if (!c) c = '?';
+	if (c >= 0x100)
+		pb->buf[i++] = (BYTE)(c >> 8);
+	pb->buf[i++] = (BYTE)c;
+#endif
+#else							/* Write a character without conversion */
+	pb->buf[i++] = (BYTE)c;
+#endif
+
+	if (i >= (int)(sizeof pb->buf) - 3) {	/* Write buffered characters to the file */
+		f_write(pb->fp, pb->buf, (UINT)i, &bw);
+		i = (bw == (UINT)i) ? 0 : -1;
+	}
+	pb->idx = i;
+	pb->nchr++;
+}
+
+
+
+int f_putc (
+		TCHAR c,	/* A character to be output */
+		FIL* fp		/* Pointer to the file object */
+)
+{
+	putbuff pb;
+	UINT nw;
+
+
+	pb.fp = fp;			/* Initialize output buffer */
+	pb.nchr = pb.idx = 0;
+
+	putc_bfd(&pb, c);	/* Put a character */
+
+	if (   pb.idx >= 0	/* Flush buffered characters to the file */
+			&& f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK
+			&& (UINT)pb.idx == nw) return pb.nchr;
+	return EOF;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a string to the file                                              */
+/*-----------------------------------------------------------------------*/
+
+int f_puts (
+		const TCHAR* str,	/* Pointer to the string to be output */
+		FIL* fp				/* Pointer to the file object */
+)
+{
+	putbuff pb;
+	UINT nw;
+
+
+	pb.fp = fp;				/* Initialize output buffer */
+	pb.nchr = pb.idx = 0;
+
+	while (*str)			/* Put the string */
+		putc_bfd(&pb, *str++);
+
+	if (   pb.idx >= 0		/* Flush buffered characters to the file */
+			&& f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK
+			&& (UINT)pb.idx == nw) return pb.nchr;
+	return EOF;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a formatted string to the file                                    */
+/*-----------------------------------------------------------------------*/
+
+int f_printf (
+		FIL* fp,			/* Pointer to the file object */
+		const TCHAR* fmt,	/* Pointer to the format string */
+		...					/* Optional arguments... */
+)
+{
+	va_list arp;
+	BYTE f, r;
+	UINT nw, i, j, w;
+	DWORD v;
+	TCHAR c, d, s[16], *p;
+	putbuff pb;
+
+
+	pb.fp = fp;				/* Initialize output buffer */
+	pb.nchr = pb.idx = 0;
+
+	va_start(arp, fmt);
+
+	for (;;) {
+		c = *fmt++;
+		if (c == 0) break;			/* End of string */
+		if (c != '%') {				/* Non escape character */
+			putc_bfd(&pb, c);
+			continue;
+		}
+		w = f = 0;
+		c = *fmt++;
+		if (c == '0') {				/* Flag: '0' padding */
+			f = 1; c = *fmt++;
+		} else {
+			if (c == '-') {			/* Flag: left justified */
+				f = 2; c = *fmt++;
+			}
+		}
+		while (IsDigit(c)) {		/* Precision */
+			w = w * 10 + c - '0';
+			c = *fmt++;
+		}
+		if (c == 'l' || c == 'L') {	/* Prefix: Size is long int */
+			f |= 4; c = *fmt++;
+		}
+		if (!c) break;
+		d = c;
+		if (IsLower(d)) d -= 0x20;
+		switch (d) {				/* Type is... */
+		case 'S' :					/* String */
+			p = va_arg(arp, TCHAR*);
+			for (j = 0; p[j]; j++) ;
+			if (!(f & 2)) {
+				while (j++ < w) putc_bfd(&pb, ' ');
+			}
+			while (*p) putc_bfd(&pb, *p++);
+			while (j++ < w) putc_bfd(&pb, ' ');
+			continue;
+		case 'C' :					/* Character */
+			putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue;
+		case 'B' :					/* Binary */
+			r = 2; break;
+		case 'O' :					/* Octal */
+			r = 8; break;
+		case 'D' :					/* Signed decimal */
+		case 'U' :					/* Unsigned decimal */
+			r = 10; break;
+		case 'X' :					/* Hexdecimal */
+			r = 16; break;
+		default:					/* Unknown type (pass-through) */
+			putc_bfd(&pb, c); continue;
+		}
+
+		/* Get an argument and put it in numeral */
+		v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int));
+		if (d == 'D' && (v & 0x80000000)) {
+			v = 0 - v;
+			f |= 8;
+		}
+		i = 0;
+		do {
+			d = (TCHAR)(v % r); v /= r;
+			if (d > 9) d += (c == 'x') ? 0x27 : 0x07;
+			s[i++] = d + '0';
+		} while (v && i < sizeof s / sizeof s[0]);
+		if (f & 8) s[i++] = '-';
+		j = i; d = (f & 1) ? '0' : ' ';
+		while (!(f & 2) && j++ < w) putc_bfd(&pb, d);
+		do putc_bfd(&pb, s[--i]); while (i);
+		while (j++ < w) putc_bfd(&pb, d);
+	}
+
+	va_end(arp);
+
+	if (   pb.idx >= 0		/* Flush buffered characters to the file */
+			&& f_write(pb.fp, pb.buf, (UINT)pb.idx, &nw) == FR_OK
+			&& (UINT)pb.idx == nw) return pb.nchr;
+	return EOF;
+}
+
+#endif /* !_FS_READONLY */
+#endif /* _USE_STRFUNC */

+ 342 - 0
iap/FATFS/ff.h

@@ -0,0 +1,342 @@
+/*---------------------------------------------------------------------------/
+/  FatFs - FAT file system module include file  R0.10b    (C)ChaN, 2014
+/----------------------------------------------------------------------------/
+/ FatFs module is a generic FAT file system module for small embedded systems.
+/ This is a free software that opened for education, research and commercial
+/ developments under license policy of following terms.
+/
+/  Copyright (C) 2014, ChaN, all right reserved.
+/
+/ * The FatFs module is a free software and there is NO WARRANTY.
+/ * No restriction on use. You can use, modify and redistribute it for
+/   personal, non-profit or commercial product UNDER YOUR RESPONSIBILITY.
+/ * Redistributions of source code must retain the above copyright notice.
+/
+/----------------------------------------------------------------------------*/
+
+#ifndef _FATFS
+#define _FATFS	8051	/* Revision ID */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "integer.h"	/* Basic integer types */
+#include "ffconf.h"		/* FatFs configuration options */
+
+#if _FATFS != _FFCONF
+#error Wrong configuration file (ffconf.h).
+#endif
+
+
+
+/* Definitions of volume management */
+
+#if _MULTI_PARTITION		/* Multiple partition configuration */
+typedef struct {
+	BYTE pd;	/* Physical drive number */
+	BYTE pt;	/* Partition: 0:Auto detect, 1-4:Forced partition) */
+} PARTITION;
+extern PARTITION VolToPart[];	/* Volume - Partition resolution table */
+#define LD2PD(vol) (VolToPart[vol].pd)	/* Get physical drive number */
+#define LD2PT(vol) (VolToPart[vol].pt)	/* Get partition index */
+
+#else							/* Single partition configuration */
+#define LD2PD(vol) (BYTE)(vol)	/* Each logical drive is bound to the same physical drive number */
+#define LD2PT(vol) 0			/* Find first valid partition or in SFD */
+
+#endif
+
+
+
+/* Type of path name strings on FatFs API */
+
+#if _LFN_UNICODE			/* Unicode string */
+#if !_USE_LFN
+#error _LFN_UNICODE must be 0 at non-LFN cfg.
+#endif
+#ifndef _INC_TCHAR
+typedef WCHAR TCHAR;
+#define _T(x) L ## x
+#define _TEXT(x) L ## x
+#endif
+
+#else						/* ANSI/OEM string */
+#ifndef _INC_TCHAR
+typedef char TCHAR;
+#define _T(x) x
+#define _TEXT(x) x
+#endif
+
+#endif
+
+
+
+/* File system object structure (FATFS) */
+
+typedef struct {
+	BYTE	fs_type;		/* FAT sub-type (0:Not mounted) */
+	BYTE	drv;			/* Physical drive number */
+	BYTE	csize;			/* Sectors per cluster (1,2,4...128) */
+	BYTE	n_fats;			/* Number of FAT copies (1 or 2) */
+	BYTE	wflag;			/* win[] flag (b0:dirty) */
+	BYTE	fsi_flag;		/* FSINFO flags (b7:disabled, b0:dirty) */
+	WORD	id;				/* File system mount ID */
+	WORD	n_rootdir;		/* Number of root directory entries (FAT12/16) */
+#if _MAX_SS != _MIN_SS
+	WORD	ssize;			/* Bytes per sector (512, 1024, 2048 or 4096) */
+#endif
+#if _FS_REENTRANT
+	_SYNC_t	sobj;			/* Identifier of sync object */
+#endif
+#if !_FS_READONLY
+	DWORD	last_clust;		/* Last allocated cluster */
+	DWORD	free_clust;		/* Number of free clusters */
+#endif
+#if _FS_RPATH
+	DWORD	cdir;			/* Current directory start cluster (0:root) */
+#endif
+	DWORD	n_fatent;		/* Number of FAT entries, = number of clusters + 2 */
+	DWORD	fsize;			/* Sectors per FAT */
+	DWORD	volbase;		/* Volume start sector */
+	DWORD	fatbase;		/* FAT start sector */
+	DWORD	dirbase;		/* Root directory start sector (FAT32:Cluster#) */
+	DWORD	database;		/* Data start sector */
+	DWORD	winsect;		/* Current sector appearing in the win[] */
+	BYTE	win[_MAX_SS];	/* Disk access window for Directory, FAT (and file data at tiny cfg) */
+} FATFS;
+
+
+
+/* File object structure (FIL) */
+
+typedef struct {
+	FATFS*	fs;				/* Pointer to the related file system object (**do not change order**) */
+	WORD	id;				/* Owner file system mount ID (**do not change order**) */
+	BYTE	flag;			/* Status flags */
+	BYTE	err;			/* Abort flag (error code) */
+	DWORD	fptr;			/* File read/write pointer (Zeroed on file open) */
+	DWORD	fsize;			/* File size */
+	DWORD	sclust;			/* File start cluster (0:no cluster chain, always 0 when fsize is 0) */
+	DWORD	clust;			/* Current cluster of fpter (not valid when fprt is 0) */
+	DWORD	dsect;			/* Sector number appearing in buf[] (0:invalid) */
+#if !_FS_READONLY
+	DWORD	dir_sect;		/* Sector number containing the directory entry */
+	BYTE*	dir_ptr;		/* Pointer to the directory entry in the win[] */
+#endif
+#if _USE_FASTSEEK
+	DWORD*	cltbl;			/* Pointer to the cluster link map table (Nulled on file open) */
+#endif
+#if _FS_LOCK
+	UINT	lockid;			/* File lock ID origin from 1 (index of file semaphore table Files[]) */
+#endif
+#if !_FS_TINY
+	BYTE	buf[_MAX_SS];	/* File private data read/write window */
+#endif
+} FIL;
+
+
+
+/* Directory object structure (DIR) */
+
+typedef struct {
+	FATFS*	fs;				/* Pointer to the owner file system object (**do not change order**) */
+	WORD	id;				/* Owner file system mount ID (**do not change order**) */
+	WORD	index;			/* Current read/write index number */
+	DWORD	sclust;			/* Table start cluster (0:Root dir) */
+	DWORD	clust;			/* Current cluster */
+	DWORD	sect;			/* Current sector */
+	BYTE*	dir;			/* Pointer to the current SFN entry in the win[] */
+	BYTE*	fn;				/* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */
+#if _FS_LOCK
+	UINT	lockid;			/* File lock ID (index of file semaphore table Files[]) */
+#endif
+#if _USE_LFN
+	WCHAR*	lfn;			/* Pointer to the LFN working buffer */
+	WORD	lfn_idx;		/* Last matched LFN index number (0xFFFF:No LFN) */
+#endif
+} DIR;
+
+
+
+/* File status structure (FILINFO) */
+
+typedef struct {
+	DWORD	fsize;			/* File size */
+	WORD	fdate;			/* Last modified date */
+	WORD	ftime;			/* Last modified time */
+	BYTE	fattrib;		/* Attribute */
+	TCHAR	fname[13];		/* Short file name (8.3 format) */
+#if _USE_LFN
+	TCHAR*	lfname;			/* Pointer to the LFN buffer */
+	UINT 	lfsize;			/* Size of LFN buffer in TCHAR */
+#endif
+} FILINFO;
+
+
+
+/* File function return code (FRESULT) */
+
+typedef enum {
+	FR_OK = 0,				/* (0) Succeeded */
+	FR_DISK_ERR,			/* (1) A hard error occurred in the low level disk I/O layer */
+	FR_INT_ERR,				/* (2) Assertion failed */
+	FR_NOT_READY,			/* (3) The physical drive cannot work */
+	FR_NO_FILE,				/* (4) Could not find the file */
+	FR_NO_PATH,				/* (5) Could not find the path */
+	FR_INVALID_NAME,		/* (6) The path name format is invalid */
+	FR_DENIED,				/* (7) Access denied due to prohibited access or directory full */
+	FR_EXIST,				/* (8) Access denied due to prohibited access */
+	FR_INVALID_OBJECT,		/* (9) The file/directory object is invalid */
+	FR_WRITE_PROTECTED,		/* (10) The physical drive is write protected */
+	FR_INVALID_DRIVE,		/* (11) The logical drive number is invalid */
+	FR_NOT_ENABLED,			/* (12) The volume has no work area */
+	FR_NO_FILESYSTEM,		/* (13) There is no valid FAT volume */
+	FR_MKFS_ABORTED,		/* (14) The f_mkfs() aborted due to any parameter error */
+	FR_TIMEOUT,				/* (15) Could not get a grant to access the volume within defined period */
+	FR_LOCKED,				/* (16) The operation is rejected according to the file sharing policy */
+	FR_NOT_ENOUGH_CORE,		/* (17) LFN working buffer could not be allocated */
+	FR_TOO_MANY_OPEN_FILES,	/* (18) Number of open files > _FS_SHARE */
+	FR_INVALID_PARAMETER	/* (19) Given parameter is invalid */
+} FRESULT;
+
+
+
+/*--------------------------------------------------------------*/
+/* FatFs module application interface                           */
+
+FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode);				/* Open or create a file */
+FRESULT f_close (FIL* fp);											/* Close an open file object */
+FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br);			/* Read data from a file */
+FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw);	/* Write data to a file */
+FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf);	/* Forward data to the stream */
+FRESULT f_lseek (FIL* fp, DWORD ofs);								/* Move file pointer of a file object */
+FRESULT f_truncate (FIL* fp);										/* Truncate file */
+FRESULT f_sync (FIL* fp);											/* Flush cached data of a writing file */
+FRESULT f_opendir (DIR* dp, const TCHAR* path);						/* Open a directory */
+FRESULT f_closedir (DIR* dp);										/* Close an open directory */
+FRESULT f_readdir (DIR* dp, FILINFO* fno);							/* Read a directory item */
+FRESULT f_mkdir (const TCHAR* path);								/* Create a sub directory */
+FRESULT f_unlink (const TCHAR* path);								/* Delete an existing file or directory */
+FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new);	/* Rename/Move a file or directory */
+FRESULT f_stat (const TCHAR* path, FILINFO* fno);					/* Get file status */
+FRESULT f_chmod (const TCHAR* path, BYTE value, BYTE mask);			/* Change attribute of the file/dir */
+FRESULT f_utime (const TCHAR* path, const FILINFO* fno);			/* Change times-tamp of the file/dir */
+FRESULT f_chdir (const TCHAR* path);								/* Change current directory */
+FRESULT f_chdrive (const TCHAR* path);								/* Change current drive */
+FRESULT f_getcwd (TCHAR* buff, UINT len);							/* Get current directory */
+FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs);	/* Get number of free clusters on the drive */
+FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn);	/* Get volume label */
+FRESULT f_setlabel (const TCHAR* label);							/* Set volume label */
+FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt);			/* Mount/Unmount a logical drive */
+FRESULT f_mkfs (const TCHAR* path, BYTE sfd, UINT au);				/* Create a file system on the volume */
+FRESULT f_fdisk (BYTE pdrv, const DWORD szt[], void* work);			/* Divide a physical drive into some partitions */
+int f_putc (TCHAR c, FIL* fp);										/* Put a character to the file */
+int f_puts (const TCHAR* str, FIL* cp);								/* Put a string to the file */
+int f_printf (FIL* fp, const TCHAR* str, ...);						/* Put a formatted string to the file */
+TCHAR* f_gets (TCHAR* buff, int len, FIL* fp);						/* Get a string from the file */
+
+#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0)
+#define f_error(fp) ((fp)->err)
+#define f_tell(fp) ((fp)->fptr)
+#define f_size(fp) ((fp)->fsize)
+
+#ifndef EOF
+#define EOF (-1)
+#endif
+
+
+
+
+/*--------------------------------------------------------------*/
+/* Additional user defined functions                            */
+
+/* RTC function */
+#if !_FS_READONLY
+DWORD get_fattime (void);
+#endif
+
+/* Unicode support functions */
+#if _USE_LFN							/* Unicode - OEM code conversion */
+WCHAR ff_convert (WCHAR chr, UINT dir);	/* OEM-Unicode bidirectional conversion */
+WCHAR ff_wtoupper (WCHAR chr);			/* Unicode upper-case conversion */
+#if _USE_LFN == 3						/* Memory functions */
+void* ff_memalloc (UINT msize);			/* Allocate memory block */
+void ff_memfree (void* mblock);			/* Free memory block */
+#endif
+#endif
+
+/* Sync functions */
+#if _FS_REENTRANT
+int ff_cre_syncobj (BYTE vol, _SYNC_t* sobj);	/* Create a sync object */
+int ff_req_grant (_SYNC_t sobj);				/* Lock sync object */
+void ff_rel_grant (_SYNC_t sobj);				/* Unlock sync object */
+int ff_del_syncobj (_SYNC_t sobj);				/* Delete a sync object */
+#endif
+
+
+
+
+/*--------------------------------------------------------------*/
+/* Flags and offset address                                     */
+
+
+/* File access control and file status flags (FIL.flag) */
+
+#define	FA_READ				0x01
+#define	FA_OPEN_EXISTING	0x00
+
+#if !_FS_READONLY
+#define	FA_WRITE			0x02
+#define	FA_CREATE_NEW		0x04
+#define	FA_CREATE_ALWAYS	0x08
+#define	FA_OPEN_ALWAYS		0x10
+#define FA__WRITTEN			0x20
+#define FA__DIRTY			0x40
+#endif
+
+
+/* FAT sub type (FATFS.fs_type) */
+
+#define FS_FAT12	1
+#define FS_FAT16	2
+#define FS_FAT32	3
+
+
+/* File attribute bits for directory entry */
+
+#define	AM_RDO	0x01	/* Read only */
+#define	AM_HID	0x02	/* Hidden */
+#define	AM_SYS	0x04	/* System */
+#define	AM_VOL	0x08	/* Volume label */
+#define AM_LFN	0x0F	/* LFN entry */
+#define AM_DIR	0x10	/* Directory */
+#define AM_ARC	0x20	/* Archive */
+#define AM_MASK	0x3F	/* Mask of defined bits */
+
+
+/* Fast seek feature */
+#define CREATE_LINKMAP	0xFFFFFFFF
+
+
+
+/*--------------------------------*/
+/* Multi-byte word access macros  */
+
+#if _WORD_ACCESS == 1	/* Enable word access to the FAT structure */
+#define	LD_WORD(ptr)		(WORD)(*(WORD*)(BYTE*)(ptr))
+#define	LD_DWORD(ptr)		(DWORD)(*(DWORD*)(BYTE*)(ptr))
+#define	ST_WORD(ptr,val)	*(WORD*)(BYTE*)(ptr)=(WORD)(val)
+#define	ST_DWORD(ptr,val)	*(DWORD*)(BYTE*)(ptr)=(DWORD)(val)
+#else					/* Use byte-by-byte access to the FAT structure */
+#define	LD_WORD(ptr)		(WORD)(((WORD)*((BYTE*)(ptr)+1)<<8)|(WORD)*(BYTE*)(ptr))
+#define	LD_DWORD(ptr)		(DWORD)(((DWORD)*((BYTE*)(ptr)+3)<<24)|((DWORD)*((BYTE*)(ptr)+2)<<16)|((WORD)*((BYTE*)(ptr)+1)<<8)|*(BYTE*)(ptr))
+#define	ST_WORD(ptr,val)	*(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8)
+#define	ST_DWORD(ptr,val)	*(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8); *((BYTE*)(ptr)+2)=(BYTE)((DWORD)(val)>>16); *((BYTE*)(ptr)+3)=(BYTE)((DWORD)(val)>>24)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FATFS */

+ 228 - 0
iap/FATFS/ffconf.h

@@ -0,0 +1,228 @@
+/*---------------------------------------------------------------------------/
+/  FatFs - FAT file system module configuration file  R0.10b (C)ChaN, 2014
+/---------------------------------------------------------------------------*/
+
+#ifndef _FFCONF
+#define _FFCONF 8051	/* Revision ID */
+
+
+/*---------------------------------------------------------------------------/
+/ Functions and Buffer Configurations
+/---------------------------------------------------------------------------*/
+
+#define	_FS_TINY		0	/* 0:Normal or 1:Tiny */
+/* When _FS_TINY is set to 1, it reduces memory consumption _MAX_SS bytes each
+/  file object. For file data transfer, FatFs uses the common sector buffer in
+/  the file system object (FATFS) instead of private sector buffer eliminated
+/  from the file object (FIL). */
+
+
+#define _FS_READONLY	1	/* 0:Read/Write or 1:Read only */
+/* Setting _FS_READONLY to 1 defines read only configuration. This removes
+/  writing functions, f_write(), f_sync(), f_unlink(), f_mkdir(), f_chmod(),
+/  f_rename(), f_truncate() and useless f_getfree(). */
+
+
+#define _FS_MINIMIZE	2	/* 0 to 3 */
+/* The _FS_MINIMIZE option defines minimization level to remove API functions.
+/
+/   0: All basic functions are enabled.
+/   1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(),
+/      f_truncate() and f_rename() function are removed.
+/   2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
+/   3: f_lseek() function is removed in addition to 2. */
+
+
+#define	_USE_STRFUNC	0	/* 0:Disable or 1-2:Enable */
+/* To enable string functions, set _USE_STRFUNC to 1 or 2. */
+
+
+#define	_USE_MKFS		0	/* 0:Disable or 1:Enable */
+/* To enable f_mkfs() function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */
+
+
+#define	_USE_FASTSEEK	0	/* 0:Disable or 1:Enable */
+/* To enable fast seek feature, set _USE_FASTSEEK to 1. */
+
+
+#define _USE_LABEL		0	/* 0:Disable or 1:Enable */
+/* To enable volume label functions, set _USE_LAVEL to 1 */
+
+
+#define	_USE_FORWARD	0	/* 0:Disable or 1:Enable */
+/* To enable f_forward() function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */
+
+
+/*---------------------------------------------------------------------------/
+/ Locale and Namespace Configurations
+/---------------------------------------------------------------------------*/
+
+#define _CODE_PAGE	1251
+/* The _CODE_PAGE specifies the OEM code page to be used on the target system.
+/  Incorrect setting of the code page can cause a file open failure.
+/
+/   932  - Japanese Shift_JIS (DBCS, OEM, Windows)
+/   936  - Simplified Chinese GBK (DBCS, OEM, Windows)
+/   949  - Korean (DBCS, OEM, Windows)
+/   950  - Traditional Chinese Big5 (DBCS, OEM, Windows)
+/   1250 - Central Europe (Windows)
+/   1251 - Cyrillic (Windows)
+/   1252 - Latin 1 (Windows)
+/   1253 - Greek (Windows)
+/   1254 - Turkish (Windows)
+/   1255 - Hebrew (Windows)
+/   1256 - Arabic (Windows)
+/   1257 - Baltic (Windows)
+/   1258 - Vietnam (OEM, Windows)
+/   437  - U.S. (OEM)
+/   720  - Arabic (OEM)
+/   737  - Greek (OEM)
+/   775  - Baltic (OEM)
+/   850  - Multilingual Latin 1 (OEM)
+/   858  - Multilingual Latin 1 + Euro (OEM)
+/   852  - Latin 2 (OEM)
+/   855  - Cyrillic (OEM)
+/   866  - Russian (OEM)
+/   857  - Turkish (OEM)
+/   862  - Hebrew (OEM)
+/   874  - Thai (OEM, Windows)
+/   1    - ASCII (Valid for only non-LFN configuration) */
+
+
+#define	_USE_LFN	0		/* 0 to 3 */
+#define	_MAX_LFN	255		/* Maximum LFN length to handle (12 to 255) */
+/* The _USE_LFN option switches the LFN feature.
+/
+/   0: Disable LFN feature. _MAX_LFN has no effect.
+/   1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
+/   2: Enable LFN with dynamic working buffer on the STACK.
+/   3: Enable LFN with dynamic working buffer on the HEAP.
+/
+/  When enable LFN feature, Unicode handling functions ff_convert() and ff_wtoupper()
+/  function must be added to the project.
+/  The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. When use stack for the
+/  working buffer, take care on stack overflow. When use heap memory for the working
+/  buffer, memory management functions, ff_memalloc() and ff_memfree(), must be added
+/  to the project. */
+
+
+#define	_LFN_UNICODE	0	/* 0:ANSI/OEM or 1:Unicode */
+/* To switch the character encoding on the FatFs API (TCHAR) to Unicode, enable LFN
+/  feature and set _LFN_UNICODE to 1. This option affects behavior of string I/O
+/  functions. This option must be 0 when LFN feature is not enabled. */
+
+
+#define _STRF_ENCODE	3	/* 0:ANSI/OEM, 1:UTF-16LE, 2:UTF-16BE, 3:UTF-8 */
+/* When Unicode API is enabled by _LFN_UNICODE option, this option selects the character
+/  encoding on the file to be read/written via string I/O functions, f_gets(), f_putc(),
+/  f_puts and f_printf(). This option has no effect when _LFN_UNICODE == 0. Note that
+/  FatFs supports only BMP. */
+
+
+#define _FS_RPATH		0	/* 0 to 2 */
+/* The _FS_RPATH option configures relative path feature.
+/
+/   0: Disable relative path feature and remove related functions.
+/   1: Enable relative path. f_chdrive() and f_chdir() function are available.
+/   2: f_getcwd() function is available in addition to 1.
+/
+/  Note that output of the f_readdir() fnction is affected by this option. */
+
+
+/*---------------------------------------------------------------------------/
+/ Drive/Volume Configurations
+/---------------------------------------------------------------------------*/
+
+#define _VOLUMES	1
+/* Number of volumes (logical drives) to be used. */
+
+
+#define _STR_VOLUME_ID	0	/* 0:Use only 0-9 for drive ID, 1:Use strings for drive ID */
+#define _VOLUME_STRS	"RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3"
+/* When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive
+/  number in the path name. _VOLUME_STRS defines the drive ID strings for each logical
+/  drives. Number of items must be equal to _VOLUMES. Valid characters for the drive ID
+/  strings are: 0-9 and A-Z. */
+
+
+#define	_MULTI_PARTITION	0	/* 0:Single partition, 1:Enable multiple partition */
+/* By default(0), each logical drive number is bound to the same physical drive number
+/  and only a FAT volume found on the physical drive is mounted. When it is set to 1,
+/  each logical drive number is bound to arbitrary drive/partition listed in VolToPart[].
+*/
+
+
+#define	_MIN_SS		512
+#define	_MAX_SS		512
+/* These options configure the range of sector size to be supported. (512, 1024, 2048 or
+/  4096) Always set both 512 for most systems, all memory card and harddisk. But a larger
+/  value may be required for on-board flash memory and some type of optical media.
+/  When _MAX_SS is larger than _MIN_SS, FatFs is configured to variable sector size and
+/  GET_SECTOR_SIZE command must be implemented to the disk_ioctl() function. */
+
+
+#define	_USE_ERASE	0	/* 0:Disable or 1:Enable */
+/* To enable sector erase feature, set _USE_ERASE to 1. Also CTRL_ERASE_SECTOR command
+/  should be added to the disk_ioctl() function. */
+
+
+#define _FS_NOFSINFO	0	/* 0 to 3 */
+/* If you need to know correct free space on the FAT32 volume, set bit 0 of this option
+/  and f_getfree() function at first time after volume mount will force a full FAT scan.
+/  Bit 1 controls the last allocated cluster number as bit 0.
+/
+/  bit0=0: Use free cluster count in the FSINFO if available.
+/  bit0=1: Do not trust free cluster count in the FSINFO.
+/  bit1=0: Use last allocated cluster number in the FSINFO if available.
+/  bit1=1: Do not trust last allocated cluster number in the FSINFO.
+*/
+
+
+
+/*---------------------------------------------------------------------------/
+/ System Configurations
+/---------------------------------------------------------------------------*/
+
+#define	_FS_LOCK	0	/* 0:Disable or >=1:Enable */
+/* To enable file lock control feature, set _FS_LOCK to non-zero value.
+/  The value defines how many files/sub-directories can be opened simultaneously
+/  with file lock control. This feature uses bss _FS_LOCK * 12 bytes. */
+
+
+#define _FS_REENTRANT	0		/* 0:Disable or 1:Enable */
+#define _FS_TIMEOUT		1000	/* Timeout period in unit of time tick */
+#define	_SYNC_t			HANDLE	/* O/S dependent sync object type. e.g. HANDLE, OS_EVENT*, ID, SemaphoreHandle_t and etc.. */
+/* The _FS_REENTRANT option switches the re-entrancy (thread safe) of the FatFs module.
+/
+/   0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect.
+/   1: Enable re-entrancy. Also user provided synchronization handlers,
+/      ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
+/      function must be added to the project.
+*/
+
+
+#define _WORD_ACCESS	0	/* 0 or 1 */
+/* The _WORD_ACCESS option is an only platform dependent option. It defines
+/  which access method is used to the word data on the FAT volume.
+/
+/   0: Byte-by-byte access. Always compatible with all platforms.
+/   1: Word access. Do not choose this unless under both the following conditions.
+/
+/  * Address misaligned memory access is always allowed for ALL instructions.
+/  * Byte order on the memory is little-endian.
+/
+/  If it is the case, _WORD_ACCESS can also be set to 1 to improve performance and
+/  reduce code size. Following table shows an example of some processor types.
+/
+/   ARM7TDMI    0           ColdFire    0           V850E       0
+/   Cortex-M3   0           Z80         0/1         V850ES      0/1
+/   Cortex-M0   0           RX600(LE)   0/1         TLCS-870    0/1
+/   AVR         0/1         RX600(BE)   0           TLCS-900    0/1
+/   AVR32       0           RL78        0           R32C        0
+/   PIC18       0/1         SH-2        0           M16C        0/1
+/   PIC24       0           H8S         0           MSP430      0
+/   PIC32       0           H8/300H     0           x86         0/1
+*/
+
+
+#endif /* _FFCONF */

+ 33 - 0
iap/FATFS/integer.h

@@ -0,0 +1,33 @@
+/*-------------------------------------------*/
+/* Integer type definitions for FatFs module */
+/*-------------------------------------------*/
+
+#ifndef _FF_INTEGER
+#define _FF_INTEGER
+
+#ifdef _WIN32	/* FatFs development platform */
+
+#include <windows.h>
+#include <tchar.h>
+
+#else			/* Embedded platform */
+
+/* This type MUST be 8 bit */
+typedef unsigned char	BYTE;
+
+/* These types MUST be 16 bit */
+typedef short			SHORT;
+typedef unsigned short	WORD;
+typedef unsigned short	WCHAR;
+
+/* These types MUST be 16 bit or 32 bit */
+typedef int				INT;
+typedef unsigned int	UINT;
+
+/* These types MUST be 32 bit */
+typedef long			LONG;
+typedef unsigned long	DWORD;
+
+#endif
+
+#endif

+ 4 - 0
iap/Makefile

@@ -6,6 +6,7 @@ INCLUDES += -I../config
 
 #----
 INCLUDES += -I.
+INCLUDES += -IFATFS
 INCLUDES += -I../thirdparty/lwip/src/include
 INCLUDES += -I../thirdparty/lwip/src/include/netif
 INCLUDES += -I../thirdparty/lwip/src/include/lwip
@@ -21,6 +22,7 @@ INCLUDES += -IModules/Ethernet
 INCLUDES += -IModules/Console
 INCLUDES += -I../stm32/stm32f4x7_ethernet
 INCLUDES += -IUser
+INCLUDES += -Istm32sprog
 INCLUDES += -I../thirdparty/TinyStdio
 #----
 
@@ -28,6 +30,7 @@ CSRC = $(wildcard *.c ../stm32/system/*.c)
 CSRC += $(wildcard ../stm32/stm32f4xx_spl/src/*.c)
 
 #----
+CSRC += $(wildcard FATFS/*.c)
 CSRC += $(wildcard ../thirdparty/lwip/src/*.c)
 CSRC += $(wildcard ../thirdparty/lwip/src/api/*.c)
 CSRC += $(wildcard ../thirdparty/lwip/src/core/*.c)
@@ -40,6 +43,7 @@ CSRC += $(wildcard Modules/Ethernet/httpserver.c)
 CSRC += $(wildcard Modules/Ethernet/netconf.c)
 CSRC += $(wildcard ../stm32/stm32f4x7_ethernet/*.c)
 CSRC += $(wildcard User/*.c)
+CSRC += $(wildcard stm32sprog/*.c)
 CSRC += $(wildcard ../thirdparty/TinyStdio/*.c)
 
 #---

文件差異過大導致無法顯示
+ 861 - 475
iap/Modules/Ethernet/httpserver.c


+ 19 - 0
iap/Modules/Ethernet/httpserver.h

@@ -44,6 +44,21 @@
 #include "lwip/def.h"
 #include "fsdata.h"
 #include "stm32f4xx.h"
+#include "stdbool.h"
+
+#define SET_FWINVALID_FLAG()    do {\
+                                    fInvalidFw = true;\
+                                    RTC_WriteBackupRegister(RTC_BKP_DR7, fInvalidFw);\
+                                } while(0)
+
+#define CLEAR_FWINVALID_FLAG()   do {\
+                                    fInvalidFw = false;\
+                                    RTC_WriteBackupRegister(RTC_BKP_DR7, fInvalidFw);\
+                                } while(0)
+
+#define SET_FWUPDATED_FLAG()    do {\
+                                    RTC_WriteBackupRegister(RTC_BKP_DR3, 0x01);\
+                                } while(0)
 
 typedef enum 
 {
@@ -66,4 +81,8 @@ uint16_t Finder(char* data, uint16_t len);
 
 uint32_t ReturnFlashWriteAddress(void);
 
+bool GetFileName(char *inStr, char *fileName, uint8_t *fileNameLen);
+
+uint8_t update_timeout;
+
 #endif /* __HTTPD_H__ */

+ 2 - 2
iap/Modules/Ethernet/lwipopts.h

@@ -104,7 +104,7 @@ a lot of data that needs to be copied, this should be set high. */
 /* TCP sender buffer space (bytes). */
 //#define TCP_SND_BUF             (5*TCP_MSS)
 //#define TCP_SND_BUF             (18*TCP_MSS)
-#define TCP_SND_BUF             (18*TCP_MSS)
+#define TCP_SND_BUF             (6*TCP_MSS)
 
 /*  TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least
   as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. */
@@ -113,7 +113,7 @@ a lot of data that needs to be copied, this should be set high. */
 //#define TCP_SND_QUEUELEN        (4* TCP_SND_BUF/TCP_MSS)
 
 /* TCP receive window. */
-#define TCP_WND                 (4*TCP_MSS)
+#define TCP_WND                 (1*TCP_MSS)
 
 
 /* ---------- ICMP options ---------- */

+ 46 - 56
iap/Modules/Ethernet/netconf.c

@@ -6,15 +6,19 @@
 #include "netif/etharp.h"
 #include "lwip/dhcp.h"
 #include "ethernetif.h"
+#include "stm32f4x7_eth.h"
 #include "tcpip.h"
 #include "main.h"
 #include "netconf.h"
 #include "common_config.h"
+#include "conf.h"
 #include "settings_api.h"
 #include "tinystdio.h"
-#include "lwip/init.h"
+#ifdef LCD_ENABLE
+#include "lcd.h"
+#endif
 #include "lwip/timeouts.h"
-#include "lwip/sys.h"
+#include "lwip/init.h"
 
 /* Private typedef -----------------------------------------------------------*/
 #define MAX_DHCP_TRIES        4
@@ -76,6 +80,11 @@ void LwIP_Init(void)
         ipaddr.addr = ipaddr_addr(sSettings.sWebParams.ip);
         netmask.addr = ipaddr_addr(sSettings.sWebParams.mask);
         gw.addr = ipaddr_addr(sSettings.sWebParams.gate);
+#ifdef LCD_ENABLE
+        sprintf(str, "IP: %s", ipaddr_ntoa(&ipaddr));
+        LCD_ClearRow(3);
+        LCD_PrintAligned(3, alignCENTER, str);
+#endif
     }
 
     netif_add(&netif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &ethernet_input);
@@ -84,59 +93,6 @@ void LwIP_Init(void)
     netif_set_up(&netif);
 }
 
-/**
-  * @brief  Called when a frame is received
-  * @param  None
-  * @retval None
-  */
-void LwIP_Pkt_Handle(void)
-{
-    /* Read a received packet from the Ethernet buffers and send it to the lwIP for handling */
-    ethernetif_input(&netif);
-}
-
-///**
-//  * @brief  LwIP periodic tasks
-//  * @param  localtime the current LocalTime value
-//  * @retval None
-//  */
-//void LwIP_Periodic_Handle(__IO uint32_t localtime)
-//{
-//#if LWIP_TCP
-//  /* TCP periodic process every 250 ms */
-//  if (localtime - TCPTimer >= TCP_TMR_INTERVAL) {
-//    TCPTimer =  localtime;
-//    tcp_tmr();
-//  }
-//#endif
-//
-//  /* ARP periodic process every 5s */
-//  if ((localtime - ARPTimer) >= ARP_TMR_INTERVAL) {
-//    ARPTimer =  localtime;
-//    etharp_tmr();
-//  }
-//
-//  if (sSettings.sWebParams.dhcpEnable)
-//  {
-//    /* Fine DHCP periodic process every 500ms */
-//    if (localtime - DHCPfineTimer >= DHCP_FINE_TIMER_MSECS) {
-//      DHCPfineTimer =  localtime;
-//      dhcp_fine_tmr();
-//      if ((DHCP_state != DHCP_ADDRESS_ASSIGNED)&&(DHCP_state != DHCP_TIMEOUT)) {
-//        /* process DHCP state machine */
-//        LwIP_DHCP_Process_Handle();
-//      }
-//    }
-//
-//    /* DHCP Coarse periodic process every 60s */
-//    if (localtime - DHCPcoarseTimer >= DHCP_COARSE_TIMER_MSECS) {
-//      DHCPcoarseTimer =  localtime;
-//      dhcp_coarse_tmr();
-//    }
-//  }
-//
-//}
-
 /**
   * @brief  LwIP periodic tasks
   * @param  localtime the current LocalTime value
@@ -144,10 +100,16 @@ void LwIP_Pkt_Handle(void)
   */
 void LwIP_Periodic_Handle(__IO uint32_t localtime)
 {
+    (void)localtime;
+
+    /* Check if any packet received */
+    if (ETH_CheckFrameReceived()) {
+        /* Read a received packet from the Ethernet buffers and send it to the lwIP for handling */
+        ethernetif_input(&netif);
+    }
 
     if (sSettings.sWebParams.dhcpEnable) {
         /* Fine DHCP periodic process every 500ms */
-
         if ((DHCP_state != DHCP_ADDRESS_ASSIGNED) && (DHCP_state != DHCP_TIMEOUT)) {
             /* process DHCP state machine */
             LwIP_DHCP_Process_Handle();
@@ -174,6 +136,9 @@ void LwIP_DHCP_Process_Handle()
             dhcp_start(&netif);
             DHCP_state = DHCP_WAIT_ADDRESS;
             PRINT_USART("\n\rLooking for DHCP server please wait...\n\r");
+#ifdef LCD_ENABLE
+            LCD_PrintAligned(3, alignCENTER, "Получение IP адреса..");
+#endif
         }
         break;
 
@@ -197,6 +162,21 @@ void LwIP_DHCP_Process_Handle()
                 PRINT_USART(ipaddr_ntoa(&gw));
                 PRINT_USART("\n\r");
 
+#ifdef LCD_ENABLE
+                char str[30];
+                sprintf(str, "IP: %s", ipaddr_ntoa(&ipaddr));
+                LCD_ClearRow(3);
+                LCD_PrintAligned(3, alignCENTER, str);
+
+                // sprintf(str, "Mask: %s", ipaddr_ntoa(&netmask));
+                // LCD_ClearRow(4);
+                // LCD_PrintAligned(4, alignCENTER, str);
+
+                // sprintf(str, "GW: %s", ipaddr_ntoa(&gw));
+                // LCD_ClearRow(5);
+                // LCD_PrintAligned(5, alignCENTER, str);
+#endif
+
             } else {
                 /* DHCP timeout */
                 if (dhcp->tries > MAX_DHCP_TRIES) {
@@ -215,6 +195,16 @@ void LwIP_DHCP_Process_Handle()
                     PRINT_USART("\n\rStatic IP address\n\r");
                     PRINT_USART(ipaddr_ntoa(&ipaddr));
                     PRINT_USART("\n\r");
+
+#ifdef LCD_ENABLE
+                    char str[30];
+                    sprintf(str, "IP: %s", ipaddr_ntoa(&ipaddr));
+                    LCD_ClearRow(3);
+                    LCD_PrintAligned(3, alignCENTER, str);
+
+                    LCD_ClearRow(5);
+                    LCD_PrintAligned(5, alignCENTER, "Таймаут DHCP");
+#endif
                 }
             }
         }

+ 0 - 1
iap/Modules/Ethernet/netconf.h

@@ -57,7 +57,6 @@
 /* Exported macro ------------------------------------------------------------*/
 /* Exported functions ------------------------------------------------------- */
 void LwIP_Init(void);
-void LwIP_Pkt_Handle(void);
 void LwIP_Periodic_Handle(__IO uint32_t localtime);
 
 #ifdef __cplusplus

+ 3238 - 0
iap/Modules/SD_Card/sdio_sd.c

@@ -0,0 +1,3238 @@
+/**
+ ******************************************************************************
+ * @file    stm32f4sdio_sd.c
+ * @author  MCD Application Team
+ * @version V1.0.2
+ * @date    09-March-2012
+ * @brief   This file provides a set of functions needed to manage the SDIO SD
+ *          Card memory mounted on STM324xG-EVAL evaluation board.
+ *
+ *
+ *  @verbatim
+ *
+ *          ===================================================================
+ *                                   How to use this driver
+ *          ===================================================================
+ *          It implements a high level communication layer for read and write
+ *          from/to this memory. The needed STM32 hardware resources (SDIO and
+ *          GPIO) are defined in stm324xg_eval.h file, and the initialization is
+ *          performed in SD_LowLevel_Init() function declared in stm324xg_eval.c
+ *          file.
+ *          You can easily tailor this driver to any other development board,
+ *          by just adapting the defines for hardware resources and
+ *          SD_LowLevel_Init() function.
+ *
+ *          A - SD Card Initialization and configuration
+ *          ============================================
+ *            - To initialize the SD Card, use the SD_Init() function.  It
+ *              Initializes the SD Card and put it into StandBy State (Ready
+ *              for data transfer). This function provide the following operations:
+ *
+ *              1 - Apply the SD Card initialization process at 400KHz and check
+ *                  the SD Card type (Standard Capacity or High Capacity). You
+ *                  can change or adapt this frequency by adjusting the
+ *                  "SDIO_INIT_CLK_DIV" define inside the stm324xg_eval.h file.
+ *                  The SD Card frequency (SDIO_CK) is computed as follows:
+ *
+ *                     +---------------------------------------------+
+ *                     | SDIO_CK = SDIOCLK / (SDIO_INIT_CLK_DIV + 2) |
+ *                     +---------------------------------------------+
+ *
+ *                  In initialization mode and according to the SD Card standard,
+ *                  make sure that the SDIO_CK frequency don't exceed 400KHz.
+ *
+ *              2 - Get the SD CID and CSD data. All these information are
+ *                  managed by the SDCardInfo structure. This structure provide
+ *                  also ready computed SD Card capacity and Block size.
+ *
+ *              3 - Configure the SD Card Data transfer frequency. By Default,
+ *                  the card transfer frequency is set to 24MHz. You can change
+ *                  or adapt this frequency by adjusting the "SDIO_TRANSFER_CLK_DIV"
+ *                  define inside the stm324xg_eval.h file.
+ *                  The SD Card frequency (SDIO_CK) is computed as follows:
+ *
+ *                     +---------------------------------------------+
+ *                     | SDIO_CK = SDIOCLK / (SDIO_INIT_CLK_DIV + 2) |
+ *                     +---------------------------------------------+
+ *
+ *                  In transfer mode and according to the SD Card standard,
+ *                  make sure that the SDIO_CK frequency don't exceed 25MHz
+ *                  and 50MHz in High-speed mode switch.
+ *                  To be able to use a frequency higher than 24MHz, you should
+ *                  use the SDIO peripheral in bypass mode. Refer to the
+ *                  corresponding reference manual for more details.
+ *
+ *              4 -  Select the corresponding SD Card according to the address
+ *                   read with the step 2.
+ *
+ *              5 -  Configure the SD Card in wide bus mode: 4-bits data.
+ *
+ *          B - SD Card Read operation
+ *          ==========================
+ *           - You can read SD card by using two function: SD_ReadBlock() and
+ *             SD_ReadMultiBlocks() functions. These functions support only
+ *             512-byte block length.
+ *           - The SD_ReadBlock() function read only one block (512-byte). This
+ *             function can transfer the data using DMA controller or using
+ *             polling mode. To select between DMA or polling mode refer to
+ *             "SD_DMA_MODE" or "SD_POLLING_MODE" inside the stm324xg_eval_sdio_sd.h
+ *             file and uncomment the corresponding line. By default the SD DMA
+ *             mode is selected
+ *           - The SD_ReadMultiBlocks() function read only mutli blocks (multiple
+ *             of 512-byte).
+ *           - Any read operation should be followed by two functions to check
+ *             if the DMA Controller and SD Card status.
+ *              - SD_ReadWaitOperation(): this function insure that the DMA
+ *                controller has finished all data transfer.
+ *              - SD_GetStatus(): to check that the SD Card has finished the
+ *                data transfer and it is ready for data.
+ *
+ *           - The DMA transfer is finished by the SDIO Data End interrupt.
+ *             User has to call the SD_ProcessIRQ() function inside the SDIO_IRQHandler()
+ *             and SD_ProcessDMAIRQ() function inside the DMA2_Streamx_IRQHandler().
+ *             Don't forget to enable the SDIO_IRQn and DMA2_Stream3_IRQn or
+ *             DMA2_Stream6_IRQn interrupts using the NVIC controller.
+ *
+ *          C - SD Card Write operation
+ *          ===========================
+ *           - You can write SD card by using two function: SD_WriteBlock() and
+ *             SD_WriteMultiBlocks() functions. These functions support only
+ *             512-byte block length.
+ *           - The SD_WriteBlock() function write only one block (512-byte). This
+ *             function can transfer the data using DMA controller or using
+ *             polling mode. To select between DMA or polling mode refer to
+ *             "SD_DMA_MODE" or "SD_POLLING_MODE" inside the stm324xg_eval_sdio_sd.h
+ *             file and uncomment the corresponding line. By default the SD DMA
+ *             mode is selected
+ *           - The SD_WriteMultiBlocks() function write only mutli blocks (multiple
+ *             of 512-byte).
+ *           - Any write operation should be followed by two functions to check
+ *             if the DMA Controller and SD Card status.
+ *              - SD_ReadWaitOperation(): this function insure that the DMA
+ *                controller has finished all data transfer.
+ *              - SD_GetStatus(): to check that the SD Card has finished the
+ *                data transfer and it is ready for data.
+ *
+ *           - The DMA transfer is finished by the SDIO Data End interrupt.
+ *             User has to call the SD_ProcessIRQ() function inside the SDIO_IRQHandler()
+ *             and SD_ProcessDMAIRQ() function inside the DMA2_Streamx_IRQHandler().
+ *             Don't forget to enable the SDIO_IRQn and DMA2_Stream3_IRQn or
+ *             DMA2_Stream6_IRQn interrupts using the NVIC controller.
+ *
+ *
+ *          D - SD card status
+ *          ==================
+ *           - At any time, you can check the SD Card status and get the SD card
+ *             state by using the SD_GetStatus() function. This function checks
+ *             first if the SD card is still connected and then get the internal
+ *             SD Card transfer state.
+ *           - You can also get the SD card SD Status register by using the
+ *             SD_SendSDStatus() function.
+ *
+ *          E - Programming Model (Selecting DMA for SDIO data Transfer)
+ *          ============================================================
+ *             Status = SD_Init(); // Initialization Step as described in section A
+ *
+ *             // SDIO Interrupt ENABLE
+ *             NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn;
+ *             NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
+ *             NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
+ *             NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
+ *             NVIC_Init(&NVIC_InitStructure);
+ *             // DMA2 STREAMx Interrupt ENABLE
+ *             NVIC_InitStructure.NVIC_IRQChannel = SD_SDIO_DMA_IRQn;
+ *             NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
+ *             NVIC_Init(&NVIC_InitStructure);
+ *
+ *             // Write operation as described in Section C
+ *             Status = SD_WriteBlock(buffer, address, 512);
+ *             Status = SD_WaitWriteOperation();
+ *             while(SD_GetStatus() != SD_TRANSFER_OK);
+ *
+ *             Status = SD_WriteMultiBlocks(buffer, address, 512, NUMBEROFBLOCKS);
+ *             Status = SD_WaitWriteOperation();
+ *             while(SD_GetStatus() != SD_TRANSFER_OK);
+ *
+ *             // Read operation as described in Section B
+ *             Status = SD_ReadBlock(buffer, address, 512);
+ *             Status = SD_WaitReadOperation();
+ *             while(SD_GetStatus() != SD_TRANSFER_OK);
+ *
+ *             Status = SD_ReadMultiBlocks(buffer, address, 512, NUMBEROFBLOCKS);
+ *             Status = SD_WaitReadOperation();
+ *             while(SD_GetStatus() != SD_TRANSFER_OK);
+ *
+ *             - Add the SDIO and DMA2 StreamX (3 or 6) IRQ Handlers:
+ *                 void SDIO_IRQHandler(void)
+ *                 {
+ *                   SD_ProcessIRQ();
+ *                 }
+ *                 void SD_SDIO_DMA_IRQHANDLER(void)
+ *                 {
+ *                   SD_ProcessDMAIRQ();
+ *                 }
+ *
+ *          F - Programming Model (Selecting Polling for SDIO data Transfer)
+ *          ================================================================
+ *            //Only SD Card Single Block operation are managed.
+ *            Status = SD_Init(); // Initialization Step as described in section
+ *
+ *            // Write operation as described in Section C
+ *            Status = SD_WriteBlock(buffer, address, 512);
+ *
+ *            // Read operation as described in Section B
+ *            Status = SD_ReadBlock(buffer, address, 512);
+ *
+ *          STM32 SDIO Pin assignment
+ *          =========================
+ *          +-----------------------------------------------------------+
+ *          |                     Pin assignment                        |
+ *          +-----------------------------+---------------+-------------+
+ *          |  STM32 SDIO Pins            |     SD        |    Pin      |
+ *          +-----------------------------+---------------+-------------+
+ *          |      SDIO D2 	  PC10       |   D2          |    1        |
+ *          |      SDIO D3  		PC11       |   D3          |    2        |
+ *          |      SDIO CMD 		PD2        |   CMD         |    3        |
+ *          |                             |   VCC         |    4 (3.3 V)|
+ *          |      SDIO CLK 		PC12       |   CLK         |    5        |
+ *          |                             |   GND         |    6 (0 V)  |
+ *          |      SDIO D0  		PC8        |   D0          |    7        |
+ *          |      SDIO D1  		PC9        |   D1          |    8        |
+ *          +-----------------------------+---------------+-------------+
+ *
+ *  @endverbatim
+ *
+ ******************************************************************************
+ * @attention
+ *
+ * <h2><center>&copy; COPYRIGHT 2012 STMicroelectronics</center></h2>
+ *
+ * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *        http://www.st.com/software_license_agreement_liberty_v2
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************
+ */
+
+/* Includes ------------------------------------------------------------------*/
+#include "sdio_sd.h"
+
+
+/** @addtogroup Utilities
+ * @{
+ */
+
+/** @addtogroup STM32_EVAL
+ * @{
+ */
+
+/** @addtogroup STM324xG_EVAL
+ * @{
+ */
+
+/** @addtogroup STM324xG_EVAL_SDIO_SD
+ * @brief      This file provides all the SD Card driver firmware functions.
+ * @{
+ */
+
+/** @defgroup STM324xG_EVAL_SDIO_SD_Private_Types
+ * @{
+ */
+/**
+ * @}
+ */
+
+
+/** @defgroup SDIO_SD_Private_Defines
+ * @{
+ */
+
+/** @brief  SD FLASH SDIO Interface
+ */
+//#define SD_DETECT_PIN                    GPIO_Pin_8                 /* PA.8 */
+//#define SD_DETECT_GPIO_PORT              GPIOA                       /* GPIOA */
+//#define SD_DETECT_GPIO_CLK               RCC_AHB1Periph_GPIOA
+
+#define SDIO_FIFO_ADDRESS                ((uint32_t)0x40012C80)
+/** 
+ * @brief  SDIO Intialization Frequency (400KHz max)
+ */
+#define SDIO_INIT_CLK_DIV                ((uint8_t)0xFF)
+/** 
+ * @brief  SDIO Data Transfer Frequency (25MHz max)
+ */
+#define SDIO_TRANSFER_CLK_DIV            ((uint8_t)0x4)
+
+#define SD_SDIO_DMA                   DMA2
+#define SD_SDIO_DMA_CLK               RCC_AHB1Periph_DMA2
+
+#define SD_SDIO_DMA_STREAM3	          3
+//#define SD_SDIO_DMA_STREAM6           6
+
+#ifdef SD_SDIO_DMA_STREAM3
+#define SD_SDIO_DMA_STREAM            DMA2_Stream3
+#define SD_SDIO_DMA_CHANNEL           DMA_Channel_4
+#define SD_SDIO_DMA_FLAG_FEIF         DMA_LISR_FEIF3
+#define SD_SDIO_DMA_FLAG_DMEIF        DMA_LISR_DMEIF3
+#define SD_SDIO_DMA_FLAG_TEIF         DMA_LISR_TEIF3
+#define SD_SDIO_DMA_FLAG_HTIF         DMA_LISR_HTIF3
+#define SD_SDIO_DMA_FLAG_TCIF         DMA_LISR_TCIF3
+#define SD_SDIO_DMA_IRQn              DMA2_Stream3_IRQn
+#define SD_SDIO_DMA_IRQHANDLER        DMA2_Stream3_IRQHandler
+#elif defined SD_SDIO_DMA_STREAM6
+#define SD_SDIO_DMA_STREAM            DMA2_Stream6
+#define SD_SDIO_DMA_CHANNEL           DMA_Channel_4
+#define SD_SDIO_DMA_FLAG_FEIF         DMA_LISR_FEIF6
+#define SD_SDIO_DMA_FLAG_DMEIF        DMA_LISR_DMEIF6
+#define SD_SDIO_DMA_FLAG_TEIF         DMA_LISR_TEIF6
+#define SD_SDIO_DMA_FLAG_HTIF         DMA_LISR_HTIF6
+#define SD_SDIO_DMA_FLAG_TCIF         DMA_LISR_TCIF6
+#define SD_SDIO_DMA_IRQn              DMA2_Stream6_IRQn
+#define SD_SDIO_DMA_IRQHANDLER        DMA2_Stream6_IRQHandler
+#endif /* SD_SDIO_DMA_STREAM3 */
+
+/** 
+ * @brief  SDIO Static flags, TimeOut, FIFO Address
+ */
+#define NULL 0
+#define SDIO_STATIC_FLAGS               ((uint32_t)0x000005FF)
+#define SDIO_CMD0TIMEOUT                ((uint32_t)0x00010000)
+
+/** 
+ * @brief  Mask for errors Card Status R1 (OCR Register)
+ */
+#define SD_OCR_ADDR_OUT_OF_RANGE        ((uint32_t)0x80000000)
+#define SD_OCR_ADDR_MISALIGNED          ((uint32_t)0x40000000)
+#define SD_OCR_BLOCK_LEN_ERR            ((uint32_t)0x20000000)
+#define SD_OCR_ERASE_SEQ_ERR            ((uint32_t)0x10000000)
+#define SD_OCR_BAD_ERASE_PARAM          ((uint32_t)0x08000000)
+#define SD_OCR_WRITE_PROT_VIOLATION     ((uint32_t)0x04000000)
+#define SD_OCR_LOCK_UNLOCK_FAILED       ((uint32_t)0x01000000)
+#define SD_OCR_COM_CRC_FAILED           ((uint32_t)0x00800000)
+#define SD_OCR_ILLEGAL_CMD              ((uint32_t)0x00400000)
+#define SD_OCR_CARD_ECC_FAILED          ((uint32_t)0x00200000)
+#define SD_OCR_CC_ERROR                 ((uint32_t)0x00100000)
+#define SD_OCR_GENERAL_UNKNOWN_ERROR    ((uint32_t)0x00080000)
+#define SD_OCR_STREAM_READ_UNDERRUN     ((uint32_t)0x00040000)
+#define SD_OCR_STREAM_WRITE_OVERRUN     ((uint32_t)0x00020000)
+#define SD_OCR_CID_CSD_OVERWRIETE       ((uint32_t)0x00010000)
+#define SD_OCR_WP_ERASE_SKIP            ((uint32_t)0x00008000)
+#define SD_OCR_CARD_ECC_DISABLED        ((uint32_t)0x00004000)
+#define SD_OCR_ERASE_RESET              ((uint32_t)0x00002000)
+#define SD_OCR_AKE_SEQ_ERROR            ((uint32_t)0x00000008)
+#define SD_OCR_ERRORBITS                ((uint32_t)0xFDFFE008)
+
+/** 
+ * @brief  Masks for R6 Response
+ */
+#define SD_R6_GENERAL_UNKNOWN_ERROR     ((uint32_t)0x00002000)
+#define SD_R6_ILLEGAL_CMD               ((uint32_t)0x00004000)
+#define SD_R6_COM_CRC_FAILED            ((uint32_t)0x00008000)
+
+#define SD_VOLTAGE_WINDOW_SD            ((uint32_t)0x80100000)
+#define SD_HIGH_CAPACITY                ((uint32_t)0x40000000)
+#define SD_STD_CAPACITY                 ((uint32_t)0x00000000)
+#define SD_CHECK_PATTERN                ((uint32_t)0x000001AA)
+
+#define SD_MAX_VOLT_TRIAL               ((uint32_t)0x0000FFFF)
+#define SD_ALLZERO                      ((uint32_t)0x00000000)
+
+#define SD_WIDE_BUS_SUPPORT             ((uint32_t)0x00040000)
+#define SD_SINGLE_BUS_SUPPORT           ((uint32_t)0x00010000)
+#define SD_CARD_LOCKED                  ((uint32_t)0x02000000)
+
+#define SD_DATATIMEOUT                  ((uint32_t)0xFFFFFFFF)
+#define SD_0TO7BITS                     ((uint32_t)0x000000FF)
+#define SD_8TO15BITS                    ((uint32_t)0x0000FF00)
+#define SD_16TO23BITS                   ((uint32_t)0x00FF0000)
+#define SD_24TO31BITS                   ((uint32_t)0xFF000000)
+#define SD_MAX_DATA_LENGTH              ((uint32_t)0x01FFFFFF)
+
+#define SD_HALFFIFO                     ((uint32_t)0x00000008)
+#define SD_HALFFIFOBYTES                ((uint32_t)0x00000020)
+
+/** 
+ * @brief  Command Class Supported
+ */
+#define SD_CCCC_LOCK_UNLOCK             ((uint32_t)0x00000080)
+#define SD_CCCC_WRITE_PROT              ((uint32_t)0x00000040)
+#define SD_CCCC_ERASE                   ((uint32_t)0x00000020)
+
+/** 
+ * @brief  Following commands are SD Card Specific commands.
+ *         SDIO_APP_CMD should be sent before sending these commands.
+ */
+#define SDIO_SEND_IF_COND               ((uint32_t)0x00000008)
+
+/**
+ * @}
+ */
+
+/** @defgroup SDIO_SD_Private_Macros
+ * @{
+ */
+/**
+ * @}
+ */
+
+/** @defgroup SDIO_SD_Private_Variables
+ * @{
+ */
+
+static uint32_t CardType =  SDIO_STD_CAPACITY_SD_CARD_V1_1;
+static uint32_t CSD_Tab[4], CID_Tab[4], RCA = 0;
+static uint8_t SDSTATUS_Tab[16];
+__IO uint32_t StopCondition = 0;
+__IO SD_Error TransferError = SD_OK;
+__IO uint32_t TransferEnd = 0, DMAEndOfTransfer = 0;
+SD_CardInfo SDCardInfo;
+
+SDIO_InitTypeDef SDIO_InitStructure;
+SDIO_CmdInitTypeDef SDIO_CmdInitStructure;
+SDIO_DataInitTypeDef SDIO_DataInitStructure;
+/**
+ * @}
+ */
+
+
+/** @defgroup STM324xG_EVAL_SDIO_SD_Private_Function_Prototypes
+ * @{
+ */
+static SD_Error CmdError(void);
+static SD_Error CmdResp1Error(uint8_t cmd);
+static SD_Error CmdResp7Error(void);
+static SD_Error CmdResp3Error(void);
+static SD_Error CmdResp2Error(void);
+static SD_Error CmdResp6Error(uint8_t cmd, uint16_t *prca);
+static SD_Error SDEnWideBus(FunctionalState NewState);
+static SD_Error IsCardProgramming(uint8_t *pstatus);
+static SD_Error FindSCR(uint16_t rca, uint32_t *pscr);
+uint8_t convert_from_bytes_to_power_of_two(uint16_t NumberOfBytes);
+
+/**
+ * @}
+ */
+
+/**
+ * @brief  DeInitializes the SDIO interface.
+ * @param  None
+ * @retval None
+ */
+void SD_LowLevel_DeInit(void)
+{
+	GPIO_InitTypeDef  GPIO_InitStructure;
+
+	/*!< Disable SDIO Clock */
+	SDIO_ClockCmd(DISABLE);
+
+	/*!< Set Power State to OFF */
+	SDIO_SetPowerState(SDIO_PowerState_OFF);
+
+	/*!< DeInitializes the SDIO peripheral */
+	SDIO_DeInit();
+
+	/* Disable the SDIO APB2 Clock */
+	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDIO, DISABLE);
+
+	GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_MCO);
+	GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_MCO);
+	GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_MCO);
+	GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_MCO);
+	GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_MCO);
+	GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_MCO);
+
+	/* Configure PC.08, PC.09, PC.10, PC.11 pins: D0, D1, D2, D3 pins */
+	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;
+	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
+	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
+	GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+	/* Configure PD.02 CMD line */
+	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
+	GPIO_Init(GPIOD, &GPIO_InitStructure);
+
+	/* Configure PC.12 pin: CLK pin */
+	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
+	GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+
+
+
+
+
+}
+
+/**
+ * @brief  Initializes the SD Card and put it into StandBy State (Ready for
+ *         data transfer).
+ * @param  None
+ * @retval None
+ */
+void SD_LowLevel_Init(void)
+{
+	GPIO_InitTypeDef  GPIO_InitStructure;
+
+	/* GPIOC and GPIOD Periph clock enable */
+	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD/* | SD_DETECT_GPIO_CLK*/, ENABLE);
+
+	GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_SDIO);
+	GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_SDIO);
+	GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_SDIO);
+	GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_SDIO);
+	GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_SDIO);
+	GPIO_PinAFConfig(GPIOD, GPIO_PinSource2, GPIO_AF_SDIO);
+
+	/* Configure PC.08, PC.09, PC.10, PC.11 pins: D0, D1, D2, D3 pins */
+	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;
+	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
+	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
+	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
+	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
+	GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+	/* Configure PD.02 CMD line */
+	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
+	GPIO_Init(GPIOD, &GPIO_InitStructure);
+
+	/* Configure PC.12 pin: CLK pin */
+	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
+	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
+	GPIO_Init(GPIOC, &GPIO_InitStructure);
+
+	/*!< Configure SD_SPI_DETECT_PIN pin: SD Card detect pin
+  GPIO_InitStructure.GPIO_Pin = SD_DETECT_PIN;
+  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
+  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
+  GPIO_Init(SD_DETECT_GPIO_PORT, &GPIO_InitStructure);*/
+
+	/* Enable the SDIO APB2 Clock */
+	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDIO, ENABLE);
+
+	/* Enable the DMA2 Clock */
+	RCC_AHB1PeriphClockCmd(SD_SDIO_DMA_CLK, ENABLE);
+}
+
+/**
+ * @brief  Configures the DMA2 Channel4 for SDIO Tx request.
+ * @param  BufferSRC: pointer to the source buffer
+ * @param  BufferSize: buffer size
+ * @retval None
+ */
+void SD_LowLevel_DMA_TxConfig(uint32_t *BufferSRC, uint32_t BufferSize)
+{
+	DMA_InitTypeDef SDDMA_InitStructure;
+
+	DMA_ClearFlag(SD_SDIO_DMA_STREAM, SD_SDIO_DMA_FLAG_FEIF | SD_SDIO_DMA_FLAG_DMEIF | SD_SDIO_DMA_FLAG_TEIF | SD_SDIO_DMA_FLAG_HTIF | SD_SDIO_DMA_FLAG_TCIF);
+
+	/* DMA2 Stream3  or Stream6 disable */
+	DMA_Cmd(SD_SDIO_DMA_STREAM, DISABLE);
+
+	/* DMA2 Stream3  or Stream6 Config */
+	DMA_DeInit(SD_SDIO_DMA_STREAM);
+
+	SDDMA_InitStructure.DMA_Channel = SD_SDIO_DMA_CHANNEL;
+	SDDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS;
+	SDDMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)BufferSRC;
+	SDDMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
+	SDDMA_InitStructure.DMA_BufferSize = BufferSize;	//0;
+	SDDMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
+	SDDMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
+	SDDMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
+	SDDMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
+	SDDMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
+	SDDMA_InitStructure.DMA_Priority = DMA_Priority_High;
+	SDDMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
+	SDDMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
+	SDDMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_INC4;
+	SDDMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_INC4;
+	DMA_Init(SD_SDIO_DMA_STREAM, &SDDMA_InitStructure);
+	DMA_ITConfig(SD_SDIO_DMA_STREAM, DMA_IT_TC, ENABLE);
+	DMA_FlowControllerConfig(SD_SDIO_DMA_STREAM, DMA_FlowCtrl_Peripheral);
+
+	/* DMA2 Stream3  or Stream6 enable */
+	DMA_Cmd(SD_SDIO_DMA_STREAM, ENABLE);
+
+}
+
+/**
+ * @brief  Configures the DMA2 Channel4 for SDIO Rx request.
+ * @param  BufferDST: pointer to the destination buffer
+ * @param  BufferSize: buffer size
+ * @retval None
+ */
+void SD_LowLevel_DMA_RxConfig(uint32_t *BufferDST, uint32_t BufferSize)
+{
+	DMA_InitTypeDef SDDMA_InitStructure;
+
+	DMA_ClearFlag(SD_SDIO_DMA_STREAM, SD_SDIO_DMA_FLAG_FEIF | SD_SDIO_DMA_FLAG_DMEIF | SD_SDIO_DMA_FLAG_TEIF | SD_SDIO_DMA_FLAG_HTIF | SD_SDIO_DMA_FLAG_TCIF);
+
+	/* DMA2 Stream3  or Stream6 disable */
+	DMA_Cmd(SD_SDIO_DMA_STREAM, DISABLE);
+
+	/* DMA2 Stream3 or Stream6 Config */
+	DMA_DeInit(SD_SDIO_DMA_STREAM);
+
+	SDDMA_InitStructure.DMA_Channel = SD_SDIO_DMA_CHANNEL;
+	SDDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS;
+	SDDMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)BufferDST;
+	SDDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
+	SDDMA_InitStructure.DMA_BufferSize = BufferSize;	//0;
+	SDDMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
+	SDDMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
+	SDDMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
+	SDDMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
+	SDDMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
+	SDDMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
+	SDDMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
+	SDDMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
+	SDDMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_INC4;
+	SDDMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_INC4;
+	DMA_Init(SD_SDIO_DMA_STREAM, &SDDMA_InitStructure);
+	DMA_ITConfig(SD_SDIO_DMA_STREAM, DMA_IT_TC, ENABLE);
+	DMA_FlowControllerConfig(SD_SDIO_DMA_STREAM, DMA_FlowCtrl_Peripheral);
+
+	/* DMA2 Stream3 or Stream6 enable */
+	DMA_Cmd(SD_SDIO_DMA_STREAM, ENABLE);
+}
+
+void SD_NVIC_Init(void) {
+	NVIC_InitTypeDef NVIC_InitStructure;
+	NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn;
+	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;
+	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
+	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
+	NVIC_Init(&NVIC_InitStructure);
+}
+
+/** @defgroup STM324xG_EVAL_SDIO_SD_Private_Functions
+ * @{
+ */
+
+/**
+ * @brief  DeInitializes the SDIO interface.
+ * @param  None
+ * @retval None
+ */
+void SD_DeInit(void)
+{ 
+	SD_LowLevel_DeInit();
+}
+
+/**
+ * @brief  Initializes the SD Card and put it into StandBy State (Ready for data
+ *         transfer).
+ * @param  None
+ * @retval SD_Error: SD Card Error code.
+ */
+SD_Error SD_Init(void)
+{
+	__IO SD_Error errorstatus = SD_OK;
+
+	/* SDIO Peripheral Low Level Init */
+	SD_LowLevel_Init();
+
+	SDIO_DeInit();
+
+	errorstatus = SD_PowerON();
+
+	if (errorstatus != SD_OK)
+	{
+		/*!< CMD Response TimeOut (wait for CMDSENT flag) */
+		return(errorstatus);
+	}
+
+	errorstatus = SD_InitializeCards();
+
+	if (errorstatus != SD_OK)
+	{
+		/*!< CMD Response TimeOut (wait for CMDSENT flag) */
+		return(errorstatus);
+	}
+
+	/*!< Configure the SDIO peripheral */
+	/*!< SDIO_CK = SDIOCLK / (SDIO_TRANSFER_CLK_DIV + 2) */
+	/*!< on STM32F4xx devices, SDIOCLK is fixed to 48MHz */
+	SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV;
+	SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
+	SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
+	SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
+	SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;
+	SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
+	SDIO_Init(&SDIO_InitStructure);
+
+	/*----------------- Read CSD/CID MSD registers ------------------*/
+	errorstatus = SD_GetCardInfo(&SDCardInfo);
+
+	if (errorstatus == SD_OK)
+	{
+		/*----------------- Select Card --------------------------------*/
+		errorstatus = SD_SelectDeselect((uint32_t) (SDCardInfo.RCA << 16));
+	}
+
+	if (errorstatus == SD_OK)
+	{
+		errorstatus = SD_EnableWideBusOperation(SDIO_BusWide_4b);
+	}
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  Gets the cuurent sd card data transfer status.
+ * @param  None
+ * @retval SDTransferState: Data Transfer state.
+ *   This value can be:
+ *        - SD_TRANSFER_OK: No data transfer is acting
+ *        - SD_TRANSFER_BUSY: Data transfer is acting
+ */
+SDTransferState SD_GetStatus(void)
+{
+	SDCardState cardstate =  SD_CARD_TRANSFER;
+
+	cardstate = SD_GetState();
+
+	if (cardstate == SD_CARD_TRANSFER)
+	{
+		return(SD_TRANSFER_OK);
+	}
+	else if(cardstate == SD_CARD_ERROR)
+	{
+		return (SD_TRANSFER_ERROR);
+	}
+	else
+	{
+		return(SD_TRANSFER_BUSY);
+	}
+}
+
+/**
+ * @brief  Returns the current card's state.
+ * @param  None
+ * @retval SDCardState: SD Card Error or SD Card Current State.
+ */
+SDCardState SD_GetState(void)
+{
+	uint32_t resp1 = 0;
+
+	if(SD_Detect()== SD_PRESENT)
+	{
+		if (SD_SendStatus(&resp1) != SD_OK)
+		{
+			return SD_CARD_ERROR;
+		}
+		else
+		{
+			return (SDCardState)((resp1 >> 9) & 0x0F);
+		}
+	}
+	else
+	{
+		return SD_CARD_ERROR;
+	}
+}
+
+/**
+ * @brief  Detect if SD card is correctly plugged in the memory slot.
+ * @param  None
+ * @retval Return if SD is detected or not
+ */
+uint8_t SD_Detect(void)
+{
+	__IO uint8_t status = SD_PRESENT;
+
+	/*!< Check GPIO to detect SD
+  if (GPIO_ReadInputDataBit(SD_DETECT_GPIO_PORT, SD_DETECT_PIN) != Bit_RESET)
+  {
+    //status = SD_NOT_PRESENT;
+      status = SD_PRESENT;
+  }*/
+	return status;
+}
+
+/**
+ * @brief  Enquires cards about their operating voltage and configures
+ *   clock controls.
+ * @param  None
+ * @retval SD_Error: SD Card Error code.
+ */
+SD_Error SD_PowerON(void)
+{
+	__IO SD_Error errorstatus = SD_OK;
+	uint32_t response = 0, count = 0, validvoltage = 0;
+	uint32_t SDType = SD_STD_CAPACITY;
+
+	/*!< Power ON Sequence -----------------------------------------------------*/
+	/*!< Configure the SDIO peripheral */
+	/*!< SDIO_CK = SDIOCLK / (SDIO_INIT_CLK_DIV + 2) */
+	/*!< on STM32F4xx devices, SDIOCLK is fixed to 48MHz */
+	/*!< SDIO_CK for initialization should not exceed 400 KHz */
+	SDIO_InitStructure.SDIO_ClockDiv = SDIO_INIT_CLK_DIV;
+	SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
+	//  SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Enable;
+	SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
+	SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
+	SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_4b;
+	SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
+	SDIO_Init(&SDIO_InitStructure);
+
+	/*!< Set Power State to ON */
+	SDIO_SetPowerState(SDIO_PowerState_ON);
+
+	/*!< Enable SDIO Clock */
+	SDIO_ClockCmd(ENABLE);
+
+	/*!< CMD0: GO_IDLE_STATE ---------------------------------------------------*/
+	/*!< No CMD response required */
+	SDIO_CmdInitStructure.SDIO_Argument = 0x0;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_GO_IDLE_STATE;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_No;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdError();
+
+	if (errorstatus != SD_OK)
+	{
+		/*!< CMD Response TimeOut (wait for CMDSENT flag) */
+		return(errorstatus);
+	}
+
+	/*!< CMD8: SEND_IF_COND ----------------------------------------------------*/
+	/*!< Send CMD8 to verify SD card interface operating condition */
+	/*!< Argument: - [31:12]: Reserved (shall be set to '0')
+               - [11:8]: Supply Voltage (VHS) 0x1 (Range: 2.7-3.6 V)
+               - [7:0]: Check Pattern (recommended 0xAA) */
+	/*!< CMD Response: R7 */
+	SDIO_CmdInitStructure.SDIO_Argument = SD_CHECK_PATTERN;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_SEND_IF_COND;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp7Error();
+
+	if (errorstatus == SD_OK)
+	{
+		CardType = SDIO_STD_CAPACITY_SD_CARD_V2_0; /*!< SD Card 2.0 */
+		SDType = SD_HIGH_CAPACITY;
+	}
+	else
+	{
+		/*!< CMD55 */
+		SDIO_CmdInitStructure.SDIO_Argument = 0x00;
+		SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
+		SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+		SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+		SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+		SDIO_SendCommand(&SDIO_CmdInitStructure);
+		errorstatus = CmdResp1Error(SD_CMD_APP_CMD);
+	}
+	/*!< CMD55 */
+	SDIO_CmdInitStructure.SDIO_Argument = 0x00;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+	errorstatus = CmdResp1Error(SD_CMD_APP_CMD);
+
+	/*!< If errorstatus is Command TimeOut, it is a MMC card */
+	/*!< If errorstatus is SD_OK it is a SD card: SD card 2.0 (voltage range mismatch)
+     or SD card 1.x */
+	if (errorstatus == SD_OK)
+	{
+		/*!< SD CARD */
+		/*!< Send ACMD41 SD_APP_OP_COND with Argument 0x80100000 */
+		while ((!validvoltage) && (count < SD_MAX_VOLT_TRIAL))
+		{
+
+			/*!< SEND CMD55 APP_CMD with RCA as 0 */
+			SDIO_CmdInitStructure.SDIO_Argument = 0x00;
+			SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
+			SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+			SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+			SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+			SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+			errorstatus = CmdResp1Error(SD_CMD_APP_CMD);
+
+			if (errorstatus != SD_OK)
+			{
+				return(errorstatus);
+			}
+			SDIO_CmdInitStructure.SDIO_Argument = SD_VOLTAGE_WINDOW_SD | SDType;
+			SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_APP_OP_COND;
+			SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+			SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+			SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+			SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+			errorstatus = CmdResp3Error();
+			if (errorstatus != SD_OK)
+			{
+				return(errorstatus);
+			}
+
+			response = SDIO_GetResponse(SDIO_RESP1);
+			validvoltage = (((response >> 31) == 1) ? 1 : 0);
+			count++;
+		}
+		if (count >= SD_MAX_VOLT_TRIAL)
+		{
+			errorstatus = SD_INVALID_VOLTRANGE;
+			return(errorstatus);
+		}
+
+		if (response &= SD_HIGH_CAPACITY)
+		{
+			CardType = SDIO_HIGH_CAPACITY_SD_CARD;
+		}
+
+	}/*!< else MMC Card */
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  Turns the SDIO output signals off.
+ * @param  None
+ * @retval SD_Error: SD Card Error code.
+ */
+SD_Error SD_PowerOFF(void)
+{
+	SD_Error errorstatus = SD_OK;
+
+	/*!< Set Power State to OFF */
+	SDIO_SetPowerState(SDIO_PowerState_OFF);
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  Intialises all cards or single card as the case may be Card(s) come
+ *         into standby state.
+ * @param  None
+ * @retval SD_Error: SD Card Error code.
+ */
+SD_Error SD_InitializeCards(void)
+{
+	SD_Error errorstatus = SD_OK;
+	uint16_t rca = 0x01;
+
+	if (SDIO_GetPowerState() == SDIO_PowerState_OFF)
+	{
+		errorstatus = SD_REQUEST_NOT_APPLICABLE;
+		return(errorstatus);
+	}
+
+	if (SDIO_SECURE_DIGITAL_IO_CARD != CardType)
+	{
+		/*!< Send CMD2 ALL_SEND_CID */
+		SDIO_CmdInitStructure.SDIO_Argument = 0x0;
+		SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ALL_SEND_CID;
+		SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long;
+		SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+		SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+		SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+		errorstatus = CmdResp2Error();
+
+		if (SD_OK != errorstatus)
+		{
+			return(errorstatus);
+		}
+
+		CID_Tab[0] = SDIO_GetResponse(SDIO_RESP1);
+		CID_Tab[1] = SDIO_GetResponse(SDIO_RESP2);
+		CID_Tab[2] = SDIO_GetResponse(SDIO_RESP3);
+		CID_Tab[3] = SDIO_GetResponse(SDIO_RESP4);
+	}
+	if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) ||  (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) ||  (SDIO_SECURE_DIGITAL_IO_COMBO_CARD == CardType)
+			||  (SDIO_HIGH_CAPACITY_SD_CARD == CardType))
+	{
+		/*!< Send CMD3 SET_REL_ADDR with argument 0 */
+		/*!< SD Card publishes its RCA. */
+		SDIO_CmdInitStructure.SDIO_Argument = 0x00;
+		SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_REL_ADDR;
+		SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+		SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+		SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+		SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+		errorstatus = CmdResp6Error(SD_CMD_SET_REL_ADDR, &rca);
+
+		if (SD_OK != errorstatus)
+		{
+			return(errorstatus);
+		}
+	}
+
+	if (SDIO_SECURE_DIGITAL_IO_CARD != CardType)
+	{
+		RCA = rca;
+
+		/*!< Send CMD9 SEND_CSD with argument as card's RCA */
+		SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)(rca << 16);
+		SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_CSD;
+		SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long;
+		SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+		SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+		SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+		errorstatus = CmdResp2Error();
+
+		if (SD_OK != errorstatus)
+		{
+			return(errorstatus);
+		}
+
+		CSD_Tab[0] = SDIO_GetResponse(SDIO_RESP1);
+		CSD_Tab[1] = SDIO_GetResponse(SDIO_RESP2);
+		CSD_Tab[2] = SDIO_GetResponse(SDIO_RESP3);
+		CSD_Tab[3] = SDIO_GetResponse(SDIO_RESP4);
+	}
+
+	errorstatus = SD_OK; /*!< All cards get intialized */
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  Returns information about specific card.
+ * @param  cardinfo: pointer to a SD_CardInfo structure that contains all SD card
+ *         information.
+ * @retval SD_Error: SD Card Error code.
+ */
+SD_Error SD_GetCardInfo(SD_CardInfo *cardinfo)
+{
+	SD_Error errorstatus = SD_OK;
+	uint8_t tmp = 0;
+
+	cardinfo->CardType = (uint8_t)CardType;
+	cardinfo->RCA = (uint16_t)RCA;
+
+	/*!< Byte 0 */
+	tmp = (uint8_t)((CSD_Tab[0] & 0xFF000000) >> 24);
+	cardinfo->SD_csd.CSDStruct = (tmp & 0xC0) >> 6;
+	cardinfo->SD_csd.SysSpecVersion = (tmp & 0x3C) >> 2;
+	cardinfo->SD_csd.Reserved1 = tmp & 0x03;
+
+	/*!< Byte 1 */
+	tmp = (uint8_t)((CSD_Tab[0] & 0x00FF0000) >> 16);
+	cardinfo->SD_csd.TAAC = tmp;
+
+	/*!< Byte 2 */
+	tmp = (uint8_t)((CSD_Tab[0] & 0x0000FF00) >> 8);
+	cardinfo->SD_csd.NSAC = tmp;
+
+	/*!< Byte 3 */
+	tmp = (uint8_t)(CSD_Tab[0] & 0x000000FF);
+	cardinfo->SD_csd.MaxBusClkFrec = tmp;
+
+	/*!< Byte 4 */
+	tmp = (uint8_t)((CSD_Tab[1] & 0xFF000000) >> 24);
+	cardinfo->SD_csd.CardComdClasses = tmp << 4;
+
+	/*!< Byte 5 */
+	tmp = (uint8_t)((CSD_Tab[1] & 0x00FF0000) >> 16);
+	cardinfo->SD_csd.CardComdClasses |= (tmp & 0xF0) >> 4;
+	cardinfo->SD_csd.RdBlockLen = tmp & 0x0F;
+
+	/*!< Byte 6 */
+	tmp = (uint8_t)((CSD_Tab[1] & 0x0000FF00) >> 8);
+	cardinfo->SD_csd.PartBlockRead = (tmp & 0x80) >> 7;
+	cardinfo->SD_csd.WrBlockMisalign = (tmp & 0x40) >> 6;
+	cardinfo->SD_csd.RdBlockMisalign = (tmp & 0x20) >> 5;
+	cardinfo->SD_csd.DSRImpl = (tmp & 0x10) >> 4;
+	cardinfo->SD_csd.Reserved2 = 0; /*!< Reserved */
+
+	if ((CardType == SDIO_STD_CAPACITY_SD_CARD_V1_1) || (CardType == SDIO_STD_CAPACITY_SD_CARD_V2_0))
+	{
+		cardinfo->SD_csd.DeviceSize = (tmp & 0x03) << 10;
+
+		/*!< Byte 7 */
+		tmp = (uint8_t)(CSD_Tab[1] & 0x000000FF);
+		cardinfo->SD_csd.DeviceSize |= (tmp) << 2;
+
+		/*!< Byte 8 */
+		tmp = (uint8_t)((CSD_Tab[2] & 0xFF000000) >> 24);
+		cardinfo->SD_csd.DeviceSize |= (tmp & 0xC0) >> 6;
+
+		cardinfo->SD_csd.MaxRdCurrentVDDMin = (tmp & 0x38) >> 3;
+		cardinfo->SD_csd.MaxRdCurrentVDDMax = (tmp & 0x07);
+
+		/*!< Byte 9 */
+		tmp = (uint8_t)((CSD_Tab[2] & 0x00FF0000) >> 16);
+		cardinfo->SD_csd.MaxWrCurrentVDDMin = (tmp & 0xE0) >> 5;
+		cardinfo->SD_csd.MaxWrCurrentVDDMax = (tmp & 0x1C) >> 2;
+		cardinfo->SD_csd.DeviceSizeMul = (tmp & 0x03) << 1;
+		/*!< Byte 10 */
+		tmp = (uint8_t)((CSD_Tab[2] & 0x0000FF00) >> 8);
+		cardinfo->SD_csd.DeviceSizeMul |= (tmp & 0x80) >> 7;
+
+		cardinfo->CardCapacity = (cardinfo->SD_csd.DeviceSize + 1) ;
+		cardinfo->CardCapacity *= (1 << (cardinfo->SD_csd.DeviceSizeMul + 2));
+		cardinfo->CardBlockSize = 1 << (cardinfo->SD_csd.RdBlockLen);
+		cardinfo->CardCapacity *= cardinfo->CardBlockSize;
+	}
+	else if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
+	{
+		/*!< Byte 7 */
+		tmp = (uint8_t)(CSD_Tab[1] & 0x000000FF);
+		cardinfo->SD_csd.DeviceSize = (tmp & 0x3F) << 16;
+
+		/*!< Byte 8 */
+		tmp = (uint8_t)((CSD_Tab[2] & 0xFF000000) >> 24);
+
+		cardinfo->SD_csd.DeviceSize |= (tmp << 8);
+
+		/*!< Byte 9 */
+		tmp = (uint8_t)((CSD_Tab[2] & 0x00FF0000) >> 16);
+
+		cardinfo->SD_csd.DeviceSize |= (tmp);
+
+		/*!< Byte 10 */
+		tmp = (uint8_t)((CSD_Tab[2] & 0x0000FF00) >> 8);
+
+		cardinfo->CardCapacity = (cardinfo->SD_csd.DeviceSize + 1) * 512 * 1024;
+		cardinfo->CardBlockSize = 512;
+	}
+
+
+	cardinfo->SD_csd.EraseGrSize = (tmp & 0x40) >> 6;
+	cardinfo->SD_csd.EraseGrMul = (tmp & 0x3F) << 1;
+
+	/*!< Byte 11 */
+	tmp = (uint8_t)(CSD_Tab[2] & 0x000000FF);
+	cardinfo->SD_csd.EraseGrMul |= (tmp & 0x80) >> 7;
+	cardinfo->SD_csd.WrProtectGrSize = (tmp & 0x7F);
+
+	/*!< Byte 12 */
+	tmp = (uint8_t)((CSD_Tab[3] & 0xFF000000) >> 24);
+	cardinfo->SD_csd.WrProtectGrEnable = (tmp & 0x80) >> 7;
+	cardinfo->SD_csd.ManDeflECC = (tmp & 0x60) >> 5;
+	cardinfo->SD_csd.WrSpeedFact = (tmp & 0x1C) >> 2;
+	cardinfo->SD_csd.MaxWrBlockLen = (tmp & 0x03) << 2;
+
+	/*!< Byte 13 */
+	tmp = (uint8_t)((CSD_Tab[3] & 0x00FF0000) >> 16);
+	cardinfo->SD_csd.MaxWrBlockLen |= (tmp & 0xC0) >> 6;
+	cardinfo->SD_csd.WriteBlockPaPartial = (tmp & 0x20) >> 5;
+	cardinfo->SD_csd.Reserved3 = 0;
+	cardinfo->SD_csd.ContentProtectAppli = (tmp & 0x01);
+
+	/*!< Byte 14 */
+	tmp = (uint8_t)((CSD_Tab[3] & 0x0000FF00) >> 8);
+	cardinfo->SD_csd.FileFormatGrouop = (tmp & 0x80) >> 7;
+	cardinfo->SD_csd.CopyFlag = (tmp & 0x40) >> 6;
+	cardinfo->SD_csd.PermWrProtect = (tmp & 0x20) >> 5;
+	cardinfo->SD_csd.TempWrProtect = (tmp & 0x10) >> 4;
+	cardinfo->SD_csd.FileFormat = (tmp & 0x0C) >> 2;
+	cardinfo->SD_csd.ECC = (tmp & 0x03);
+
+	/*!< Byte 15 */
+	tmp = (uint8_t)(CSD_Tab[3] & 0x000000FF);
+	cardinfo->SD_csd.CSD_CRC = (tmp & 0xFE) >> 1;
+	cardinfo->SD_csd.Reserved4 = 1;
+
+
+	/*!< Byte 0 */
+	tmp = (uint8_t)((CID_Tab[0] & 0xFF000000) >> 24);
+	cardinfo->SD_cid.ManufacturerID = tmp;
+
+	/*!< Byte 1 */
+	tmp = (uint8_t)((CID_Tab[0] & 0x00FF0000) >> 16);
+	cardinfo->SD_cid.OEM_AppliID = tmp << 8;
+
+	/*!< Byte 2 */
+	tmp = (uint8_t)((CID_Tab[0] & 0x000000FF00) >> 8);
+	cardinfo->SD_cid.OEM_AppliID |= tmp;
+
+	/*!< Byte 3 */
+	tmp = (uint8_t)(CID_Tab[0] & 0x000000FF);
+	cardinfo->SD_cid.ProdName1 = tmp << 24;
+
+	/*!< Byte 4 */
+	tmp = (uint8_t)((CID_Tab[1] & 0xFF000000) >> 24);
+	cardinfo->SD_cid.ProdName1 |= tmp << 16;
+
+	/*!< Byte 5 */
+	tmp = (uint8_t)((CID_Tab[1] & 0x00FF0000) >> 16);
+	cardinfo->SD_cid.ProdName1 |= tmp << 8;
+
+	/*!< Byte 6 */
+	tmp = (uint8_t)((CID_Tab[1] & 0x0000FF00) >> 8);
+	cardinfo->SD_cid.ProdName1 |= tmp;
+
+	/*!< Byte 7 */
+	tmp = (uint8_t)(CID_Tab[1] & 0x000000FF);
+	cardinfo->SD_cid.ProdName2 = tmp;
+
+	/*!< Byte 8 */
+	tmp = (uint8_t)((CID_Tab[2] & 0xFF000000) >> 24);
+	cardinfo->SD_cid.ProdRev = tmp;
+
+	/*!< Byte 9 */
+	tmp = (uint8_t)((CID_Tab[2] & 0x00FF0000) >> 16);
+	cardinfo->SD_cid.ProdSN = tmp << 24;
+
+	/*!< Byte 10 */
+	tmp = (uint8_t)((CID_Tab[2] & 0x0000FF00) >> 8);
+	cardinfo->SD_cid.ProdSN |= tmp << 16;
+
+	/*!< Byte 11 */
+	tmp = (uint8_t)(CID_Tab[2] & 0x000000FF);
+	cardinfo->SD_cid.ProdSN |= tmp << 8;
+
+	/*!< Byte 12 */
+	tmp = (uint8_t)((CID_Tab[3] & 0xFF000000) >> 24);
+	cardinfo->SD_cid.ProdSN |= tmp;
+
+	/*!< Byte 13 */
+	tmp = (uint8_t)((CID_Tab[3] & 0x00FF0000) >> 16);
+	cardinfo->SD_cid.Reserved1 |= (tmp & 0xF0) >> 4;
+	cardinfo->SD_cid.ManufactDate = (tmp & 0x0F) << 8;
+
+	/*!< Byte 14 */
+	tmp = (uint8_t)((CID_Tab[3] & 0x0000FF00) >> 8);
+	cardinfo->SD_cid.ManufactDate |= tmp;
+
+	/*!< Byte 15 */
+	tmp = (uint8_t)(CID_Tab[3] & 0x000000FF);
+	cardinfo->SD_cid.CID_CRC = (tmp & 0xFE) >> 1;
+	cardinfo->SD_cid.Reserved2 = 1;
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  Enables wide bus opeartion for the requeseted card if supported by
+ *         card.
+ * @param  WideMode: Specifies the SD card wide bus mode.
+ *   This parameter can be one of the following values:
+ *     @arg SDIO_BusWide_8b: 8-bit data transfer (Only for MMC)
+ *     @arg SDIO_BusWide_4b: 4-bit data transfer
+ *     @arg SDIO_BusWide_1b: 1-bit data transfer
+ * @retval SD_Error: SD Card Error code.
+ */
+SD_Error SD_GetCardStatus(SD_CardStatus *cardstatus)
+{
+	SD_Error errorstatus = SD_OK;
+	uint8_t tmp = 0;
+
+	errorstatus = SD_SendSDStatus((uint32_t *)SDSTATUS_Tab);
+
+	if (errorstatus  != SD_OK)
+	{
+		return(errorstatus);
+	}
+
+	/*!< Byte 0 */
+	tmp = (uint8_t)((SDSTATUS_Tab[0] & 0xC0) >> 6);
+	cardstatus->DAT_BUS_WIDTH = tmp;
+
+	/*!< Byte 0 */
+	tmp = (uint8_t)((SDSTATUS_Tab[0] & 0x20) >> 5);
+	cardstatus->SECURED_MODE = tmp;
+
+	/*!< Byte 2 */
+	tmp = (uint8_t)((SDSTATUS_Tab[2] & 0xFF));
+	cardstatus->SD_CARD_TYPE = tmp << 8;
+
+	/*!< Byte 3 */
+	tmp = (uint8_t)((SDSTATUS_Tab[3] & 0xFF));
+	cardstatus->SD_CARD_TYPE |= tmp;
+
+	/*!< Byte 4 */
+	tmp = (uint8_t)(SDSTATUS_Tab[4] & 0xFF);
+	cardstatus->SIZE_OF_PROTECTED_AREA = tmp << 24;
+
+	/*!< Byte 5 */
+	tmp = (uint8_t)(SDSTATUS_Tab[5] & 0xFF);
+	cardstatus->SIZE_OF_PROTECTED_AREA |= tmp << 16;
+
+	/*!< Byte 6 */
+	tmp = (uint8_t)(SDSTATUS_Tab[6] & 0xFF);
+	cardstatus->SIZE_OF_PROTECTED_AREA |= tmp << 8;
+
+	/*!< Byte 7 */
+	tmp = (uint8_t)(SDSTATUS_Tab[7] & 0xFF);
+	cardstatus->SIZE_OF_PROTECTED_AREA |= tmp;
+
+	/*!< Byte 8 */
+	tmp = (uint8_t)((SDSTATUS_Tab[8] & 0xFF));
+	cardstatus->SPEED_CLASS = tmp;
+
+	/*!< Byte 9 */
+	tmp = (uint8_t)((SDSTATUS_Tab[9] & 0xFF));
+	cardstatus->PERFORMANCE_MOVE = tmp;
+
+	/*!< Byte 10 */
+	tmp = (uint8_t)((SDSTATUS_Tab[10] & 0xF0) >> 4);
+	cardstatus->AU_SIZE = tmp;
+
+	/*!< Byte 11 */
+	tmp = (uint8_t)(SDSTATUS_Tab[11] & 0xFF);
+	cardstatus->ERASE_SIZE = tmp << 8;
+
+	/*!< Byte 12 */
+	tmp = (uint8_t)(SDSTATUS_Tab[12] & 0xFF);
+	cardstatus->ERASE_SIZE |= tmp;
+
+	/*!< Byte 13 */
+	tmp = (uint8_t)((SDSTATUS_Tab[13] & 0xFC) >> 2);
+	cardstatus->ERASE_TIMEOUT = tmp;
+
+	/*!< Byte 13 */
+	tmp = (uint8_t)((SDSTATUS_Tab[13] & 0x3));
+	cardstatus->ERASE_OFFSET = tmp;
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  Enables wide bus opeartion for the requeseted card if supported by
+ *         card.
+ * @param  WideMode: Specifies the SD card wide bus mode.
+ *   This parameter can be one of the following values:
+ *     @arg SDIO_BusWide_8b: 8-bit data transfer (Only for MMC)
+ *     @arg SDIO_BusWide_4b: 4-bit data transfer
+ *     @arg SDIO_BusWide_1b: 1-bit data transfer
+ * @retval SD_Error: SD Card Error code.
+ */
+SD_Error SD_EnableWideBusOperation(uint32_t WideMode)
+{
+	SD_Error errorstatus = SD_OK;
+
+	/*!< MMC Card doesn't support this feature */
+	if (SDIO_MULTIMEDIA_CARD == CardType)
+	{
+		errorstatus = SD_UNSUPPORTED_FEATURE;
+		return(errorstatus);
+	}
+	else if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) || (SDIO_HIGH_CAPACITY_SD_CARD == CardType))
+	{
+		if (SDIO_BusWide_8b == WideMode)
+		{
+			errorstatus = SD_UNSUPPORTED_FEATURE;
+			return(errorstatus);
+		}
+		else if (SDIO_BusWide_4b == WideMode)
+		{
+			errorstatus = SDEnWideBus(ENABLE);
+
+			if (SD_OK == errorstatus)
+			{
+				/*!< Configure the SDIO peripheral */
+				SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV;
+				SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
+				SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
+				SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
+				SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_4b;
+				SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
+				SDIO_Init(&SDIO_InitStructure);
+			}
+		}
+		else
+		{
+			errorstatus = SDEnWideBus(DISABLE);
+
+			if (SD_OK == errorstatus)
+			{
+				/*!< Configure the SDIO peripheral */
+				SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV;
+				SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
+				SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
+				SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
+				SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;
+				SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
+				SDIO_Init(&SDIO_InitStructure);
+			}
+		}
+	}
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  Selects od Deselects the corresponding card.
+ * @param  addr: Address of the Card to be selected.
+ * @retval SD_Error: SD Card Error code.
+ */
+SD_Error SD_SelectDeselect(uint32_t addr)
+{
+	SD_Error errorstatus = SD_OK;
+
+	/*!< Send CMD7 SDIO_SEL_DESEL_CARD */
+	SDIO_CmdInitStructure.SDIO_Argument =  addr;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEL_DESEL_CARD;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_SEL_DESEL_CARD);
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  Allows to read one block from a specified address in a card. The Data
+ *         transfer can be managed by DMA mode or Polling mode.
+ * @note   This operation should be followed by two functions to check if the
+ *         DMA Controller and SD Card status.
+ *          - SD_ReadWaitOperation(): this function insure that the DMA
+ *            controller has finished all data transfer.
+ *          - SD_GetStatus(): to check that the SD Card has finished the
+ *            data transfer and it is ready for data.
+ * @param  readbuff: pointer to the buffer that will contain the received data
+ * @param  ReadAddr: Address from where data are to be read.
+ * @param  BlockSize: the SD card Data block size. The Block size should be 512.
+ * @retval SD_Error: SD Card Error code.
+ */
+SD_Error SD_ReadBlock(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize)
+{
+	SD_Error errorstatus = SD_OK;
+#if defined (SD_POLLING_MODE) 
+	uint32_t count = 0, *tempbuff = (uint32_t *)readbuff;
+#endif
+
+	TransferError = SD_OK;
+	TransferEnd = 0;
+	StopCondition = 0;
+
+	SDIO->DCTRL = 0x0;
+
+
+	if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
+	{
+		BlockSize = 512;
+		ReadAddr /= 512;
+	}
+
+	/* Set Block Size for Card */
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);
+
+	if (SD_OK != errorstatus)
+	{
+		return(errorstatus);
+	}
+
+	SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
+	SDIO_DataInitStructure.SDIO_DataLength = BlockSize;
+	SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4;
+	SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO;
+	SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
+	SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
+	SDIO_DataConfig(&SDIO_DataInitStructure);
+
+	/*!< Send CMD17 READ_SINGLE_BLOCK */
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)ReadAddr;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_READ_SINGLE_BLOCK;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_READ_SINGLE_BLOCK);
+
+	if (errorstatus != SD_OK)
+	{
+		return(errorstatus);
+	}
+
+#if defined (SD_POLLING_MODE)  
+	/*!< In case of single block transfer, no need of stop transfer at all.*/
+	/*!< Polling mode */
+	while (!(SDIO->STA &(SDIO_FLAG_RXOVERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_DBCKEND | SDIO_FLAG_STBITERR)))
+	{
+		if (SDIO_GetFlagStatus(SDIO_FLAG_RXFIFOHF) != RESET)
+		{
+			for (count = 0; count < 8; count++)
+			{
+				*(tempbuff + count) = SDIO_ReadData();
+			}
+			tempbuff += 8;
+		}
+	}
+
+	if (SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) != RESET)
+	{
+		SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT);
+		errorstatus = SD_DATA_TIMEOUT;
+		return(errorstatus);
+	}
+	else if (SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) != RESET)
+	{
+		SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL);
+		errorstatus = SD_DATA_CRC_FAIL;
+		return(errorstatus);
+	}
+	else if (SDIO_GetFlagStatus(SDIO_FLAG_RXOVERR) != RESET)
+	{
+		SDIO_ClearFlag(SDIO_FLAG_RXOVERR);
+		errorstatus = SD_RX_OVERRUN;
+		return(errorstatus);
+	}
+	else if (SDIO_GetFlagStatus(SDIO_FLAG_STBITERR) != RESET)
+	{
+		SDIO_ClearFlag(SDIO_FLAG_STBITERR);
+		errorstatus = SD_START_BIT_ERR;
+		return(errorstatus);
+	}
+	count = SD_DATATIMEOUT;
+	while ((SDIO_GetFlagStatus(SDIO_FLAG_RXDAVL) != RESET) && (count > 0))
+	{
+		*tempbuff = SDIO_ReadData();
+		tempbuff++;
+		count--;
+	}
+
+	/*!< Clear all the static flags */
+	SDIO_ClearFlag(SDIO_STATIC_FLAGS);
+
+#elif defined (SD_DMA_MODE)
+	SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND | SDIO_IT_RXOVERR | SDIO_IT_STBITERR, ENABLE);
+	SDIO_DMACmd(ENABLE);
+	SD_LowLevel_DMA_RxConfig((uint32_t *)readbuff, BlockSize);
+#endif
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  Allows to read blocks from a specified address  in a card.  The Data
+ *         transfer can be managed by DMA mode or Polling mode.
+ * @note   This operation should be followed by two functions to check if the
+ *         DMA Controller and SD Card status.
+ *          - SD_ReadWaitOperation(): this function insure that the DMA
+ *            controller has finished all data transfer.
+ *          - SD_GetStatus(): to check that the SD Card has finished the
+ *            data transfer and it is ready for data.
+ * @param  readbuff: pointer to the buffer that will contain the received data.
+ * @param  ReadAddr: Address from where data are to be read.
+ * @param  BlockSize: the SD card Data block size. The Block size should be 512.
+ * @param  NumberOfBlocks: number of blocks to be read.
+ * @retval SD_Error: SD Card Error code.
+ */
+SD_Error SD_ReadMultiBlocks(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize, uint32_t NumberOfBlocks)
+{
+	SD_Error errorstatus = SD_OK;
+	TransferError = SD_OK;
+	TransferEnd = 0;
+	StopCondition = 1;
+
+	SDIO->DCTRL = 0x0;
+
+	if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
+	{
+		BlockSize = 512;
+		ReadAddr /= 512;
+	}
+
+	/*!< Set Block Size for Card */
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);
+
+	if (SD_OK != errorstatus)
+	{
+		return(errorstatus);
+	}
+
+	SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
+	SDIO_DataInitStructure.SDIO_DataLength = NumberOfBlocks * BlockSize;
+	SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4;
+	SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO;
+	SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
+	SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
+	SDIO_DataConfig(&SDIO_DataInitStructure);
+
+	/*!< Send CMD18 READ_MULT_BLOCK with argument data address */
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)ReadAddr;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_READ_MULT_BLOCK;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_READ_MULT_BLOCK);
+
+	if (errorstatus != SD_OK)
+	{
+		return(errorstatus);
+	}
+
+	SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND | SDIO_IT_RXOVERR | SDIO_IT_STBITERR, ENABLE);
+	SDIO_DMACmd(ENABLE);
+	SD_LowLevel_DMA_RxConfig((uint32_t *)readbuff, (NumberOfBlocks * BlockSize));
+
+	return(errorstatus);
+}
+
+
+SD_Error SD_ReadMultiBlocksFIXED(uint8_t *readbuff, uint32_t ReadAddr, uint32_t BlockSize, uint32_t NumberOfBlocks)
+{
+	SD_Error errorstatus = SD_OK;
+	TransferError = SD_OK;
+	TransferEnd = 0;
+	StopCondition = 1;
+
+	SDIO->DCTRL = 0x0;
+
+	if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
+		BlockSize = 512;
+	else
+		ReadAddr *= BlockSize; // Convert to Bytes for NON SDHC
+
+	SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND | SDIO_IT_RXOVERR | SDIO_IT_STBITERR, ENABLE);
+	SDIO_DMACmd(ENABLE);
+	SD_LowLevel_DMA_RxConfig((uint32_t *)readbuff, (NumberOfBlocks * BlockSize));
+
+	/*!< Set Block Size for Card */
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);
+
+	if (SD_OK != errorstatus)
+	{
+		SDIO_DMACmd(DISABLE);
+		return(errorstatus);
+	}
+
+	SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
+	SDIO_DataInitStructure.SDIO_DataLength = NumberOfBlocks * BlockSize;
+	SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4;
+	SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO;
+	SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
+	SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
+	SDIO_DataConfig(&SDIO_DataInitStructure);
+
+	/*!< Send CMD18 READ_MULT_BLOCK with argument data address */
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)ReadAddr;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_READ_MULT_BLOCK;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_READ_MULT_BLOCK);
+
+	if (errorstatus != SD_OK)
+	{
+		SDIO_DMACmd(DISABLE);
+		return(errorstatus);
+	}
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  This function waits until the SDIO DMA data transfer is finished.
+ *         This function should be called after SDIO_ReadMultiBlocks() function
+ *         to insure that all data sent by the card are already transferred by
+ *         the DMA controller.
+ * @param  None.
+ * @retval SD_Error: SD Card Error code.
+ */
+SD_Error SD_WaitReadOperation(void)
+{
+	SD_Error errorstatus = SD_OK;
+	uint32_t timeout;
+
+	timeout = SD_DATATIMEOUT >> 16;
+
+	while ((DMAEndOfTransfer == 0x00) && (TransferEnd == 0) && (TransferError == SD_OK) && (timeout > 0))
+	{
+		timeout--;
+	}
+
+	DMAEndOfTransfer = 0x00;
+
+	timeout = SD_DATATIMEOUT >> 16;
+
+	while(((SDIO->STA & SDIO_FLAG_RXACT)) && (timeout > 0))
+	{
+		timeout--;
+	}
+
+	if (StopCondition == 1)
+	{
+		errorstatus = SD_StopTransfer();
+		StopCondition = 0;
+	}
+
+	if ((timeout == 0) && (errorstatus == SD_OK))
+	{
+		errorstatus = SD_DATA_TIMEOUT;
+	}
+
+	/*!< Clear all the static flags */
+	SDIO_ClearFlag(SDIO_STATIC_FLAGS);
+
+	if (TransferError != SD_OK)
+	{
+		return(TransferError);
+	}
+	else
+	{
+		return(errorstatus);
+	}
+}
+
+/**
+ * @brief  Allows to write one block starting from a specified address in a card.
+ *         The Data transfer can be managed by DMA mode or Polling mode.
+ * @note   This operation should be followed by two functions to check if the
+ *         DMA Controller and SD Card status.
+ *          - SD_ReadWaitOperation(): this function insure that the DMA
+ *            controller has finished all data transfer.
+ *          - SD_GetStatus(): to check that the SD Card has finished the
+ *            data transfer and it is ready for data.
+ * @param  writebuff: pointer to the buffer that contain the data to be transferred.
+ * @param  WriteAddr: Address from where data are to be read.
+ * @param  BlockSize: the SD card Data block size. The Block size should be 512.
+ * @retval SD_Error: SD Card Error code.
+ */
+SD_Error SD_WriteBlock(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize)
+{
+	SD_Error errorstatus = SD_OK;
+
+#if defined (SD_POLLING_MODE)
+	uint32_t bytestransferred = 0, count = 0, restwords = 0;
+	uint32_t *tempbuff = (uint32_t *)writebuff;
+#endif
+
+	TransferError = SD_OK;
+	TransferEnd = 0;
+	StopCondition = 0;
+
+	SDIO->DCTRL = 0x0;
+
+
+	if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
+	{
+		BlockSize = 512;
+		WriteAddr /= 512;
+	}
+
+	/* Set Block Size for Card */
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);
+
+	if (SD_OK != errorstatus)
+	{
+		return(errorstatus);
+	}
+
+	/*!< Send CMD24 WRITE_SINGLE_BLOCK */
+	SDIO_CmdInitStructure.SDIO_Argument = WriteAddr;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_WRITE_SINGLE_BLOCK;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_WRITE_SINGLE_BLOCK);
+
+	if (errorstatus != SD_OK)
+	{
+		return(errorstatus);
+	}
+
+	SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
+	SDIO_DataInitStructure.SDIO_DataLength = BlockSize;
+	SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4;
+	SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToCard;
+	SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
+	SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
+	SDIO_DataConfig(&SDIO_DataInitStructure);
+
+	/*!< In case of single data block transfer no need of stop command at all */
+#if defined (SD_POLLING_MODE) 
+	while (!(SDIO->STA & (SDIO_FLAG_DBCKEND | SDIO_FLAG_TXUNDERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_STBITERR)))
+	{
+		if (SDIO_GetFlagStatus(SDIO_FLAG_TXFIFOHE) != RESET)
+		{
+			if ((512 - bytestransferred) < 32)
+			{
+				restwords = ((512 - bytestransferred) % 4 == 0) ? ((512 - bytestransferred) / 4) : (( 512 -  bytestransferred) / 4 + 1);
+				for (count = 0; count < restwords; count++, tempbuff++, bytestransferred += 4)
+				{
+					SDIO_WriteData(*tempbuff);
+				}
+			}
+			else
+			{
+				for (count = 0; count < 8; count++)
+				{
+					SDIO_WriteData(*(tempbuff + count));
+				}
+				tempbuff += 8;
+				bytestransferred += 32;
+			}
+		}
+	}
+	if (SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) != RESET)
+	{
+		SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT);
+		errorstatus = SD_DATA_TIMEOUT;
+		return(errorstatus);
+	}
+	else if (SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) != RESET)
+	{
+		SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL);
+		errorstatus = SD_DATA_CRC_FAIL;
+		return(errorstatus);
+	}
+	else if (SDIO_GetFlagStatus(SDIO_FLAG_TXUNDERR) != RESET)
+	{
+		SDIO_ClearFlag(SDIO_FLAG_TXUNDERR);
+		errorstatus = SD_TX_UNDERRUN;
+		return(errorstatus);
+	}
+	else if (SDIO_GetFlagStatus(SDIO_FLAG_STBITERR) != RESET)
+	{
+		SDIO_ClearFlag(SDIO_FLAG_STBITERR);
+		errorstatus = SD_START_BIT_ERR;
+		return(errorstatus);
+	}
+#elif defined (SD_DMA_MODE)
+	SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND | SDIO_IT_RXOVERR | SDIO_IT_STBITERR, ENABLE);
+	SD_LowLevel_DMA_TxConfig((uint32_t *)writebuff, BlockSize);
+	SDIO_DMACmd(ENABLE);
+#endif
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  Allows to write blocks starting from a specified address in a card.
+ *         The Data transfer can be managed by DMA mode only.
+ * @note   This operation should be followed by two functions to check if the
+ *         DMA Controller and SD Card status.
+ *          - SD_ReadWaitOperation(): this function insure that the DMA
+ *            controller has finished all data transfer.
+ *          - SD_GetStatus(): to check that the SD Card has finished the
+ *            data transfer and it is ready for data.
+ * @param  WriteAddr: Address from where data are to be read.
+ * @param  writebuff: pointer to the buffer that contain the data to be transferred.
+ * @param  BlockSize: the SD card Data block size. The Block size should be 512.
+ * @param  NumberOfBlocks: number of blocks to be written.
+ * @retval SD_Error: SD Card Error code.
+ */
+SD_Error SD_WriteMultiBlocks(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize, uint32_t NumberOfBlocks)
+{
+	SD_Error errorstatus = SD_OK;
+
+	TransferError = SD_OK;
+	TransferEnd = 0;
+	StopCondition = 1;
+
+	SDIO->DCTRL = 0x0;
+
+	if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
+	{
+		BlockSize = 512;
+		WriteAddr /= 512;
+	}
+
+	/* Set Block Size for Card */
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);
+
+	if (SD_OK != errorstatus)
+	{
+		return(errorstatus);
+	}
+
+	/*!< To improve performance */
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) (RCA << 16);
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+
+	errorstatus = CmdResp1Error(SD_CMD_APP_CMD);
+
+	if (errorstatus != SD_OK)
+	{
+		return(errorstatus);
+	}
+	/*!< To improve performance */
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)NumberOfBlocks;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCK_COUNT;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_SET_BLOCK_COUNT);
+
+	if (errorstatus != SD_OK)
+	{
+		return(errorstatus);
+	}
+
+
+	/*!< Send CMD25 WRITE_MULT_BLOCK with argument data address */
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)WriteAddr;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_WRITE_MULT_BLOCK;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_WRITE_MULT_BLOCK);
+
+	if (SD_OK != errorstatus)
+	{
+		return(errorstatus);
+	}
+
+	SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
+	SDIO_DataInitStructure.SDIO_DataLength = NumberOfBlocks * BlockSize;
+	SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4;
+	SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToCard;
+	SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
+	SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
+	SDIO_DataConfig(&SDIO_DataInitStructure);
+
+	SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND | SDIO_IT_RXOVERR | SDIO_IT_STBITERR, ENABLE);
+	SDIO_DMACmd(ENABLE);
+	SD_LowLevel_DMA_TxConfig((uint32_t *)writebuff, (NumberOfBlocks * BlockSize));
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  This function waits until the SDIO DMA data transfer is finished.
+ *         This function should be called after SDIO_WriteBlock() and
+ *         SDIO_WriteMultiBlocks() function to insure that all data sent by the
+ *         card are already transferred by the DMA controller.
+ * @param  None.
+ * @retval SD_Error: SD Card Error code.
+ */
+SD_Error SD_WriteMultiBlocksFIXED(uint8_t *writebuff, uint32_t WriteAddr, uint32_t BlockSize, uint32_t NumberOfBlocks)
+{
+	SD_Error errorstatus = SD_OK;
+
+	TransferError = SD_OK;
+	TransferEnd = 0;
+	StopCondition = 1;
+
+	SDIO->DCTRL = 0x0;
+
+	if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
+		BlockSize = 512;
+	else
+		WriteAddr *= BlockSize; // Convert to Bytes for NON SDHC
+
+	//SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND | SDIO_IT_RXOVERR | SDIO_IT_STBITERR, ENABLE);
+	//SDIO_DMACmd(ENABLE);
+	//SD_LowLevel_DMA_TxConfig((uint32_t *)writebuff, (NumberOfBlocks * BlockSize));
+
+	/* Set Block Size for Card */
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) BlockSize;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);
+
+	if (SD_OK != errorstatus)
+	{
+		SDIO_DMACmd(DISABLE);
+		return(errorstatus);
+	}
+
+	/*!< To improve performance */
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) (RCA << 16);
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_APP_CMD);
+
+	if (errorstatus != SD_OK)
+	{
+		SDIO_DMACmd(DISABLE);
+		return(errorstatus);
+	}
+	/*!< To improve performance */
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)NumberOfBlocks;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCK_COUNT;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_SET_BLOCK_COUNT);
+
+	if (errorstatus != SD_OK)
+	{
+		SDIO_DMACmd(DISABLE);
+		return(errorstatus);
+	}
+
+	/*!< Send CMD25 WRITE_MULT_BLOCK with argument data address */
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)WriteAddr;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_WRITE_MULT_BLOCK;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_WRITE_MULT_BLOCK);
+
+	if (SD_OK != errorstatus)
+	{
+		SDIO_DMACmd(DISABLE);
+		return(errorstatus);
+	}
+
+	SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
+	SDIO_DataInitStructure.SDIO_DataLength = NumberOfBlocks * BlockSize;
+	SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4;
+	SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToCard;
+	SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
+	SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
+	SDIO_DataConfig(&SDIO_DataInitStructure);
+	SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND | SDIO_IT_RXOVERR | SDIO_IT_STBITERR, ENABLE);
+	SDIO_DMACmd(ENABLE);
+	SD_LowLevel_DMA_TxConfig((uint32_t *)writebuff, (NumberOfBlocks * BlockSize));
+
+	return(errorstatus);
+}
+
+SD_Error SD_WaitWriteOperation(void)
+{
+	SD_Error errorstatus = SD_OK;
+	uint32_t timeout;
+
+	timeout = SD_DATATIMEOUT;
+
+	while ((DMAEndOfTransfer == 0x00) && (TransferEnd == 0) && (TransferError == SD_OK) && (timeout > 0))
+	{
+		timeout--;
+	}
+
+	DMAEndOfTransfer = 0x00;
+
+	timeout = SD_DATATIMEOUT;
+
+	while(((SDIO->STA & SDIO_FLAG_TXACT)) && (timeout > 0))
+	{
+		timeout--;
+	}
+
+	if (StopCondition == 1)
+	{
+		errorstatus = SD_StopTransfer();
+		StopCondition = 0;
+	}
+
+	if ((timeout == 0) && (errorstatus == SD_OK))
+	{
+		errorstatus = SD_DATA_TIMEOUT;
+	}
+
+	/*!< Clear all the static flags */
+	SDIO_ClearFlag(SDIO_STATIC_FLAGS);
+
+	if (TransferError != SD_OK)
+	{
+		return(TransferError);
+	}
+	else
+	{
+		return(errorstatus);
+	}
+}
+
+/**
+ * @brief  Gets the cuurent data transfer state.
+ * @param  None
+ * @retval SDTransferState: Data Transfer state.
+ *   This value can be:
+ *        - SD_TRANSFER_OK: No data transfer is acting
+ *        - SD_TRANSFER_BUSY: Data transfer is acting
+ */
+SDTransferState SD_GetTransferState(void)
+{
+	if (SDIO->STA & (SDIO_FLAG_TXACT | SDIO_FLAG_RXACT))
+	{
+		return(SD_TRANSFER_BUSY);
+	}
+	else
+	{
+		return(SD_TRANSFER_OK);
+	}
+}
+
+/**
+ * @brief  Aborts an ongoing data transfer.
+ * @param  None
+ * @retval SD_Error: SD Card Error code.
+ */
+SD_Error SD_StopTransfer(void)
+{
+	SD_Error errorstatus = SD_OK;
+
+	/*!< Send CMD12 STOP_TRANSMISSION  */
+	SDIO_CmdInitStructure.SDIO_Argument = 0x0;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_STOP_TRANSMISSION;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_STOP_TRANSMISSION);
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  Allows to erase memory area specified for the given card.
+ * @param  startaddr: the start address.
+ * @param  endaddr: the end address.
+ * @retval SD_Error: SD Card Error code.
+ */
+SD_Error SD_Erase(uint32_t startaddr, uint32_t endaddr)
+{
+	SD_Error errorstatus = SD_OK;
+	uint32_t delay = 0;
+	__IO uint32_t maxdelay = 0;
+	uint8_t cardstate = 0;
+
+	/*!< Check if the card coomnd class supports erase command */
+	if (((CSD_Tab[1] >> 20) & SD_CCCC_ERASE) == 0)
+	{
+		errorstatus = SD_REQUEST_NOT_APPLICABLE;
+		return(errorstatus);
+	}
+
+	maxdelay = 120000 / ((SDIO->CLKCR & 0xFF) + 2);
+
+	if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED)
+	{
+		errorstatus = SD_LOCK_UNLOCK_FAILED;
+		return(errorstatus);
+	}
+
+	if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
+	{
+		startaddr /= 512;
+		endaddr /= 512;
+	}
+
+	/*!< According to sd-card spec 1.0 ERASE_GROUP_START (CMD32) and erase_group_end(CMD33) */
+	if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) || (SDIO_HIGH_CAPACITY_SD_CARD == CardType))
+	{
+		/*!< Send CMD32 SD_ERASE_GRP_START with argument as addr  */
+		SDIO_CmdInitStructure.SDIO_Argument = startaddr;
+		SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_START;
+		SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+		SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+		SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+		SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+		errorstatus = CmdResp1Error(SD_CMD_SD_ERASE_GRP_START);
+		if (errorstatus != SD_OK)
+		{
+			return(errorstatus);
+		}
+
+		/*!< Send CMD33 SD_ERASE_GRP_END with argument as addr  */
+		SDIO_CmdInitStructure.SDIO_Argument = endaddr;
+		SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_END;
+		SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+		SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+		SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+		SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+		errorstatus = CmdResp1Error(SD_CMD_SD_ERASE_GRP_END);
+		if (errorstatus != SD_OK)
+		{
+			return(errorstatus);
+		}
+	}
+
+	/*!< Send CMD38 ERASE */
+	SDIO_CmdInitStructure.SDIO_Argument = 0;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ERASE;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_ERASE);
+
+	if (errorstatus != SD_OK)
+	{
+		return(errorstatus);
+	}
+
+	for (delay = 0; delay < maxdelay; delay++)
+	{}
+
+	/*!< Wait till the card is in programming state */
+	errorstatus = IsCardProgramming(&cardstate);
+	delay = SD_DATATIMEOUT;
+	while ((delay > 0) && (errorstatus == SD_OK) && ((SD_CARD_PROGRAMMING == cardstate) || (SD_CARD_RECEIVING == cardstate)))
+	{
+		errorstatus = IsCardProgramming(&cardstate);
+		delay--;
+	}
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  Returns the current card's status.
+ * @param  pcardstatus: pointer to the buffer that will contain the SD card
+ *         status (Card Status register).
+ * @retval SD_Error: SD Card Error code.
+ */
+SD_Error SD_SendStatus(uint32_t *pcardstatus)
+{
+	SD_Error errorstatus = SD_OK;
+
+	if (pcardstatus == NULL)
+	{
+		errorstatus = SD_INVALID_PARAMETER;
+		return(errorstatus);
+	}
+
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) RCA << 16;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_STATUS;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_SEND_STATUS);
+
+	if (errorstatus != SD_OK)
+	{
+		return(errorstatus);
+	}
+
+	*pcardstatus = SDIO_GetResponse(SDIO_RESP1);
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  Returns the current SD card's status.
+ * @param  psdstatus: pointer to the buffer that will contain the SD card status
+ *         (SD Status register).
+ * @retval SD_Error: SD Card Error code.
+ */
+SD_Error SD_SendSDStatus(uint32_t *psdstatus)
+{
+	SD_Error errorstatus = SD_OK;
+	uint32_t count = 0;
+
+	if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED)
+	{
+		errorstatus = SD_LOCK_UNLOCK_FAILED;
+		return(errorstatus);
+	}
+
+	/*!< Set block size for card if it is not equal to current block size for card. */
+	SDIO_CmdInitStructure.SDIO_Argument = 64;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);
+
+	if (errorstatus != SD_OK)
+	{
+		return(errorstatus);
+	}
+
+	/*!< CMD55 */
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) RCA << 16;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+	errorstatus = CmdResp1Error(SD_CMD_APP_CMD);
+
+	if (errorstatus != SD_OK)
+	{
+		return(errorstatus);
+	}
+
+	SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
+	SDIO_DataInitStructure.SDIO_DataLength = 64;
+	SDIO_DataInitStructure.SDIO_DataBlockSize = SDIO_DataBlockSize_64b;
+	SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO;
+	SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
+	SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
+	SDIO_DataConfig(&SDIO_DataInitStructure);
+
+	/*!< Send ACMD13 SD_APP_STAUS  with argument as card's RCA.*/
+	SDIO_CmdInitStructure.SDIO_Argument = 0;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_APP_STAUS;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+	errorstatus = CmdResp1Error(SD_CMD_SD_APP_STAUS);
+
+	if (errorstatus != SD_OK)
+	{
+		return(errorstatus);
+	}
+
+	while (!(SDIO->STA &(SDIO_FLAG_RXOVERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_DBCKEND | SDIO_FLAG_STBITERR)))
+	{
+		if (SDIO_GetFlagStatus(SDIO_FLAG_RXFIFOHF) != RESET)
+		{
+			for (count = 0; count < 8; count++)
+			{
+				*(psdstatus + count) = SDIO_ReadData();
+			}
+			psdstatus += 8;
+		}
+	}
+
+	if (SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) != RESET)
+	{
+		SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT);
+		errorstatus = SD_DATA_TIMEOUT;
+		return(errorstatus);
+	}
+	else if (SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) != RESET)
+	{
+		SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL);
+		errorstatus = SD_DATA_CRC_FAIL;
+		return(errorstatus);
+	}
+	else if (SDIO_GetFlagStatus(SDIO_FLAG_RXOVERR) != RESET)
+	{
+		SDIO_ClearFlag(SDIO_FLAG_RXOVERR);
+		errorstatus = SD_RX_OVERRUN;
+		return(errorstatus);
+	}
+	else if (SDIO_GetFlagStatus(SDIO_FLAG_STBITERR) != RESET)
+	{
+		SDIO_ClearFlag(SDIO_FLAG_STBITERR);
+		errorstatus = SD_START_BIT_ERR;
+		return(errorstatus);
+	}
+
+	count = SD_DATATIMEOUT;
+	while ((SDIO_GetFlagStatus(SDIO_FLAG_RXDAVL) != RESET) && (count > 0))
+	{
+		*psdstatus = SDIO_ReadData();
+		psdstatus++;
+		count--;
+	}
+	/*!< Clear all the static status flags*/
+	SDIO_ClearFlag(SDIO_STATIC_FLAGS);
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  Allows to process all the interrupts that are high.
+ * @param  None
+ * @retval SD_Error: SD Card Error code.
+ */
+SD_Error SD_ProcessIRQSrc(void)
+{ 
+	if (SDIO_GetITStatus(SDIO_IT_DATAEND) != RESET)
+	{
+		TransferError = SD_OK;
+		SDIO_ClearITPendingBit(SDIO_IT_DATAEND);
+		TransferEnd = 1;
+	}
+	else if (SDIO_GetITStatus(SDIO_IT_DCRCFAIL) != RESET)
+	{
+		SDIO_ClearITPendingBit(SDIO_IT_DCRCFAIL);
+		TransferError = SD_DATA_CRC_FAIL;
+	}
+	else if (SDIO_GetITStatus(SDIO_IT_DTIMEOUT) != RESET)
+	{
+		SDIO_ClearITPendingBit(SDIO_IT_DTIMEOUT);
+		TransferError = SD_DATA_TIMEOUT;
+	}
+	else if (SDIO_GetITStatus(SDIO_IT_RXOVERR) != RESET)
+	{
+		SDIO_ClearITPendingBit(SDIO_IT_RXOVERR);
+		TransferError = SD_RX_OVERRUN;
+	}
+	else if (SDIO_GetITStatus(SDIO_IT_TXUNDERR) != RESET)
+	{
+		SDIO_ClearITPendingBit(SDIO_IT_TXUNDERR);
+		TransferError = SD_TX_UNDERRUN;
+	}
+	else if (SDIO_GetITStatus(SDIO_IT_STBITERR) != RESET)
+	{
+		SDIO_ClearITPendingBit(SDIO_IT_STBITERR);
+		TransferError = SD_START_BIT_ERR;
+	}
+
+	SDIO_ITConfig(SDIO_IT_DCRCFAIL | SDIO_IT_DTIMEOUT | SDIO_IT_DATAEND |
+			SDIO_IT_TXFIFOHE | SDIO_IT_RXFIFOHF | SDIO_IT_TXUNDERR |
+			SDIO_IT_RXOVERR | SDIO_IT_STBITERR, DISABLE);
+	return(TransferError);
+}
+
+/**
+ * @brief  This function waits until the SDIO DMA data transfer is finished.
+ * @param  None.
+ * @retval None.
+ */
+void SD_ProcessDMAIRQ(void)
+{
+	if(DMA2->LISR & SD_SDIO_DMA_FLAG_TCIF)
+	{
+		DMAEndOfTransfer = 0x01;
+		DMA_ClearFlag(SD_SDIO_DMA_STREAM, SD_SDIO_DMA_FLAG_TCIF|SD_SDIO_DMA_FLAG_FEIF);
+	}
+}
+
+/**
+ * @brief  Checks for error conditions for CMD0.
+ * @param  None
+ * @retval SD_Error: SD Card Error code.
+ */
+static SD_Error CmdError(void)
+{
+	SD_Error errorstatus = SD_OK;
+	uint32_t timeout;
+
+	timeout = SDIO_CMD0TIMEOUT; /*!< 10000 */
+
+	while ((timeout > 0) && (SDIO_GetFlagStatus(SDIO_FLAG_CMDSENT) == RESET))
+	{
+		timeout--;
+	}
+
+	if (timeout == 0)
+	{
+		errorstatus = SD_CMD_RSP_TIMEOUT;
+		return(errorstatus);
+	}
+
+	/*!< Clear all the static flags */
+	SDIO_ClearFlag(SDIO_STATIC_FLAGS);
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  Checks for error conditions for R7 response.
+ * @param  None
+ * @retval SD_Error: SD Card Error code.
+ */
+static SD_Error CmdResp7Error(void)
+{
+	SD_Error errorstatus = SD_OK;
+	uint32_t status;
+	uint32_t timeout = SDIO_CMD0TIMEOUT;
+
+	status = SDIO->STA;
+
+	while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND | SDIO_FLAG_CTIMEOUT)) && (timeout > 0))
+	{
+		timeout--;
+		status = SDIO->STA;
+	}
+
+	if ((timeout == 0) || (status & SDIO_FLAG_CTIMEOUT))
+	{
+		/*!< Card is not V2.0 complient or card does not support the set voltage range */
+		errorstatus = SD_CMD_RSP_TIMEOUT;
+		SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT);
+		return(errorstatus);
+	}
+
+	if (status & SDIO_FLAG_CMDREND)
+	{
+		/*!< Card is SD V2.0 compliant */
+		errorstatus = SD_OK;
+		SDIO_ClearFlag(SDIO_FLAG_CMDREND);
+		return(errorstatus);
+	}
+	return(errorstatus);
+}
+
+/**
+ * @brief  Checks for error conditions for R1 response.
+ * @param  cmd: The sent command index.
+ * @retval SD_Error: SD Card Error code.
+ */
+static SD_Error CmdResp1Error(uint8_t cmd)
+{
+	SD_Error errorstatus = SD_OK;
+	uint32_t status;
+	uint32_t response_r1;
+
+	status = SDIO->STA;
+
+	while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND | SDIO_FLAG_CTIMEOUT)))
+	{
+		status = SDIO->STA;
+	}
+
+	if (status & SDIO_FLAG_CTIMEOUT)
+	{
+		errorstatus = SD_CMD_RSP_TIMEOUT;
+		SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT);
+		return(errorstatus);
+	}
+	else if (status & SDIO_FLAG_CCRCFAIL)
+	{
+		errorstatus = SD_CMD_CRC_FAIL;
+		SDIO_ClearFlag(SDIO_FLAG_CCRCFAIL);
+		return(errorstatus);
+	}
+
+	/*!< Check response received is of desired command */
+	if (SDIO_GetCommandResponse() != cmd)
+	{
+		errorstatus = SD_ILLEGAL_CMD;
+		return(errorstatus);
+	}
+
+	/*!< Clear all the static flags */
+	SDIO_ClearFlag(SDIO_STATIC_FLAGS);
+
+	/*!< We have received response, retrieve it for analysis  */
+	response_r1 = SDIO_GetResponse(SDIO_RESP1);
+
+	if ((response_r1 & SD_OCR_ERRORBITS) == SD_ALLZERO)
+	{
+		return(errorstatus);
+	}
+
+	if (response_r1 & SD_OCR_ADDR_OUT_OF_RANGE)
+	{
+		return(SD_ADDR_OUT_OF_RANGE);
+	}
+
+	if (response_r1 & SD_OCR_ADDR_MISALIGNED)
+	{
+		return(SD_ADDR_MISALIGNED);
+	}
+
+	if (response_r1 & SD_OCR_BLOCK_LEN_ERR)
+	{
+		return(SD_BLOCK_LEN_ERR);
+	}
+
+	if (response_r1 & SD_OCR_ERASE_SEQ_ERR)
+	{
+		return(SD_ERASE_SEQ_ERR);
+	}
+
+	if (response_r1 & SD_OCR_BAD_ERASE_PARAM)
+	{
+		return(SD_BAD_ERASE_PARAM);
+	}
+
+	if (response_r1 & SD_OCR_WRITE_PROT_VIOLATION)
+	{
+		return(SD_WRITE_PROT_VIOLATION);
+	}
+
+	if (response_r1 & SD_OCR_LOCK_UNLOCK_FAILED)
+	{
+		return(SD_LOCK_UNLOCK_FAILED);
+	}
+
+	if (response_r1 & SD_OCR_COM_CRC_FAILED)
+	{
+		return(SD_COM_CRC_FAILED);
+	}
+
+	if (response_r1 & SD_OCR_ILLEGAL_CMD)
+	{
+		return(SD_ILLEGAL_CMD);
+	}
+
+	if (response_r1 & SD_OCR_CARD_ECC_FAILED)
+	{
+		return(SD_CARD_ECC_FAILED);
+	}
+
+	if (response_r1 & SD_OCR_CC_ERROR)
+	{
+		return(SD_CC_ERROR);
+	}
+
+	if (response_r1 & SD_OCR_GENERAL_UNKNOWN_ERROR)
+	{
+		return(SD_GENERAL_UNKNOWN_ERROR);
+	}
+
+	if (response_r1 & SD_OCR_STREAM_READ_UNDERRUN)
+	{
+		return(SD_STREAM_READ_UNDERRUN);
+	}
+
+	if (response_r1 & SD_OCR_STREAM_WRITE_OVERRUN)
+	{
+		return(SD_STREAM_WRITE_OVERRUN);
+	}
+
+	if (response_r1 & SD_OCR_CID_CSD_OVERWRIETE)
+	{
+		return(SD_CID_CSD_OVERWRITE);
+	}
+
+	if (response_r1 & SD_OCR_WP_ERASE_SKIP)
+	{
+		return(SD_WP_ERASE_SKIP);
+	}
+
+	if (response_r1 & SD_OCR_CARD_ECC_DISABLED)
+	{
+		return(SD_CARD_ECC_DISABLED);
+	}
+
+	if (response_r1 & SD_OCR_ERASE_RESET)
+	{
+		return(SD_ERASE_RESET);
+	}
+
+	if (response_r1 & SD_OCR_AKE_SEQ_ERROR)
+	{
+		return(SD_AKE_SEQ_ERROR);
+	}
+	return(errorstatus);
+}
+
+/**
+ * @brief  Checks for error conditions for R3 (OCR) response.
+ * @param  None
+ * @retval SD_Error: SD Card Error code.
+ */
+static SD_Error CmdResp3Error(void)
+{
+	SD_Error errorstatus = SD_OK;
+	uint32_t status;
+
+	status = SDIO->STA;
+
+	while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND | SDIO_FLAG_CTIMEOUT)))
+	{
+		status = SDIO->STA;
+	}
+
+	if (status & SDIO_FLAG_CTIMEOUT)
+	{
+		errorstatus = SD_CMD_RSP_TIMEOUT;
+		SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT);
+		return(errorstatus);
+	}
+	/*!< Clear all the static flags */
+	SDIO_ClearFlag(SDIO_STATIC_FLAGS);
+	return(errorstatus);
+}
+
+/**
+ * @brief  Checks for error conditions for R2 (CID or CSD) response.
+ * @param  None
+ * @retval SD_Error: SD Card Error code.
+ */
+static SD_Error CmdResp2Error(void)
+{
+	SD_Error errorstatus = SD_OK;
+	uint32_t status;
+
+	status = SDIO->STA;
+
+	while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CTIMEOUT | SDIO_FLAG_CMDREND)))
+	{
+		status = SDIO->STA;
+	}
+
+	if (status & SDIO_FLAG_CTIMEOUT)
+	{
+		errorstatus = SD_CMD_RSP_TIMEOUT;
+		SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT);
+		return(errorstatus);
+	}
+	else if (status & SDIO_FLAG_CCRCFAIL)
+	{
+		errorstatus = SD_CMD_CRC_FAIL;
+		SDIO_ClearFlag(SDIO_FLAG_CCRCFAIL);
+		return(errorstatus);
+	}
+
+	/*!< Clear all the static flags */
+	SDIO_ClearFlag(SDIO_STATIC_FLAGS);
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  Checks for error conditions for R6 (RCA) response.
+ * @param  cmd: The sent command index.
+ * @param  prca: pointer to the variable that will contain the SD card relative
+ *         address RCA.
+ * @retval SD_Error: SD Card Error code.
+ */
+static SD_Error CmdResp6Error(uint8_t cmd, uint16_t *prca)
+{
+	SD_Error errorstatus = SD_OK;
+	uint32_t status;
+	uint32_t response_r1;
+
+	status = SDIO->STA;
+
+	while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CTIMEOUT | SDIO_FLAG_CMDREND)))
+	{
+		status = SDIO->STA;
+	}
+
+	if (status & SDIO_FLAG_CTIMEOUT)
+	{
+		errorstatus = SD_CMD_RSP_TIMEOUT;
+		SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT);
+		return(errorstatus);
+	}
+	else if (status & SDIO_FLAG_CCRCFAIL)
+	{
+		errorstatus = SD_CMD_CRC_FAIL;
+		SDIO_ClearFlag(SDIO_FLAG_CCRCFAIL);
+		return(errorstatus);
+	}
+
+	/*!< Check response received is of desired command */
+	if (SDIO_GetCommandResponse() != cmd)
+	{
+		errorstatus = SD_ILLEGAL_CMD;
+		return(errorstatus);
+	}
+
+	/*!< Clear all the static flags */
+	SDIO_ClearFlag(SDIO_STATIC_FLAGS);
+
+	/*!< We have received response, retrieve it.  */
+	response_r1 = SDIO_GetResponse(SDIO_RESP1);
+
+	if (SD_ALLZERO == (response_r1 & (SD_R6_GENERAL_UNKNOWN_ERROR | SD_R6_ILLEGAL_CMD | SD_R6_COM_CRC_FAILED)))
+	{
+		*prca = (uint16_t) (response_r1 >> 16);
+		return(errorstatus);
+	}
+
+	if (response_r1 & SD_R6_GENERAL_UNKNOWN_ERROR)
+	{
+		return(SD_GENERAL_UNKNOWN_ERROR);
+	}
+
+	if (response_r1 & SD_R6_ILLEGAL_CMD)
+	{
+		return(SD_ILLEGAL_CMD);
+	}
+
+	if (response_r1 & SD_R6_COM_CRC_FAILED)
+	{
+		return(SD_COM_CRC_FAILED);
+	}
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  Enables or disables the SDIO wide bus mode.
+ * @param  NewState: new state of the SDIO wide bus mode.
+ *   This parameter can be: ENABLE or DISABLE.
+ * @retval SD_Error: SD Card Error code.
+ */
+static SD_Error SDEnWideBus(FunctionalState NewState)
+{
+	SD_Error errorstatus = SD_OK;
+
+	uint32_t scr[2] = {0, 0};
+
+	if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED)
+	{
+		errorstatus = SD_LOCK_UNLOCK_FAILED;
+		return(errorstatus);
+	}
+
+	/*!< Get SCR Register */
+	errorstatus = FindSCR(RCA, scr);
+
+	if (errorstatus != SD_OK)
+	{
+		return(errorstatus);
+	}
+
+	/*!< If wide bus operation to be enabled */
+	if (NewState == ENABLE)
+	{
+		/*!< If requested card supports wide bus operation */
+		if ((scr[1] & SD_WIDE_BUS_SUPPORT) != SD_ALLZERO)
+		{
+			/*!< Send CMD55 APP_CMD with argument as card's RCA.*/
+			SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) RCA << 16;
+			SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
+			SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+			SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+			SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+			SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+			errorstatus = CmdResp1Error(SD_CMD_APP_CMD);
+
+			if (errorstatus != SD_OK)
+			{
+				return(errorstatus);
+			}
+
+			/*!< Send ACMD6 APP_CMD with argument as 2 for wide bus mode */
+			SDIO_CmdInitStructure.SDIO_Argument = 0x2;
+			SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_SD_SET_BUSWIDTH;
+			SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+			SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+			SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+			SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+			errorstatus = CmdResp1Error(SD_CMD_APP_SD_SET_BUSWIDTH);
+
+			if (errorstatus != SD_OK)
+			{
+				return(errorstatus);
+			}
+			return(errorstatus);
+		}
+		else
+		{
+			errorstatus = SD_REQUEST_NOT_APPLICABLE;
+			return(errorstatus);
+		}
+	}   /*!< If wide bus operation to be disabled */
+	else
+	{
+		/*!< If requested card supports 1 bit mode operation */
+		if ((scr[1] & SD_SINGLE_BUS_SUPPORT) != SD_ALLZERO)
+		{
+			/*!< Send CMD55 APP_CMD with argument as card's RCA.*/
+			SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) RCA << 16;
+			SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
+			SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+			SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+			SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+			SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+
+			errorstatus = CmdResp1Error(SD_CMD_APP_CMD);
+
+			if (errorstatus != SD_OK)
+			{
+				return(errorstatus);
+			}
+
+			/*!< Send ACMD6 APP_CMD with argument as 2 for wide bus mode */
+			SDIO_CmdInitStructure.SDIO_Argument = 0x00;
+			SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_SD_SET_BUSWIDTH;
+			SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+			SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+			SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+			SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+			errorstatus = CmdResp1Error(SD_CMD_APP_SD_SET_BUSWIDTH);
+
+			if (errorstatus != SD_OK)
+			{
+				return(errorstatus);
+			}
+
+			return(errorstatus);
+		}
+		else
+		{
+			errorstatus = SD_REQUEST_NOT_APPLICABLE;
+			return(errorstatus);
+		}
+	}
+}
+
+/**
+ * @brief  Checks if the SD card is in programming state.
+ * @param  pstatus: pointer to the variable that will contain the SD card state.
+ * @retval SD_Error: SD Card Error code.
+ */
+static SD_Error IsCardProgramming(uint8_t *pstatus)
+{
+	SD_Error errorstatus = SD_OK;
+	__IO uint32_t respR1 = 0, status = 0;
+
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) RCA << 16;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_STATUS;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	status = SDIO->STA;
+	while (!(status & (SDIO_FLAG_CCRCFAIL | SDIO_FLAG_CMDREND | SDIO_FLAG_CTIMEOUT)))
+	{
+		status = SDIO->STA;
+	}
+
+	if (status & SDIO_FLAG_CTIMEOUT)
+	{
+		errorstatus = SD_CMD_RSP_TIMEOUT;
+		SDIO_ClearFlag(SDIO_FLAG_CTIMEOUT);
+		return(errorstatus);
+	}
+	else if (status & SDIO_FLAG_CCRCFAIL)
+	{
+		errorstatus = SD_CMD_CRC_FAIL;
+		SDIO_ClearFlag(SDIO_FLAG_CCRCFAIL);
+		return(errorstatus);
+	}
+
+	status = (uint32_t)SDIO_GetCommandResponse();
+
+	/*!< Check response received is of desired command */
+	if (status != SD_CMD_SEND_STATUS)
+	{
+		errorstatus = SD_ILLEGAL_CMD;
+		return(errorstatus);
+	}
+
+	/*!< Clear all the static flags */
+	SDIO_ClearFlag(SDIO_STATIC_FLAGS);
+
+
+	/*!< We have received response, retrieve it for analysis  */
+	respR1 = SDIO_GetResponse(SDIO_RESP1);
+
+	/*!< Find out card status */
+	*pstatus = (uint8_t) ((respR1 >> 9) & 0x0000000F);
+
+	if ((respR1 & SD_OCR_ERRORBITS) == SD_ALLZERO)
+	{
+		return(errorstatus);
+	}
+
+	if (respR1 & SD_OCR_ADDR_OUT_OF_RANGE)
+	{
+		return(SD_ADDR_OUT_OF_RANGE);
+	}
+
+	if (respR1 & SD_OCR_ADDR_MISALIGNED)
+	{
+		return(SD_ADDR_MISALIGNED);
+	}
+
+	if (respR1 & SD_OCR_BLOCK_LEN_ERR)
+	{
+		return(SD_BLOCK_LEN_ERR);
+	}
+
+	if (respR1 & SD_OCR_ERASE_SEQ_ERR)
+	{
+		return(SD_ERASE_SEQ_ERR);
+	}
+
+	if (respR1 & SD_OCR_BAD_ERASE_PARAM)
+	{
+		return(SD_BAD_ERASE_PARAM);
+	}
+
+	if (respR1 & SD_OCR_WRITE_PROT_VIOLATION)
+	{
+		return(SD_WRITE_PROT_VIOLATION);
+	}
+
+	if (respR1 & SD_OCR_LOCK_UNLOCK_FAILED)
+	{
+		return(SD_LOCK_UNLOCK_FAILED);
+	}
+
+	if (respR1 & SD_OCR_COM_CRC_FAILED)
+	{
+		return(SD_COM_CRC_FAILED);
+	}
+
+	if (respR1 & SD_OCR_ILLEGAL_CMD)
+	{
+		return(SD_ILLEGAL_CMD);
+	}
+
+	if (respR1 & SD_OCR_CARD_ECC_FAILED)
+	{
+		return(SD_CARD_ECC_FAILED);
+	}
+
+	if (respR1 & SD_OCR_CC_ERROR)
+	{
+		return(SD_CC_ERROR);
+	}
+
+	if (respR1 & SD_OCR_GENERAL_UNKNOWN_ERROR)
+	{
+		return(SD_GENERAL_UNKNOWN_ERROR);
+	}
+
+	if (respR1 & SD_OCR_STREAM_READ_UNDERRUN)
+	{
+		return(SD_STREAM_READ_UNDERRUN);
+	}
+
+	if (respR1 & SD_OCR_STREAM_WRITE_OVERRUN)
+	{
+		return(SD_STREAM_WRITE_OVERRUN);
+	}
+
+	if (respR1 & SD_OCR_CID_CSD_OVERWRIETE)
+	{
+		return(SD_CID_CSD_OVERWRITE);
+	}
+
+	if (respR1 & SD_OCR_WP_ERASE_SKIP)
+	{
+		return(SD_WP_ERASE_SKIP);
+	}
+
+	if (respR1 & SD_OCR_CARD_ECC_DISABLED)
+	{
+		return(SD_CARD_ECC_DISABLED);
+	}
+
+	if (respR1 & SD_OCR_ERASE_RESET)
+	{
+		return(SD_ERASE_RESET);
+	}
+
+	if (respR1 & SD_OCR_AKE_SEQ_ERROR)
+	{
+		return(SD_AKE_SEQ_ERROR);
+	}
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  Find the SD card SCR register value.
+ * @param  rca: selected card address.
+ * @param  pscr: pointer to the buffer that will contain the SCR value.
+ * @retval SD_Error: SD Card Error code.
+ */
+static SD_Error FindSCR(uint16_t rca, uint32_t *pscr)
+{
+	uint32_t index = 0;
+	SD_Error errorstatus = SD_OK;
+	uint32_t tempscr[2] = {0, 0};
+
+	/*!< Set Block Size To 8 Bytes */
+	/*!< Send CMD55 APP_CMD with argument as card's RCA */
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)8;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);
+
+	if (errorstatus != SD_OK)
+	{
+		return(errorstatus);
+	}
+
+	/*!< Send CMD55 APP_CMD with argument as card's RCA */
+	SDIO_CmdInitStructure.SDIO_Argument = (uint32_t) RCA << 16;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_APP_CMD);
+
+	if (errorstatus != SD_OK)
+	{
+		return(errorstatus);
+	}
+	SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
+	SDIO_DataInitStructure.SDIO_DataLength = 8;
+	SDIO_DataInitStructure.SDIO_DataBlockSize = SDIO_DataBlockSize_8b;
+	SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO;
+	SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
+	SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
+	SDIO_DataConfig(&SDIO_DataInitStructure);
+
+
+	/*!< Send ACMD51 SD_APP_SEND_SCR with argument as 0 */
+	SDIO_CmdInitStructure.SDIO_Argument = 0x0;
+	SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_APP_SEND_SCR;
+	SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
+	SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
+	SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
+	SDIO_SendCommand(&SDIO_CmdInitStructure);
+
+	errorstatus = CmdResp1Error(SD_CMD_SD_APP_SEND_SCR);
+
+	if (errorstatus != SD_OK)
+	{
+		return(errorstatus);
+	}
+
+	while (!(SDIO->STA & (SDIO_FLAG_RXOVERR | SDIO_FLAG_DCRCFAIL | SDIO_FLAG_DTIMEOUT | SDIO_FLAG_DBCKEND | SDIO_FLAG_STBITERR)))
+	{
+		if (SDIO_GetFlagStatus(SDIO_FLAG_RXDAVL) != RESET)
+		{
+			*(tempscr + index) = SDIO_ReadData();
+			index++;
+		}
+	}
+
+	if (SDIO_GetFlagStatus(SDIO_FLAG_DTIMEOUT) != RESET)
+	{
+		SDIO_ClearFlag(SDIO_FLAG_DTIMEOUT);
+		errorstatus = SD_DATA_TIMEOUT;
+		return(errorstatus);
+	}
+	else if (SDIO_GetFlagStatus(SDIO_FLAG_DCRCFAIL) != RESET)
+	{
+		SDIO_ClearFlag(SDIO_FLAG_DCRCFAIL);
+		errorstatus = SD_DATA_CRC_FAIL;
+		return(errorstatus);
+	}
+	else if (SDIO_GetFlagStatus(SDIO_FLAG_RXOVERR) != RESET)
+	{
+		SDIO_ClearFlag(SDIO_FLAG_RXOVERR);
+		errorstatus = SD_RX_OVERRUN;
+		return(errorstatus);
+	}
+	else if (SDIO_GetFlagStatus(SDIO_FLAG_STBITERR) != RESET)
+	{
+		SDIO_ClearFlag(SDIO_FLAG_STBITERR);
+		errorstatus = SD_START_BIT_ERR;
+		return(errorstatus);
+	}
+
+	/*!< Clear all the static flags */
+	SDIO_ClearFlag(SDIO_STATIC_FLAGS);
+
+	*(pscr + 1) = ((tempscr[0] & SD_0TO7BITS) << 24) | ((tempscr[0] & SD_8TO15BITS) << 8) | ((tempscr[0] & SD_16TO23BITS) >> 8) | ((tempscr[0] & SD_24TO31BITS) >> 24);
+
+	*(pscr) = ((tempscr[1] & SD_0TO7BITS) << 24) | ((tempscr[1] & SD_8TO15BITS) << 8) | ((tempscr[1] & SD_16TO23BITS) >> 8) | ((tempscr[1] & SD_24TO31BITS) >> 24);
+
+	return(errorstatus);
+}
+
+/**
+ * @brief  Converts the number of bytes in power of two and returns the power.
+ * @param  NumberOfBytes: number of bytes.
+ * @retval None
+ */
+uint8_t convert_from_bytes_to_power_of_two(uint16_t NumberOfBytes)
+{
+	uint8_t count = 0;
+
+	while (NumberOfBytes != 1)
+	{
+		NumberOfBytes >>= 1;
+		count++;
+	}
+	return(count);
+}
+
+
+/**
+ * @brief  Configures SDIO IRQ channel.
+ * @param  None
+ * @retval None
+ */
+// void NVIC_Configuration(void)
+// {
+//   NVIC_InitTypeDef NVIC_InitStructure;
+
+//   /* Configure the NVIC Preemption Priority Bits */
+//   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
+// 	// SDIO Interrupt ENABLE
+//   NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn;
+//   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
+//   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
+//   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
+//   NVIC_Init(&NVIC_InitStructure);
+// 	// DMA2 STREAMx Interrupt ENABLE
+//   NVIC_InitStructure.NVIC_IRQChannel = SD_SDIO_DMA_IRQn;
+//   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
+//   NVIC_Init(&NVIC_InitStructure);  
+// }
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

+ 409 - 0
iap/Modules/SD_Card/sdio_sd.h

@@ -0,0 +1,409 @@
+/**
+  ******************************************************************************
+  * @file    stm32f4_sdio_sd.h
+  * @author  MCD Application Team
+  * @version V1.0.2
+  * @date    09-March-2012
+  * @brief   This file contains all the functions prototypes for the SD Card 
+  *          stm32f4_sdio_sd driver firmware library.
+  ******************************************************************************
+  * @attention
+  *
+  * <h2><center>&copy; COPYRIGHT 2012 STMicroelectronics</center></h2>
+  *
+  * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
+  * You may not use this file except in compliance with the License.
+  * You may obtain a copy of the License at:
+  *
+  *        http://www.st.com/software_license_agreement_liberty_v2
+  *
+  * Unless required by applicable law or agreed to in writing, software 
+  * distributed under the License is distributed on an "AS IS" BASIS, 
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  *
+  ******************************************************************************
+  */ 
+
+/* Define to prevent recursive inclusion -------------------------------------*/
+#ifndef __SDIO_SD_H
+#define __SDIO_SD_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Includes ------------------------------------------------------------------*/
+#include "stm32f4xx.h"
+
+/** @addtogroup Utilities
+  * @{
+  */
+  
+/** @addtogroup STM32_EVAL
+  * @{
+  */ 
+
+/** @addtogroup STM324xG_EVAL
+  * @{
+  */
+  
+/** @addtogroup STM324xG_EVAL_SDIO_SD
+  * @{
+  */  
+
+/** @defgroup STM324xG_EVAL_SDIO_SD_Exported_Types
+  * @{
+  */ 
+typedef enum
+{
+/** 
+  * @brief  SDIO specific error defines  
+  */   
+  SD_CMD_CRC_FAIL                    = (1), /*!< Command response received (but CRC check failed) */
+  SD_DATA_CRC_FAIL                   = (2), /*!< Data bock sent/received (CRC check Failed) */
+  SD_CMD_RSP_TIMEOUT                 = (3), /*!< Command response timeout */
+  SD_DATA_TIMEOUT                    = (4), /*!< Data time out */
+  SD_TX_UNDERRUN                     = (5), /*!< Transmit FIFO under-run */
+  SD_RX_OVERRUN                      = (6), /*!< Receive FIFO over-run */
+  SD_START_BIT_ERR                   = (7), /*!< Start bit not detected on all data signals in widE bus mode */
+  SD_CMD_OUT_OF_RANGE                = (8), /*!< CMD's argument was out of range.*/
+  SD_ADDR_MISALIGNED                 = (9), /*!< Misaligned address */
+  SD_BLOCK_LEN_ERR                   = (10), /*!< Transferred block length is not allowed for the card or the number of transferred bytes does not match the block length */
+  SD_ERASE_SEQ_ERR                   = (11), /*!< An error in the sequence of erase command occurs.*/
+  SD_BAD_ERASE_PARAM                 = (12), /*!< An Invalid selection for erase groups */
+  SD_WRITE_PROT_VIOLATION            = (13), /*!< Attempt to program a write protect block */
+  SD_LOCK_UNLOCK_FAILED              = (14), /*!< Sequence or password error has been detected in unlock command or if there was an attempt to access a locked card */
+  SD_COM_CRC_FAILED                  = (15), /*!< CRC check of the previous command failed */
+  SD_ILLEGAL_CMD                     = (16), /*!< Command is not legal for the card state */
+  SD_CARD_ECC_FAILED                 = (17), /*!< Card internal ECC was applied but failed to correct the data */
+  SD_CC_ERROR                        = (18), /*!< Internal card controller error */
+  SD_GENERAL_UNKNOWN_ERROR           = (19), /*!< General or Unknown error */
+  SD_STREAM_READ_UNDERRUN            = (20), /*!< The card could not sustain data transfer in stream read operation. */
+  SD_STREAM_WRITE_OVERRUN            = (21), /*!< The card could not sustain data programming in stream mode */
+  SD_CID_CSD_OVERWRITE               = (22), /*!< CID/CSD overwrite error */
+  SD_WP_ERASE_SKIP                   = (23), /*!< only partial address space was erased */
+  SD_CARD_ECC_DISABLED               = (24), /*!< Command has been executed without using internal ECC */
+  SD_ERASE_RESET                     = (25), /*!< Erase sequence was cleared before executing because an out of erase sequence command was received */
+  SD_AKE_SEQ_ERROR                   = (26), /*!< Error in sequence of authentication. */
+  SD_INVALID_VOLTRANGE               = (27),
+  SD_ADDR_OUT_OF_RANGE               = (28),
+  SD_SWITCH_ERROR                    = (29),
+  SD_SDIO_DISABLED                   = (30),
+  SD_SDIO_FUNCTION_BUSY              = (31),
+  SD_SDIO_FUNCTION_FAILED            = (32),
+  SD_SDIO_UNKNOWN_FUNCTION           = (33),
+
+/** 
+  * @brief  Standard error defines   
+  */ 
+  SD_INTERNAL_ERROR, 
+  SD_NOT_CONFIGURED,
+  SD_REQUEST_PENDING, 
+  SD_REQUEST_NOT_APPLICABLE, 
+  SD_INVALID_PARAMETER,  
+  SD_UNSUPPORTED_FEATURE,  
+  SD_UNSUPPORTED_HW,  
+  SD_ERROR,  
+  SD_OK = 0 
+} SD_Error;
+
+/** 
+  * @brief  SDIO Transfer state  
+  */   
+typedef enum
+{
+  SD_TRANSFER_OK  = 0,
+  SD_TRANSFER_BUSY = 1,
+  SD_TRANSFER_ERROR
+} SDTransferState;
+
+/** 
+  * @brief  SD Card States 
+  */   
+typedef enum
+{
+  SD_CARD_READY                  = ((uint32_t)0x00000001),
+  SD_CARD_IDENTIFICATION         = ((uint32_t)0x00000002),
+  SD_CARD_STANDBY                = ((uint32_t)0x00000003),
+  SD_CARD_TRANSFER               = ((uint32_t)0x00000004),
+  SD_CARD_SENDING                = ((uint32_t)0x00000005),
+  SD_CARD_RECEIVING              = ((uint32_t)0x00000006),
+  SD_CARD_PROGRAMMING            = ((uint32_t)0x00000007),
+  SD_CARD_DISCONNECTED           = ((uint32_t)0x00000008),
+  SD_CARD_ERROR                  = ((uint32_t)0x000000FF)
+}SDCardState;
+
+
+/** 
+  * @brief  Card Specific Data: CSD Register   
+  */ 
+typedef struct
+{
+  __IO uint8_t  CSDStruct;            /*!< CSD structure */
+  __IO uint8_t  SysSpecVersion;       /*!< System specification version */
+  __IO uint8_t  Reserved1;            /*!< Reserved */
+  __IO uint8_t  TAAC;                 /*!< Data read access-time 1 */
+  __IO uint8_t  NSAC;                 /*!< Data read access-time 2 in CLK cycles */
+  __IO uint8_t  MaxBusClkFrec;        /*!< Max. bus clock frequency */
+  __IO uint16_t CardComdClasses;      /*!< Card command classes */
+  __IO uint8_t  RdBlockLen;           /*!< Max. read data block length */
+  __IO uint8_t  PartBlockRead;        /*!< Partial blocks for read allowed */
+  __IO uint8_t  WrBlockMisalign;      /*!< Write block misalignment */
+  __IO uint8_t  RdBlockMisalign;      /*!< Read block misalignment */
+  __IO uint8_t  DSRImpl;              /*!< DSR implemented */
+  __IO uint8_t  Reserved2;            /*!< Reserved */
+  __IO uint32_t DeviceSize;           /*!< Device Size */
+  __IO uint8_t  MaxRdCurrentVDDMin;   /*!< Max. read current @ VDD min */
+  __IO uint8_t  MaxRdCurrentVDDMax;   /*!< Max. read current @ VDD max */
+  __IO uint8_t  MaxWrCurrentVDDMin;   /*!< Max. write current @ VDD min */
+  __IO uint8_t  MaxWrCurrentVDDMax;   /*!< Max. write current @ VDD max */
+  __IO uint8_t  DeviceSizeMul;        /*!< Device size multiplier */
+  __IO uint8_t  EraseGrSize;          /*!< Erase group size */
+  __IO uint8_t  EraseGrMul;           /*!< Erase group size multiplier */
+  __IO uint8_t  WrProtectGrSize;      /*!< Write protect group size */
+  __IO uint8_t  WrProtectGrEnable;    /*!< Write protect group enable */
+  __IO uint8_t  ManDeflECC;           /*!< Manufacturer default ECC */
+  __IO uint8_t  WrSpeedFact;          /*!< Write speed factor */
+  __IO uint8_t  MaxWrBlockLen;        /*!< Max. write data block length */
+  __IO uint8_t  WriteBlockPaPartial;  /*!< Partial blocks for write allowed */
+  __IO uint8_t  Reserved3;            /*!< Reserded */
+  __IO uint8_t  ContentProtectAppli;  /*!< Content protection application */
+  __IO uint8_t  FileFormatGrouop;     /*!< File format group */
+  __IO uint8_t  CopyFlag;             /*!< Copy flag (OTP) */
+  __IO uint8_t  PermWrProtect;        /*!< Permanent write protection */
+  __IO uint8_t  TempWrProtect;        /*!< Temporary write protection */
+  __IO uint8_t  FileFormat;           /*!< File Format */
+  __IO uint8_t  ECC;                  /*!< ECC code */
+  __IO uint8_t  CSD_CRC;              /*!< CSD CRC */
+  __IO uint8_t  Reserved4;            /*!< always 1*/
+} SD_CSD;
+
+/** 
+  * @brief  Card Identification Data: CID Register   
+  */
+typedef struct
+{
+  __IO uint8_t  ManufacturerID;       /*!< ManufacturerID */
+  __IO uint16_t OEM_AppliID;          /*!< OEM/Application ID */
+  __IO uint32_t ProdName1;            /*!< Product Name part1 */
+  __IO uint8_t  ProdName2;            /*!< Product Name part2*/
+  __IO uint8_t  ProdRev;              /*!< Product Revision */
+  __IO uint32_t ProdSN;               /*!< Product Serial Number */
+  __IO uint8_t  Reserved1;            /*!< Reserved1 */
+  __IO uint16_t ManufactDate;         /*!< Manufacturing Date */
+  __IO uint8_t  CID_CRC;              /*!< CID CRC */
+  __IO uint8_t  Reserved2;            /*!< always 1 */
+} SD_CID;
+
+/** 
+  * @brief SD Card Status 
+  */
+typedef struct
+{
+  __IO uint8_t DAT_BUS_WIDTH;
+  __IO uint8_t SECURED_MODE;
+  __IO uint16_t SD_CARD_TYPE;
+  __IO uint32_t SIZE_OF_PROTECTED_AREA;
+  __IO uint8_t SPEED_CLASS;
+  __IO uint8_t PERFORMANCE_MOVE;
+  __IO uint8_t AU_SIZE;
+  __IO uint16_t ERASE_SIZE;
+  __IO uint8_t ERASE_TIMEOUT;
+  __IO uint8_t ERASE_OFFSET;
+} SD_CardStatus;
+
+
+/** 
+  * @brief SD Card information 
+  */
+typedef struct
+{
+  SD_CSD SD_csd;
+  SD_CID SD_cid;
+  uint64_t CardCapacity;  /*!< Card Capacity */
+  uint32_t CardBlockSize; /*!< Card Block Size */
+  uint16_t RCA;
+  uint8_t CardType;
+} SD_CardInfo;
+
+/**
+  * @}
+  */
+  
+/** @defgroup STM324xG_EVAL_SDIO_SD_Exported_Constants
+  * @{
+  */ 
+
+/** 
+  * @brief SDIO Commands  Index 
+  */
+#define SD_CMD_GO_IDLE_STATE                       ((uint8_t)0)
+#define SD_CMD_SEND_OP_COND                        ((uint8_t)1)
+#define SD_CMD_ALL_SEND_CID                        ((uint8_t)2)
+#define SD_CMD_SET_REL_ADDR                        ((uint8_t)3) /*!< SDIO_SEND_REL_ADDR for SD Card */
+#define SD_CMD_SET_DSR                             ((uint8_t)4)
+#define SD_CMD_SDIO_SEN_OP_COND                    ((uint8_t)5)
+#define SD_CMD_HS_SWITCH                           ((uint8_t)6)
+#define SD_CMD_SEL_DESEL_CARD                      ((uint8_t)7)
+#define SD_CMD_HS_SEND_EXT_CSD                     ((uint8_t)8)
+#define SD_CMD_SEND_CSD                            ((uint8_t)9)
+#define SD_CMD_SEND_CID                            ((uint8_t)10)
+#define SD_CMD_READ_DAT_UNTIL_STOP                 ((uint8_t)11) /*!< SD Card doesn't support it */
+#define SD_CMD_STOP_TRANSMISSION                   ((uint8_t)12)
+#define SD_CMD_SEND_STATUS                         ((uint8_t)13)
+#define SD_CMD_HS_BUSTEST_READ                     ((uint8_t)14)
+#define SD_CMD_GO_INACTIVE_STATE                   ((uint8_t)15)
+#define SD_CMD_SET_BLOCKLEN                        ((uint8_t)16)
+#define SD_CMD_READ_SINGLE_BLOCK                   ((uint8_t)17)
+#define SD_CMD_READ_MULT_BLOCK                     ((uint8_t)18)
+#define SD_CMD_HS_BUSTEST_WRITE                    ((uint8_t)19)
+#define SD_CMD_WRITE_DAT_UNTIL_STOP                ((uint8_t)20) /*!< SD Card doesn't support it */
+#define SD_CMD_SET_BLOCK_COUNT                     ((uint8_t)23) /*!< SD Card doesn't support it */
+#define SD_CMD_WRITE_SINGLE_BLOCK                  ((uint8_t)24)
+#define SD_CMD_WRITE_MULT_BLOCK                    ((uint8_t)25)
+#define SD_CMD_PROG_CID                            ((uint8_t)26) /*!< reserved for manufacturers */
+#define SD_CMD_PROG_CSD                            ((uint8_t)27)
+#define SD_CMD_SET_WRITE_PROT                      ((uint8_t)28)
+#define SD_CMD_CLR_WRITE_PROT                      ((uint8_t)29)
+#define SD_CMD_SEND_WRITE_PROT                     ((uint8_t)30)
+#define SD_CMD_SD_ERASE_GRP_START                  ((uint8_t)32) /*!< To set the address of the first write
+                                                                  block to be erased. (For SD card only) */
+#define SD_CMD_SD_ERASE_GRP_END                    ((uint8_t)33) /*!< To set the address of the last write block of the
+                                                                  continuous range to be erased. (For SD card only) */
+#define SD_CMD_ERASE_GRP_START                     ((uint8_t)35) /*!< To set the address of the first write block to be erased.
+                                                                  (For MMC card only spec 3.31) */
+
+#define SD_CMD_ERASE_GRP_END                       ((uint8_t)36) /*!< To set the address of the last write block of the
+                                                                  continuous range to be erased. (For MMC card only spec 3.31) */
+
+#define SD_CMD_ERASE                               ((uint8_t)38)
+#define SD_CMD_FAST_IO                             ((uint8_t)39) /*!< SD Card doesn't support it */
+#define SD_CMD_GO_IRQ_STATE                        ((uint8_t)40) /*!< SD Card doesn't support it */
+#define SD_CMD_LOCK_UNLOCK                         ((uint8_t)42)
+#define SD_CMD_APP_CMD                             ((uint8_t)55)
+#define SD_CMD_GEN_CMD                             ((uint8_t)56)
+#define SD_CMD_NO_CMD                              ((uint8_t)64)
+
+/** 
+  * @brief Following commands are SD Card Specific commands.
+  *        SDIO_APP_CMD should be sent before sending these commands. 
+  */
+#define SD_CMD_APP_SD_SET_BUSWIDTH                 ((uint8_t)6)  /*!< For SD Card only */
+#define SD_CMD_SD_APP_STAUS                        ((uint8_t)13) /*!< For SD Card only */
+#define SD_CMD_SD_APP_SEND_NUM_WRITE_BLOCKS        ((uint8_t)22) /*!< For SD Card only */
+#define SD_CMD_SD_APP_OP_COND                      ((uint8_t)41) /*!< For SD Card only */
+#define SD_CMD_SD_APP_SET_CLR_CARD_DETECT          ((uint8_t)42) /*!< For SD Card only */
+#define SD_CMD_SD_APP_SEND_SCR                     ((uint8_t)51) /*!< For SD Card only */
+#define SD_CMD_SDIO_RW_DIRECT                      ((uint8_t)52) /*!< For SD I/O Card only */
+#define SD_CMD_SDIO_RW_EXTENDED                    ((uint8_t)53) /*!< For SD I/O Card only */
+
+/** 
+  * @brief Following commands are SD Card Specific security commands.
+  *        SDIO_APP_CMD should be sent before sending these commands. 
+  */
+#define SD_CMD_SD_APP_GET_MKB                      ((uint8_t)43) /*!< For SD Card only */
+#define SD_CMD_SD_APP_GET_MID                      ((uint8_t)44) /*!< For SD Card only */
+#define SD_CMD_SD_APP_SET_CER_RN1                  ((uint8_t)45) /*!< For SD Card only */
+#define SD_CMD_SD_APP_GET_CER_RN2                  ((uint8_t)46) /*!< For SD Card only */
+#define SD_CMD_SD_APP_SET_CER_RES2                 ((uint8_t)47) /*!< For SD Card only */
+#define SD_CMD_SD_APP_GET_CER_RES1                 ((uint8_t)48) /*!< For SD Card only */
+#define SD_CMD_SD_APP_SECURE_READ_MULTIPLE_BLOCK   ((uint8_t)18) /*!< For SD Card only */
+#define SD_CMD_SD_APP_SECURE_WRITE_MULTIPLE_BLOCK  ((uint8_t)25) /*!< For SD Card only */
+#define SD_CMD_SD_APP_SECURE_ERASE                 ((uint8_t)38) /*!< For SD Card only */
+#define SD_CMD_SD_APP_CHANGE_SECURE_AREA           ((uint8_t)49) /*!< For SD Card only */
+#define SD_CMD_SD_APP_SECURE_WRITE_MKB             ((uint8_t)48) /*!< For SD Card only */
+  
+/* Uncomment the following line to select the SDIO Data transfer mode */  
+#if !defined (SD_DMA_MODE) && !defined (SD_POLLING_MODE)
+#define SD_DMA_MODE                                ((uint32_t)0x00000000)
+//#define SD_POLLING_MODE                            ((uint32_t)0x00000002)
+#endif
+
+/**
+  * @brief  SD detection on its memory slot
+  */
+#define SD_PRESENT                                 ((uint8_t)0x01)
+#define SD_NOT_PRESENT                             ((uint8_t)0x00)
+
+/** 
+  * @brief Supported SD Memory Cards 
+  */
+#define SDIO_STD_CAPACITY_SD_CARD_V1_1             ((uint32_t)0x00000000)
+#define SDIO_STD_CAPACITY_SD_CARD_V2_0             ((uint32_t)0x00000001)
+#define SDIO_HIGH_CAPACITY_SD_CARD                 ((uint32_t)0x00000002)
+#define SDIO_MULTIMEDIA_CARD                       ((uint32_t)0x00000003)
+#define SDIO_SECURE_DIGITAL_IO_CARD                ((uint32_t)0x00000004)
+#define SDIO_HIGH_SPEED_MULTIMEDIA_CARD            ((uint32_t)0x00000005)
+#define SDIO_SECURE_DIGITAL_IO_COMBO_CARD          ((uint32_t)0x00000006)
+#define SDIO_HIGH_CAPACITY_MMC_CARD                ((uint32_t)0x00000007)
+
+/**
+  * @}
+  */ 
+  
+/** @defgroup STM324xG_EVAL_SDIO_SD_Exported_Macros
+  * @{
+  */ 
+/**
+  * @}
+  */ 
+
+/** @defgroup STM324xG_EVAL_SDIO_SD_Exported_Functions
+  * @{
+  */ 
+void SD_NVIC_Init(void);
+void SD_DeInit(void);
+SD_Error SD_Init(void);
+SDTransferState SD_GetStatus(void);
+SDCardState SD_GetState(void);
+uint8_t SD_Detect(void);
+SD_Error SD_PowerON(void);
+SD_Error SD_PowerOFF(void);
+SD_Error SD_InitializeCards(void);
+SD_Error SD_GetCardInfo(SD_CardInfo *cardinfo);
+SD_Error SD_GetCardStatus(SD_CardStatus *cardstatus);
+SD_Error SD_EnableWideBusOperation(uint32_t WideMode);
+SD_Error SD_SelectDeselect(uint32_t addr);
+SD_Error SD_ReadBlock(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize);
+SD_Error SD_ReadMultiBlocks(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize, uint32_t NumberOfBlocks);
+SD_Error SD_ReadMultiBlocksFIXED(uint8_t *writebuff, uint32_t WriteAddr, uint32_t BlockSize, uint32_t NumberOfBlocks);
+SD_Error SD_WriteBlock(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize);
+SD_Error SD_WriteMultiBlocks(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize, uint32_t NumberOfBlocks);
+SD_Error SD_WriteMultiBlocksFIXED(uint8_t *writebuff, uint32_t WriteAddr, uint32_t BlockSize, uint32_t NumberOfBlocks);
+SDTransferState SD_GetTransferState(void);
+SD_Error SD_StopTransfer(void);
+SD_Error SD_Erase(uint32_t startaddr, uint32_t endaddr);
+SD_Error SD_SendStatus(uint32_t *pcardstatus);
+SD_Error SD_SendSDStatus(uint32_t *psdstatus);
+SD_Error SD_ProcessIRQSrc(void);
+void SD_ProcessDMAIRQ(void);
+SD_Error SD_WaitReadOperation(void);
+SD_Error SD_WaitWriteOperation(void);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __STM324xG_EVAL_SDIO_SD_H */
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */
+
+/**
+  * @}
+  */ 
+
+/**
+  * @}
+  */ 
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

+ 15 - 2
iap/Modules/crc.c

@@ -13,6 +13,9 @@
 #include "stm32f4xx.h"
 #include "crc.h"
 #include "common_config.h"
+#include <stddef.h>
+#include <stdint.h>
+
 /**
   * @brief  
   * @retval 
@@ -44,12 +47,22 @@ uint32_t CRC_Read(void) {
   * @brief Считаем crc для записанной прошивки
   * @retval 
   */
-uint32_t CRC_Calcucate(void) {
+uint32_t CRC_Calculate(void (* periodic_handler)(uint8_t)) {
   uint32_t res;
+  static uint32_t last_progress = 0;
   /* Reset CRC */
   CRC->CR = ((uint8_t)0x01);
-  for(uint32_t* ptr=(uint32_t*)USER_FLASH_FIRST_PAGE_ADDRESS;ptr != (uint32_t*)USER_FLASH_CRC_ADDRESS;ptr++)
+  for (uint32_t* ptr = (uint32_t*)USER_FLASH_FIRST_PAGE_ADDRESS; ptr != (uint32_t*)USER_FLASH_CRC_ADDRESS; ptr++) {
     CRC->DR = *ptr; // добавляем всю прошивку в CRC
+    if (periodic_handler != NULL) {
+      uint32_t progress = ((uint32_t)ptr - USER_FLASH_FIRST_PAGE_ADDRESS) * 100 /
+        (USER_FLASH_CRC_ADDRESS - USER_FLASH_FIRST_PAGE_ADDRESS);
+      if (progress > last_progress) {
+        last_progress = progress;
+        periodic_handler(progress);
+      }
+    }
+  }
   res = CRC->DR;
   return res;
 }

+ 1 - 1
iap/Modules/crc.h

@@ -29,7 +29,7 @@ uint32_t CRC_Read(void);
   * @brief Считаем crc для записанной прошивки
   * @retval 
   */
-uint32_t CRC_Calcucate(void);
+uint32_t CRC_Calculate(void (* periodic_handler)(uint8_t));
 
 #endif /* #ifndef CRC_H */
 /****************************** (C) SWITRON ***************** end of file ****/

+ 16 - 0
iap/Modules/rng.c

@@ -0,0 +1,16 @@
+#include <stdint.h>
+#include "rng.h"
+#include "stm32f4xx.h"
+
+
+void RNG_Init(void)
+{
+    RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_RNG,ENABLE);
+    RNG_Cmd(ENABLE);
+}
+
+uint32_t GetRandomNumber(void) {
+    return RNG_GetRandomNumber();;
+}
+
+

+ 16 - 0
iap/Modules/rng.h

@@ -0,0 +1,16 @@
+/*
+ * rng.h
+ *
+ *  Created on: 26.01.2016
+ *      Author: pavel
+ */
+#include <stdint.h>
+
+#ifndef RNG_H_
+#define RNG_H_
+
+
+void RNG_Init(void);
+uint32_t GetRandomNumber(void);
+
+#endif /* RNG_H_ */

+ 18 - 0
iap/Modules/systick.c

@@ -202,6 +202,24 @@ void timer_Stop(TTimerHandler Handler)
   }
 }
 
+/**
+  * @brief  Получить значение счетчика
+  * @retval значение счетчика или 0 если не найдено
+  */
+uint16_t timer_GetCountdown(TTimerHandler Handler)
+{
+  int i;
+
+  for(i = 0; i < TIMER_HANDLERS; i++)
+  {
+    if(Handlers[i].Handler == Handler)
+    {
+      return Handlers[i].Countdown;
+    }
+  }
+  return 0;
+}
+
 /**
   * @brief  Функция перебора и вызова актуальных задач. 
   *         Должна вызываться в главном цикле

+ 5 - 0
iap/Modules/systick.h

@@ -56,6 +56,11 @@ void timer_RestartAtOnce(TTimerHandler Handler);
   */
 void timer_Stop(TTimerHandler Handler);
 
+/**
+  * @brief  Получить значение счетчика
+  */
+uint16_t timer_GetCountdown(TTimerHandler Handler);
+
 /**
   * @brief  Функция перебора и вызова актуальных задач. 
   *         Должна вызываться в главном цикле

+ 23 - 0
iap/User/conf.h

@@ -0,0 +1,23 @@
+/*
+ * iap_config.h
+ *
+ *  Created on: 26.06.2017
+ *      Author: pavel
+ */
+
+#ifndef CONF_H_
+#define CONF_H_
+
+/* Slave board update enable/disable */
+//#define SLAVEBRD_ENABLE
+
+/* LCD enable/disable */
+//#define LCD_ENABLE
+
+/* Update from SD enable/disable */
+//#define SD_ENABLE
+
+/* Buttons enable/disable */
+//#define BUTTONS_ENABLE
+
+#endif /* CONF_H_ */

+ 230 - 121
iap/User/main.c

@@ -1,5 +1,6 @@
-#include "stm32f4xx.h"
+#include "stm32f2xx.h"
 #include "common_config.h"
+#include "conf.h"
 #include "main.h"
 #include "led.h"
 #include "systick.h"
@@ -12,27 +13,52 @@
 #include "crc.h"
 #include "wdg.h"
 #include "tinystdio.h"
+#include "time.h"
 #include "string.h"
-
-#include "stm32f4x7_eth.h"
+#include "stm32f4xx_eth.h"
 #include "netconf.h"
+#include "rng.h"
+#ifdef SD_ENABLE
+#include "SD_Card/sdio_sd.h"
+#include "FATFS/ff.h"
+#include "FATFS/diskio.h"
+#endif
+#ifdef LCD_ENABLE
+#include "lcd.h"
+#endif
+#ifdef SLAVEBRD_ENABLE
+#include "stm32sprog.h"
+#endif
 
 /* Секция размещения СRC прошивки */
 #if defined ( __GNUC__ )
 uint32_t crc __attribute__ ((section (".crc"))) = 0xAABBCCDD;
 #endif
 
+#define FW_FILE_NAME    MAIN_FW_NAME
+
+/*
+ * Bootloader verification key section.
+ * Use "openssl rand -hex 8" to generate new key.
+ * */
+uint64_t bootkey __attribute__ ((section (".bootkey"))) = 0x92dc73b8fef3b041;
+
 bool IAPviaETH = false;
-uint8_t fDoneReset = 0;
-uint8_t fErrorReset = 0;
-uint8_t fUpload = 0;
-uint8_t fInvalidFw = 0;
-uint8_t fBootFailed = 0;
-uint32_t resetCounter = 0;
+bool fDoneReset = false;
+bool fUpload = false;
+bool fInvalidFw = false;
+bool fBootFailed = false;
+
+volatile uint32_t resetCounter = 0;
 bool UpdateTimeoutFlag = false;
 
+#ifdef SD_ENABLE
+extern FATFS    fs;
+extern FIL      fil_obj;
+#endif
+
 /* this variable is used to create a time reference incremented by 10ms */
-__IO uint32_t LocalTime = 0; 
+__IO uint32_t LocalTime = 0;
 
 pFunction Jump_To_App;
 
@@ -45,6 +71,27 @@ extern SETTINGS_t sSettings;
 
 void UpdateTimeout_Handler(void);
 
+
+#ifdef SD_ENABLE
+bool mmc_mount(void)
+{
+    if (disk_initialize(0) != RES_OK) return false;
+    if (f_mount(&fs, "0:", 1) != FR_OK) return false;
+
+    return true;
+}
+#endif
+
+
+bool CheckFWIsValid(void)
+{
+	if (((*(__IO uint32_t *)USER_FLASH_FIRST_PAGE_ADDRESS) & 0x2FFE0000 ) == 0x20000000 &&
+        *(__IO uint32_t *)USER_FLASH_CRC_ADDRESS != 0xFFFFFFFF) {
+        return true;
+    }
+    return false;
+}
+
 void main(void)
 {
   	uint8_t bootTry;
@@ -54,6 +101,12 @@ void main(void)
     WDG_Init();
     InitUSART();
 
+#ifdef PRINTF_STDLIB
+    setvbuf(stdin, NULL, _IONBF, 0);
+    setvbuf(stdout, NULL, _IONBF, 0);
+    setvbuf(stderr, NULL, _IONBF, 0);
+#endif
+
   /* Enable PWR peripheral clock */
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
 
@@ -71,75 +124,81 @@ void main(void)
   
   /* Проверка флага bootTry. Если флаг установлен, значит произошел сбой в 
      основной прошивке. Нужно загружать bootloader и ждать обновления ПО */
-  /* TODO remove if tested */
-  //bootTry = sSettings.bootParams.bootTry;
   loadMode = RTC_ReadBackupRegister(RTC_BKP_DR1);
   bootTry = RTC_ReadBackupRegister(RTC_BKP_DR2);
+  fInvalidFw = RTC_ReadBackupRegister(RTC_BKP_DR7);
   printf("loadMode: %d\r\nbootTry: %d\r\n", loadMode, bootTry);
+
+  printf("loadMode: %d\r\n", loadMode);
+  printf("loadMode: %d\r\n", bootTry);
+  printf("fInvalidFw: %s\r\n", fInvalidFw ? "true" : "false");
   
-  if (bootTry > 1)
-  {
-    bootTry--;
-    RTC_WriteBackupRegister(RTC_BKP_DR2, bootTry);
-
-    /* Check if valid stack address (RAM address) then jump to user application */
-    if (((*(__IO uint32_t*)USER_FLASH_FIRST_PAGE_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
-    {
-        JumpAdd = *(__IO uint32_t*) (USER_FLASH_FIRST_PAGE_ADDRESS + 4);
-        Jump_To_App = (pFunction) JumpAdd;
-        __set_MSP(*(__IO uint32_t*) USER_FLASH_FIRST_PAGE_ADDRESS);
-        Jump_To_App();
-    }
-    else {
-        /* Флеш пустая, нечего загружать, висим в аварийном режиме */
-        fInvalidFw = 1;
-        PRINT_USART("\n\rFW empty. Started bootloader\n\r");
-    }
-  }
-  else if (bootTry == 1)
-  {
-    fBootFailed = 1;
-    PRINT_USART("\n\rFW boot failed. Started bootloader\n\r");
+  if (bootTry > 1) {
+      bootTry--;
+      RTC_WriteBackupRegister(RTC_BKP_DR2, bootTry);
+
+      /* Check if valid stack address (RAM address) then jump to user application */
+      if (CheckFWIsValid()) {
+          CLEAR_FWINVALID_FLAG();
+          /* Jump to user application */
+          JumpAdd = *(__IO uint32_t *) (USER_FLASH_FIRST_PAGE_ADDRESS + 4);
+          Jump_To_App = (pFunction) JumpAdd;
+          /* Initialize user application's Stack Pointer */
+          __set_MSP(*(__IO uint32_t *) USER_FLASH_FIRST_PAGE_ADDRESS);
+          Jump_To_App();
+      } else {
+          /* Флеш пустая, нечего загружать, висим в аварийном режиме */
+          SET_FWINVALID_FLAG();
+          printf("\r\nFW empty. Started bootloader\r\n");
+      }
+  } else if (bootTry == 1) {
+      fBootFailed = 1;
+      printf("\r\nFW boot failed. Started bootloader\r\n");
 
-    bootTry = 0;
-    loadMode = 1;
-    RTC_WriteBackupRegister(RTC_BKP_DR2, bootTry);
-    RTC_WriteBackupRegister(RTC_BKP_DR1, loadMode);
+      bootTry = 0;
+      loadMode = 1;
+      RTC_WriteBackupRegister(RTC_BKP_DR2, bootTry);
+      RTC_WriteBackupRegister(RTC_BKP_DR1, loadMode);
   }
 
   /* Флаг не установлен прыгаем на основную программу */
-  if (loadMode == 0)
-  {
-      printf("Run main FW\n\r");
-      //printf("*(__IO uint32_t*)(USER_FLASH_FIRST_PAGE_ADDRESS) = 0x%X\n\r", *(__IO uint32_t*)USER_FLASH_FIRST_PAGE_ADDRESS);
-      //printf("*(__IO uint32_t*)(USER_FLASH_FIRST_PAGE_ADDRESS + 4) = 0x%X\n\r", *(__IO uint32_t*)(USER_FLASH_FIRST_PAGE_ADDRESS + 4));
+  if (loadMode == 0) {
+      printf("Run main FW\r\n");
+      //printf("*(__IO uint32_t*)(USER_FLASH_FIRST_PAGE_ADDRESS) = 0x%X\r\n", *(__IO uint32_t*)USER_FLASH_FIRST_PAGE_ADDRESS);
+      //printf("*(__IO uint32_t*)(USER_FLASH_FIRST_PAGE_ADDRESS + 4) = 0x%X\r\n", *(__IO uint32_t*)(USER_FLASH_FIRST_PAGE_ADDRESS + 4));
 
       /* Set bootTry flag every time to ensure that
-       * IAP will starts again if FW is corrupted */
+        * IAP will starts again if FW is corrupted */
       bootTry = BOOT_TRY;
       RTC_WriteBackupRegister(RTC_BKP_DR2, bootTry);
 
-    /* Check if valid stack address (RAM address) then jump to user application */
-    if (((*(__IO uint32_t*)USER_FLASH_FIRST_PAGE_ADDRESS) & 0x2FFE0000 ) == 0x20000000)
-    {
-      /* Jump to user application */
-      JumpAdd = *(__IO uint32_t*) (USER_FLASH_FIRST_PAGE_ADDRESS + 4);
-      Jump_To_App = (pFunction) JumpAdd;
-      /* Initialize user application's Stack Pointer */
-      __set_MSP(*(__IO uint32_t*) USER_FLASH_FIRST_PAGE_ADDRESS);
-      Jump_To_App();
-    }
+      /* Check if valid stack address (RAM address) then jump to user application */
+      if (CheckFWIsValid()) {
+          CLEAR_FWINVALID_FLAG();
+          /* Jump to user application */
+          JumpAdd = *(__IO uint32_t *) (USER_FLASH_FIRST_PAGE_ADDRESS + 4);
+          Jump_To_App = (pFunction) JumpAdd;
+          /* Initialize user application's Stack Pointer */
+          __set_MSP(*(__IO uint32_t *) USER_FLASH_FIRST_PAGE_ADDRESS);
+          Jump_To_App();
+      }
   }
   
   /* Загружается Bootloader... */
   
   SysTick_Config(168000);//120000
   LED_Init();
-
+  LED_On(LED_INIT_ERR);
 
   PRINT_USART("\n\rBootloader starting...   \n\r");
 
-  LED_On(LED_INIT_ERR);
+#ifdef LCD_ENABLE
+    LCD_Init();
+    LCD_PrintAligned(1, alignCENTER, "Обновление ПО");
+#endif
+
+  /* Random number generator */
+  RNG_Init();
 
   ETH_BSP_Config();
   LwIP_Init();
@@ -147,76 +206,122 @@ void main(void)
   CRC_Init();
 
   //Если нажата DEF начинаем обновление с sd
-  if (IO_BtnDefaultPressed()) 
-  {
-//	IAPviaETH = false;
-//    timer_AddFunction(500, &LED_Blinky_Yellow);
-//    SD_NVIC_Init();
+  if (IO_BtnDefaultPressed()) {
+#ifdef SD_ENABLE
+      IAPviaETH = false;
+      timer_AddFunction(500, &LED_Blinky_Red);
+      SD_NVIC_Init();
+#endif
   } else {
-    IAPviaETH = true;
-    timer_AddFunction(500, &LED_Blinky_Red);
+      IAPviaETH = true;
+      timer_AddFunction(500, &LED_Blinky_Red);
   }
   
   /* Check if valid stack address (RAM address) */
-  if (((*(__IO uint32_t*)USER_FLASH_FIRST_PAGE_ADDRESS) & 0x2FFE0000 ) == 0x20000000) {
+  if (((*(__IO uint32_t *)USER_FLASH_FIRST_PAGE_ADDRESS) & 0x2FFE0000 ) == 0x20000000 && !fInvalidFw) {
+#if UPDATE_TIMEOUT != 0
       timer_AddFunction(1000, &UpdateTimeout_Handler);
-  }
-  else {
+#endif
+  } else {
+      update_timeout = 0;
       /* Флеш пустая, нечего загружать, висим в аварийном режиме */
-      fInvalidFw = 1;
+      SET_FWINVALID_FLAG();
+#ifdef LCD_ENABLE
+      LCD_PrintAligned(5, alignCENTER, "Аварийный режим");
+      LCD_PrintAligned(7, alignCENTER, "Ошибка ПО");
+#endif
   }
 
+  if (fBootFailed) {
+#ifdef LCD_ENABLE
+      LCD_PrintAligned(5, alignCENTER, "Аварийный режим");
+      LCD_ClearRow(7);
+#endif
+  }
 
   while (1)
   {
-    timer_Main();
-
-    if (IAPviaETH) { // Обновление по ETH
-      /* check if any packet received */
-      if (ETH_CheckFrameReceived())
-      { 
-        /* process received ethernet packet */
-        LwIP_Pkt_Handle();
+      timer_Main();
+
+      if (IAPviaETH) { // Обновление по ETH
+          /* Handle received packets and periodic timers for LwIP */
+          LwIP_Periodic_Handle(0);
+
+          if (fDoneReset) {
+              resetCounter++;
+              if (resetCounter > 400000) {
+                  loadMode = 0;
+                  bootTry = BOOT_TRY;
+                  RTC_WriteBackupRegister(RTC_BKP_DR1, loadMode);
+                  RTC_WriteBackupRegister(RTC_BKP_DR2, bootTry);
+
+                  SET_FWUPDATED_FLAG();
+                  CLEAR_FWINVALID_FLAG();
+#ifdef SLAVEBRD_ENABLE
+                  /* Reboot daughter board */
+                  stmReboot();
+#endif
+                  /* Self reboot */
+                  NVIC_SystemReset();
+              }
+          }
+      } else {
+#ifdef SD_ENABLE
+          // Обновление с SD
+          // Пробуем смонтировать SD
+          if (mmc_mount()) {
+              // Пробуем открыть файл с именем FW_FILE_NAME
+              timer_Stop(&LED_Blinky_Yellow);
+              LED_Off(RED1_INT);
+              LED_Off(GREEN_INT);
+              if (f_open(&fil_obj, FW_FILE_NAME, FA_READ | FA_OPEN_EXISTING) == FR_OK) {  // открываем файл
+                  LED_On(GREEN_INT);
+
+                  if (startFlashing() < 0) {
+                      Error_Handler();
+                  }
+                  f_close(&fil_obj);
+
+                  // CRC посчитанная при сборке и записанная в последине 4 байта прошивки
+                  // Должна сойтись с CRC посчитанной на контроллере
+                  if (CRC_Read() == CRC_Calculate()) {
+                      loadMode = 0;
+                      bootTry = BOOT_TRY;
+                      RTC_WriteBackupRegister(RTC_BKP_DR1, loadMode);
+                      RTC_WriteBackupRegister(RTC_BKP_DR2, bootTry);
+#ifdef SLAVEBRD_ENABLE
+                      /* Reboot daughter board */
+                      stmReboot();
+#endif
+                      /* Self reboot */
+                      NVIC_SystemReset();
+                  } else {
+                      Error_Handler();
+                  }
+
+              } else { // Файл не найден
+                  LED_On(RED1_INT);
+              }
+          }
+#endif
       }
-      /* handle periodic timers for LwIP */
-      LwIP_Periodic_Handle(LocalTime);
-	
-	  if (fDoneReset)
-	  {
-		resetCounter++;
-	    if (resetCounter > 100000)
-		{  
-	      loadMode = 0;
-	      bootTry = BOOT_TRY;
-	      RTC_WriteBackupRegister(RTC_BKP_DR1, loadMode);
-	      RTC_WriteBackupRegister(RTC_BKP_DR2, bootTry);
-	      /* Set FW update flag */
-	      RTC_WriteBackupRegister(RTC_BKP_DR3, 1);
-
-          NVIC_SystemReset();
-		}  
-	  }	
-	  if (fErrorReset)
-	  {
-		resetCounter++;
-	    if (resetCounter > 100000) {
-          NVIC_SystemReset();
-	    }
-	  }
-    }
 
-    //Если нажата DEF переходим в основную прошивку
-    if (IO_BtnDefaultPressed() || UpdateTimeoutFlag)
-    {
-      if (!fUpload && ((*(__IO uint32_t*)USER_FLASH_FIRST_PAGE_ADDRESS) != 0xFFFFFFFF)) {
-          PRINT_USART("\n\rUpdate timeout... Return to main FW\n\r");
-          loadMode = 0;
-          bootTry = BOOT_TRY;
-          RTC_WriteBackupRegister(RTC_BKP_DR1, loadMode);
-          RTC_WriteBackupRegister(RTC_BKP_DR2, bootTry);
-		  NVIC_SystemReset();
+      //Если нажата DEF переходим в основную прошивку
+      if (IO_BtnDefaultPressed() || UpdateTimeoutFlag) {
+          if (!fUpload && ((*(__IO uint32_t *)USER_FLASH_FIRST_PAGE_ADDRESS) != 0xFFFFFFFF)) {
+              printf("\r\nUpdate timeout... Return to main FW\r\n");
+              loadMode = 0;
+              bootTry = BOOT_TRY;
+              RTC_WriteBackupRegister(RTC_BKP_DR1, loadMode);
+              RTC_WriteBackupRegister(RTC_BKP_DR2, bootTry);
+#ifdef SLAVEBRD_ENABLE
+              /* Reboot daughter board */
+              stmReboot();
+#endif
+              /* Self reboot */
+              NVIC_SystemReset();
+          }
       }
-    }
   }
 }
 
@@ -257,15 +362,19 @@ void Error_Handler(void) {
   */
 void UpdateTimeout_Handler(void)
 {
-    static char lcdbuf[32] = {0};
-    static uint8_t time = UPDATE_TIMEOUT;
-
-    if ((fUpload) || (fInvalidFw)) return;
+    if ((fUpload) || (fInvalidFw) || (fDoneReset)) {
+        return;
+    }
 
-    if (time == 0) {
+    if (update_timeout == 0) {
         UpdateTimeoutFlag = true;
-    }
-    else {
-        time--;
+    } else {
+#ifdef LCD_ENABLE
+        static char lcdbuf[32] = {0};
+        sprintf(lcdbuf, "Ожидание (%d) ", update_timeout);
+        LCD_ClearRow(7);
+        LCD_PrintAligned(7, alignCENTER, lcdbuf);
+#endif
+        update_timeout--;
     }
 }

+ 633 - 0
iap/lcd/drivers/jlx12864.c

@@ -0,0 +1,633 @@
+#include <stdbool.h>
+#include <stdint.h>
+#include "fonts.h"
+#include "jlx12864.h"
+#include "pins.h"
+
+/* LCD type: 0 - uc1701, 1 - ssd1306 */
+uint8_t lcd_type = 0;
+
+void init_uc1701(void);
+void init_ssd1306(void);
+void ssd1306_off();
+void ssd1306_on();
+
+void delay(int i) 
+{ 
+      volatile int j,k; 
+      for(j=0;j<i;j++) 
+            for(k=0;k<110;k++); 
+} 
+
+void transfer_command(int data) { 
+	uint8_t i; 
+	gpio_set(OLED_CS, 0);
+	gpio_set(OLED_CD, 0);
+	for(i=0;i<8;i++) {
+		gpio_set(OLED_SCK, 0);	
+		if (data & 0x80)
+			gpio_set(OLED_MOSI, 1);	
+		else
+		    gpio_set(OLED_MOSI, 0);
+		gpio_set(OLED_SCK, 1);
+        	data <<= 1;     
+	}
+	gpio_set(OLED_CS, 1);
+} 
+
+void transfer_data(int data) { 
+	uint8_t i; 
+	gpio_set(OLED_CS, 0);
+	gpio_set(OLED_CD, 1);
+	for(i=0;i<8;i++) {
+		gpio_set(OLED_SCK, 0);	 
+		if (data & 0x80)
+			gpio_set(OLED_MOSI, 1);
+		else   
+			gpio_set(OLED_MOSI, 0);
+		gpio_set(OLED_SCK, 1);
+		data <<= 1;     
+	} 
+	gpio_set(OLED_CS, 1);
+} 
+
+void init_lcd() {
+    /* Detect LCD type */
+    if (gpio_get(OLED_TYPE)) {
+        gpio_set(LCD_BKLGHT, true);
+        init_uc1701();
+        lcd_type = 0;
+    }
+    else {
+        init_ssd1306();
+        lcd_type = 1;
+    }
+}
+
+void init_uc1701()
+{
+	gpio_set(OLED_CS, 0);
+	gpio_set(OLED_RST, 0);
+	delay(500);
+	gpio_set(OLED_RST, 1);
+	delay(200);
+	transfer_command(0xe2);
+	transfer_command(0x2c);
+	transfer_command(0x2e);
+	transfer_command(0x2f);
+	transfer_command(0x24);
+	transfer_command(0x81);
+	transfer_command(0x1a);
+    transfer_command(0xa2);
+	transfer_command(0xc8);
+	transfer_command(0xa0);
+	transfer_command(0x40);
+	transfer_command(0xaf);
+	gpio_set(OLED_CS, 1);
+}
+
+void init_ssd1306()
+{
+    gpio_set(OLED_CS, 0);
+    gpio_set(OLED_RST, 0);
+    delay(500);
+    gpio_set(OLED_RST, 1);
+    delay(200);
+
+    /* Display off */
+    transfer_command(0xAE);
+
+    /* Set lower column address */
+    transfer_command(0x02);
+    /* Set higher column address */
+    transfer_command(0x10);
+
+    /* Set display start line */
+    transfer_command(0x40);
+
+    /* Set page address */
+    transfer_command(0xB0);
+
+    /* Set contrast value 0xFF */
+    transfer_command(0x81);
+    transfer_command(0xFF);
+
+    /* Normal / reverse */
+    transfer_command(0xA6);
+
+    /* Multiplex ratio */
+    transfer_command(0xA8);
+    /* duty = 1/64 */
+    transfer_command(0x3F);
+
+    /* Set charge pump enable */
+    transfer_command(0xAD);
+    transfer_command(0x8B);
+
+    /* Set Vpp 9V (0x30 - 0x33) */
+    transfer_command(0x33);
+
+//    /* Set segment remap */
+//    transfer_command(0xA1);
+//    /* COM scan direction */
+//    transfer_command(0xC8);
+
+    /* Rotate display 180 degree*/
+    /* Set segment remap */
+    transfer_command(0xA0);
+    /* COM scan direction */
+    transfer_command(0xC0);
+
+    /* Set display offset */
+    transfer_command(0xD3);
+    transfer_command(0x00);
+
+    /* Set osc divider */
+    transfer_command(0xD5);
+    transfer_command(0x80);
+
+    /* Set precharge period */
+    transfer_command(0xD9);
+    transfer_command(0x1F);
+
+    /* setc COM pins */
+    transfer_command(0xDA);
+    transfer_command(0x12);
+
+    /* Set vcomh */
+    transfer_command(0xDB);
+    transfer_command(0x40);
+
+    /* Display on */
+    transfer_command(0xAF);
+    gpio_set(OLED_CS, 1);
+}
+
+void ssd1306_off() {
+        transfer_command(0xAE);
+}
+
+void ssd1306_on() {
+        transfer_command(0xAF);
+}
+
+void lcd_address(uint8_t page, uint8_t column) {
+	gpio_set(OLED_CS, 0);
+
+	/* Specify different column offset for lcd types */
+	if (lcd_type == 0) {
+		column=column-1;
+	}
+	else {
+	    column=column+1;
+	}
+    page=page-1; 
+    
+	transfer_command(0xb0 + page);                
+	transfer_command(((column >> 4) & 0x0f) + 0x10);
+	transfer_command(column & 0x0f);  
+} 
+
+void clear_screen(void) {
+	uint8_t i,j; 
+	gpio_set(OLED_CS, 0);
+	for(j=0;j<8;j++) {
+		lcd_address(1 + j,1); 
+		for(i=0;i<132;i++) { 
+			transfer_data(0x00); 
+		} 
+	} 
+} 
+
+//full display test 
+void full_display(uint8_t data1, uint8_t data2) { 
+	int i,j; 
+	for(i=0; i<8; i++) { 
+		lcd_address(i+1,1); 
+		for(j=0;j<64;j++) { 
+			transfer_data(data1); 
+			transfer_data(data2);                        
+		} 
+      }           
+} 
+
+void test_box(void) { 
+	int i,j; 
+	lcd_address(1,1); 
+	transfer_data(0xff); 
+	for(i=1;i<127;i++) { 
+		transfer_data(0x01);   
+	} 
+	transfer_data(0xff); 	
+	lcd_address(2,1); 
+	transfer_data(0xff); 
+	for(i=1;i<127;i++) { 
+		transfer_data(0x80);   
+	} 
+	transfer_data(0xff); 
+	lcd_address(3,1); 
+	transfer_data(0xff); 
+	for(i=1;i<127;i++) { 
+		transfer_data(0x01);   
+	} 
+	transfer_data(0xff);  
+	for(j=4;j<=7;j++) {
+		lcd_address(j,1); 
+		transfer_data(0xff); 
+		for(i=1;i<127;i++) { 
+			transfer_data(0x00);   
+		}	 
+		transfer_data(0xff);   
+	} 
+	lcd_address(8,1); 
+	transfer_data(0xff); 
+	for(i=1;i<127;i++) { 
+		transfer_data(0x80);   
+	} 
+	transfer_data(0xff); 
+} 
+
+void test(void) { 
+	full_display(0xff,0xff);           
+	delay(1000);
+	full_display(0x55,0x55); 
+	delay(1000);
+	full_display(0xaa,0xaa); 
+	delay(1000);
+	full_display(0xff,0x00);
+	delay(1000);
+	full_display(0x00,0xff); 
+	delay(1000);
+	full_display(0x55,0xaa); 
+	delay(1000);
+	full_display(0xaa,0x55);     
+	delay(1000);
+	test_box(); 
+	delay(1000);
+} 
+
+/*
+void display_128x64(uchar *dp) { 
+	uint i,j; 
+	for(j=0;j<8;j++) { 
+		lcd_address(j+1,1); 
+		for (i=0;i<128;i++) {  
+			transfer_data(*dp); 
+			dp++;
+  		} 
+ 	} 
+} 
+
+void display_128x16(uchar page,uchar column,uchar *dp) { 
+	uint i,j; 
+	for(j=0;j<2;j++) { 
+		lcd_address(page+j,column); 
+		for (i=0;i<128;i++) {  
+			transfer_data(*dp); 
+			dp++; 
+		} 
+	} 
+} 
+
+
+void display_graphic_32x32(uchar page,uchar column,uchar *dp) { 
+	uchar i,j; 
+	for(j=0;j<4;j++) { 
+		lcd_address(page+j,column); 
+		for (i=0;i<32;i++) {  
+			transfer_data(*dp);         
+			dp++; 
+		} 
+	} 
+} 
+
+void display_graphic_16x16(uchar page,uchar column,uchar *dp) { 
+	uchar i,j; 
+	for(j=0;j<2;j++) { 
+		lcd_address(page+j,column); 
+		for (i=0;i<16;i++) { 
+		transfer_data(*dp);         
+		dp++; 
+		} 
+	} 
+} 
+
+
+
+void display_graphic_8x16(uchar page,uchar column,uchar *dp) { 
+	uchar i,j; 
+	for(j=0;j<2;j++) { 
+		lcd_address(page+j,column); 
+		for (i=0;i<8;i++) {  
+			transfer_data(*dp); 
+			dp++; 
+		} 
+	} 
+} 
+
+void display_string_8x16(uint page,uint column,uchar *text) 
+{ 
+      uint i=0,j,k,n; 
+      if(column>123) 
+ { 
+            column=1; 
+            page+=2; 
+ } 
+      while(text[i]>0x00) 
+ {  
+            if((text[i]>=0x20)&&(text[i]<=0x7e)) 
+  { 
+                  j=text[i]-0x20; 
+                  for(n=0;n<2;n++) 
+   {    
+lcd_address(page+n,column); 
+for(k=0;k<8;k++) 
+    {      
+transfer_data(ascii_table_8x16[j][k+8*n]);  
+
+    } 
+   } 
+                  i++; 
+                  column+=8; 
+  } 
+  else 
+            i++; 
+ } 
+} 
+*/
+
+void display_string_5x8(uint8_t page, uint8_t column, uint8_t reverse, char *text) {
+	uint8_t i=0,k,disp_data;
+	uint16_t j;
+	bool is_symbol = 0;
+
+	while (text[i] > 0x00) {
+		if ((text[i] == 0xd0) || (text[i] == 0xd1))
+			is_symbol = 1;
+		if ((text[i] >= 0x20) && (text[i] <= 0x7e))
+		      is_symbol = 1;
+  		if (is_symbol) {
+			if (text[i] == 0xd0)
+			{
+				i++;
+				if(text[i] >= 0x90 && text[i] <= 0xbf)
+					j = 6*(text[i] - 0x90);
+				else
+					j = 6*65;
+			}
+			else if(text[i] == 0xd1)
+			{
+				i++;
+				if((text[i] >= 0x80 && text[i] <= 0x8f))
+					j = 6*(text[i] - 0x50);
+				else
+					j = 6*64;
+			}
+			else
+				j = 6*(text[i]-0x20);
+			lcd_address(page,column);
+			for(k=0;k<6;k++) {
+				if (reverse==1) { 
+					if(text[i]>=0x80 || text[i]==0x01)//((text[i]<0x20)||(text[i]>0x7f))
+						disp_data = ~cyrillic_font5x8[j + k];
+					else
+						disp_data = ~Fonts5x8[j + k];
+				}
+				else { 
+					if(text[i]>=0x80 || text[i]==0x01)//((text[i]<0x20)||(text[i]>0x7f))
+						disp_data = cyrillic_font5x8[j + k];
+					else
+						disp_data = Fonts5x8[j + k];
+				} 
+				transfer_data(disp_data);
+			} 
+			i++; 
+			column+=6; 
+			if(column>123) { 
+				column=1; 
+				page++; 
+			} 
+		} 
+		else i++; 
+	} 
+} 
+
+
+
+
+/*
+void display_string_16x16(uchar page,uchar column,uchar *text) 
+{ 
+    uchar i,j,k; 
+    uint address;  
+    j = 0; 
+    while(text[j] != '\0') 
+    { 
+        i = 0; 
+        address = 1; 
+        while(Chinese_text_16x16[i] > 0x7e)    
+
+        { 
+            if(Chinese_text_16x16[i] == text[j]) 
+            { 
+                if(Chinese_text_16x16[i + 1] == text[j + 1]) 
+                { 
+                    address = i * 16; 
+                    break; 
+                } 
+            } 
+            i += 2;             
+        } 
+        if(column > 113) 
+        { 
+            column = 0; 
+            page += 2; 
+        } 
+        if(address != 1)
+        { 
+                  for(k=0;k<2;k++) 
+   { 
+lcd_address(page+k,column); 
+                  for(i = 0; i < 16; i++)                
+                  { 
+                      transfer_data(Chinese_code_16x16[address]);   
+                      address++; 
+                  } 
+                  } 
+            j += 2;         
+        } 
+        else           
+        { 
+                  for(k=0;k<2;k++) 
+   { 
+lcd_address(page+k,column)
+
+
+for(i = 0; i < 16; i++)
+                  { 
+                      transfer_data(0x00);    
+                  } 
+                  } 
+            j++; 
+        } 
+        column+=16; 
+    } 
+} 
+
+
+void disp_string_8x16_16x16(uchar page,uchar column,uchar *text
+) 
+{ 
+    uchar temp[3]; 
+    uchar i = 0;     
+    while(text[i] != '\0') 
+    { 
+        if(text[i] > 0x7e) 
+        { 
+            temp[0] = text[i]; 
+            temp[1] = text[i + 1]; 
+            temp[2] = '\0';          
+            display_string_16x16(page,column,temp);  
+            column += 16; 
+            i += 2; 
+        } 
+        else 
+        { 
+            temp[0] = text[i];     
+            temp[1] = '\0';          
+            display_string_8x16(page, column, temp);  
+            column += 8; 
+            i++; 
+        } 
+    } 
+} 
+//显示镜像        
+void display_mirror() 
+{ 
+      clear_screen(); 
+      disp_string_8x16_16x16(1,1,"  左右上下镜像: "); 
+      delay(7000); 
+      clear_screen(); 
+      display_128x64(bmp12864_4); 
+//          delay(7000); 
+            waitkey(); 
+      transfer_command(0xc8);           
+      transfer_command(0xa0);          
+      clear_screen(); 
+      display_128x64(bmp12864_4); 
+//          delay(7000); 
+            waitkey(); 
+      transfer_command(0xc0);         
+      transfer_command(0xa1);        
+      clear_screen(); 
+      display_128x64(bmp12864_4); 
+//          delay(7000); 
+            waitkey(); 
+      transfer_command(0xc0);       
+      transfer_command(0xa0);      
+      clear_screen(); 
+      display_128x64(bmp12864_4); 
+//          delay(7000); 
+            waitkey(); 
+      transfer_command(0xc8);     
+      transfer_command(0xa1);    
+} 
+//对比度调节 
+void contrast_control() 
+{ 
+            clear_screen(); 
+            disp_string_8x16_16x16(1,1,"软件调节亮度:"); 
+            display_string_8x16(4,52,"0xcf"); 
+            display_128x16(7,1,bmp12816_1); 
+            display_graphic_16x16(7,1+16*4,bmp16x16_1); 
+//          delay(7000); 
+            waitkey(); 
+            
+            transfer_command(0x81); 
+            transfer_command(0xdf); 
+            display_string_8x16(4,52,"0xdf"); 
+            display_128x16(7,1,bmp12816_1); 
+            display_graphic_16x16(7,1+16*5,bmp16x16_1); 
+//          delay(7000); 
+            waitkey(); 
+            transfer_command(0x81); 
+            transfer_command(0xef); 
+            display_string_8x16(4,52,"0xef"); 
+            display_128x16(7,1,bmp12816_1); 
+            display_graphic_16x16(7,1+16*6,bmp16x16_1); 
+//          delay(7000); 
+            waitkey(); 
+            transfer_command(0x81); 
+            transfer_command(0xff); 
+            display_string_8x16(4,52,"0xff"); 
+            display_128x16(7,1,bmp12816_1); 
+            display_graphic_16x16(7,1+16*7,bmp16x16_1); 
+//          delay(7000); 
+            waitkey(); 
+            transfer_command(0x81); 
+            transfer_command(0x00); 
+            display_string_8x16(4,52,"0x00"); 
+            display_128x16(7,1,bmp12816_1); 
+            display_graphic_16x16(7,1+16*0,bmp16x16_1); 
+//          delay(7000); 
+            waitkey(); 
+            transfer_command(0x81); 
+            transfer_command(0x5f); 
+            display_string_8x16(4,52,"0x5f"); 
+            display_128x16(7,1,bmp12816_1); 
+            display_graphic_16x16(7,1+16*1,bmp16x16_1); 
+//          delay(7000); 
+            waitkey(); 
+            transfer_command(0x81); 
+            transfer_command(0xcf); 
+            display_string_8x16(4,52,"0xcf"); 
+            display_128x16(7,1,bmp12816_1); 
+            display_graphic_16x16(7,1+16*4,bmp16x16_1); 
+//          delay(7000); 
+            waitkey(); 
+            } 
+void main(void) 
+{  
+      while(1) {
+        initial_lcd(); 
+
+        clear_screen(); 
+
+
+
+	display_string_5x8(1,1,0,"{(5x8dot ASCII char)}");
+
+        display_string_5x8(2,1,0,"{[(<~!@#$%^&*_+=?>)]}"); 
+        disp_string_8x16_16x16(3,1,"标准 16x16dot 汉字");        
+
+
+	display_graphic_32x32 (5,1+32*0,jing1);  
+        display_graphic_32x32 (5,1+32*1,lian1); 
+        display_graphic_32x32 (5,1+32*2,xun1);                                     
+        disp_string_8x16_16x16(5,1+32*3,"JLX:"); 
+        disp_string_8x16_16x16(7,1+32*3,"OLED"); 
+        waitkey(); 
+        clear_screen();     
+//clear all dots 
+        display_string_5x8(1,1,1,"012345678901234567890");
+        display_string_5x8(1,1,1,"        MENU         "); 
+
+        display_string_5x8(3,1,0,"Select>>>>"); 
+        display_string_5x8(3,64,1,"1.Graphic ");         
+        display_string_5x8(4,64,0,"2.Chinese " ); 
+        display_string_5x8(5,64,0,"3.Movie   "); 
+	display_string_5x8(6,64,0,"4.Contrast"); 
+	display_string_5x8(7,64,0,"5.Mirror  "); 
+	display_string_5x8(8,1,1,"PRE  USER   DEL   NEW"); 
+        display_string_5x8(8,19,0," "); 
+        display_string_5x8(8,65,0," "); 
+        display_string_5x8(8,97,0," "); 
+        waitkey(); 
+        contrast_control(); 
+        waitkey(); 
+        display_mirror(); 
+	waitkey(); 
+	test(); 
+    }
+}
+*/

+ 22 - 0
iap/lcd/drivers/jlx12864.h

@@ -0,0 +1,22 @@
+#ifndef JLX12864_H
+#define JLX12864_H
+
+#define pgm_read_byte(b) (*(unsigned char *)(b))
+
+/* LCD type: 0 - uc1701, 1 - ssd1306 */
+uint8_t lcd_type;
+
+void delay(int i);
+void transfer_command(int data);
+void transfer_data(int data);
+void init_lcd(void);
+void lcd_bcklight_on(void);
+void lcd_bcklight_off(void);
+void lcd_address(uint8_t page, uint8_t column);
+void clear_screen(void);
+void full_display(uint8_t data1, uint8_t data2);
+void test_box(void);
+void display_string_5x8(uint8_t page, uint8_t column, uint8_t reverse, char *text);
+void test(void);
+
+#endif

+ 182 - 0
iap/lcd/fonts.h

@@ -0,0 +1,182 @@
+#ifndef FONTS_H
+#define FONTS_H
+
+/********************************************************************************
+ *                                                                              *
+ *  Funktion:      Fonts5x8                                                     *
+ *                                                                              *
+ *------------------------------------------------------------------------------*
+ *  Description:  Zeichensatz 5 x 8 Zeichen
+ *                ascii zeichen 0x20 bis 0x7F
+ *                linkes byte ist linker Pixelstreifen
+ ********************************************************************************/
+const unsigned char Fonts5x8[]  = {
+0x00,0x00,0x00,0x00,0x00,0x00,     //
+0x00,0x00,0x00,0x4F,0x00,0x00,     // !
+0x00,0x00,0x07,0x00,0x07,0x00,     // "
+0x00,0x14,0x7F,0x14,0x7F,0x14,     // #
+0x00,0x24,0x2A,0x7F,0x2A,0x12,     // $
+0x00,0x23,0x13,0x08,0x64,0x62,     // %
+0x00,0x36,0x49,0x55,0x22,0x50,     // &
+0x00,0x00,0x05,0x03,0x00,0x00,     // '
+0x00,0x00,0x1C,0x22,0x41,0x00,     // (
+0x00,0x00,0x41,0x22,0x1C,0x00,     // )
+0x00,0x14,0x08,0x3E,0x08,0x14,     // *
+0x00,0x08,0x08,0x3E,0x08,0x08,     // +
+0x00,0x00,0x50,0x30,0x00,0x00,     // ,
+0x00,0x08,0x08,0x08,0x08,0x08,     // -
+0x00,0x00,0x60,0x60,0x00,0x00,     // .
+0x00,0x20,0x10,0x08,0x04,0x02,     // /
+0x00,0x3E,0x51,0x49,0x45,0x3E,     // 0
+0x00,0x00,0x42,0x7F,0x40,0x00,     // 1
+0x00,0x42,0x61,0x51,0x49,0x46,     // 2
+0x00,0x21,0x41,0x45,0x4B,0x31,     // 3
+0x00,0x18,0x14,0x12,0x7F,0x10,     // 4
+0x00,0x27,0x45,0x45,0x45,0x39,     // 5
+0x00,0x3C,0x4A,0x49,0x49,0x30,     // 6
+0x00,0x01,0x71,0x09,0x05,0x03,     // 7
+0x00,0x36,0x49,0x49,0x49,0x36,     // 8
+0x00,0x06,0x49,0x49,0x29,0x1E,     // 9
+0x00,0x00,0x36,0x36,0x00,0x00,     // :
+0x00,0x00,0x56,0x36,0x00,0x00,     // ;
+0x00,0x08,0x14,0x22,0x41,0x00,     // <
+0x00,0x14,0x14,0x14,0x14,0x14,     // =
+0x00,0x00,0x41,0x22,0x14,0x08,     // >
+0x00,0x02,0x01,0x51,0x09,0x06,     // ?
+0x00,0x32,0x49,0x79,0x41,0x3E,     // @
+0x00,0x7E,0x11,0x11,0x11,0x7E,     // A
+0x00,0x7F,0x49,0x49,0x49,0x36,     // B
+0x00,0x3E,0x41,0x41,0x41,0x22,     // C
+0x00,0x7F,0x41,0x41,0x22,0x1C,     // D
+0x00,0x7F,0x49,0x49,0x49,0x41,     // E
+0x00,0x7F,0x09,0x09,0x09,0x01,     // F
+0x00,0x3E,0x41,0x49,0x49,0x7A,     // G
+0x00,0x7F,0x08,0x08,0x08,0x7F,     // H
+0x00,0x00,0x41,0x7F,0x41,0x00,     // I
+0x00,0x20,0x40,0x41,0x3F,0x01,     // J
+0x00,0x7F,0x08,0x14,0x22,0x41,     // K
+0x00,0x7F,0x40,0x40,0x40,0x40,     // L
+0x00,0x7F,0x02,0x0C,0x02,0x7F,     // M
+0x00,0x7F,0x04,0x08,0x10,0x7F,     // N
+0x00,0x3E,0x41,0x41,0x41,0x3E,     // O
+0x00,0x7F,0x09,0x09,0x09,0x06,     // P
+0x00,0x3E,0x41,0x51,0x21,0x5E,     // Q
+0x00,0x7F,0x09,0x19,0x29,0x46,     // R
+0x00,0x46,0x49,0x49,0x49,0x31,     // S
+0x00,0x01,0x01,0x7F,0x01,0x01,     // T
+0x00,0x3F,0x40,0x40,0x40,0x3F,     // U
+0x00,0x1F,0x20,0x40,0x20,0x1F,     // V
+0x00,0x3F,0x40,0x38,0x40,0x3F,     // W
+0x00,0x63,0x14,0x08,0x14,0x63,     // X
+0x00,0x07,0x08,0x70,0x08,0x07,     // Y
+0x00,0x61,0x51,0x49,0x45,0x43,     // Z
+0x00,0x00,0x7F,0x41,0x41,0x00,     // [
+0x00,0x15,0x16,0x7C,0x16,0x15,     // slash
+0x00,0x00,0x41,0x41,0x7F,0x00,     // ]
+0x00,0x04,0x02,0x01,0x02,0x04,     // ^
+0x00,0x40,0x40,0x40,0x40,0x40,     // _
+0x00,0x00,0x01,0x02,0x04,0x00,     // `
+0x00,0x20,0x54,0x54,0x54,0x78,     // a
+0x00,0x7F,0x48,0x44,0x44,0x38,     // b
+0x00,0x38,0x44,0x44,0x44,0x20,     // c
+0x00,0x38,0x44,0x44,0x48,0x7F,     // d
+0x00,0x38,0x54,0x54,0x54,0x18,     // e
+0x00,0x08,0x7E,0x09,0x01,0x02,     // f
+0x00,0x0C,0x52,0x52,0x52,0x3E,     // g
+0x00,0x7F,0x08,0x04,0x04,0x78,     // h
+0x00,0x00,0x44,0x7D,0x40,0x00,     // i
+0x00,0x20,0x40,0x44,0x3D,0x00,     // j
+0x00,0x7F,0x10,0x28,0x44,0x00,     // k
+0x00,0x00,0x41,0x7F,0x40,0x00,     // l
+0x00,0x7C,0x04,0x18,0x04,0x78,     // m
+0x00,0x7C,0x08,0x04,0x04,0x78,     // n
+0x00,0x38,0x44,0x44,0x44,0x38,     // o
+0x00,0x7C,0x14,0x14,0x14,0x08,     // p
+0x00,0x08,0x14,0x14,0x18,0x7C,     // q
+0x00,0x7C,0x08,0x04,0x04,0x08,     // r
+0x00,0x48,0x54,0x54,0x54,0x20,     // s
+0x00,0x04,0x3F,0x44,0x40,0x20,     // t
+0x00,0x3C,0x40,0x40,0x20,0x7C,     // u
+0x00,0x1C,0x20,0x40,0x20,0x1C,     // v
+0x00,0x3C,0x40,0x30,0x40,0x3C,     // w
+0x00,0x44,0x28,0x10,0x28,0x44,     // x
+0x00,0x0C,0x50,0x50,0x50,0x3C,     // y
+0x00,0x44,0x64,0x54,0x4C,0x44,     // z
+0x00,0x00,0x08,0x36,0x41,0x00,     // {
+0x00,0x00,0x00,0x7F,0x00,0x00,     // |
+0x00,0x00,0x41,0x36,0x08,0x00,     // }
+0x00,0x08,0x08,0x2A,0x1C,0x08,     // pfeil rechts
+0x00,0x08,0x1C,0x2A,0x08,0x08,     // pfeil links
+};
+
+const unsigned char  cyrillic_font5x8[] = {
+// 0xc0 è äàëåå ŵóññêèå (0x60-0xa0)
+0x00,0x7e,0x11,0x11,0x11,0x7e, //A 192
+0x00,0x7f,0x49,0x49,0x49,0x33, //Á
+0x00,0x7f,0x49,0x49,0x49,0x36, //Â
+0x00,0x7f,0x01,0x01,0x01,0x03, //Ã
+0x00,0xe0,0x51,0x4f,0x41,0xff, //Ä
+0x00,0x7f,0x49,0x49,0x49,0x41, //E
+0x00,0x77,0x08,0x7f,0x08,0x77, //Æ
+0x00,0x41,0x49,0x49,0x49,0x36, //Ç
+0x00,0x7f,0x10,0x08,0x04,0x7f, //È
+0x00,0x7c,0x21,0x12,0x09,0x7c, //É
+0x00,0x7f,0x08,0x14,0x22,0x41, //K
+0x00,0x20,0x41,0x3f,0x01,0x7f, //Ë
+0x00,0x7f,0x02,0x0c,0x02,0x7f, //M
+0x00,0x7f,0x08,0x08,0x08,0x7f, //H
+0x00,0x3e,0x41,0x41,0x41,0x3e, //O
+0x00,0x7f,0x01,0x01,0x01,0x7f, //Ï
+0x00,0x7f,0x09,0x09,0x09,0x06, //P
+0x00,0x3e,0x41,0x41,0x41,0x22, //C
+0x00,0x01,0x01,0x7f,0x01,0x01, //T
+0x00,0x47,0x28,0x10,0x08,0x07, //Ó
+0x00,0x1c,0x22,0x7f,0x22,0x1c, //Ô
+0x00,0x63,0x14,0x08,0x14,0x63, //X
+0x00,0x7f,0x40,0x40,0x40,0xff, //Ö
+0x00,0x07,0x08,0x08,0x08,0x7f, //Ṫ
+0x00,0x7f,0x40,0x7f,0x40,0x7f, //Ø
+0x00,0x7f,0x40,0x7f,0x40,0xff, //Ù
+0x00,0x01,0x7f,0x48,0x48,0x30, //Ú
+0x00,0x7f,0x48,0x30,0x00,0x7f, //Û
+0x00,0x00,0x7f,0x48,0x48,0x30, //Ý
+0x00,0x22,0x41,0x49,0x49,0x3e, //Ü
+0x00,0x7f,0x08,0x3e,0x41,0x3e, //Ŷ
+0x00,0x46,0x29,0x19,0x09,0x7f, //ß
+0x00,0x20,0x54,0x54,0x54,0x78, //a
+0x00,0x3c,0x4a,0x4a,0x49,0x31, //á
+0x00,0x7c,0x54,0x54,0x28,0x00, //â
+0x00,0x7c,0x04,0x04,0x04,0x0c, //ã
+0x00,0xe0,0x54,0x4c,0x44,0xfc, //ä
+0x00,0x38,0x54,0x54,0x54,0x18, //e
+0x00,0x6c,0x10,0x7c,0x10,0x6c, //æ
+0x00,0x44,0x44,0x54,0x54,0x28, //ç
+0x00,0x7c,0x20,0x10,0x08,0x7c, //è
+0x00,0x7c,0x41,0x22,0x11,0x7c, //é
+0x00,0x7c,0x10,0x28,0x44,0x00, //ê
+0x00,0x20,0x44,0x3c,0x04,0x7c, //ë
+0x00,0x7c,0x08,0x10,0x08,0x7c, //ì
+0x00,0x7c,0x10,0x10,0x10,0x7c, //í
+0x00,0x38,0x44,0x44,0x44,0x38, //o
+0x00,0x7c,0x04,0x04,0x04,0x7c, //ï
+0x00,0x7C,0x14,0x14,0x14,0x08, //p
+0x00,0x38,0x44,0x44,0x44,0x20, //c
+0x00,0x04,0x04,0x7c,0x04,0x04, //ò
+0x00,0x0C,0x50,0x50,0x50,0x3C, //ó
+0x00,0x30,0x48,0xfc,0x48,0x30, //ô
+0x00,0x44,0x28,0x10,0x28,0x44, //x
+0x00,0x7c,0x40,0x40,0x40,0xfc, //ö
+0x00,0x0c,0x10,0x10,0x10,0x7c, //ṫ
+0x00,0x7c,0x40,0x7c,0x40,0x7c, //ø
+0x00,0x7c,0x40,0x7c,0x40,0xfc, //ù
+0x00,0x04,0x7c,0x50,0x50,0x20, //ú
+0x00,0x7c,0x50,0x50,0x20,0x7c, //û
+0x00,0x7c,0x50,0x50,0x20,0x00, //ü
+0x00,0x28,0x44,0x54,0x54,0x38, //ý
+0x00,0x7c,0x10,0x38,0x44,0x38, //ŷ
+0x00,0x08,0x54,0x34,0x14,0x7c, //ÿ
+0x00,0x38,0x55,0x54,0x55,0x18, //ẁ 184
+0x00,0x7C,0x55,0x54,0x55,0x44, //Ẁ 168
+};
+
+#endif /* FONTS_H */

+ 77 - 0
iap/lcd/lcd.c

@@ -0,0 +1,77 @@
+#include "lcd.h"
+
+#ifdef LCD_ENABLE
+#include "jlx12864.h"
+
+static uint32_t utf8_strlen(const char *str)
+{
+    uint32_t len = 0;
+
+    while (*str) {
+        len += (*str++ & 0xc0) != 0x80;
+    }
+    return len;
+}
+
+void LCD_Init()
+{
+    init_lcd();
+    clear_screen();
+}
+
+void LCD_PrintRow(uint8_t row, uint8_t y, char *text)
+{
+    LCD_PutText(row * FONTSIZE_X, y, FONT_1X, COLOR_BLACK, text);
+    LCD_LoadData();
+}
+
+void LCD_ClearRow(uint8_t row)
+{
+    clear_page(row);
+}
+
+void LCD_PrintAligned(uint8_t row, align_t align, char *text)
+{
+    uint8_t x = 0;
+
+    switch (align) {
+        case alignLEFT:
+            x = 0;
+            break;
+        case alignCENTER:
+            x = (LCD_WIDTH - FONTSIZE_X * utf8_strlen(text)) / 2;
+            break;
+        case alignRIGHT:
+            x = LCD_WIDTH - FONTSIZE_X * utf8_strlen(text);
+            break;
+        default:
+            break;
+    }
+    LCD_ClearRow(row);
+    LCD_PutText(x, row * FONTSIZE_Y, FONT_1X, COLOR_BLACK, text);
+    LCD_LoadData();
+}
+
+void LCD_PrintBar(uint8_t row, uint8_t progress)
+{
+    const uint8_t x1 = 5;
+    const uint8_t x2 = LCD_WIDTH - 10;
+
+    const uint8_t y1 = row * FONTSIZE_Y;
+    const uint8_t y2 = y1 + FONTSIZE_Y - 1;
+
+    if (progress > 100) progress == 100;
+    const uint8_t len = (x2 - x1 - 3) * progress / 100;
+
+    LCD_ClearRow(row);
+
+    LCD_DrawLine(x1, y1, x2, y1, COLOR_BLACK);
+    LCD_DrawLine(x1, y2, x2, y2, COLOR_BLACK);
+
+    LCD_DrawLine(x1, y1, x1, y2, COLOR_BLACK);
+    LCD_DrawLine(x2, y1, x2, y2, COLOR_BLACK);
+
+    LCD_FillRegion(x1 + 2, y1 + 2, len, 4, COLOR_BLACK);
+    LCD_LoadData();
+}
+#endif /* LCD_ENABLE */

+ 25 - 0
iap/lcd/lcd.h

@@ -0,0 +1,25 @@
+#ifndef LCD_H_
+#define LCD_H_
+
+#include "common_config.h"
+#include <stdint.h>
+
+#define LCD_HEIGH   65
+#define LCD_WIDTH   132
+
+#define FONTSIZE_X   6
+#define FONTSIZE_Y   8
+
+typedef enum {
+    alignLEFT,
+    alignRIGHT,
+    alignCENTER
+} align_t;
+
+void LCD_Init();
+void LCD_PrintRow(uint8_t row, uint8_t y, char *text);
+void LCD_PrintAligned(uint8_t x, align_t align, char *text);
+void LCD_ClearRow(uint8_t row);
+void LCD_PrintBar(uint8_t row, uint8_t progress);
+
+#endif /* LCD_H_ */

+ 36 - 0
iap/lcd/pins.h

@@ -0,0 +1,36 @@
+/*
+ * pins.h
+ *
+ *  Created on: 12.12.2015
+ *      Author: pavel
+ */
+
+#ifndef PINS_H_
+#define PINS_H_
+
+#include "stm32f2xx.h"
+
+# if 0
+#define LCD \
+X( LCD_BKLGHT,    GPIOC,  2,  GPIO_OUT) \
+X( OLED_CS,		GPIOD,	0,	GPIO_OUT) \
+X( OLED_CD,		GPIOC,	11,	GPIO_OUT) \
+X( OLED_RST,		GPIOC,	12,	GPIO_OUT) \
+X( OLED_MOSI,		GPIOA,  15,	GPIO_OUT) \
+X( OLED_TYPE,     GPIOE,  12,  GPIO_IN_PU) \
+X( OLED_SCK,		GPIOC,	10,	GPIO_OUT)
+#endif
+
+#define OLED_CS 	GPIOA, GPIO_Pin_15
+#define OLED_CD 	GPIOB, GPIO_Pin_4
+#define OLED_RST 	GPIOB, GPIO_Pin_8
+#define OLED_MOSI 	GPIOB, GPIO_Pin_5
+#define OLED_SCK 	GPIOB, GPIO_Pin_3
+#define OLED_TYPE   GPIOE, GPIO_Pin_12
+
+#define LCD_BKLGHT    GPIOC, GPIO_Pin_2
+
+#define gpio_set GPIO_WriteBit
+#define gpio_get GPIO_ReadInputDataBit
+
+#endif /* PINS_H_ */

+ 53 - 0
iap/stm32sprog/serial.c

@@ -0,0 +1,53 @@
+#include "serial.h"
+
+#include "stm32f2xx_usart.h"
+
+#include "tinystdio.h"
+
+/** Data to track a serial device connection. */
+struct SSerialDev {
+    /** The file descriptor for the open serial device. */
+    int fd;
+};
+
+
+bool serialRead(SerialDev *dev, uint8_t *buffer, size_t n) {
+    while(n) {
+    	size_t result = 0;
+    	unsigned int retries = 0;
+
+        while ((USART_GetFlagStatus(USARTn, USART_FLAG_RXNE) == RESET) &&
+        		(retries++ < MAX_READ_RETRIES)) {
+        }
+        if(retries >= MAX_READ_RETRIES) {
+        	printf("Read error\r\n");
+        	return false;
+        }
+
+        *buffer = USART_ReceiveData(USARTn);
+        //printf("Rx: 0x%X\r\n", *buffer);
+        result++;
+
+		buffer += result;
+		n -= result;
+    }
+
+    return true;
+}
+
+bool serialWrite(SerialDev *dev, const uint8_t *buffer, size_t n) {
+    while(n) {
+       	size_t result = 0;
+
+    	while(USART_GetFlagStatus(USARTn, USART_FLAG_TXE) == RESET)
+    		;
+    	USART_SendData(USARTn, *buffer);
+    	//printf("Tx: 0x%X\r\n", *buffer);
+		result++;
+
+		buffer += result;
+		n -= result;
+    }
+
+    return true;
+}

+ 46 - 0
iap/stm32sprog/serial.h

@@ -0,0 +1,46 @@
+ 
+#ifndef STM32SPROG_SERIAL_H
+#define STM32SPROG_SERIAL_H
+/** \file serial.h
+ *
+ * Provides a serial communication interface.
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include "usart.h"
+
+#define USARTn  SWC_USART
+
+#define MAX_READ_RETRIES 50000000
+
+/** Serial device handle. */
+typedef struct SSerialDev SerialDev;
+
+/** \brief Read data from a serial device.
+ *
+ * Blocks until all data has been read or an error has occurred.
+ *
+ * \param dev An open serial device.
+ * \param buffer The data buffer to fill.
+ * \param n The number of bytes to read.
+ *
+ * \return \c true on success, \c false if any error occurred.
+ */
+bool serialRead(SerialDev *dev, uint8_t *buffer, size_t n);
+
+/** \brief Write data to a serial device.
+ *
+ * Blocks until all data has been written or an error has occurred.
+ *
+ * \param dev An open serial device.
+ * \param buffer The data to write.
+ * \param n The number of bytes to write.
+ *
+ * \return \c true on success, \c false if any error occurred.
+ */
+bool serialWrite(SerialDev *dev, const uint8_t *buffer, size_t n);
+
+
+#endif /* STM32SPROG_SERIAL_H */

+ 503 - 0
iap/stm32sprog/stm32sprog.c

@@ -0,0 +1,503 @@
+#include <limits.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include "tinystdio.h"
+#include <string.h>
+#include "stm32f2xx_usart.h"
+#include "stm32f2xx_crc.h"
+#include "stm32sprog.h"
+#include "serial.h"
+#include "systick.h"
+#include "common_config.h"
+
+
+#undef DBG
+#define DBG if(0)
+
+static int cmdIndex(uint8_t cmd);
+static bool cmdSupported(Command cmd);
+
+static bool stmRecvAck(void);
+static bool stmSendByte(uint8_t byte);
+static bool stmSendAddr(uint32_t addr);
+static bool stmSendBlock(const uint8_t *buffer, size_t size);
+static void printProgressBar(int percent);
+static bool stmReadBlock(uint32_t addr, uint8_t *buff, size_t size);
+static bool stmRun();
+
+static SerialDev *dev = NULL;
+static DeviceParameters devParams;
+
+static uint32_t flash_start = 0;
+
+void usleep(uint32_t us) {
+    if (us < 1000) us = 1000;
+    Delay_ms(us / 1000);
+}
+
+
+int stm32sprog_test(void) {
+	bool ok = false;
+
+	stmRebootForFlash();
+
+	/* Connect */
+	ok = stmConnect();
+
+	if(!ok) {
+		printf("STM32 not detected.\n");
+		return 1;
+	} else printf("STM32 connected\n");
+
+	/* Get params */
+	ok = stmGetDevParams();
+	if(!ok) {
+		printf("Device not supported.\n");
+		return 2;
+	}
+
+	int major = devParams.bootloaderVer >> 4;
+	int minor = devParams.bootloaderVer & 0x0F;
+	printf("Bootloader version %d.%d detected.\n", major, minor);
+
+	/* Erase */
+	printf("Erasing.. ");
+	ok = stmEraseFlash();
+	if(ok) printf("OK\n");
+	else {
+		printf("Erase error!\n");
+		return 3;
+	}
+
+	/* Write */
+    uint8_t buff[MAX_BLOCK_SIZE];
+    memset(buff, 0xAA, MAX_BLOCK_SIZE);
+
+    uint32_t addr = devParams.flashBeginAddr;
+    printf("Start addr: 0x%X\n", (unsigned int)devParams.flashBeginAddr);
+    printf("End addr: x%X\n", (unsigned int)devParams.flashEndAddr);
+
+    ok = stmWriteBlock(addr, buff, MAX_BLOCK_SIZE);
+    usleep(devParams.writeDelay);
+
+    printf("Writing.. ");
+
+    if (ok) printf("Written %d bytes\r\n", MAX_BLOCK_SIZE);
+    	else printf("Write error!\r\n");
+
+    printf("Comparing.. ");
+
+	uint8_t refbuff[MAX_BLOCK_SIZE];
+	memset(refbuff, 0xBB, MAX_BLOCK_SIZE);
+	ok = false;
+
+	ok = stmReadBlock(addr, refbuff, MAX_BLOCK_SIZE);
+	if(ok) ok = (memcmp(buff, refbuff, MAX_BLOCK_SIZE) == 0);
+		else printf("Read error!\r\n");
+
+    if (ok) printf("OK\r\n");
+    	else printf("Compare error!\r\n");
+
+    printf("Erasing.. ");
+    if (stmEraseFlash()) printf("OK\r\n");
+        else printf("Erase error!\r\n");
+
+    if (ok) printf("Test OK\r\n");
+    	else printf("Test failed!\n");
+
+    return 0;
+}
+
+bool stmConnect(void) {
+
+    uint8_t data = 0x7F;
+    int retries = 0;
+    do {
+    	usleep(10000);
+        if(++retries > MAX_CONNECT_RETRIES) return false;
+        (void)serialWrite(dev, &data, 1);
+    } while(!stmRecvAck());
+    return true;
+}
+
+static int cmdIndex(uint8_t cmd) {
+    int idx = -1;
+    switch(cmd) {
+    case CMD_GET_VERSION:     idx++;
+    case CMD_GET_READ_STATUS: idx++;
+    case CMD_GET_ID:          idx++;
+    case CMD_READ_MEM:        idx++;
+    case CMD_GO:              idx++;
+    case CMD_WRITE_MEM:       idx++;
+    case CMD_ERASE:           idx++;
+    case CMD_EXTENDED_ERASE:  idx++;
+    case CMD_WRITE_PROTECT:   idx++;
+    case CMD_WRITE_UNPROTECT: idx++;
+    case CMD_READ_PROTECT:    idx++;
+    case CMD_READ_UNPROTECT:  idx++;
+    default:                  break;
+    }
+    //assert(idx < NUM_COMMANDS_KNOWN);
+    return idx;
+}
+
+static bool cmdSupported(Command cmd) {
+    int idx = cmdIndex(cmd);
+    //assert(idx >= 0);
+    return devParams.commands[idx];
+}
+
+static bool stmRecvAck(void) {
+    uint8_t data = 0;
+    if(!serialRead(dev, &data, 1)) return false;
+    return data == ACK;
+}
+
+static bool stmSendByte(uint8_t byte) {
+    uint8_t buffer[] = { byte, ~byte };
+    if(!serialWrite(dev, buffer, sizeof(buffer))) return false;
+    return stmRecvAck();
+}
+
+static bool stmSendAddr(uint32_t addr) {
+    //assert(addr % 4 == 0);
+    uint8_t buffer[5] = { 0 };
+    int i;
+    for(i = 0; i < 4; ++i) {
+        buffer[i] = (uint8_t)(addr >> ((3 - i) * CHAR_BIT));
+        buffer[4] ^= buffer[i];
+    }
+    if(!serialWrite(dev, buffer, sizeof(buffer))) return false;
+    return stmRecvAck();
+}
+
+static bool stmSendBlock(const uint8_t *buffer, size_t size) {
+    //assert(size > 0 && size <= MAX_BLOCK_SIZE);
+    size_t padding = (4 - (size % 4)) % 4;
+    uint8_t n = size + padding - 1;
+    uint8_t checksum = n;
+    size_t i;
+    for(i = 0; i < size; ++i) checksum ^= buffer[i];
+    if(!serialWrite(dev, &n, 1)) return false;
+    if(!serialWrite(dev, buffer, size)) return false;
+    for(i = 0; i < padding; ++i) {
+        uint8_t data = 0xFF;
+        checksum ^= data;
+        if(!serialWrite(dev, &data, 1)) return false;
+    }
+    if(!serialWrite(dev, &checksum, 1)) return false;
+    return stmRecvAck();
+}
+
+bool stmGetDevParams(void) {
+    uint8_t data = 0;
+    int i;
+
+	if (flash_start) devParams.flashBeginAddr = flash_start;
+    else devParams.flashBeginAddr = 0x08000000;
+    devParams.flashEndAddr = 0x08008000;
+    devParams.flashPagesPerSector = 4;
+    devParams.flashPageSize = 1024;
+    devParams.eraseDelay = 40000;
+    devParams.writeDelay = 80000;
+
+    if(!stmSendByte(CMD_GET_VERSION)) return false;
+    if(!serialRead(dev, &data, 1)) return false;
+    if(!serialRead(dev, &devParams.bootloaderVer, 1)) return false;
+    for(i = 0; i < NUM_COMMANDS_KNOWN; ++i) devParams.commands[i] = false;
+    for(i = data; i > 0; --i) {
+        if(!serialRead(dev, &data, 1)) return false;
+        int idx = cmdIndex(data);
+        if(idx >= 0) devParams.commands[idx] = true;
+    }
+    if(!stmRecvAck()) return false;
+
+    if(!cmdSupported(CMD_GET_ID)) {
+        printf("Target device does not support GET_ID command.\n");
+        return false;
+    }
+    if(!stmSendByte(CMD_GET_ID)) return false;
+    if(!serialRead(dev, &data, 1)) return false;
+    if(data != 1) return false;
+    uint16_t id = 0;
+    for(i = data; i >= 0; --i) {
+        if(!serialRead(dev, &data, 1)) return false;
+        if(i < 2) {
+            id |= data << (i * CHAR_BIT);
+        }
+    }
+    if(!stmRecvAck()) return false;
+    switch(id) {
+    case ID_LOW_DENSITY:
+        devParams.flashEndAddr = 0x08008000;
+        break;
+    case ID_MED_DENSITY:
+        devParams.flashEndAddr = 0x08020000;
+        break;
+    case ID_HI_DENSITY:
+        devParams.flashEndAddr = 0x08080000;
+        devParams.flashPagesPerSector = 2;
+        devParams.flashPageSize = 2048;
+        break;
+    case ID_CONNECTIVITY:
+        devParams.flashEndAddr = 0x08040000;
+        devParams.flashPagesPerSector = 2;
+        devParams.flashPageSize = 2048;
+        break;
+    case ID_VALUE:
+        devParams.flashEndAddr = 0x08020000;
+        break;
+    case ID_HI_DENSITY_VALUE:
+        devParams.flashEndAddr = 0x08080000;
+        devParams.flashPagesPerSector = 2;
+        devParams.flashPageSize = 2048;
+        break;
+    case ID_XL_DENSITY:
+        devParams.flashEndAddr = 0x08100000;
+        devParams.flashPagesPerSector = 2;
+        devParams.flashPageSize = 2048;
+        break;
+    case ID_MED_DENSITY_ULTRA_LOW_POWER:
+        devParams.flashEndAddr = 0x08060000;
+        devParams.flashPagesPerSector = 16;
+        devParams.flashPageSize = 256;
+        break;
+    case ID_HI_DENSITY_ULTRA_LOW_POWER:
+        devParams.flashEndAddr = 0x08020000;
+        devParams.flashPagesPerSector = 16;
+        devParams.flashPageSize = 256;
+        break;
+    default:
+        printf("Device id %d\n not supported", id);
+        return false;
+    }
+
+    return true;
+}
+
+bool stmEraseFlash(void) {
+    if(cmdSupported(CMD_ERASE)) {
+        if(!stmSendByte(CMD_ERASE)) return false;
+        if(!stmSendByte(0xFF)) return false;
+    } else if(cmdSupported(CMD_EXTENDED_ERASE)) {
+        /* CMD_EXTENDED_ERASE not tested */
+    	printf("CMD_EXTENDED_ERASE supported\r\n");
+    	int c;
+        do {
+            c = 'y';
+            if(c == 'n' || c == 'N' || c == '\n') {
+                printf("\n");
+                return false;
+            }
+        } while(c != 'y' && c != 'Y');
+        printf("\n");
+
+        if(!stmSendByte(CMD_EXTENDED_ERASE)) return false;
+        uint8_t data[] = { 0xFF, 0xFF, 0x00 };
+        if(!serialWrite(dev, data, sizeof(data))) return false;
+        if(!stmRecvAck()) return false;
+    } else {
+        printf("Target device does not support known erase commands.\n");
+        return false;
+    }
+
+    /*
+	int i;
+    int delay = 60000;
+    printf("Erasing:\n");
+    for(i = 1; i <= 100; ++i) {
+        usleep(delay);
+        printProgressBar(i);
+    }
+    printf("\n");
+    */
+
+    return true;
+}
+
+/* Erase only FW flash pages, keep settings page */
+bool stmEraseFW(void) {
+    if(cmdSupported(CMD_ERASE)) {
+        if(!stmSendByte(CMD_ERASE)) return false;
+        /* n + 1 pages would be erased */
+        uint8_t n = DB_CPU_SETTINGS_PAGE - 1;
+        if(!serialWrite(dev, &n, 1)) return false;
+        uint8_t checksum = n;
+
+        for (uint8_t i = 0; i <= n; i++) {
+            if(!serialWrite(dev, &i, 1)) return false;
+            checksum ^= i;
+        }
+
+        if(!serialWrite(dev, &checksum, 1)) return false;
+        if(!stmRecvAck()) return false;
+    }
+    else {
+        printf("Target device does not support known erase commands.\n");
+        return false;
+    }
+    return true;
+}
+
+bool stmWriteBlock(uint32_t addr, const uint8_t *buff, size_t size) {
+    if(!stmSendByte(CMD_WRITE_MEM)) return false;
+    if(!stmSendAddr(addr)) return false;
+    if(!stmSendBlock(buff, size)) return false;
+    return true;
+}
+
+static bool stmReadBlock(uint32_t addr, uint8_t *buff, size_t size) {
+    if(!stmSendByte(CMD_READ_MEM)) return false;
+    if(!stmSendAddr(addr)) return false;
+    if(!stmSendByte(size - 1)) return false;
+    return serialRead(dev, buff, size);
+}
+
+void stmRebootForFlash(void) {
+    IO_SetDbBoot0();
+    IO_ClearDbReset();
+    usleep(60000);
+    IO_SetDbReset();
+    usleep(60000);
+    IO_ClearDbBoot0();
+}
+
+void stmReboot(void) {
+    IO_ClearDbBoot0();
+    IO_ClearDbReset();
+    usleep(60000);
+    IO_SetDbReset();
+}
+
+uint32_t stmCalcFlashCrc(void (* periodic_handler)(uint8_t)) {
+    bool ok = false;
+    uint8_t buf[4];
+    uint32_t crc = 0;
+    uint8_t block[MAX_BLOCK_SIZE];
+    uint32_t last_block_size, n = 0;
+    volatile uint32_t* ptr;
+    static uint32_t last_progress = 0;
+
+    CRC_ResetDR();
+
+#if 0
+    /* Read by 4 bytes blocks. Slow method */
+    for(uint32_t* ptr = (uint32_t*)DB_CPU_FLASH_FIRST_PAGE_ADDRESS; ptr != (uint32_t*)DB_CPU_FLASH_CRC_ADDRESS; ptr++) {
+        ok = stmReadBlock((uint32_t)ptr, buf, 4);
+        if(ok) {
+            crc = CRC_CalcCRC(*(uint32_t *)buf);
+            DBG printf("Verify block %u\r\n", n++);
+        }
+        else printf("Device read error!\r\n");
+    }
+#endif
+
+    /* Read by MAX_BLOCK_SIZE bytes blocks. Fast method */
+    for (ptr = (uint32_t*)DB_CPU_FLASH_FIRST_PAGE_ADDRESS; ptr < (uint32_t*)DB_CPU_FLASH_CRC_ADDRESS - MAX_BLOCK_SIZE / 4; ptr += MAX_BLOCK_SIZE/4) {
+
+        ok = stmReadBlock((uint32_t)ptr, block, MAX_BLOCK_SIZE);
+        if (ok) {
+            crc = CRC_CalcBlockCRC((uint32_t *)block, MAX_BLOCK_SIZE / 4);
+            DBG printf("Verify block %u\r\n", n++);
+            if (periodic_handler != NULL) {
+                uint32_t progress = ((uint32_t)ptr - DB_CPU_FLASH_FIRST_PAGE_ADDRESS) * 100 /
+                    (DB_CPU_FLASH_CRC_ADDRESS - MAX_BLOCK_SIZE / 4 - DB_CPU_FLASH_FIRST_PAGE_ADDRESS);
+                if (progress > last_progress) {
+                    last_progress = progress;
+                    periodic_handler(progress);
+                }
+            }
+        }
+        else printf("Device read error!\r\n");
+    }
+
+    last_block_size = ((uint32_t*)DB_CPU_FLASH_CRC_ADDRESS - ptr) * 4;
+    ok = stmReadBlock((uint32_t)ptr, block, last_block_size);
+    if(ok) {
+        crc = CRC_CalcBlockCRC((uint32_t *)block, last_block_size / 4);
+        DBG printf("Verify block %u\r\n", n++);
+        if (periodic_handler != NULL) {
+            periodic_handler(100);
+        }
+    }
+    else printf("Device read error!\r\n");
+
+    return crc;
+}
+
+uint32_t stmReadFlashCrc(void) {
+    bool ok = false;
+    uint8_t buf[4];
+    uint32_t crc = 0x0;
+
+    ok = stmReadBlock(DB_CPU_FLASH_CRC_ADDRESS, buf, 4);
+    if(ok) {
+          crc = *(uint32_t *)buf;
+    }
+    else printf("Read error!\r\n");
+
+    printf("Written CRC: 0x%X\r\n", (unsigned int)crc);
+
+    return crc;
+}
+
+void stmProg( uint32_t* addr, uint8_t * ptr, uint32_t len)
+{
+  uint32_t count, remain = 0, i = 0;
+  static uint32_t n = 0;
+  bool ok = false;
+
+  /* Reset blocks count */
+  if (*addr == DB_CPU_FLASH_FIRST_PAGE_ADDRESS) n = 0;
+
+
+  /* write received bytes into flash */
+  count = len / MAX_BLOCK_SIZE;
+
+  /* check if remaining bytes */
+  remain = len % MAX_BLOCK_SIZE;
+
+  for (i = 0; i < count; i++) {
+    ok = stmWriteBlock(*addr, ptr + (i * MAX_BLOCK_SIZE ), MAX_BLOCK_SIZE);
+    usleep(8000);
+
+    if (ok) {
+        DBG printf("Block %u (0x%X) %ub\r\n", (unsigned int)n++, *addr, MAX_BLOCK_SIZE);
+    }
+    else {
+      printf("Device write error!\r\n");
+      return;
+    }
+
+      *addr += MAX_BLOCK_SIZE;
+  }
+
+  if (remain > 0)
+  {
+      ok = stmWriteBlock(*addr, ptr + (i * MAX_BLOCK_SIZE ), remain);
+      if (ok) {
+          DBG printf("Block %u (0x%X) %ub\r\n", (unsigned int)n++, *addr, (unsigned int)remain);
+      }
+      else {
+          printf("Device write error!\r\n");
+          return;
+      }
+
+      *addr += remain;
+  }
+}
+
+static bool stmRun() {
+    if(!stmSendByte(CMD_GO)) return false;
+    return stmSendAddr(devParams.flashBeginAddr);
+}
+
+static void printProgressBar(int percent) {
+    int num = percent * 70 / 100;
+    int i = 0;
+    printf("\r%3d%%[", percent);
+    for(i = 0; i < 70; ++i) {
+        printf(i < num ? "=" : " ");
+    }
+    printf("]");
+}

+ 78 - 0
iap/stm32sprog/stm32sprog.h

@@ -0,0 +1,78 @@
+/*
+ * srm32sprog.h
+ *
+ *  Created on: Apr 12, 2016
+ *      Author: jesstr
+ */
+
+#ifndef SRM32SPROG_H_
+#define SRM32SPROG_H_
+
+#include "stm32f2xx_usart.h"
+#include "gpio_io.h"
+
+#include "serial.h"
+#include "sys/types.h"
+
+#define MAX_CONNECT_RETRIES	3
+
+#define DEFAULT_BAUD 57600
+
+#define MAX_BLOCK_SIZE 256
+
+
+static const uint8_t ACK = 0x79;
+typedef enum {
+    CMD_GET_VERSION = 0x00,
+    CMD_GET_READ_STATUS = 0x01,
+    CMD_GET_ID = 0x02,
+    CMD_READ_MEM = 0x11,
+    CMD_GO = 0x21,
+    CMD_WRITE_MEM = 0x31,
+    CMD_ERASE = 0x43,
+    CMD_EXTENDED_ERASE = 0x44,
+    CMD_WRITE_PROTECT = 0x63,
+    CMD_WRITE_UNPROTECT = 0x73,
+    CMD_READ_PROTECT = 0x82,
+    CMD_READ_UNPROTECT = 0x92
+} Command;
+#define NUM_COMMANDS_KNOWN 12
+
+enum {
+    ID_LOW_DENSITY = 0x0412,
+    ID_MED_DENSITY = 0x0410,
+    ID_HI_DENSITY = 0x0414,
+    ID_CONNECTIVITY = 0x0418,
+    ID_VALUE = 0x0420,
+    ID_HI_DENSITY_VALUE = 0x0428,
+    ID_XL_DENSITY = 0x0430,
+    ID_MED_DENSITY_ULTRA_LOW_POWER = 0x0436,
+    ID_HI_DENSITY_ULTRA_LOW_POWER = 0x0416
+};
+
+typedef struct {
+    uint8_t bootloaderVer;
+    bool commands[NUM_COMMANDS_KNOWN];
+    uint32_t flashBeginAddr;
+    uint32_t flashEndAddr;
+    int flashPagesPerSector;
+    size_t flashPageSize;
+    useconds_t eraseDelay;
+    useconds_t writeDelay;
+} DeviceParameters;
+
+int stm32sprog_test(void);
+void usleep(uint32_t us);
+void stmRebootForFlash(void);
+void stmReboot(void);
+uint32_t stmCalcFlashCrc(void (* periodic_handler)(uint8_t));
+uint32_t stmReadFlashCrc(void);
+bool stmConnect(void);
+bool stmGetDevParams(void);
+bool stmEraseFlash(void);
+bool stmEraseFW(void);
+bool stmWriteBlock(uint32_t addr, const uint8_t *buff, size_t size);
+void stmProg( uint32_t* addr, uint8_t * ptr, uint32_t len);
+
+
+#endif /* SRM32SPROG_H_ */

+ 38 - 20
modules/HTTP_Server/http_server.c

@@ -160,28 +160,30 @@ typedef struct {
 
 web_func_handler_t process_web_funcs[] = {
 #ifdef HTTP_AUTH_ENABLE
-    { "POST /login.cgi",            15,   COMMON_ANSWER,        ALL_ACCESS,   HTTP_LoginPage },
-    { "GET /logout.cgi",            15,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_LogoutPage },
-    { "GET /changepwd.cgi",         18,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_ChangeUserPwd },
+    { "POST /login.cgi",                15,   COMMON_ANSWER,        ALL_ACCESS,   HTTP_LoginPage },
+    { "GET /logout.cgi",                15,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_LogoutPage },
+    { "GET /changepwd.cgi",             18,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_ChangeUserPwd },
 #endif
-    { "GET /getJson.cgi",           16,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_GetParamsPage },
-    { "GET /settings.cgi",          17,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_GetSettingsPage },
-    { "POST /settings.cgi",         18,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_SetSettingsPage },
+    { "GET /getJson.cgi",               16,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_GetParamsPage },
+    { "GET /settings.cgi",              17,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_GetSettingsPage },
+    { "POST /settings.cgi",             18,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_SetSettingsPage },
 #ifdef NOTIFICATION_CONTROL_ENABLE
-    { "GET /snmp.cgi",              13,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_SnmpParam },
+    { "GET /snmp.cgi",                  13,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_SnmpParam },
 #endif
-    { "GET /info.cgi",              13,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_GetInfo },
-    { "POST /info.cgi",             14,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_SetInfoPage },
-    { "GET /history.cgi",           16,   HISTORY_ANSWER,       TIME_ACCESS,  HTTP_HistoryPage },
-    { "GET /ups_history.cgi",       19,   UPS_HISTORY_ANSWER,   TIME_ACCESS,  HTTP_UpsHistoryPage },
-    { "GET /reset.cgi",             14,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_Reset },
-    { "GET /bat_test.cgi",          17,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_UPSTest },
-    { "GET /ups_power.cgi",         18,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_UPSshutdown },
-    { "GET /reboot.cgi",            15,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_Reboot },
-    { "GET /confirm.cgi",           16,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_Confirm },
-    { "GET /fw_update.cgi",         18,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_ConfirmBootPwd },
-    { "GET",                        3,    COMMON_ANSWER,        ALL_ACCESS,   HTTP_GetRequest },
-    { "",                           0,    COMMON_ANSWER,        ALL_ACCESS,   HTTP_NoFound },
+    { "GET /info.cgi",                  13,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_GetInfo },
+    { "POST /info.cgi",                 14,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_SetInfoPage },
+    { "GET /history.cgi?page=all",      25,   HISTORY_ANSWER,       TIME_ACCESS,  HTTP_HistoryPage },
+    { "GET /history.cgi",               16,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_HistoryPage },
+    { "GET /ups_history.cgi?page=all",  29,   UPS_HISTORY_ANSWER,   TIME_ACCESS,  HTTP_UpsHistoryPage },
+    { "GET /ups_history.cgi",           20,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_UpsHistoryPage },
+    { "GET /reset.cgi",                 14,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_Reset },
+    { "GET /bat_test.cgi",              17,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_UPSTest },
+    { "GET /ups_power.cgi",             18,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_UPSshutdown },
+    { "GET /reboot.cgi",                15,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_Reboot },
+    { "GET /confirm.cgi",               16,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_Confirm },
+    { "GET /fw_update.cgi",             18,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_ConfirmBootPwd },
+    { "GET",                            3,    COMMON_ANSWER,        ALL_ACCESS,   HTTP_GetRequest },
+    { "",                               0,    COMMON_ANSWER,        ALL_ACCESS,   HTTP_NoFound },
     { "", 0, 0, 0, NULL }
 };
 
@@ -263,7 +265,9 @@ static err_t http_recv(void *arg, struct tcp_pcb *pcb,  struct pbuf *p, err_t er
     web_func_handler_t *h;
 
     hs = arg;
-
+    if (GetRebootStatus() == true) {;
+        Reboot(WEB_ACT);
+    }
     if (err == ERR_OK && p != NULL) {
         tcp_recved(pcb, p->tot_len);
 
@@ -417,6 +421,12 @@ static err_t http_sent_history(void *arg, struct tcp_pcb *pcb, u16_t len)
     if (hs->left > 0) {
         send_data(pcb, hs);
     } else {
+        /* Release previously used send buffer to use 
+        * separate file transfer buffer (logFileBuf) */
+        if (hs->locked != 0) {
+            unlock_buf(hs->locked);
+        }
+
         memset(logFileBuf, 0, FILE_BUF_MAX_LEN);
         if (log_ptr + FILE_BUF_MAX_LEN <= log_size) {
             nbytes = History_GetData(log_ptr, logFileBuf, FILE_BUF_MAX_LEN, start);
@@ -463,6 +473,12 @@ static err_t http_sent_log(void *arg, struct tcp_pcb *pcb, u16_t len)
     if (hs->left > 0) {
         send_data(pcb, hs);
     } else {
+        /* Release previously used send buffer to use 
+        * separate file transfer buffer (logFileBuf) */
+        if (hs->locked != 0) {
+            unlock_buf(hs->locked);
+        }
+
         memset(logFileBuf, 0, FILE_BUF_MAX_LEN);
         if (log_ptr + FILE_BUF_MAX_LEN_LOG <= log_size) {
             nbytes = LOG_GetData(log_ptr, logFileBuf, FILE_BUF_MAX_LEN_LOG, start);
@@ -633,6 +649,8 @@ static err_t http_accept(void *arg, struct tcp_pcb *pcb, err_t err)
     /* Initialize the structure. */
     hs->file = NULL;
     hs->left = 0;
+    hs->reqnum = 0;
+    hs->locked = NULL;
 
     /* Tell TCP that this is the structure we wish to be passed for our
        callbacks. */

+ 29 - 0
modules/HTTP_Server/my_ssl_server.c

@@ -176,6 +176,7 @@ web_func_handler_t process_web_funcs[] = {
     { "GET /reboot.cgi",            15,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_Reboot },
     { "GET /confirm.cgi",           16,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_Confirm },
     { "GET /fw_update.cgi",         18,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_ConfirmBootPwd },
+    { "GET /ups_serial.cgi",        19,   COMMON_ANSWER,        TIME_ACCESS,  HTTP_UPS_Serial },
     { "GET",                        3,    COMMON_ANSWER,        ALL_ACCESS,   HTTP_GetRequest },
     { "",                           0,    COMMON_ANSWER,        ALL_ACCESS,   HTTP_NoFound },
     { "", 0, 0, 0, NULL }
@@ -1482,6 +1483,34 @@ char *HTTP_UPSshutdown(uint32_t reqNum, char *bufIn, char *bufOut, uint16_t lenB
     return bufOut;
 }
 
+char *HTTP_UPS_Serial(uint32_t reqNum, char *bufIn, char *bufOut, uint16_t lenBufIn, uint16_t *lenBufOut)
+{
+    uint8_t valueLen = 0;
+    char tempValue[50];
+
+    (void)lenBufIn;
+    (void)reqNum;
+
+    memset(tempValue, 0, 50);
+
+    strcpy(bufOut, HTTP_200_OK);
+
+    GetParamValue(bufIn, "cmd_us", tempValue, &valueLen);
+    if (valueLen <= 8) {
+        SetUPSSerialStr(tempValue);
+        SETTINGS_Save();
+        strcat(bufOut,
+        "<!DOCTYPE html><html><head><meta charset=\"utf-8\"><meta http-equiv=\"refresh\" content=\"3; url=/info.html\" /></head><center><h2>Серийный номер ИБП установлен</h2></center></html>");
+    } else {
+        strcat(bufOut,
+        "<!DOCTYPE html><html><head><meta charset=\"utf-8\"><meta http-equiv=\"refresh\" content=\"3; url=/info.html\" /></head><center><h2>Ошибка установки серийного номера ИБП</h2></center></html>");
+    }
+    
+    *lenBufOut = strlen(bufOut);
+
+    return bufOut;
+}
+
 char *HTTP_Reset(uint32_t reqNum, char *bufIn, char *bufOut, uint16_t lenBufIn, uint16_t *lenBufOut)
 {
     (void)bufIn;

+ 2 - 0
modules/HTTP_Server/my_ssl_server.h

@@ -171,6 +171,8 @@ char* HTTP_UPSTest(uint32_t reqNum, char *bufIn, char *bufOut, uint16_t lenBufIn
   */
 char* HTTP_UPSshutdown(uint32_t reqNum, char *bufIn, char *bufOut, uint16_t lenBufIn, uint16_t *lenBufOut);
 
+char *HTTP_UPS_Serial(uint32_t reqNum, char *bufIn, char *bufOut, uint16_t lenBufIn, uint16_t *lenBufOut);
+
 /**
   * @brief  Проверка пароля для перехода в режим bootloader
   */

+ 7 - 1
modules/Makefile

@@ -21,6 +21,8 @@ CFLAGS += -DPRINTF_$(shell echo $(PRINTF) | tr a-z A-Z)
 
 CFLAGS += -DHARDWARE_$(shell echo $(HARDWARE) | tr a-z A-Z)
 
+CFLAGS += -DORDER_$(shell echo $(ORDER) | tr a-z A-Z)
+
 INCLUDES = -I../config
 INCLUDES += -I../stm32/stm32f4xx_spl/inc 
 INCLUDES += -I../stm32/system
@@ -247,8 +249,12 @@ endif
 $(FSDATA_DIR)/fsdata.c: $(WUI_DIR)/*
 	mkdir -p $(FSDATA_DIR)
 	../docs/makefsdata.pl $(FSDATA_DIR) $(WUI_DIR)
-	
+
+ifneq (,$(filter $(ORDER),mts beeline))	
+ARTIFACTS_PATH := ~/Releases/$(HARDWARE)/$(ORDER)
+else
 ARTIFACTS_PATH := ~/Releases/$(HARDWARE)
+endif
 RELEASE_VERSION := $(shell ../projects/gcc/tools/version.sh $(HARDWARE) $(INCLUDES))
 RELEASE_PATH := $(ARTIFACTS_PATH)/$(RELEASE_VERSION)
 

+ 21 - 30
modules/MegaTec/megatec.c

@@ -393,7 +393,8 @@ void ups_general_status_response(char *data)
 
 void ups_info_response(char *data)
 {
-    uint8_t i = 0;
+    uint8_t i = 0, j = 0;
+    char value[20];
     DBG printf("ups_info_response: %s\r\n", data);
     if (data[0] != '#') {
         return;
@@ -409,31 +410,30 @@ void ups_info_response(char *data)
 
     data += 16; //пропускаем поле название компании
 
-    while (data[0] == ' ') {
-        data ++;
-        i ++;
+    memset(value, 0, sizeof(value));
+    for (uint8_t i = 0; i < KSTAR_MODEL_LENGTH; i ++) {
+        if (data[i] != ' ') {
+            value[j] = data[i];
+            j ++;
+        }
     }
-    if (i < 15) {
-        /*endValue = strpbrk(data," ");
-        len = endValue - data;*/
+
+    if (j != 0) {
         if (UPS.model[0] == 0) {
-            strncpy(UPS.model, data, KSTAR_MODEL_LENGTH);
+            strncpy(UPS.model, value, strlen(value));
             SNMP_SetObjDescr();
         } else {
-            strncpy(UPS.model, data, KSTAR_MODEL_LENGTH);
+            strncpy(UPS.model, data, strlen(value));
         }
-
-        data += 11;
     } else {
         if (UPS.model[0] == 0) {
-            strcpy(UPS.model, "RTMP II");
+            strcpy(UPS.model, "RTMP-II");
             SNMP_SetObjDescr();
         } else {
-            strcpy(UPS.model, "RTMP II");
+            strcpy(UPS.model, "RTMP-II");
         }
-        data += 11;
     }
-
+    data += (KSTAR_MODEL_LENGTH + 1);
     strncpy(UPS.serial, data, 8);
     data += 8;
 
@@ -514,6 +514,7 @@ void ups_model_response(char *data)
 {
     uint8_t j = 0;
     char value[20];
+    uint8_t len = 0;
     DBG printf("ups_akb_info_response: %s\r\n", data);
     if (data[0] != '(') {
         return;
@@ -535,25 +536,15 @@ void ups_model_response(char *data)
                 j ++;
             }
         }
-        if(strncmp(value, "WPHV", 4) == 0 || value[0] != 0) {
+        if(strncmp(value, "WPHV", 4) == 0 || value[0] == 0) {
             strcpy(UPS.model, "RTMP II");
         } else {
-            strcpy(UPS.model, value);
+            len = strlen(value);
+            strncpy(UPS.model, value, len);
         }
         SNMP_SetObjDescr();
     } else {
-        j = 0;
-        for (uint8_t i = 0; i < VOLTRONIC_MODEL_LENGTH; i ++) {
-            if (data[i] != '#') {
-                value[j] = data[i];
-                j ++;
-            }
-            if(strncmp(value, "WPHV", 4) == 0 || value[0] != 0) {
-                strcpy(UPS.model, "RTMP II");
-            } else {
-                strcpy(UPS.model, value);
-            }
-        }
+        return;
     }
 }
 
@@ -748,7 +739,7 @@ void request_task(void *params)
     uint8_t num_req = 0;
     uint8_t *req;
 
-    //ups_megatec_rx_pdu();
+    ups_megatec_rx_pdu();
     for (;;) {
         if (UPS.Present == true) {
             if (UPS.Flag_Present == false) {

+ 38 - 3
modules/parameters.c

@@ -48,6 +48,10 @@
 #ifdef NOTIFICATION_CONTROL_ENABLE
 extern const char* name_traps[];
 #endif
+
+
+static bool flag_reboot = false; // флаг перезагрузки контроллера (устанавливается при смене настроек ssh)
+
 /**
   * @brief  Структура для хранения состояний дискретных входов
   */
@@ -67,6 +71,16 @@ extern WEB_PARAMS_t sTempWebParams;
   * @brief  Флаг подтверждения новых сетевых параметров пользователем
   */
 extern bool fConfirmWebParams;
+
+/**
+  * @brief 
+  */
+bool GetRebootStatus(void)
+{
+  return flag_reboot;
+}
+
+
 #ifndef BT6702_SERVICE
 
 // ************************************************************************** //
@@ -964,8 +978,17 @@ void GetUPSModelStr(char *str, uint8_t *len)
   */
 void GetUPSSerialStr(char *str, uint8_t *len)
 {
-    *len = strlen(UPS.serial);
-    strncpy(str, UPS.serial, *len);
+    char tempStr[30];
+
+    if (sSettings.UPS_Setting.serial == 0) {
+      *len = strlen(UPS.serial);
+      strncpy(str, UPS.serial, *len);
+    } else {
+      memset(tempStr, 0, sizeof(tempStr));
+      sprintf(tempStr, "%d", sSettings.UPS_Setting.serial);
+      strcpy(str, tempStr);
+      *len = strlen(str);
+    }
 }
 
 /**
@@ -2365,6 +2388,7 @@ void SetSSHPortStr(char *str)
     bool port_changed = new_port != sSettings.sSSH.port;
     sSettings.sSSH.port = new_port;
     if (port_changed) {
+        flag_reboot = true;
         ssh_server_restart();
     }
 }
@@ -2374,8 +2398,8 @@ void SetSSHPortStr(char *str)
   */
 void SetSSHEnableStateStr(char *str)
 {
+    bool old_state = sSettings.sSSH.SSHEnable;
     if (strncmp(str, "on", 2) == 0) {
-        bool old_state = sSettings.sSSH.SSHEnable;
         sSettings.sSSH.SSHEnable = 1;
         if (old_state != true && fl_reinit_ssh == true) {
             ssh_server_init();
@@ -2384,6 +2408,9 @@ void SetSSHEnableStateStr(char *str)
         sSettings.sSSH.SSHEnable = 0;
         ssh_server_restart();
     }
+    if(old_state != sSettings.sSSH.SSHEnable) {
+        flag_reboot = true;
+    }
 }
 #endif // SSH_ENABLE
 #endif // !BT6702_SERVICE
@@ -2769,6 +2796,14 @@ void SetCapacityNominalAKBStr(char *str)
     sSettings.UPS_Setting.common_capacity = atoi(str);
 }
 
+/**
+  * @brief  Серийный номер ИБП
+  */
+void SetUPSSerialStr(char *str)
+{
+	sSettings.UPS_Setting.serial = atoi(str);
+}
+
 // ************************************************************************** //
 //                             Параметры менеджера аварий
 

+ 11 - 0
modules/parameters.h

@@ -16,6 +16,12 @@
 
 #include <stdbool.h>
 #include "common_config.h"
+
+/**
+  * @brief 
+  */
+bool GetRebootStatus(void);
+
 // ************************************************************************** //
 //                        Параметры UPS
 
@@ -1192,6 +1198,11 @@ void SetUPSPowerStr(char *str);
   */
 void SetCapacityNominalAKBStr(char *str);
 
+/**
+  * @brief  Серийный номер ИБП
+  */
+void SetUPSSerialStr(char *str);
+
 // ************************************************************************** //
 //                             Параметры менеджера аварий
 

+ 1 - 0
modules/settings_api.c

@@ -146,6 +146,7 @@ void SETTINGS_SetAllDefault(void)
   SETTINGS_SetTempWebParamsDef();
   SETTINGS_SetInfoDef();
   SETTINGS_SetFlagsDef();
+  SETTINGS_SetUPSSerialSettingsDef();
 #define XSETTING(type, name, setter, type_reset) setter();
   SETTINGS_TABLE
 #undef XSETTING

+ 6 - 0
modules/settings_api.h

@@ -293,6 +293,7 @@ typedef struct
   uint32_t set_data;
   uint32_t common_capacity;
 	uint8_t type_ups;
+  uint32_t serial;
 } UPS_Setting_t;
 
 /**
@@ -458,6 +459,11 @@ void SETTINGS_SetSnmpDef(void);
   */
 void SETTINGS_SetUPSSettingsDef(void);
 
+/**
+  * @brief  Настройки серийного номера ИБП по умолчанию
+  */
+void SETTINGS_SetUPSSerialSettingsDef(void);
+
 /**
   * @brief  Установить Информацию об устройстве по умолчанию
   */

+ 8 - 0
modules/settings_api_bt6703.c

@@ -96,6 +96,14 @@ void SETTINGS_SetUPSSettingsDef(void)
   sSettings.UPS_Setting.type_ups = ups_kestar;
 }
 
+/**
+  * @brief  Настройки серийного номера ИБП по умолчанию
+  */
+void SETTINGS_SetUPSSerialSettingsDef(void)
+{
+  sSettings.UPS_Setting.serial = 0;
+}
+
 /**
   * @brief  Установить наcтройки менеджера Аварий по умолчанию
   */

+ 8 - 0
modules/settings_api_bt6707.c

@@ -98,6 +98,14 @@ void SETTINGS_SetUPSSettingsDef(void)
   sSettings.UPS_Setting.type_ups = ups_kestar;
 }
 
+/**
+  * @brief  Настройки серийного номера ИБП по умолчанию
+  */
+void SETTINGS_SetUPSSerialSettingsDef(void)
+{
+  sSettings.UPS_Setting.serial = 0;
+}
+
 /**
   * @brief  Установить наcтройки менеджера Аварий по умолчанию
   */

+ 12 - 0
modules/settings_api_bt6709.c

@@ -96,6 +96,14 @@ void SETTINGS_SetUPSSettingsDef(void)
   sSettings.UPS_Setting.type_ups = ups_kestar;
 }
 
+/**
+  * @brief  Настройки серийного номера ИБП по умолчанию
+  */
+void SETTINGS_SetUPSSerialSettingsDef(void)
+{
+  sSettings.UPS_Setting.serial = 0;
+}
+
 /**
   * @brief  Установить наcтройки менеджера Аварий по умолчанию
   */
@@ -221,6 +229,10 @@ void SETTINGS_SetFlagNotificationDef(void)
     for(i = 0; i < ALL_TRAPS; i++ ) {
         sSettings.sFlagNotification[i] = 1;
     }
+#ifdef ORDER_BEELINE
+    sSettings.sFlagNotification[BATTERY_CHANGE_ALARM] = 0;
+    sSettings.sFlagNotification[BATTERY_CHANGE_MORM] = 0;
+#endif
 }
 
 /**

+ 8 - 0
modules/settings_api_bt6710.c

@@ -99,6 +99,14 @@ void SETTINGS_SetUPSSettingsDef(void)
     sSettings.UPS_Setting.type_ups = ups_kestar;
 }
 
+/**
+  * @brief  Настройки серийного номера ИБП по умолчанию
+  */
+void SETTINGS_SetUPSSerialSettingsDef(void)
+{
+  sSettings.UPS_Setting.serial = 0;
+}
+
 /**
   * @brief  Установить наcтройки менеджера Аварий по умолчанию
   */

部分文件因文件數量過多而無法顯示