// clang-format off
/*----------------------------------------------------------------------------/
/ FatFs - Generic FAT Filesystem Module R0 .15 w / patch1 /
/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - /
/
/ Copyright ( C ) 2022 , ChaN , all right reserved .
/
/ FatFs module is an open source software . Redistribution and use of FatFs in
/ source and binary forms , with or without modification , are permitted provided
/ that the following condition is met :
/
/ 1. Redistributions of source code must retain the above copyright notice ,
/ this condition and the following disclaimer .
/
/ This software is provided by the copyright holder and contributors " AS IS "
/ and any warranties related to this software are DISCLAIMED .
/ The copyright owner or contributors be NOT LIABLE for any damages caused
/ by use of this software .
/
/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
# include <string.h>
# include "ff.h" /* Declarations of FatFs API */
# include "diskio.h" /* Declarations of device I/O functions */
/*--------------------------------------------------------------------------
Module Private Definitions
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
# if FF_DEFINED != 80286 /* Revision ID */
# error Wrong include file (ff.h).
# endif
/* Limits and boundaries */
# define MAX_DIR 0x200000 /* Max size of FAT directory */
# define MAX_DIR_EX 0x10000000 /* Max size of exFAT directory */
# define MAX_FAT12 0xFF5 /* Max FAT12 clusters (differs from specs, but right for real DOS/Windows behavior) */
# define MAX_FAT16 0xFFF5 /* Max FAT16 clusters (differs from specs, but right for real DOS/Windows behavior) */
# define MAX_FAT32 0x0FFFFFF5 /* Max FAT32 clusters (not specified, practical limit) */
# define MAX_EXFAT 0x7FFFFFFD /* Max exFAT clusters (differs from specs, implementation limit) */
/* 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')
# define IsSeparator(c) ((c) == ' / ' || (c) == '\\')
# define IsTerminator(c) ((UINT)(c) < (FF_USE_LFN ? ' ' : '!'))
# define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF)
# define IsSurrogateH(c) ((c) >= 0xD800 && (c) <= 0xDBFF)
# define IsSurrogateL(c) ((c) >= 0xDC00 && (c) <= 0xDFFF)
/* Additional file access control and file status flags for internal use */
# define FA_SEEKEND 0x20 /* Seek to end of the file on file open */
# define FA_MODIFIED 0x40 /* File has been modified */
# define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */
/* Additional file attribute bits for internal use */
# define AM_VOL 0x08 /* Volume label */
# define AM_LFN 0x0F /* LFN entry */
# define AM_MASK 0x3F /* Mask of defined bits in FAT */
# define AM_MASKX 0x37 /* Mask of defined bits in exFAT */
/* Name status flags in fn[11] */
# define NSFLAG 11 /* Index of the name status byte */
# 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 */
# define NS_NOLFN 0x40 /* Do not find LFN */
# define NS_NONAME 0x80 /* Not followed */
/* exFAT directory entry types */
# define ET_BITMAP 0x81 /* Allocation bitmap */
# define ET_UPCASE 0x82 /* Up-case table */
# define ET_VLABEL 0x83 /* Volume label */
# define ET_FILEDIR 0x85 /* File and directory */
# define ET_STREAM 0xC0 /* Stream extension */
# define ET_FILENAME 0xC1 /* Name extension */
/* FatFs refers the FAT structure as simple byte array instead of structure member
/ because the C structure is not binary compatible between different platforms */
# define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */
# define BS_OEMName 3 /* OEM name (8-byte) */
# define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */
# define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */
# define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */
# define BPB_NumFATs 16 /* Number of FATs (BYTE) */
# define BPB_RootEntCnt 17 /* Size of root directory area for FAT [entry] (WORD) */
# define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */
# define BPB_Media 21 /* Media descriptor byte (BYTE) */
# define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */
# define BPB_SecPerTrk 24 /* Number of sectors per track for int13h [sector] (WORD) */
# define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */
# define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */
# define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */
# define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */
# define BS_NTres 37 /* WindowsNT error flag (BYTE) */
# define BS_BootSig 38 /* Extended boot signature (BYTE) */
# define BS_VolID 39 /* Volume serial number (DWORD) */
# define BS_VolLab 43 /* Volume label string (8-byte) */
# define BS_FilSysType 54 /* Filesystem type string (8-byte) */
# define BS_BootCode 62 /* Boot code (448-byte) */
# define BS_55AA 510 /* Signature word (WORD) */
# define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */
# define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */
# define BPB_FSVer32 42 /* FAT32: Filesystem version (WORD) */
# define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */
# define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */
# define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */
# define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */
# define BS_NTres32 65 /* FAT32: Error flag (BYTE) */
# define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */
# define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */
# define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */
# define BS_FilSysType32 82 /* FAT32: Filesystem type string (8-byte) */
# define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */
# define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */
# define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */
# define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */
# define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */
# define BPB_FatSzEx 84 /* exFAT: FAT size [sector] (DWORD) */
# define BPB_DataOfsEx 88 /* exFAT: Data offset from top of the volume [sector] (DWORD) */
# define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */
# define BPB_RootClusEx 96 /* exFAT: Root directory start cluster (DWORD) */
# define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */
# define BPB_FSVerEx 104 /* exFAT: Filesystem version (WORD) */
# define BPB_VolFlagEx 106 /* exFAT: Volume flags (WORD) */
# define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in unit of byte (BYTE) */
# define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in unit of sector (BYTE) */
# define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */
# define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */
# define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE) */
# define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */
# define BS_BootCodeEx 120 /* exFAT: Boot code (390-byte) */
# define DIR_Name 0 /* Short file name (11-byte) */
# define DIR_Attr 11 /* Attribute (BYTE) */
# define DIR_NTres 12 /* Lower case flag (BYTE) */
# define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */
# define DIR_CrtTime 14 /* Created time (DWORD) */
# define DIR_LstAccDate 18 /* Last accessed date (WORD) */
# define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */
# define DIR_ModTime 22 /* Modified time (DWORD) */
# define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */
# define DIR_FileSize 28 /* File size (DWORD) */
# define LDIR_Ord 0 /* LFN: LFN order and LLE flag (BYTE) */
# define LDIR_Attr 11 /* LFN: LFN attribute (BYTE) */
# define LDIR_Type 12 /* LFN: Entry type (BYTE) */
# define LDIR_Chksum 13 /* LFN: Checksum of the SFN (BYTE) */
# define LDIR_FstClusLO 26 /* LFN: MBZ field (WORD) */
# define XDIR_Type 0 /* exFAT: Type of exFAT directory entry (BYTE) */
# define XDIR_NumLabel 1 /* exFAT: Number of volume label characters (BYTE) */
# define XDIR_Label 2 /* exFAT: Volume label (11-WORD) */
# define XDIR_CaseSum 4 /* exFAT: Sum of case conversion table (DWORD) */
# define XDIR_NumSec 1 /* exFAT: Number of secondary entries (BYTE) */
# define XDIR_SetSum 2 /* exFAT: Sum of the set of directory entries (WORD) */
# define XDIR_Attr 4 /* exFAT: File attribute (WORD) */
# define XDIR_CrtTime 8 /* exFAT: Created time (DWORD) */
# define XDIR_ModTime 12 /* exFAT: Modified time (DWORD) */
# define XDIR_AccTime 16 /* exFAT: Last accessed time (DWORD) */
# define XDIR_CrtTime10 20 /* exFAT: Created time subsecond (BYTE) */
# define XDIR_ModTime10 21 /* exFAT: Modified time subsecond (BYTE) */
# define XDIR_CrtTZ 22 /* exFAT: Created timezone (BYTE) */
# define XDIR_ModTZ 23 /* exFAT: Modified timezone (BYTE) */
# define XDIR_AccTZ 24 /* exFAT: Last accessed timezone (BYTE) */
# define XDIR_GenFlags 33 /* exFAT: General secondary flags (BYTE) */
# define XDIR_NumName 35 /* exFAT: Number of file name characters (BYTE) */
# define XDIR_NameHash 36 /* exFAT: Hash of file name (WORD) */
# define XDIR_ValidFileSize 40 /* exFAT: Valid file size (QWORD) */
# define XDIR_FstClus 52 /* exFAT: First cluster of the file data (DWORD) */
# define XDIR_FileSize 56 /* exFAT: File/Directory size (QWORD) */
# define SZDIRE 32 /* Size of a directory entry */
# define DDEM 0xE5 /* Deleted directory entry mark set to DIR_Name[0] */
# define RDDEM 0x05 /* Replacement of the character collides with DDEM */
# define LLEF 0x40 /* Last long entry flag in LDIR_Ord */
# define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */
# define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */
# define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */
# define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */
# define MBR_Table 446 /* MBR: Offset of partition table in the MBR */
# define SZ_PTE 16 /* MBR: Size of a partition table entry */
# define PTE_Boot 0 /* MBR PTE: Boot indicator */
# define PTE_StHead 1 /* MBR PTE: Start head */
# define PTE_StSec 2 /* MBR PTE: Start sector */
# define PTE_StCyl 3 /* MBR PTE: Start cylinder */
# define PTE_System 4 /* MBR PTE: System ID */
# define PTE_EdHead 5 /* MBR PTE: End head */
# define PTE_EdSec 6 /* MBR PTE: End sector */
# define PTE_EdCyl 7 /* MBR PTE: End cylinder */
# define PTE_StLba 8 /* MBR PTE: Start in LBA */
# define PTE_SizLba 12 /* MBR PTE: Size in LBA */
# define GPTH_Sign 0 /* GPT HDR: Signature (8-byte) */
# define GPTH_Rev 8 /* GPT HDR: Revision (DWORD) */
# define GPTH_Size 12 /* GPT HDR: Header size (DWORD) */
# define GPTH_Bcc 16 /* GPT HDR: Header BCC (DWORD) */
# define GPTH_CurLba 24 /* GPT HDR: This header LBA (QWORD) */
# define GPTH_BakLba 32 /* GPT HDR: Another header LBA (QWORD) */
# define GPTH_FstLba 40 /* GPT HDR: First LBA for partition data (QWORD) */
# define GPTH_LstLba 48 /* GPT HDR: Last LBA for partition data (QWORD) */
# define GPTH_DskGuid 56 /* GPT HDR: Disk GUID (16-byte) */
# define GPTH_PtOfs 72 /* GPT HDR: Partition table LBA (QWORD) */
# define GPTH_PtNum 80 /* GPT HDR: Number of table entries (DWORD) */
# define GPTH_PteSize 84 /* GPT HDR: Size of table entry (DWORD) */
# define GPTH_PtBcc 88 /* GPT HDR: Partition table BCC (DWORD) */
# define SZ_GPTE 128 /* GPT PTE: Size of partition table entry */
# define GPTE_PtGuid 0 /* GPT PTE: Partition type GUID (16-byte) */
# define GPTE_UpGuid 16 /* GPT PTE: Partition unique GUID (16-byte) */
# define GPTE_FstLba 32 /* GPT PTE: First LBA of partition (QWORD) */
# define GPTE_LstLba 40 /* GPT PTE: Last LBA of partition (QWORD) */
# define GPTE_Flags 48 /* GPT PTE: Partition flags (QWORD) */
# define GPTE_Name 56 /* GPT PTE: Partition name */
/* Post process on fatal error in the file operations */
# define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); }
/* Re-entrancy related */
# define LEAVE_FF(fs, res) return res
/* Definitions of logical drive - physical location conversion */
# define LD2PD(vol) (BYTE)(vol) /* Each logical drive is associated with the same physical drive number */
# define LD2PT(vol) 0 /* Auto partition search */
/* Definitions of sector size */
# if (FF_MAX_SS < FF_MIN_SS) || (FF_MAX_SS != 512 && FF_MAX_SS != 1024 && FF_MAX_SS != 2048 && FF_MAX_SS != 4096) || (FF_MIN_SS != 512 && FF_MIN_SS != 1024 && FF_MIN_SS != 2048 && FF_MIN_SS != 4096)
# error Wrong sector size configuration
# endif
# if FF_MAX_SS == FF_MIN_SS
# define SS(fs) ((UINT)FF_MAX_SS) /* Fixed sector size */
# else
# define SS(fs) ((fs)->ssize) /* Variable sector size */
# endif
/* Timestamp */
# if FF_NORTC_YEAR < 1980 || FF_NORTC_YEAR > 2107 || FF_NORTC_MON < 1 || FF_NORTC_MON > 12 || FF_NORTC_MDAY < 1 || FF_NORTC_MDAY > 31
# error Invalid FF_FS_NORTC settings
# endif
# define GET_FATTIME() ((DWORD)(FF_NORTC_YEAR - 1980) << 25 | (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16)
/* File lock controls */
/* SBCS up-case tables (\x80-\xFF) */
# define TBL_CT437 {0x80,0x9A,0x45,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 , 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 }
# define TBL_CT720 {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 }
# define TBL_CT737 {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 , 0xEF , 0xF5 , 0xF0 , 0xEA , 0xEB , 0xEC , 0xED , 0xEE , 0xEF , \
0xF0 , 0xF1 , 0xF2 , 0xF3 , 0xF4 , 0xF5 , 0xF6 , 0xF7 , 0xF8 , 0xF9 , 0xFA , 0xFB , 0xFC , 0xFD , 0xFE , 0xFF }
# define TBL_CT771 {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 , 0xDC , 0xDE , 0xDE , \
0x90 , 0x91 , 0x92 , 0x93 , 0x94 , 0x95 , 0x96 , 0x97 , 0x98 , 0x99 , 0x9A , 0x9B , 0x9C , 0x9D , 0x9E , 0x9F , \
0xF0 , 0xF0 , 0xF2 , 0xF2 , 0xF4 , 0xF4 , 0xF6 , 0xF6 , 0xF8 , 0xF8 , 0xFA , 0xFA , 0xFC , 0xFC , 0xFE , 0xFF }
# define TBL_CT775 {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 }
# define TBL_CT850 {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \
0x45 , 0x92 , 0x92 , 0x4F , 0x4F , 0x4F , 0x55 , 0x55 , 0x59 , 0x4F , 0x55 , 0x4F , 0x9C , 0x4F , 0x9E , 0x9F , \
0x41 , 0x49 , 0x4F , 0x55 , 0xA5 , 0xA5 , 0xA6 , 0xA7 , 0xA8 , 0xA9 , 0xAA , 0xAB , 0xAC , 0xAD , 0xAE , 0xAF , \
0xB0 , 0xB1 , 0xB2 , 0xB3 , 0xB4 , 0x41 , 0x41 , 0x41 , 0xB8 , 0xB9 , 0xBA , 0xBB , 0xBC , 0xBD , 0xBE , 0xBF , \
0xC0 , 0xC1 , 0xC2 , 0xC3 , 0xC4 , 0xC5 , 0x41 , 0x41 , 0xC8 , 0xC9 , 0xCA , 0xCB , 0xCC , 0xCD , 0xCE , 0xCF , \
0xD1 , 0xD1 , 0x45 , 0x45 , 0x45 , 0x49 , 0x49 , 0x49 , 0x49 , 0xD9 , 0xDA , 0xDB , 0xDC , 0xDD , 0x49 , 0xDF , \
0x4F , 0xE1 , 0x4F , 0x4F , 0x4F , 0x4F , 0xE6 , 0xE8 , 0xE8 , 0x55 , 0x55 , 0x55 , 0x59 , 0x59 , 0xEE , 0xEF , \
0xF0 , 0xF1 , 0xF2 , 0xF3 , 0xF4 , 0xF5 , 0xF6 , 0xF7 , 0xF8 , 0xF9 , 0xFA , 0xFB , 0xFC , 0xFD , 0xFE , 0xFF }
# define TBL_CT852 {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 , 0xAC , \
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 }
# define TBL_CT855 {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 }
# define TBL_CT857 {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,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 , 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 , 0xC7 , 0xC7 , 0xC8 , 0xC9 , 0xCA , 0xCB , 0xCC , 0xCD , 0xCE , 0xCF , \
0xD0 , 0xD1 , 0xD2 , 0xD3 , 0xD4 , 0x49 , 0xD6 , 0xD7 , 0xD8 , 0xD9 , 0xDA , 0xDB , 0xDC , 0xDD , 0xDE , 0xDF , \
0xE0 , 0xE1 , 0xE2 , 0xE3 , 0xE5 , 0xE5 , 0xE6 , 0xE7 , 0xE8 , 0xE9 , 0xEA , 0xEB , 0xDE , 0xED , 0xEE , 0xEF , \
0xF0 , 0xF1 , 0xF2 , 0xF3 , 0xF4 , 0xF5 , 0xF6 , 0xF7 , 0xF8 , 0xF9 , 0xFA , 0xFB , 0xFC , 0xFD , 0xFE , 0xFF }
# define TBL_CT860 {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \
0x90 , 0x91 , 0x92 , 0x8C , 0x99 , 0xA9 , 0x96 , 0x9D , 0x98 , 0x99 , 0x9A , 0x9B , 0x9C , 0x9D , 0x9E , 0x9F , \
0x86 , 0x8B , 0x9F , 0x96 , 0xA5 , 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 }
# define TBL_CT861 {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \
0x90 , 0x92 , 0x92 , 0x4F , 0x99 , 0x8D , 0x55 , 0x97 , 0x97 , 0x99 , 0x9A , 0x9D , 0x9C , 0x9D , 0x9E , 0x9F , \
0xA4 , 0xA5 , 0xA6 , 0xA7 , 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 }
# define TBL_CT862 {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 , 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 }
# define TBL_CT863 {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \
0x45 , 0x45 , 0x45 , 0x4F , 0x45 , 0x49 , 0x55 , 0x55 , 0x98 , 0x4F , 0x55 , 0x9B , 0x9C , 0x55 , 0x55 , 0x9F , \
0xA0 , 0xA1 , 0x4F , 0x55 , 0xA4 , 0xA5 , 0xA6 , 0xA7 , 0x49 , 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 }
# define TBL_CT864 {0x80,0x9A,0x45,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 , 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 }
# define TBL_CT865 {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 , 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 }
# define TBL_CT866 {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 , 0x94 , 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 }
# define TBL_CT869 {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 , 0x86 , 0x9C , 0x8D , 0x8F , 0x90 , \
0x91 , 0x90 , 0x92 , 0x95 , 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 , 0xA4 , 0xA5 , 0xA6 , 0xD9 , 0xDA , 0xDB , 0xDC , 0xA7 , 0xA8 , 0xDF , \
0xA9 , 0xAA , 0xAC , 0xAD , 0xB5 , 0xB6 , 0xB7 , 0xB8 , 0xBD , 0xBE , 0xC6 , 0xC7 , 0xCF , 0xCF , 0xD0 , 0xEF , \
0xF0 , 0xF1 , 0xD1 , 0xD2 , 0xD3 , 0xF5 , 0xD4 , 0xF7 , 0xF8 , 0xF9 , 0xD5 , 0x96 , 0x95 , 0x98 , 0xFE , 0xFF }
/* DBCS code range |----- 1st byte -----| |----------- 2nd byte -----------| */
/* <------> <------> <------> <------> <------> */
# define TBL_DC932 {0x81, 0x9F, 0xE0, 0xFC, 0x40, 0x7E, 0x80, 0xFC, 0x00, 0x00}
# define TBL_DC936 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0x80, 0xFE, 0x00, 0x00}
# define TBL_DC949 {0x81, 0xFE, 0x00, 0x00, 0x41, 0x5A, 0x61, 0x7A, 0x81, 0xFE}
# define TBL_DC950 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0xA1, 0xFE, 0x00, 0x00}
/* Macros for table definitions */
# define MERGE_2STR(a, b) a ## b
# define MKCVTBL(hd, cp) MERGE_2STR(hd, cp)
/*--------------------------------------------------------------------------
Module Private Work Area
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/* Remark: Variables defined here without initial value shall be guaranteed
/ zero / null at start - up . If not , the linker option or start - up routine is
/ not compliance with C standard . */
/*--------------------------------*/
/* File/Volume controls */
/*--------------------------------*/
static FATFS * FatFs [ FF_VOLUMES ] ; /* Pointer to the filesystem objects (logical drives) */
static WORD Fsid ; /* Filesystem mount ID */
/*--------------------------------*/
/* LFN/Directory working buffer */
/*--------------------------------*/
# if FF_MAX_LFN < 12 || FF_MAX_LFN > 255
# error Wrong setting of FF_MAX_LFN
# endif
# if FF_LFN_BUF < FF_SFN_BUF || FF_SFN_BUF < 12
# error Wrong setting of FF_LFN_BUF or FF_SFN_BUF
# endif
static const BYTE LfnOfs [ ] = { 1 , 3 , 5 , 7 , 9 , 14 , 16 , 18 , 20 , 22 , 24 , 28 , 30 } ; /* FAT: Offset of LFN characters in the directory entry */
# define MAXDIRB(nc) ((nc + 44U) / 15 * SZDIRE) /* exFAT: Size of directory entry block scratchpad buffer needed for the name length */
static WCHAR LfnBuf [ FF_MAX_LFN + 1 ] ; /* LFN working buffer */
# define DEF_NAMBUF
# define INIT_NAMBUF(fs)
# define FREE_NAMBUF()
# define LEAVE_MKFS(res) return res
/*--------------------------------*/
/* Code conversion tables */
/*--------------------------------*/
# define CODEPAGE FF_CODE_PAGE
static const BYTE ExCvt [ ] = MKCVTBL ( TBL_CT , FF_CODE_PAGE ) ;
/*--------------------------------------------------------------------------
Module Private Functions
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*-----------------------------------------------------------------------*/
/* Load/Store multi-byte word in the FAT structure */
/*-----------------------------------------------------------------------*/
static WORD ld_word ( const BYTE * ptr ) /* Load a 2-byte little-endian word */
{
WORD rv = 0 ;
rv = ptr [ 1 ] ;
rv = rv < < 8 | ptr [ 0 ] ;
return rv ;
}
static DWORD ld_dword ( const BYTE * ptr ) /* Load a 4-byte little-endian word */
{
DWORD rv = 0 ;
rv = ptr [ 3 ] ;
rv = rv < < 8 | ptr [ 2 ] ;
rv = rv < < 8 | ptr [ 1 ] ;
rv = rv < < 8 | ptr [ 0 ] ;
return rv ;
}
static void st_word ( BYTE * ptr , WORD val ) /* Store a 2-byte word in little-endian */
{
* ptr + + = ( BYTE ) val ; val > > = 8 ;
* ptr + + = ( BYTE ) val ;
}
static void st_dword ( BYTE * ptr , DWORD val ) /* Store a 4-byte word in little-endian */
{
* ptr + + = ( BYTE ) val ; val > > = 8 ;
* ptr + + = ( BYTE ) val ; val > > = 8 ;
* ptr + + = ( BYTE ) val ; val > > = 8 ;
* ptr + + = ( BYTE ) val ;
}
/*-----------------------------------------------------------------------*/
/* String functions */
/*-----------------------------------------------------------------------*/
/* Test if the byte is DBC 1st byte */
static int dbc_1st ( BYTE c )
{
if ( c ! = 0 ) return 0 ; /* Always false */
return 0 ;
}
/* Test if the byte is DBC 2nd byte */
static int dbc_2nd ( BYTE c )
{
if ( c ! = 0 ) return 0 ; /* Always false */
return 0 ;
}
/* Get a Unicode code point from the TCHAR string in defined API encodeing */
static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on surrogate pair, 0xFFFFFFFF on decode error) */
const TCHAR * * str /* Pointer to pointer to TCHAR string in configured encoding */
)
{
DWORD uc = 0 ;
const TCHAR * p = * str ;
BYTE b = 0 ;
int nf = 0 ;
uc = ( BYTE ) * p + + ; /* Get an encoding unit */
if ( uc & 0x80 ) { /* Multiple byte code? */
if ( ( uc & 0xE0 ) = = 0xC0 ) { /* 2-byte sequence? */
uc & = 0x1F ; nf = 1 ;
} else if ( ( uc & 0xF0 ) = = 0xE0 ) { /* 3-byte sequence? */
uc & = 0x0F ; nf = 2 ;
} else if ( ( uc & 0xF8 ) = = 0xF0 ) { /* 4-byte sequence? */
uc & = 0x07 ; nf = 3 ;
} else { /* Wrong sequence */
return 0xFFFFFFFF ;
}
do { /* Get trailing bytes */
b = ( BYTE ) * p + + ;
if ( ( b & 0xC0 ) ! = 0x80 ) return 0xFFFFFFFF ; /* Wrong sequence? */
uc = uc < < 6 | ( b & 0x3F ) ;
} while ( - - nf ! = 0 ) ;
if ( uc < 0x80 | | IsSurrogate ( uc ) | | uc > = 0x110000 ) return 0xFFFFFFFF ; /* Wrong code? */
if ( uc > = 0x010000 ) uc = 0xD800DC00 | ( ( uc - 0x10000 ) < < 6 & 0x3FF0000 ) | ( uc & 0x3FF ) ; /* Make a surrogate pair if needed */
}
* str = p ; /* Next read pointer */
return uc ;
}
/* Store a Unicode char in defined API encoding */
static UINT put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */
DWORD chr , /* UTF-16 encoded character (Surrogate pair if >=0x10000) */
TCHAR * buf , /* Output buffer */
UINT szb /* Size of the buffer */
)
{
DWORD hc = 0 ;
if ( chr < 0x80 ) { /* Single byte code? */
if ( szb < 1 ) return 0 ; /* Buffer overflow? */
* buf = ( TCHAR ) chr ;
return 1 ;
}
if ( chr < 0x800 ) { /* 2-byte sequence? */
if ( szb < 2 ) return 0 ; /* Buffer overflow? */
* buf + + = ( TCHAR ) ( 0xC0 | ( chr > > 6 & 0x1F ) ) ;
* buf + + = ( TCHAR ) ( 0x80 | ( chr > > 0 & 0x3F ) ) ;
return 2 ;
}
if ( chr < 0x10000 ) { /* 3-byte sequence? */
if ( szb < 3 | | IsSurrogate ( chr ) ) return 0 ; /* Buffer overflow or wrong code? */
* buf + + = ( TCHAR ) ( 0xE0 | ( chr > > 12 & 0x0F ) ) ;
* buf + + = ( TCHAR ) ( 0x80 | ( chr > > 6 & 0x3F ) ) ;
* buf + + = ( TCHAR ) ( 0x80 | ( chr > > 0 & 0x3F ) ) ;
return 3 ;
}
/* 4-byte sequence */
if ( szb < 4 ) return 0 ; /* Buffer overflow? */
hc = ( ( chr & 0xFFFF0000 ) - 0xD8000000 ) > > 6 ; /* Get high 10 bits */
chr = ( chr & 0xFFFF ) - 0xDC00 ; /* Get low 10 bits */
if ( hc > = 0x100000 | | chr > = 0x400 ) return 0 ; /* Wrong surrogate? */
chr = ( hc | chr ) + 0x10000 ;
* buf + + = ( TCHAR ) ( 0xF0 | ( chr > > 18 & 0x07 ) ) ;
* buf + + = ( TCHAR ) ( 0x80 | ( chr > > 12 & 0x3F ) ) ;
* buf + + = ( TCHAR ) ( 0x80 | ( chr > > 6 & 0x3F ) ) ;
* buf + + = ( TCHAR ) ( 0x80 | ( chr > > 0 & 0x3F ) ) ;
return 4 ;
}
/*-----------------------------------------------------------------------*/
/* Move/Flush disk access window in the filesystem object */
/*-----------------------------------------------------------------------*/
static FRESULT sync_window ( /* Returns FR_OK or FR_DISK_ERR */
FATFS * fs /* Filesystem object */
)
{
FRESULT res = FR_OK ;
if ( fs - > wflag ) { /* Is the disk access window dirty? */
if ( disk_write ( fs - > pdrv , fs - > win , fs - > winsect , 1 ) = = RES_OK ) { /* Write it back into the volume */
fs - > wflag = 0 ; /* Clear window dirty flag */
if ( fs - > winsect - fs - > fatbase < fs - > fsize ) { /* Is it in the 1st FAT? */
if ( fs - > n_fats = = 2 ) disk_write ( fs - > pdrv , fs - > win , fs - > winsect + fs - > fsize , 1 ) ; /* Reflect it to 2nd FAT if needed */
}
} else {
res = FR_DISK_ERR ;
}
}
return res ;
}
static FRESULT move_window ( /* Returns FR_OK or FR_DISK_ERR */
FATFS * fs , /* Filesystem object */
LBA_t sect /* Sector LBA to make appearance in the fs->win[] */
)
{
FRESULT res = FR_OK ;
if ( sect ! = fs - > winsect ) { /* Window offset changed? */
res = sync_window ( fs ) ; /* Flush the window */
if ( res = = FR_OK ) { /* Fill sector window with new data */
if ( disk_read ( fs - > pdrv , fs - > win , sect , 1 ) ! = RES_OK ) {
sect = ( LBA_t ) 0 - 1 ; /* Invalidate window if read data is not valid */
res = FR_DISK_ERR ;
}
fs - > winsect = sect ;
}
}
return res ;
}
/*-----------------------------------------------------------------------*/
/* Synchronize filesystem and data on the storage */
/*-----------------------------------------------------------------------*/
static FRESULT sync_fs ( /* Returns FR_OK or FR_DISK_ERR */
FATFS * fs /* Filesystem object */
)
{
FRESULT res = 0 ;
res = sync_window ( fs ) ;
if ( res = = FR_OK ) {
if ( fs - > fs_type = = FS_FAT32 & & fs - > fsi_flag = = 1 ) { /* FAT32: Update FSInfo sector if needed */
/* Create FSInfo structure */
memset ( fs - > win , 0 , sizeof fs - > win ) ;
st_word ( fs - > win + BS_55AA , 0xAA55 ) ; /* Boot signature */
st_dword ( fs - > win + FSI_LeadSig , 0x41615252 ) ; /* Leading signature */
st_dword ( fs - > win + FSI_StrucSig , 0x61417272 ) ; /* Structure signature */
st_dword ( fs - > win + FSI_Free_Count , fs - > free_clst ) ; /* Number of free clusters */
st_dword ( fs - > win + FSI_Nxt_Free , fs - > last_clst ) ; /* Last allocated culuster */
fs - > winsect = fs - > volbase + 1 ; /* Write it into the FSInfo sector (Next to VBR) */
disk_write ( fs - > pdrv , fs - > win , fs - > winsect , 1 ) ;
fs - > fsi_flag = 0 ;
}
/* Make sure that no pending write process in the lower layer */
if ( disk_ioctl ( fs - > pdrv , CTRL_SYNC , 0 ) ! = RES_OK ) res = FR_DISK_ERR ;
}
return res ;
}
/*-----------------------------------------------------------------------*/
/* Get physical sector number from cluster number */
/*-----------------------------------------------------------------------*/
static LBA_t clst2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */
FATFS * fs , /* Filesystem object */
DWORD clst /* Cluster# to be converted */
)
{
clst - = 2 ; /* Cluster number is origin from 2 */
if ( clst > = fs - > n_fatent - 2 ) return 0 ; /* Is it invalid cluster number? */
return fs - > database + ( LBA_t ) fs - > csize * clst ; /* Start sector number of the cluster */
}
/*-----------------------------------------------------------------------*/
/* FAT access - Read value of an FAT entry */
/*-----------------------------------------------------------------------*/
static DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */
FFOBJID * obj , /* Corresponding object */
DWORD clst /* Cluster number to get the value */
)
{
UINT wc = 0 , bc = 0 ;
DWORD val = 0 ;
FATFS * fs = obj - > fs ;
if ( clst < 2 | | clst > = fs - > n_fatent ) { /* Check if in valid range */
val = 1 ; /* Internal error */
} else {
val = 0xFFFFFFFF ; /* Default value falls on disk error */
switch ( fs - > fs_type ) {
case FS_FAT12 :
bc = ( UINT ) clst ; bc + = bc / 2 ;
if ( move_window ( fs , fs - > fatbase + ( bc / SS ( fs ) ) ) ! = FR_OK ) break ;
wc = fs - > win [ bc + + % SS ( fs ) ] ; /* Get 1st byte of the entry */
if ( move_window ( fs , fs - > fatbase + ( bc / SS ( fs ) ) ) ! = FR_OK ) break ;
wc | = fs - > win [ bc % SS ( fs ) ] < < 8 ; /* Merge 2nd byte of the entry */
val = ( clst & 1 ) ? ( wc > > 4 ) : ( wc & 0xFFF ) ; /* Adjust bit position */
break ;
case FS_FAT16 :
if ( move_window ( fs , fs - > fatbase + ( clst / ( SS ( fs ) / 2 ) ) ) ! = FR_OK ) break ;
val = ld_word ( fs - > win + clst * 2 % SS ( fs ) ) ; /* Simple WORD array */
break ;
case FS_FAT32 :
if ( move_window ( fs , fs - > fatbase + ( clst / ( SS ( fs ) / 4 ) ) ) ! = FR_OK ) break ;
val = ld_dword ( fs - > win + clst * 4 % SS ( fs ) ) & 0x0FFFFFFF ; /* Simple DWORD array but mask out upper 4 bits */
break ;
default :
val = 1 ; /* Internal error */
}
}
return val ;
}
/*-----------------------------------------------------------------------*/
/* FAT access - Change value of an FAT entry */
/*-----------------------------------------------------------------------*/
static FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */
FATFS * fs , /* Corresponding filesystem object */
DWORD clst , /* FAT index number (cluster number) to be changed */
DWORD val /* New value to be set to the entry */
)
{
UINT bc = 0 ;
BYTE * p = NULL ;
FRESULT res = FR_INT_ERR ;
if ( clst > = 2 & & clst < fs - > n_fatent ) { /* Check if in valid range */
switch ( fs - > fs_type ) {
case FS_FAT12 :
bc = ( UINT ) clst ; bc + = bc / 2 ; /* bc: byte offset of the entry */
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 ; /* Update 1st byte */
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 ) ) ; /* Update 2nd byte */
fs - > wflag = 1 ;
break ;
case FS_FAT16 :
res = move_window ( fs , fs - > fatbase + ( clst / ( SS ( fs ) / 2 ) ) ) ;
if ( res ! = FR_OK ) break ;
st_word ( fs - > win + clst * 2 % SS ( fs ) , ( WORD ) val ) ; /* Simple WORD array */
fs - > wflag = 1 ;
break ;
case FS_FAT32 :
res = move_window ( fs , fs - > fatbase + ( clst / ( SS ( fs ) / 4 ) ) ) ;
if ( res ! = FR_OK ) break ;
if ( ! FF_FS_EXFAT | | fs - > fs_type ! = FS_EXFAT ) {
val = ( val & 0x0FFFFFFF ) | ( ld_dword ( fs - > win + clst * 4 % SS ( fs ) ) & 0xF0000000 ) ;
}
st_dword ( fs - > win + clst * 4 % SS ( fs ) , val ) ;
fs - > wflag = 1 ;
break ;
}
}
return res ;
}
/*-----------------------------------------------------------------------*/
/* FAT handling - Remove a cluster chain */
/*-----------------------------------------------------------------------*/
static FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */
FFOBJID * obj , /* Corresponding object */
DWORD clst , /* Cluster to remove a chain from */
DWORD pclst /* Previous cluster of clst (0 if entire chain) */
)
{
FRESULT res = FR_OK ;
DWORD nxt = 0 ;
FATFS * fs = obj - > fs ;
if ( clst < 2 | | clst > = fs - > n_fatent ) return FR_INT_ERR ; /* Check if in valid range */
/* Mark the previous cluster 'EOC' on the FAT if it exists */
if ( pclst ! = 0 & & ( ! FF_FS_EXFAT | | fs - > fs_type ! = FS_EXFAT | | obj - > stat ! = 2 ) ) {
res = put_fat ( fs , pclst , 0xFFFFFFFF ) ;
if ( res ! = FR_OK ) return res ;
}
/* Remove the chain */
do {
nxt = get_fat ( obj , clst ) ; /* Get cluster status */
if ( nxt = = 0 ) break ; /* Empty cluster? */
if ( nxt = = 1 ) return FR_INT_ERR ; /* Internal error? */
if ( nxt = = 0xFFFFFFFF ) return FR_DISK_ERR ; /* Disk error? */
if ( ! FF_FS_EXFAT | | fs - > fs_type ! = FS_EXFAT ) {
res = put_fat ( fs , clst , 0 ) ; /* Mark the cluster 'free' on the FAT */
if ( res ! = FR_OK ) return res ;
}
if ( fs - > free_clst < fs - > n_fatent - 2 ) { /* Update FSINFO */
fs - > free_clst + + ;
fs - > fsi_flag | = 1 ;
}
clst = nxt ; /* Next cluster */
} while ( clst < fs - > n_fatent ) ; /* Repeat while not the last link */
return FR_OK ;
}
/*-----------------------------------------------------------------------*/
/* FAT handling - Stretch a chain or Create a new chain */
/*-----------------------------------------------------------------------*/
static DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
FFOBJID * obj , /* Corresponding object */
DWORD clst /* Cluster# to stretch, 0:Create a new chain */
)
{
DWORD cs = 0 , ncl = 0 , scl = 0 ;
FRESULT res = 0 ;
FATFS * fs = obj - > fs ;
if ( clst = = 0 ) { /* Create a new chain */
scl = fs - > last_clst ; /* Suggested cluster to start to find */
if ( scl = = 0 | | scl > = fs - > n_fatent ) scl = 1 ;
}
else { /* Stretch a chain */
cs = get_fat ( obj , clst ) ; /* Check the cluster status */
if ( cs < 2 ) return 1 ; /* Test for insanity */
if ( cs = = 0xFFFFFFFF ) return cs ; /* Test for disk error */
if ( cs < fs - > n_fatent ) return cs ; /* It is already followed by next cluster */
scl = clst ; /* Cluster to start to find */
}
if ( fs - > free_clst = = 0 ) return 0 ; /* No free cluster */
{ /* On the FAT/FAT32 volume */
ncl = 0 ;
if ( scl = = clst ) { /* Stretching an existing chain? */
ncl = scl + 1 ; /* Test if next cluster is free */
if ( ncl > = fs - > n_fatent ) ncl = 2 ;
cs = get_fat ( obj , ncl ) ; /* Get next cluster status */
if ( cs = = 1 | | cs = = 0xFFFFFFFF ) return cs ; /* Test for error */
if ( cs ! = 0 ) { /* Not free? */
cs = fs - > last_clst ; /* Start at suggested cluster if it is valid */
if ( cs > = 2 & & cs < fs - > n_fatent ) scl = cs ;
ncl = 0 ;
}
}
if ( ncl = = 0 ) { /* The new cluster cannot be contiguous and find another fragment */
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 found? */
}
cs = get_fat ( obj , ncl ) ; /* Get the cluster status */
if ( cs = = 0 ) break ; /* Found a free cluster? */
if ( cs = = 1 | | cs = = 0xFFFFFFFF ) return cs ; /* Test for error */
if ( ncl = = scl ) return 0 ; /* No free cluster found? */
}
}
res = put_fat ( fs , ncl , 0xFFFFFFFF ) ; /* Mark the new cluster 'EOC' */
if ( res = = FR_OK & & clst ! = 0 ) {
res = put_fat ( fs , clst , ncl ) ; /* Link it from the previous one if needed */
}
}
if ( res = = FR_OK ) { /* Update FSINFO if function succeeded. */
fs - > last_clst = ncl ;
if ( fs - > free_clst < = fs - > n_fatent - 2 ) fs - > free_clst - - ;
fs - > fsi_flag | = 1 ;
} else {
ncl = ( res = = FR_DISK_ERR ) ? 0xFFFFFFFF : 1 ; /* Failed. Generate error status */
}
return ncl ; /* Return new cluster number or error status */
}
/*-----------------------------------------------------------------------*/
/* Directory handling - Fill a cluster with zeros */
/*-----------------------------------------------------------------------*/
static FRESULT dir_clear ( /* Returns FR_OK or FR_DISK_ERR */
FATFS * fs , /* Filesystem object */
DWORD clst /* Directory table to clear */
)
{
LBA_t sect = 0 ;
UINT n = 0 , szb = 0 ;
BYTE * ibuf = NULL ;
if ( sync_window ( fs ) ! = FR_OK ) return FR_DISK_ERR ; /* Flush disk access window */
sect = clst2sect ( fs , clst ) ; /* Top of the cluster */
fs - > winsect = sect ; /* Set window to top of the cluster */
memset ( fs - > win , 0 , sizeof fs - > win ) ; /* Clear window buffer */
{
ibuf = fs - > win ; szb = 1 ; /* Use window buffer (many single-sector writes may take a time) */
for ( n = 0 ; n < fs - > csize & & disk_write ( fs - > pdrv , ibuf , sect + n , szb ) = = RES_OK ; n + = szb ) ; /* Fill the cluster with 0 */
}
return ( n = = fs - > csize ) ? FR_OK : FR_DISK_ERR ;
}
/*-----------------------------------------------------------------------*/
/* Directory handling - Set directory index */
/*-----------------------------------------------------------------------*/
static FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */
DIR * dp , /* Pointer to directory object */
DWORD ofs /* Offset of directory table */
)
{
DWORD csz = 0 , clst = 0 ;
FATFS * fs = dp - > obj . fs ;
if ( ofs > = ( DWORD ) ( ( FF_FS_EXFAT & & fs - > fs_type = = FS_EXFAT ) ? MAX_DIR_EX : MAX_DIR ) | | ofs % SZDIRE ) { /* Check range of offset and alignment */
return FR_INT_ERR ;
}
dp - > dptr = ofs ; /* Set current offset */
clst = dp - > obj . sclust ; /* Table start cluster (0:root) */
if ( clst = = 0 & & fs - > fs_type > = FS_FAT32 ) { /* Replace cluster# 0 with root cluster# */
clst = ( DWORD ) fs - > dirbase ;
if ( FF_FS_EXFAT ) dp - > obj . stat = 0 ; /* exFAT: Root dir has an FAT chain */
}
if ( clst = = 0 ) { /* Static table (root-directory on the FAT volume) */
if ( ofs / SZDIRE > = fs - > n_rootdir ) return FR_INT_ERR ; /* Is index out of range? */
dp - > sect = fs - > dirbase ;
} else { /* Dynamic table (sub-directory or root-directory on the FAT32/exFAT volume) */
csz = ( DWORD ) fs - > csize * SS ( fs ) ; /* Bytes per cluster */
while ( ofs > = csz ) { /* Follow cluster chain */
clst = get_fat ( & dp - > obj , clst ) ; /* Get next cluster */
if ( clst = = 0xFFFFFFFF ) return FR_DISK_ERR ; /* Disk error */
if ( clst < 2 | | clst > = fs - > n_fatent ) return FR_INT_ERR ; /* Reached to end of table or internal error */
ofs - = csz ;
}
dp - > sect = clst2sect ( fs , clst ) ;
}
dp - > clust = clst ; /* Current cluster# */
if ( dp - > sect = = 0 ) return FR_INT_ERR ;
dp - > sect + = ofs / SS ( fs ) ; /* Sector# of the directory entry */
dp - > dir = fs - > win + ( ofs % SS ( fs ) ) ; /* Pointer to the entry in the win[] */
return FR_OK ;
}
/*-----------------------------------------------------------------------*/
/* Directory handling - Move directory table index next */
/*-----------------------------------------------------------------------*/
static FRESULT dir_next ( /* FR_OK(0):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 ofs = 0 , clst = 0 ;
FATFS * fs = dp - > obj . fs ;
ofs = dp - > dptr + SZDIRE ; /* Next entry */
if ( ofs > = ( DWORD ) ( ( FF_FS_EXFAT & & fs - > fs_type = = FS_EXFAT ) ? MAX_DIR_EX : MAX_DIR ) ) dp - > sect = 0 ; /* Disable it if the offset reached the max value */
if ( dp - > sect = = 0 ) return FR_NO_FILE ; /* Report EOT if it has been disabled */
if ( ofs % SS ( fs ) = = 0 ) { /* Sector changed? */
dp - > sect + + ; /* Next sector */
if ( dp - > clust = = 0 ) { /* Static table */
if ( ofs / SZDIRE > = fs - > n_rootdir ) { /* Report EOT if it reached end of static table */
dp - > sect = 0 ; return FR_NO_FILE ;
}
}
else { /* Dynamic table */
if ( ( ofs / SS ( fs ) & ( fs - > csize - 1 ) ) = = 0 ) { /* Cluster changed? */
clst = get_fat ( & dp - > obj , dp - > clust ) ; /* Get next cluster */
if ( clst < = 1 ) return FR_INT_ERR ; /* Internal error */
if ( clst = = 0xFFFFFFFF ) return FR_DISK_ERR ; /* Disk error */
if ( clst > = fs - > n_fatent ) { /* It reached end of dynamic table */
if ( ! stretch ) { /* If no stretch, report EOT */
dp - > sect = 0 ; return FR_NO_FILE ;
}
clst = create_chain ( & dp - > obj , dp - > clust ) ; /* Allocate a cluster */
if ( clst = = 0 ) return FR_DENIED ; /* No free cluster */
if ( clst = = 1 ) return FR_INT_ERR ; /* Internal error */
if ( clst = = 0xFFFFFFFF ) return FR_DISK_ERR ; /* Disk error */
if ( dir_clear ( fs , clst ) ! = FR_OK ) return FR_DISK_ERR ; /* Clean up the stretched table */
if ( FF_FS_EXFAT ) dp - > obj . stat | = 4 ; /* exFAT: The directory has been stretched */
}
dp - > clust = clst ; /* Initialize data for new cluster */
dp - > sect = clst2sect ( fs , clst ) ;
}
}
}
dp - > dptr = ofs ; /* Current entry */
dp - > dir = fs - > win + ofs % SS ( fs ) ; /* Pointer to the entry in the win[] */
return FR_OK ;
}
/*-----------------------------------------------------------------------*/
/* Directory handling - Reserve a block of directory entries */
/*-----------------------------------------------------------------------*/
static FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */
DIR * dp , /* Pointer to the directory object */
UINT n_ent /* Number of contiguous entries to allocate */
)
{
FRESULT res = 0 ;
UINT n = 0 ;
FATFS * fs = dp - > obj . fs ;
res = dir_sdi ( dp , 0 ) ;
if ( res = = FR_OK ) {
n = 0 ;
do {
res = move_window ( fs , dp - > sect ) ;
if ( res ! = FR_OK ) break ;
if ( dp - > dir [ DIR_Name ] = = DDEM | | dp - > dir [ DIR_Name ] = = 0 ) { /* Is the entry free? */
if ( + + n = = n_ent ) break ; /* Is a block of contiguous free entries found? */
} else {
n = 0 ; /* Not a free 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 ;
}
/*-----------------------------------------------------------------------*/
/* FAT: Directory handling - Load/Store start cluster number */
/*-----------------------------------------------------------------------*/
static DWORD ld_clust ( /* Returns the top cluster value of the SFN entry */
FATFS * fs , /* Pointer to the fs object */
const BYTE * dir /* Pointer to the key entry */
)
{
DWORD cl = 0 ;
cl = ld_word ( dir + DIR_FstClusLO ) ;
if ( fs - > fs_type = = FS_FAT32 ) {
cl | = ( DWORD ) ld_word ( dir + DIR_FstClusHI ) < < 16 ;
}
return cl ;
}
static void st_clust (
FATFS * fs , /* Pointer to the fs object */
BYTE * dir , /* Pointer to the key entry */
DWORD cl /* Value to be set */
)
{
st_word ( dir + DIR_FstClusLO , ( WORD ) cl ) ;
if ( fs - > fs_type = = FS_FAT32 ) {
st_word ( dir + DIR_FstClusHI , ( WORD ) ( cl > > 16 ) ) ;
}
}
/*--------------------------------------------------------*/
/* FAT-LFN: Compare a part of file name with an LFN entry */
/*--------------------------------------------------------*/
static int cmp_lfn ( /* 1:matched, 0:not matched */
const WCHAR * lfnbuf , /* Pointer to the LFN working buffer to be compared */
BYTE * dir /* Pointer to the directory entry containing the part of LFN */
)
{
UINT i = 0 , s = 0 ;
WCHAR wc = 0 , uc = 0 ;
if ( ld_word ( dir + LDIR_FstClusLO ) ! = 0 ) return 0 ; /* Check LDIR_FstClusLO */
i = ( ( dir [ LDIR_Ord ] & 0x3F ) - 1 ) * 13 ; /* Offset in the LFN buffer */
for ( wc = 1 , s = 0 ; s < 13 ; s + + ) { /* Process all characters in the entry */
uc = ld_word ( dir + LfnOfs [ s ] ) ; /* Pick an LFN character */
if ( wc ! = 0 ) {
if ( i > = FF_MAX_LFN + 1 | | ff_wtoupper ( uc ) ! = ff_wtoupper ( lfnbuf [ i + + ] ) ) { /* Compare it */
return 0 ; /* Not matched */
}
wc = uc ;
} else {
if ( uc ! = 0xFFFF ) return 0 ; /* Check filler */
}
}
if ( ( dir [ LDIR_Ord ] & LLEF ) & & wc & & lfnbuf [ i ] ) return 0 ; /* Last segment matched but different length */
return 1 ; /* The part of LFN matched */
}
/*-----------------------------------------------------*/
/* FAT-LFN: Pick a part of file name from an LFN entry */
/*-----------------------------------------------------*/
static int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */
WCHAR * lfnbuf , /* Pointer to the LFN working buffer */
BYTE * dir /* Pointer to the LFN entry */
)
{
UINT i = 0 , s = 0 ;
WCHAR wc = 0 , uc = 0 ;
if ( ld_word ( dir + LDIR_FstClusLO ) ! = 0 ) return 0 ; /* Check LDIR_FstClusLO is 0 */
i = ( ( dir [ LDIR_Ord ] & ~ LLEF ) - 1 ) * 13 ; /* Offset in the LFN buffer */
for ( wc = 1 , s = 0 ; s < 13 ; s + + ) { /* Process all characters in the entry */
uc = ld_word ( dir + LfnOfs [ s ] ) ; /* Pick an LFN character */
if ( wc ! = 0 ) {
if ( i > = FF_MAX_LFN + 1 ) return 0 ; /* Buffer overflow? */
lfnbuf [ i + + ] = wc = uc ; /* Store it */
} else {
if ( uc ! = 0xFFFF ) return 0 ; /* Check filler */
}
}
if ( dir [ LDIR_Ord ] & LLEF & & wc ! = 0 ) { /* Put terminator if it is the last LFN part and not terminated */
if ( i > = FF_MAX_LFN + 1 ) return 0 ; /* Buffer overflow? */
lfnbuf [ i ] = 0 ;
}
return 1 ; /* The part of LFN is valid */
}
/*-----------------------------------------*/
/* FAT-LFN: Create an entry of LFN entries */
/*-----------------------------------------*/
static void put_lfn (
const WCHAR * lfn , /* Pointer to the LFN */
BYTE * dir , /* Pointer to the LFN entry to be created */
BYTE ord , /* LFN order (1-20) */
BYTE sum /* Checksum of the corresponding SFN */
)
{
UINT i = 0 , s = 0 ;
WCHAR wc = 0 ;
dir [ LDIR_Chksum ] = sum ; /* Set checksum */
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 working buffer */
s = wc = 0 ;
do {
if ( wc ! = 0xFFFF ) wc = lfn [ i + + ] ; /* Get an effective character */
st_word ( dir + LfnOfs [ s ] , wc ) ; /* Put it */
if ( wc = = 0 ) wc = 0xFFFF ; /* Padding characters for following items */
} while ( + + s < 13 ) ;
if ( wc = = 0xFFFF | | ! lfn [ i ] ) ord | = LLEF ; /* Last LFN part is the start of LFN sequence */
dir [ LDIR_Ord ] = ord ; /* Set the LFN order */
}
/*-----------------------------------------------------------------------*/
/* FAT-LFN: Create a Numbered SFN */
/*-----------------------------------------------------------------------*/
static void gen_numname (
BYTE * dst , /* Pointer to the buffer to store numbered SFN */
const BYTE * src , /* Pointer to SFN in directory form */
const WCHAR * lfn , /* Pointer to LFN */
UINT seq /* Sequence number */
)
{
BYTE ns [ 8 ] = { 0 } , c = 0 ;
UINT i = 0 , j = 0 ;
WCHAR wc = 0 ;
DWORD sreg = 0 ;
memcpy ( dst , src , 11 ) ; /* Prepare the SFN to be modified */
if ( seq > 5 ) { /* In case of many collisions, generate a hash number instead of sequential number */
sreg = seq ;
while ( * lfn ) { /* Create a CRC as hash value */
wc = * lfn + + ;
for ( i = 0 ; i < 16 ; i + + ) {
sreg = ( sreg < < 1 ) + ( wc & 1 ) ;
wc > > = 1 ;
if ( sreg & 0x10000 ) sreg ^ = 0x11021 ;
}
}
seq = ( UINT ) sreg ;
}
/* Make suffix (~ + hexadecimal) */
i = 7 ;
do {
c = ( BYTE ) ( ( seq % 16 ) + ' 0 ' ) ; seq / = 16 ;
if ( c > ' 9 ' ) c + = 7 ;
ns [ i - - ] = c ;
} while ( i & & seq ) ;
ns [ i ] = ' ~ ' ;
/* Append the suffix to the SFN body */
for ( j = 0 ; j < i & & dst [ j ] ! = ' ' ; j + + ) { /* Find the offset to append */
if ( dbc_1st ( dst [ j ] ) ) { /* To avoid DBC break up */
if ( j = = i - 1 ) break ;
j + + ;
}
}
do { /* Append the suffix */
dst [ j + + ] = ( i < 8 ) ? ns [ i + + ] : ' ' ;
} while ( j < 8 ) ;
}
/*-----------------------------------------------------------------------*/
/* FAT-LFN: Calculate checksum of an SFN entry */
/*-----------------------------------------------------------------------*/
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 ;
}
/*-----------------------------------------------------------------------*/
/* Read an object from the directory */
/*-----------------------------------------------------------------------*/
# define DIR_READ_FILE(dp) dir_read(dp, 0)
# define DIR_READ_LABEL(dp) dir_read(dp, 1)
static FRESULT dir_read (
DIR * dp , /* Pointer to the directory object */
int vol /* Filtered by 0:file/directory or 1:volume label */
)
{
FRESULT res = FR_NO_FILE ;
FATFS * fs = dp - > obj . fs ;
BYTE attr = 0 , b = 0 ;
BYTE ord = 0xFF , sum = 0xFF ;
while ( dp - > sect ) {
res = move_window ( fs , dp - > sect ) ;
if ( res ! = FR_OK ) break ;
b = dp - > dir [ DIR_Name ] ; /* Test for the entry type */
if ( b = = 0 ) {
res = FR_NO_FILE ; break ; /* Reached to end of the directory */
}
{ /* On the FAT/FAT32 volume */
dp - > obj . attr = attr = dp - > dir [ DIR_Attr ] & AM_MASK ; /* Get attribute */
if ( b = = DDEM | | b = = ' . ' | | ( int ) ( ( attr & ~ AM_ARC ) = = AM_VOL ) ! = vol ) { /* An entry without valid data */
ord = 0xFF ;
} else {
if ( attr = = AM_LFN ) { /* An LFN entry is found */
if ( b & LLEF ) { /* Is it start of an LFN sequence? */
sum = dp - > dir [ LDIR_Chksum ] ;
b & = ( BYTE ) ~ LLEF ; ord = b ;
dp - > blk_ofs = dp - > dptr ;
}
/* Check LFN validity and capture it */
ord = ( b = = ord & & sum = = dp - > dir [ LDIR_Chksum ] & & pick_lfn ( fs - > lfnbuf , dp - > dir ) ) ? ord - 1 : 0xFF ;
} else { /* An SFN entry is found */
if ( ord ! = 0 | | sum ! = sum_sfn ( dp - > dir ) ) { /* Is there a valid LFN? */
dp - > blk_ofs = 0xFFFFFFFF ; /* It has no LFN. */
}
break ;
}
}
}
res = dir_next ( dp , 0 ) ; /* Next entry */
if ( res ! = FR_OK ) break ;
}
if ( res ! = FR_OK ) dp - > sect = 0 ; /* Terminate the read operation on error or EOT */
return res ;
}
/*-----------------------------------------------------------------------*/
/* Directory handling - Find an object in the directory */
/*-----------------------------------------------------------------------*/
static FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */
DIR * dp /* Pointer to the directory object with the file name */
)
{
FRESULT res = 0 ;
FATFS * fs = dp - > obj . fs ;
BYTE c = 0 ;
BYTE a = 0 , ord = 0 , sum = 0 ;
res = dir_sdi ( dp , 0 ) ; /* Rewind directory object */
if ( res ! = FR_OK ) return res ;
/* On the FAT/FAT32 volume */
ord = sum = 0xFF ; dp - > blk_ofs = 0xFFFFFFFF ; /* Reset LFN sequence */
do {
res = move_window ( fs , dp - > sect ) ;
if ( res ! = FR_OK ) break ;
c = dp - > dir [ DIR_Name ] ;
if ( c = = 0 ) { res = FR_NO_FILE ; break ; } /* Reached to end of table */
dp - > obj . attr = a = dp - > dir [ DIR_Attr ] & AM_MASK ;
if ( c = = DDEM | | ( ( a & AM_VOL ) & & a ! = AM_LFN ) ) { /* An entry without valid data */
ord = 0xFF ; dp - > blk_ofs = 0xFFFFFFFF ; /* Reset LFN sequence */
} else {
if ( a = = AM_LFN ) { /* An LFN entry is found */
if ( ! ( dp - > fn [ NSFLAG ] & NS_NOLFN ) ) {
if ( c & LLEF ) { /* Is it start of LFN sequence? */
sum = dp - > dir [ LDIR_Chksum ] ;
c & = ( BYTE ) ~ LLEF ; ord = c ; /* LFN start order */
dp - > blk_ofs = dp - > dptr ; /* Start offset of LFN */
}
/* Check validity of the LFN entry and compare it with given name */
ord = ( c = = ord & & sum = = dp - > dir [ LDIR_Chksum ] & & cmp_lfn ( fs - > lfnbuf , dp - > dir ) ) ? ord - 1 : 0xFF ;
}
} else { /* An SFN entry is found */
if ( ord = = 0 & & sum = = sum_sfn ( dp - > dir ) ) break ; /* LFN matched? */
if ( ! ( dp - > fn [ NSFLAG ] & NS_LOSS ) & & ! memcmp ( dp - > dir , dp - > fn , 11 ) ) break ; /* SFN matched? */
ord = 0xFF ; dp - > blk_ofs = 0xFFFFFFFF ; /* Reset LFN sequence */
}
}
res = dir_next ( dp , 0 ) ; /* Next entry */
} while ( res = = FR_OK ) ;
return res ;
}
/*-----------------------------------------------------------------------*/
/* Register an object to the directory */
/*-----------------------------------------------------------------------*/
static FRESULT dir_register ( /* FR_OK:succeeded, 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 = 0 ;
FATFS * fs = dp - > obj . fs ;
UINT n = 0 , len = 0 , n_ent = 0 ;
BYTE sn [ 12 ] = { 0 } , sum = 0 ;
if ( dp - > fn [ NSFLAG ] & ( NS_DOT | NS_NONAME ) ) return FR_INVALID_NAME ; /* Check name validity */
for ( len = 0 ; fs - > lfnbuf [ len ] ; len + + ) ; /* Get lfn length */
/* On the FAT/FAT32 volume */
memcpy ( sn , dp - > fn , 12 ) ;
if ( sn [ NSFLAG ] & NS_LOSS ) { /* When LFN is out of 8.3 format, generate a numbered name */
dp - > fn [ NSFLAG ] = NS_NOLFN ; /* Find only SFN */
for ( n = 1 ; n < 100 ; n + + ) {
gen_numname ( dp - > fn , sn , fs - > lfnbuf , 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' */
dp - > fn [ NSFLAG ] = sn [ NSFLAG ] ;
}
/* Create an SFN with/without LFNs. */
n_ent = ( sn [ NSFLAG ] & NS_LFN ) ? ( len + 12 ) / 13 + 1 : 1 ; /* Number of entries to allocate */
res = dir_alloc ( dp , n_ent ) ; /* Allocate entries */
if ( res = = FR_OK & & - - n_ent ) { /* Set LFN entry if needed */
res = dir_sdi ( dp , dp - > dptr - n_ent * SZDIRE ) ;
if ( res = = FR_OK ) {
sum = sum_sfn ( dp - > fn ) ; /* Checksum value of the SFN tied to the LFN */
do { /* Store LFN entries in bottom first */
res = move_window ( fs , dp - > sect ) ;
if ( res ! = FR_OK ) break ;
put_lfn ( fs - > lfnbuf , dp - > dir , ( BYTE ) n_ent , sum ) ;
fs - > wflag = 1 ;
res = dir_next ( dp , 0 ) ; /* Next entry */
} while ( res = = FR_OK & & - - n_ent ) ;
}
}
/* Set SFN entry */
if ( res = = FR_OK ) {
res = move_window ( fs , dp - > sect ) ;
if ( res = = FR_OK ) {
memset ( dp - > dir , 0 , SZDIRE ) ; /* Clean the entry */
memcpy ( dp - > dir + DIR_Name , dp - > fn , 11 ) ; /* Put SFN */
dp - > dir [ DIR_NTres ] = dp - > fn [ NSFLAG ] & ( NS_BODY | NS_EXT ) ; /* Put NT flag */
fs - > wflag = 1 ;
}
}
return res ;
}
/*-----------------------------------------------------------------------*/
/* Remove an object from the directory */
/*-----------------------------------------------------------------------*/
static FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */
DIR * dp /* Directory object pointing the entry to be removed */
)
{
FRESULT res = 0 ;
FATFS * fs = dp - > obj . fs ;
DWORD last = dp - > dptr ;
res = ( dp - > blk_ofs = = 0xFFFFFFFF ) ? FR_OK : dir_sdi ( dp , dp - > blk_ofs ) ; /* Goto top of the entry block if LFN is exist */
if ( res = = FR_OK ) {
do {
res = move_window ( fs , dp - > sect ) ;
if ( res ! = FR_OK ) break ;
if ( FF_FS_EXFAT & & fs - > fs_type = = FS_EXFAT ) { /* On the exFAT volume */
dp - > dir [ XDIR_Type ] & = 0x7F ; /* Clear the entry InUse flag. */
} else { /* On the FAT/FAT32 volume */
dp - > dir [ DIR_Name ] = DDEM ; /* Mark the entry 'deleted'. */
}
fs - > wflag = 1 ;
if ( dp - > dptr > = last ) break ; /* If reached last entry then 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 ;
}
return res ;
}
/*-----------------------------------------------------------------------*/
/* Get file information from directory entry */
/*-----------------------------------------------------------------------*/
static void get_fileinfo (
DIR * dp , /* Pointer to the directory object */
FILINFO * fno /* Pointer to the file information to be filled */
)
{
UINT si = 0 , di = 0 ;
BYTE lcf = 0 ;
WCHAR wc = 0 , hs = 0 ;
FATFS * fs = dp - > obj . fs ;
UINT nw = 0 ;
fno - > fname [ 0 ] = 0 ; /* Invaidate file info */
if ( dp - > sect = = 0 ) return ; /* Exit if read pointer has reached end of directory */
{ /* On the FAT/FAT32 volume */
if ( dp - > blk_ofs ! = 0xFFFFFFFF ) { /* Get LFN if available */
si = di = 0 ;
hs = 0 ;
while ( fs - > lfnbuf [ si ] ! = 0 ) {
wc = fs - > lfnbuf [ si + + ] ; /* Get an LFN character (UTF-16) */
if ( hs = = 0 & & IsSurrogate ( wc ) ) { /* Is it a surrogate? */
hs = wc ; continue ; /* Get low surrogate */
}
nw = put_utf ( ( DWORD ) hs < < 16 | wc , & fno - > fname [ di ] , FF_LFN_BUF - di ) ; /* Store it in API encoding */
if ( nw = = 0 ) { /* Buffer overflow or wrong char? */
di = 0 ; break ;
}
di + = nw ;
hs = 0 ;
}
if ( hs ! = 0 ) di = 0 ; /* Broken surrogate pair? */
fno - > fname [ di ] = 0 ; /* Terminate the LFN (null string means LFN is invalid) */
}
}
si = di = 0 ;
while ( si < 11 ) { /* Get SFN from SFN entry */
wc = dp - > dir [ si + + ] ; /* Get a char */
if ( wc = = ' ' ) continue ; /* Skip padding spaces */
if ( wc = = RDDEM ) wc = DDEM ; /* Restore replaced DDEM character */
if ( si = = 9 & & di < FF_SFN_BUF ) fno - > altname [ di + + ] = ' . ' ; /* Insert a . if extension is exist */
if ( dbc_1st ( ( BYTE ) wc ) & & si ! = 8 & & si ! = 11 & & dbc_2nd ( dp - > dir [ si ] ) ) { /* Make a DBC if needed */
wc = wc < < 8 | dp - > dir [ si + + ] ;
}
wc = ff_oem2uni ( wc , CODEPAGE ) ; /* ANSI/OEM -> Unicode */
if ( wc = = 0 ) { /* Wrong char in the current code page? */
di = 0 ; break ;
}
nw = put_utf ( wc , & fno - > altname [ di ] , FF_SFN_BUF - di ) ; /* Store it in API encoding */
if ( nw = = 0 ) { /* Buffer overflow? */
di = 0 ; break ;
}
di + = nw ;
}
fno - > altname [ di ] = 0 ; /* Terminate the SFN (null string means SFN is invalid) */
if ( fno - > fname [ 0 ] = = 0 ) { /* If LFN is invalid, altname[] needs to be copied to fname[] */
if ( di = = 0 ) { /* If LFN and SFN both are invalid, this object is inaccessible */
fno - > fname [ di + + ] = ' \? ' ;
} else {
for ( si = di = 0 , lcf = NS_BODY ; fno - > altname [ si ] ; si + + , di + + ) { /* Copy altname[] to fname[] with case information */
wc = ( WCHAR ) fno - > altname [ si ] ;
if ( wc = = ' . ' ) lcf = NS_EXT ;
if ( IsUpper ( wc ) & & ( dp - > dir [ DIR_NTres ] & lcf ) ) wc + = 0x20 ;
fno - > fname [ di ] = ( TCHAR ) wc ;
}
}
fno - > fname [ di ] = 0 ; /* Terminate the LFN */
if ( ! dp - > dir [ DIR_NTres ] ) fno - > altname [ 0 ] = 0 ; /* Altname is not needed if neither LFN nor case info is exist. */
}
fno - > fattrib = dp - > dir [ DIR_Attr ] & AM_MASK ; /* Attribute */
fno - > fsize = ld_dword ( dp - > dir + DIR_FileSize ) ; /* Size */
fno - > ftime = ld_word ( dp - > dir + DIR_ModTime + 0 ) ; /* Time */
fno - > fdate = ld_word ( dp - > dir + DIR_ModTime + 2 ) ; /* Date */
}
/*-----------------------------------------------------------------------*/
/* Pick a top segment and create the object name in directory form */
/*-----------------------------------------------------------------------*/
static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */
DIR * dp , /* Pointer to the directory object */
const TCHAR * * path /* Pointer to pointer to the segment in the path string */
)
{
BYTE b = 0 , cf = 0 ;
WCHAR wc = 0 ;
WCHAR * lfn = NULL ;
const TCHAR * p = NULL ;
DWORD uc = 0 ;
UINT i = 0 , ni = 0 , si = 0 , di = 0 ;
/* Create LFN into LFN working buffer */
p = * path ; lfn = dp - > obj . fs - > lfnbuf ; di = 0 ;
for ( ; ; ) {
uc = tchar2uni ( & p ) ; /* Get a character */
if ( uc = = 0xFFFFFFFF ) return FR_INVALID_NAME ; /* Invalid code or UTF decode error */
if ( uc > = 0x10000 ) lfn [ di + + ] = ( WCHAR ) ( uc > > 16 ) ; /* Store high surrogate if needed */
wc = ( WCHAR ) uc ;
if ( wc < ' ' | | IsSeparator ( wc ) ) break ; /* Break if end of the path or a separator is found */
if ( wc < 0x80 & & strchr ( " *:<>| \" \ ? \x7F " , ( int ) wc ) ) return FR_INVALID_NAME ; /* Reject illegal characters for LFN */
if ( di > = FF_MAX_LFN ) return FR_INVALID_NAME ; /* Reject too long name */
lfn [ di + + ] = wc ; /* Store the Unicode character */
}
if ( wc < ' ' ) { /* Stopped at end of the path? */
cf = NS_LAST ; /* Last segment */
} else { /* Stopped at a separator */
while ( IsSeparator ( * p ) ) p + + ; /* Skip duplicated separators if exist */
cf = 0 ; /* Next segment may follow */
if ( IsTerminator ( * p ) ) cf = NS_LAST ; /* Ignore terminating separator */
}
* path = p ; /* Return pointer to the next segment */
while ( di ) { /* Snip off trailing spaces and dots if exist */
wc = lfn [ di - 1 ] ;
if ( wc ! = ' ' & & wc ! = ' . ' ) break ;
di - - ;
}
lfn [ di ] = 0 ; /* LFN is created into the working buffer */
if ( di = = 0 ) return FR_INVALID_NAME ; /* Reject null name */
/* Create SFN in directory form */
for ( si = 0 ; lfn [ si ] = = ' ' ; si + + ) ; /* Remove leading spaces */
if ( si > 0 | | lfn [ si ] = = ' . ' ) cf | = NS_LOSS | NS_LFN ; /* Is there any leading space or dot? */
while ( di > 0 & & lfn [ di - 1 ] ! = ' . ' ) di - - ; /* Find last dot (di<=si: no extension) */
memset ( dp - > fn , ' ' , 11 ) ;
i = b = 0 ; ni = 8 ;
for ( ; ; ) {
wc = lfn [ si + + ] ; /* Get an LFN character */
if ( wc = = 0 ) break ; /* Break on end of the LFN */
if ( wc = = ' ' | | ( wc = = ' . ' & & si ! = di ) ) { /* Remove embedded spaces and dots */
cf | = NS_LOSS | NS_LFN ;
continue ;
}
if ( i > = ni | | si = = di ) { /* End of field? */
if ( ni = = 11 ) { /* Name extension overflow? */
cf | = NS_LOSS | NS_LFN ;
break ;
}
if ( si ! = di ) cf | = NS_LOSS | NS_LFN ; /* Name body overflow? */
if ( si > di ) break ; /* No name extension? */
si = di ; i = 8 ; ni = 11 ; b < < = 2 ; /* Enter name extension */
continue ;
}
if ( wc > = 0x80 ) { /* Is this an extended character? */
cf | = NS_LFN ; /* LFN entry needs to be created */
wc = ff_uni2oem ( wc , CODEPAGE ) ; /* Unicode ==> ANSI/OEM code */
if ( wc & 0x80 ) wc = ExCvt [ wc & 0x7F ] ; /* Convert extended character to upper (SBCS) */
}
if ( wc > = 0x100 ) { /* Is this a DBC? */
if ( i > = ni - 1 ) { /* Field overflow? */
cf | = NS_LOSS | NS_LFN ;
i = ni ; continue ; /* Next field */
}
dp - > fn [ i + + ] = ( BYTE ) ( wc > > 8 ) ; /* Put 1st byte */
} else { /* SBC */
if ( wc = = 0 | | strchr ( " +,;=[] " , ( int ) wc ) ) { /* Replace illegal characters for SFN */
wc = ' _ ' ; cf | = NS_LOSS | NS_LFN ; /* Lossy conversion */
} else {
if ( IsUpper ( wc ) ) { /* ASCII upper case? */
b | = 2 ;
}
if ( IsLower ( wc ) ) { /* ASCII lower case? */
b | = 1 ; wc - = 0x20 ;
}
}
}
dp - > fn [ i + + ] = ( BYTE ) wc ;
}
if ( dp - > fn [ 0 ] = = DDEM ) dp - > fn [ 0 ] = RDDEM ; /* If the first character collides with DDEM, replace it with RDDEM */
if ( ni = = 8 ) b < < = 2 ; /* Shift capital flags if no extension */
if ( ( b & 0x0C ) = = 0x0C | | ( b & 0x03 ) = = 0x03 ) cf | = NS_LFN ; /* LFN entry needs to be created if composite capitals */
if ( ! ( cf & NS_LFN ) ) { /* When LFN is in 8.3 format without extended character, NT flags are created */
if ( b & 0x01 ) cf | = NS_EXT ; /* NT flag (Extension has small capital letters only) */
if ( b & 0x04 ) cf | = NS_BODY ; /* NT flag (Body has small capital letters only) */
}
dp - > fn [ NSFLAG ] = cf ; /* SFN is created into dp->fn[] */
return FR_OK ;
}
/*-----------------------------------------------------------------------*/
/* 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 = 0 ;
BYTE ns = 0 ;
FATFS * fs = dp - > obj . fs ;
{ /* With heading separator */
while ( IsSeparator ( * path ) ) path + + ; /* Strip separators */
dp - > obj . sclust = 0 ; /* Start from the root directory */
}
if ( ( UINT ) * path < ' ' ) { /* Null path name is the origin directory itself */
dp - > fn [ NSFLAG ] = NS_NONAME ;
res = dir_sdi ( dp , 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 segment name */
ns = dp - > fn [ NSFLAG ] ;
if ( res ! = FR_OK ) { /* Failed to find the object */
if ( res = = FR_NO_FILE ) { /* Object is not found */
if ( FF_FS_RPATH & & ( ns & NS_DOT ) ) { /* If dot entry is not exist, stay there */
if ( ! ( ns & NS_LAST ) ) continue ; /* Continue to follow if not last segment */
dp - > fn [ NSFLAG ] = NS_NONAME ;
res = FR_OK ;
} 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. */
/* Get into the sub-directory */
if ( ! ( dp - > obj . attr & AM_DIR ) ) { /* It is not a sub-directory and cannot follow */
res = FR_NO_PATH ; break ;
}
{
dp - > obj . sclust = ld_clust ( fs , fs - > win + dp - > dptr % SS ( fs ) ) ; /* Open next directory */
}
}
}
return res ;
}
/*-----------------------------------------------------------------------*/
/* Get logical drive number from path name */
/*-----------------------------------------------------------------------*/
static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive number or null pointer) */
const TCHAR * * path /* Pointer to pointer to the path name */
)
{
const TCHAR * tp = NULL ;
const TCHAR * tt = NULL ;
TCHAR tc = 0 ;
int i = 0 ;
int vol = - 1 ;
tt = tp = * path ;
if ( ! tp ) return vol ; /* Invalid path name? */
do { /* Find a colon in the path */
tc = * tt + + ;
} while ( ! IsTerminator ( tc ) & & tc ! = ' : ' ) ;
if ( tc = = ' : ' ) { /* DOS/Windows style volume ID? */
i = FF_VOLUMES ;
if ( IsDigit ( * tp ) & & tp + 2 = = tt ) { /* Is there a numeric volume ID + colon? */
i = ( int ) * tp - ' 0 ' ; /* Get the LD number */
}
if ( i < FF_VOLUMES ) { /* If a volume ID is found, get the drive number and strip it */
vol = i ; /* Drive number */
* path = tt ; /* Snip the drive prefix off */
}
return vol ;
}
/* No drive prefix is found */
vol = 0 ; /* Default drive is 0 */
return vol ; /* Return the default drive */
}
/*-----------------------------------------------------------------------*/
/* GPT support functions */
/*-----------------------------------------------------------------------*/
/*-----------------------------------------------------------------------*/
/* Load a sector and check if it is an FAT VBR */
/*-----------------------------------------------------------------------*/
/* Check what the sector is */
static UINT check_fs ( /* 0:FAT/FAT32 VBR, 1:exFAT VBR, 2:Not FAT and valid BS, 3:Not FAT and invalid BS, 4:Disk error */
FATFS * fs , /* Filesystem object */
LBA_t sect /* Sector to load and check if it is an FAT-VBR or not */
)
{
WORD w = 0 , sign = 0 ;
BYTE b = 0 ;
fs - > wflag = 0 ; fs - > winsect = ( LBA_t ) 0 - 1 ; /* Invaidate window */
if ( move_window ( fs , sect ) ! = FR_OK ) return 4 ; /* Load the boot sector */
sign = ld_word ( fs - > win + BS_55AA ) ;
b = fs - > win [ BS_JmpBoot ] ;
if ( b = = 0xEB | | b = = 0xE9 | | b = = 0xE8 ) { /* Valid JumpBoot code? (short jump, near jump or near call) */
if ( sign = = 0xAA55 & & ! memcmp ( fs - > win + BS_FilSysType32 , " FAT32 " , 8 ) ) {
return 0 ; /* It is an FAT32 VBR */
}
/* FAT volumes formatted with early MS-DOS lack BS_55AA and BS_FilSysType, so FAT VBR needs to be identified without them. */
w = ld_word ( fs - > win + BPB_BytsPerSec ) ;
b = fs - > win [ BPB_SecPerClus ] ;
if ( ( w & ( w - 1 ) ) = = 0 & & w > = FF_MIN_SS & & w < = FF_MAX_SS /* Properness of sector size (512-4096 and 2^n) */
& & b ! = 0 & & ( b & ( b - 1 ) ) = = 0 /* Properness of cluster size (2^n) */
& & ld_word ( fs - > win + BPB_RsvdSecCnt ) ! = 0 /* Properness of reserved sectors (MNBZ) */
& & ( UINT ) fs - > win [ BPB_NumFATs ] - 1 < = 1 /* Properness of FATs (1 or 2) */
& & ld_word ( fs - > win + BPB_RootEntCnt ) ! = 0 /* Properness of root dir entries (MNBZ) */
& & ( ld_word ( fs - > win + BPB_TotSec16 ) > = 128 | | ld_dword ( fs - > win + BPB_TotSec32 ) > = 0x10000 ) /* Properness of volume sectors (>=128) */
& & ld_word ( fs - > win + BPB_FATSz16 ) ! = 0 ) { /* Properness of FAT size (MNBZ) */
return 0 ; /* It can be presumed an FAT VBR */
}
}
return sign = = 0xAA55 ? 2 : 3 ; /* Not an FAT VBR (valid or invalid BS) */
}
/* Find an FAT volume */
/* (It supports only generic partitioning rules, MBR, GPT and SFD) */
static UINT find_volume ( /* Returns BS status found in the hosting drive */
FATFS * fs , /* Filesystem object */
UINT part /* Partition to fined = 0:find as SFD and partitions, >0:forced partition number */
)
{
UINT fmt , i ;
DWORD mbr_pt [ 4 ] ;
fmt = check_fs ( fs , 0 ) ; /* Load sector 0 and check if it is an FAT VBR as SFD format */
if ( fmt ! = 2 & & ( fmt > = 3 | | part = = 0 ) ) return fmt ; /* Returns if it is an FAT VBR as auto scan, not a BS or disk error */
/* Sector 0 is not an FAT VBR or forced partition number wants a partition */
if ( FF_MULTI_PARTITION & & part > 4 ) return 3 ; /* MBR has 4 partitions max */
for ( i = 0 ; i < 4 ; i + + ) { /* Load partition offset in the MBR */
mbr_pt [ i ] = ld_dword ( fs - > win + MBR_Table + i * SZ_PTE + PTE_StLba ) ;
}
i = part ? part - 1 : 0 ; /* Table index to find first */
do { /* Find an FAT volume */
fmt = mbr_pt [ i ] ? check_fs ( fs , mbr_pt [ i ] ) : 3 ; /* Check if the partition is FAT */
} while ( part = = 0 & & fmt > = 2 & & + + i < 4 ) ;
return fmt ;
}
/*-----------------------------------------------------------------------*/
/* Determine logical drive number and mount the volume if needed */
/*-----------------------------------------------------------------------*/
static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */
const TCHAR * * path , /* Pointer to pointer to the path name (drive number) */
FATFS * * rfs , /* Pointer to pointer to the found filesystem object */
BYTE mode /* Desired access mode to check write protection */
)
{
int vol = 0 ;
FATFS * fs = NULL ;
DSTATUS stat = 0 ;
LBA_t bsect = 0 ;
DWORD tsect = 0 , sysect = 0 , fasize = 0 , nclst = 0 , szbfat = 0 ;
WORD nrsv = 0 ;
UINT fmt = 0 ;
/* Get logical drive number */
* rfs = 0 ;
vol = get_ldnumber ( path ) ;
if ( vol < 0 ) return FR_INVALID_DRIVE ;
/* Check if the filesystem object is valid or not */
fs = FatFs [ vol ] ; /* Get pointer to the filesystem object */
if ( ! fs ) return FR_NOT_ENABLED ; /* Is the filesystem object available? */
* rfs = fs ; /* Return pointer to the filesystem object */
mode & = ( BYTE ) ~ FA_READ ; /* Desired access mode, write access or not */
if ( fs - > fs_type ! = 0 ) { /* If the volume has been mounted */
stat = disk_status ( fs - > pdrv ) ;
if ( ! ( stat & STA_NOINIT ) ) { /* and the physical drive is kept initialized */
if ( ! FF_FS_READONLY & & mode & & ( stat & STA_PROTECT ) ) { /* Check write protection if needed */
return FR_WRITE_PROTECTED ;
}
return FR_OK ; /* The filesystem object is already valid */
}
}
/* The filesystem object is not valid. */
/* Following code attempts to mount the volume. (find an FAT volume, analyze the BPB and initialize the filesystem object) */
fs - > fs_type = 0 ; /* Invalidate the filesystem object */
stat = disk_initialize ( fs - > pdrv ) ; /* Initialize the volume hosting 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 ( ! FF_FS_READONLY & & mode & & ( stat & STA_PROTECT ) ) { /* Check disk write protection if needed */
return FR_WRITE_PROTECTED ;
}
# if FF_MAX_SS != FF_MIN_SS /* Get sector size (multiple sector size cfg only) */
if ( disk_ioctl ( fs - > pdrv , GET_SECTOR_SIZE , & SS ( fs ) ) ! = RES_OK ) return FR_DISK_ERR ;
if ( SS ( fs ) > FF_MAX_SS | | SS ( fs ) < FF_MIN_SS | | ( SS ( fs ) & ( SS ( fs ) - 1 ) ) ) return FR_DISK_ERR ;
# endif
/* Find an FAT volume on the hosting drive */
fmt = find_volume ( fs , LD2PT ( vol ) ) ;
if ( fmt = = 4 ) return FR_DISK_ERR ; /* An error occurred in the disk I/O layer */
if ( fmt > = 2 ) return FR_NO_FILESYSTEM ; /* No FAT volume is found */
bsect = fs - > winsect ; /* Volume offset in the hosting physical drive */
/* An FAT volume is found (bsect). Following code initializes the filesystem object */
{
if ( ld_word ( fs - > win + BPB_BytsPerSec ) ! = SS ( fs ) ) return FR_NO_FILESYSTEM ; /* (BPB_BytsPerSec must be equal to the physical sector size) */
fasize = ld_word ( fs - > win + BPB_FATSz16 ) ; /* Number of sectors per FAT */
if ( fasize = = 0 ) fasize = ld_dword ( fs - > win + BPB_FATSz32 ) ;
fs - > fsize = fasize ;
fs - > n_fats = fs - > win [ BPB_NumFATs ] ; /* Number of FATs */
if ( fs - > n_fats ! = 1 & & fs - > n_fats ! = 2 ) return FR_NO_FILESYSTEM ; /* (Must be 1 or 2) */
fasize * = fs - > n_fats ; /* Number of sectors for FAT area */
fs - > csize = fs - > win [ BPB_SecPerClus ] ; /* Cluster size */
if ( fs - > csize = = 0 | | ( fs - > csize & ( fs - > csize - 1 ) ) ) return FR_NO_FILESYSTEM ; /* (Must be power of 2) */
fs - > n_rootdir = ld_word ( fs - > win + BPB_RootEntCnt ) ; /* Number of root directory entries */
if ( fs - > n_rootdir % ( SS ( fs ) / SZDIRE ) ) return FR_NO_FILESYSTEM ; /* (Must be sector aligned) */
tsect = ld_word ( fs - > win + BPB_TotSec16 ) ; /* Number of sectors on the volume */
if ( tsect = = 0 ) tsect = ld_dword ( fs - > win + BPB_TotSec32 ) ;
nrsv = ld_word ( fs - > win + BPB_RsvdSecCnt ) ; /* Number of reserved sectors */
if ( nrsv = = 0 ) return FR_NO_FILESYSTEM ; /* (Must not be 0) */
/* Determine the FAT sub type */
sysect = nrsv + fasize + fs - > n_rootdir / ( SS ( fs ) / SZDIRE ) ; /* RSV + FAT + DIR */
if ( tsect < sysect ) return FR_NO_FILESYSTEM ; /* (Invalid volume size) */
nclst = ( tsect - sysect ) / fs - > csize ; /* Number of clusters */
if ( nclst = = 0 ) return FR_NO_FILESYSTEM ; /* (Invalid volume size) */
fmt = 0 ;
if ( nclst < = MAX_FAT32 ) fmt = FS_FAT32 ;
if ( nclst < = MAX_FAT16 ) fmt = FS_FAT16 ;
if ( nclst < = MAX_FAT12 ) fmt = FS_FAT12 ;
if ( fmt = = 0 ) return FR_NO_FILESYSTEM ;
/* 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 ( ld_word ( fs - > win + BPB_FSVer32 ) ! = 0 ) return FR_NO_FILESYSTEM ; /* (Must be FAT32 revision 0.0) */
if ( fs - > n_rootdir ! = 0 ) return FR_NO_FILESYSTEM ; /* (BPB_RootEntCnt must be 0) */
fs - > dirbase = ld_dword ( fs - > win + BPB_RootClus32 ) ; /* Root directory start cluster */
szbfat = fs - > n_fatent * 4 ; /* (Needed FAT size) */
} else {
if ( fs - > n_rootdir = = 0 ) 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 ) ) return FR_NO_FILESYSTEM ; /* (BPB_FATSz must not be less than the size needed) */
/* Get FSInfo if available */
fs - > last_clst = fs - > free_clst = 0xFFFFFFFF ; /* Initialize cluster allocation information */
fs - > fsi_flag = 0x80 ;
# if (FF_FS_NOFSINFO & 3) != 3
if ( fmt = = FS_FAT32 /* Allow to update FSInfo only if BPB_FSInfo32 == 1 */
& & ld_word ( fs - > win + BPB_FSInfo32 ) = = 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 (FF_FS_NOFSINFO & 1) == 0
fs - > free_clst = ld_dword ( fs - > win + FSI_Free_Count ) ;
# endif
# if (FF_FS_NOFSINFO & 2) == 0
fs - > last_clst = ld_dword ( fs - > win + FSI_Nxt_Free ) ;
# endif
}
}
# endif /* (FF_FS_NOFSINFO & 3) != 3 */
}
fs - > fs_type = ( BYTE ) fmt ; /* FAT sub-type (the filesystem object gets valid) */
fs - > id = + + Fsid ; /* Volume mount ID */
fs - > lfnbuf = LfnBuf ; /* Static LFN working buffer */
return FR_OK ;
}
/*-----------------------------------------------------------------------*/
/* Check if the file/directory object is valid or not */
/*-----------------------------------------------------------------------*/
static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */
FFOBJID * obj , /* Pointer to the FFOBJID, the 1st member in the FIL/DIR structure, to check validity */
FATFS * * rfs /* Pointer to pointer to the owner filesystem object to return */
)
{
FRESULT res = FR_INVALID_OBJECT ;
if ( obj & & obj - > fs & & obj - > fs - > fs_type & & obj - > id = = obj - > fs - > id ) { /* Test if the object is valid */
if ( ! ( disk_status ( obj - > fs - > pdrv ) & STA_NOINIT ) ) { /* Test if the hosting phsical drive is kept initialized */
res = FR_OK ;
}
}
* rfs = ( res = = FR_OK ) ? obj - > fs : 0 ; /* Return corresponding filesystem object if it is valid */
return res ;
}
/*---------------------------------------------------------------------------
Public Functions ( FatFs API )
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*-----------------------------------------------------------------------*/
/* Mount/Unmount a Logical Drive */
/*-----------------------------------------------------------------------*/
FRESULT f_mount (
FATFS * fs , /* Pointer to the filesystem object to be registered (NULL:unmount)*/
const TCHAR * path , /* Logical drive number to be mounted/unmounted */
BYTE opt /* Mount option: 0=Do not mount (delayed mount), 1=Mount immediately */
)
{
FATFS * cfs = NULL ;
int vol = 0 ;
FRESULT res = 0 ;
const TCHAR * rp = path ;
/* Get volume ID (logical drive number) */
vol = get_ldnumber ( & rp ) ;
if ( vol < 0 ) return FR_INVALID_DRIVE ;
cfs = FatFs [ vol ] ; /* Pointer to the filesystem object of the volume */
if ( cfs ) { /* Unregister current filesystem object if regsitered */
FatFs [ vol ] = 0 ;
cfs - > fs_type = 0 ; /* Invalidate the filesystem object to be unregistered */
}
if ( fs ) { /* Register new filesystem object */
fs - > pdrv = LD2PD ( vol ) ; /* Volume hosting physical drive */
fs - > fs_type = 0 ; /* Invalidate the new filesystem object */
FatFs [ vol ] = fs ; /* Register new fs object */
}
if ( opt = = 0 ) return FR_OK ; /* Do not mount now, it will be mounted in subsequent file functions */
res = mount_volume ( & path , & fs , 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 open mode flags */
)
{
FRESULT res = 0 ;
DIR dj = { 0 } ;
FATFS * fs = NULL ;
DWORD cl = 0 , bcs = 0 , clst = 0 , tm = 0 ;
LBA_t sc = 0 ;
FSIZE_t ofs = 0 ;
DEF_NAMBUF
if ( ! fp ) return FR_INVALID_OBJECT ;
/* Get logical drive number */
mode & = FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND ;
res = mount_volume ( & path , & fs , mode ) ;
if ( res = = FR_OK ) {
dj . obj . fs = fs ;
INIT_NAMBUF ( fs ) ;
res = follow_path ( & dj , path ) ; /* Follow the file path */
if ( res = = FR_OK ) {
if ( dj . fn [ NSFLAG ] & NS_NONAME ) { /* Origin directory itself? */
res = FR_INVALID_NAME ;
}
}
/* Create or Open a file */
if ( mode & ( FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW ) ) {
if ( res ! = FR_OK ) { /* No file, create new */
if ( res = = FR_NO_FILE ) { /* There is no file to open, create a new entry */
res = dir_register ( & dj ) ;
}
mode | = FA_CREATE_ALWAYS ; /* File is created */
}
else { /* Any object with the same name is already existing */
if ( dj . obj . attr & ( AM_RDO | AM_DIR ) ) { /* Cannot overwrite it (R/O or DIR) */
res = FR_DENIED ;
} else {
if ( mode & FA_CREATE_NEW ) res = FR_EXIST ; /* Cannot create as new file */
}
}
if ( res = = FR_OK & & ( mode & FA_CREATE_ALWAYS ) ) { /* Truncate the file if overwrite mode */
{
/* Set directory entry initial state */
tm = GET_FATTIME ( ) ; /* Set created time */
st_dword ( dj . dir + DIR_CrtTime , tm ) ;
st_dword ( dj . dir + DIR_ModTime , tm ) ;
cl = ld_clust ( fs , dj . dir ) ; /* Get current cluster chain */
dj . dir [ DIR_Attr ] = AM_ARC ; /* Reset attribute */
st_clust ( fs , dj . dir , 0 ) ; /* Reset file allocation info */
st_dword ( dj . dir + DIR_FileSize , 0 ) ;
fs - > wflag = 1 ;
if ( cl ! = 0 ) { /* Remove the cluster chain if exist */
sc = fs - > winsect ;
res = remove_chain ( & dj . obj , cl , 0 ) ;
if ( res = = FR_OK ) {
res = move_window ( fs , sc ) ;
fs - > last_clst = cl - 1 ; /* Reuse the cluster hole */
}
}
}
}
}
else { /* Open an existing file */
if ( res = = FR_OK ) { /* Is the object exsiting? */
if ( dj . obj . attr & AM_DIR ) { /* File open against a directory */
res = FR_NO_FILE ;
} else {
if ( ( mode & FA_WRITE ) & & ( dj . obj . attr & AM_RDO ) ) { /* Write mode open against R/O file */
res = FR_DENIED ;
}
}
}
}
if ( res = = FR_OK ) {
if ( mode & FA_CREATE_ALWAYS ) mode | = FA_MODIFIED ; /* Set file change flag if created or overwritten */
fp - > dir_sect = fs - > winsect ; /* Pointer to the directory entry */
fp - > dir_ptr = dj . dir ;
}
if ( res = = FR_OK ) {
{
fp - > obj . sclust = ld_clust ( fs , dj . dir ) ; /* Get object allocation info */
fp - > obj . objsize = ld_dword ( dj . dir + DIR_FileSize ) ;
}
fp - > obj . fs = fs ; /* Validate the file object */
fp - > obj . id = fs - > id ;
fp - > flag = mode ; /* Set file access mode */
fp - > err = 0 ; /* Clear error flag */
fp - > sect = 0 ; /* Invalidate current data sector */
fp - > fptr = 0 ; /* Set file pointer top of the file */
memset ( fp - > buf , 0 , sizeof fp - > buf ) ; /* Clear sector buffer */
if ( ( mode & FA_SEEKEND ) & & fp - > obj . objsize > 0 ) { /* Seek to end of file if FA_OPEN_APPEND is specified */
fp - > fptr = fp - > obj . objsize ; /* Offset to seek */
bcs = ( DWORD ) fs - > csize * SS ( fs ) ; /* Cluster size in byte */
clst = fp - > obj . sclust ; /* Follow the cluster chain */
for ( ofs = fp - > obj . objsize ; res = = FR_OK & & ofs > bcs ; ofs - = bcs ) {
clst = get_fat ( & fp - > obj , clst ) ;
if ( clst < = 1 ) res = FR_INT_ERR ;
if ( clst = = 0xFFFFFFFF ) res = FR_DISK_ERR ;
}
fp - > clust = clst ;
if ( res = = FR_OK & & ofs % SS ( fs ) ) { /* Fill sector buffer if not on the sector boundary */
sc = clst2sect ( fs , clst ) ;
if ( sc = = 0 ) {
res = FR_INT_ERR ;
} else {
fp - > sect = sc + ( DWORD ) ( ofs / SS ( fs ) ) ;
if ( disk_read ( fs - > pdrv , fp - > buf , fp - > sect , 1 ) ! = RES_OK ) res = FR_DISK_ERR ;
}
}
}
}
FREE_NAMBUF ( ) ;
}
if ( res ! = FR_OK ) fp - > obj . fs = 0 ; /* Invalidate file object on error */
LEAVE_FF ( fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* Read File */
/*-----------------------------------------------------------------------*/
FRESULT f_read (
FIL * fp , /* Open file to be read */
void * buff , /* Data buffer to store the read data */
UINT btr , /* Number of bytes to read */
UINT * br /* Number of bytes read */
)
{
FRESULT res = 0 ;
FATFS * fs = NULL ;
DWORD clst = 0 ;
LBA_t sect = 0 ;
FSIZE_t remain = 0 ;
UINT rcnt = 0 , cc = 0 , csect = 0 ;
BYTE * rbuff = ( BYTE * ) buff ;
* br = 0 ; /* Clear read byte counter */
res = validate ( & fp - > obj , & fs ) ; /* Check validity of the file object */
if ( res ! = FR_OK | | ( res = ( FRESULT ) fp - > err ) ! = FR_OK ) LEAVE_FF ( fs , res ) ; /* Check validity */
if ( ! ( fp - > flag & FA_READ ) ) LEAVE_FF ( fs , FR_DENIED ) ; /* Check access mode */
remain = fp - > obj . objsize - fp - > fptr ;
if ( btr > remain ) btr = ( UINT ) remain ; /* Truncate btr by remaining bytes */
for ( ; btr > 0 ; btr - = rcnt , * br + = rcnt , rbuff + = rcnt , fp - > fptr + = rcnt ) { /* Repeat until btr bytes read */
if ( fp - > fptr % SS ( fs ) = = 0 ) { /* On the sector boundary? */
csect = ( UINT ) ( fp - > fptr / SS ( fs ) & ( fs - > csize - 1 ) ) ; /* Sector offset in the cluster */
if ( csect = = 0 ) { /* On the cluster boundary? */
if ( fp - > fptr = = 0 ) { /* On the top of the file? */
clst = fp - > obj . sclust ; /* Follow cluster chain from the origin */
} else { /* Middle or end of the file */
{
clst = get_fat ( & fp - > obj , fp - > clust ) ; /* Follow cluster chain on the FAT */
}
}
if ( clst < 2 ) ABORT ( fs , FR_INT_ERR ) ;
if ( clst = = 0xFFFFFFFF ) ABORT ( fs , FR_DISK_ERR ) ;
fp - > clust = clst ; /* Update current cluster */
}
sect = clst2sect ( fs , fp - > clust ) ; /* Get current sector */
if ( sect = = 0 ) ABORT ( fs , FR_INT_ERR ) ;
sect + = csect ;
cc = btr / SS ( fs ) ; /* When remaining bytes >= sector size, */
if ( cc > 0 ) { /* Read maximum contiguous sectors directly */
if ( csect + cc > fs - > csize ) { /* Clip at cluster boundary */
cc = fs - > csize - csect ;
}
if ( disk_read ( fs - > pdrv , rbuff , sect , cc ) ! = RES_OK ) ABORT ( fs , FR_DISK_ERR ) ;
if ( ( fp - > flag & FA_DIRTY ) & & fp - > sect - sect < cc ) {
memcpy ( rbuff + ( ( fp - > sect - sect ) * SS ( fs ) ) , fp - > buf , SS ( fs ) ) ;
}
rcnt = SS ( fs ) * cc ; /* Number of bytes transferred */
continue ;
}
if ( fp - > sect ! = sect ) { /* Load data sector if not in cache */
if ( fp - > flag & FA_DIRTY ) { /* Write-back dirty sector cache */
if ( disk_write ( fs - > pdrv , fp - > buf , fp - > sect , 1 ) ! = RES_OK ) ABORT ( fs , FR_DISK_ERR ) ;
fp - > flag & = ( BYTE ) ~ FA_DIRTY ;
}
if ( disk_read ( fs - > pdrv , fp - > buf , sect , 1 ) ! = RES_OK ) ABORT ( fs , FR_DISK_ERR ) ; /* Fill sector cache */
}
fp - > sect = sect ;
}
rcnt = SS ( fs ) - ( UINT ) fp - > fptr % SS ( fs ) ; /* Number of bytes remains in the sector */
if ( rcnt > btr ) rcnt = btr ; /* Clip it by btr if needed */
memcpy ( rbuff , fp - > buf + fp - > fptr % SS ( fs ) , rcnt ) ; /* Extract partial sector */
}
LEAVE_FF ( fs , FR_OK ) ;
}
/*-----------------------------------------------------------------------*/
/* Write File */
/*-----------------------------------------------------------------------*/
FRESULT f_write (
FIL * fp , /* Open file to be written */
const void * buff , /* Data to be written */
UINT btw , /* Number of bytes to write */
UINT * bw /* Number of bytes written */
)
{
FRESULT res = 0 ;
FATFS * fs = NULL ;
DWORD clst = 0 ;
LBA_t sect = 0 ;
UINT wcnt = 0 , cc = 0 , csect = 0 ;
const BYTE * wbuff = ( const BYTE * ) buff ;
* bw = 0 ; /* Clear write byte counter */
res = validate ( & fp - > obj , & fs ) ; /* Check validity of the file object */
if ( res ! = FR_OK | | ( res = ( FRESULT ) fp - > err ) ! = FR_OK ) LEAVE_FF ( fs , res ) ; /* Check validity */
if ( ! ( fp - > flag & FA_WRITE ) ) LEAVE_FF ( fs , FR_DENIED ) ; /* Check access mode */
/* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */
if ( ( ! FF_FS_EXFAT | | fs - > fs_type ! = FS_EXFAT ) & & ( DWORD ) ( fp - > fptr + btw ) < ( DWORD ) fp - > fptr ) {
btw = ( UINT ) ( 0xFFFFFFFF - ( DWORD ) fp - > fptr ) ;
}
for ( ; btw > 0 ; btw - = wcnt , * bw + = wcnt , wbuff + = wcnt , fp - > fptr + = wcnt , fp - > obj . objsize = ( fp - > fptr > fp - > obj . objsize ) ? fp - > fptr : fp - > obj . objsize ) { /* Repeat until all data written */
if ( fp - > fptr % SS ( fs ) = = 0 ) { /* On the sector boundary? */
csect = ( UINT ) ( fp - > fptr / SS ( fs ) ) & ( fs - > csize - 1 ) ; /* Sector offset in the cluster */
if ( csect = = 0 ) { /* On the cluster boundary? */
if ( fp - > fptr = = 0 ) { /* On the top of the file? */
clst = fp - > obj . sclust ; /* Follow from the origin */
if ( clst = = 0 ) { /* If no cluster is allocated, */
clst = create_chain ( & fp - > obj , 0 ) ; /* create a new cluster chain */
}
} else { /* On the middle or end of the file */
{
clst = create_chain ( & fp - > obj , 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 ( fs , FR_INT_ERR ) ;
if ( clst = = 0xFFFFFFFF ) ABORT ( fs , FR_DISK_ERR ) ;
fp - > clust = clst ; /* Update current cluster */
if ( fp - > obj . sclust = = 0 ) fp - > obj . sclust = clst ; /* Set start cluster if the first write */
}
if ( fp - > flag & FA_DIRTY ) { /* Write-back sector cache */
if ( disk_write ( fs - > pdrv , fp - > buf , fp - > sect , 1 ) ! = RES_OK ) ABORT ( fs , FR_DISK_ERR ) ;
fp - > flag & = ( BYTE ) ~ FA_DIRTY ;
}
sect = clst2sect ( fs , fp - > clust ) ; /* Get current sector */
if ( sect = = 0 ) ABORT ( fs , FR_INT_ERR ) ;
sect + = csect ;
cc = btw / SS ( fs ) ; /* When remaining bytes >= sector size, */
if ( cc > 0 ) { /* Write maximum contiguous sectors directly */
if ( csect + cc > fs - > csize ) { /* Clip at cluster boundary */
cc = fs - > csize - csect ;
}
if ( disk_write ( fs - > pdrv , wbuff , sect , cc ) ! = RES_OK ) ABORT ( fs , FR_DISK_ERR ) ;
if ( fp - > sect - sect < cc ) { /* Refill sector cache if it gets invalidated by the direct write */
memcpy ( fp - > buf , wbuff + ( ( fp - > sect - sect ) * SS ( fs ) ) , SS ( fs ) ) ;
fp - > flag & = ( BYTE ) ~ FA_DIRTY ;
}
wcnt = SS ( fs ) * cc ; /* Number of bytes transferred */
continue ;
}
if ( fp - > sect ! = sect & & /* Fill sector cache with file data */
fp - > fptr < fp - > obj . objsize & &
disk_read ( fs - > pdrv , fp - > buf , sect , 1 ) ! = RES_OK ) {
ABORT ( fs , FR_DISK_ERR ) ;
}
fp - > sect = sect ;
}
wcnt = SS ( fs ) - ( UINT ) fp - > fptr % SS ( fs ) ; /* Number of bytes remains in the sector */
if ( wcnt > btw ) wcnt = btw ; /* Clip it by btw if needed */
memcpy ( fp - > buf + fp - > fptr % SS ( fs ) , wbuff , wcnt ) ; /* Fit data to the sector */
fp - > flag | = FA_DIRTY ;
}
fp - > flag | = FA_MODIFIED ; /* Set file change flag */
LEAVE_FF ( fs , FR_OK ) ;
}
/*-----------------------------------------------------------------------*/
/* Synchronize the File */
/*-----------------------------------------------------------------------*/
FRESULT f_sync (
FIL * fp /* Open file to be synced */
)
{
FRESULT res = 0 ;
FATFS * fs = NULL ;
DWORD tm = 0 ;
BYTE * dir = NULL ;
res = validate ( & fp - > obj , & fs ) ; /* Check validity of the file object */
if ( res = = FR_OK ) {
if ( fp - > flag & FA_MODIFIED ) { /* Is there any change to the file? */
if ( fp - > flag & FA_DIRTY ) { /* Write-back cached data if needed */
if ( disk_write ( fs - > pdrv , fp - > buf , fp - > sect , 1 ) ! = RES_OK ) LEAVE_FF ( fs , FR_DISK_ERR ) ;
fp - > flag & = ( BYTE ) ~ FA_DIRTY ;
}
/* Update the directory entry */
tm = GET_FATTIME ( ) ; /* Modified time */
{
res = move_window ( fs , fp - > dir_sect ) ;
if ( res = = FR_OK ) {
dir = fp - > dir_ptr ;
dir [ DIR_Attr ] | = AM_ARC ; /* Set archive attribute to indicate that the file has been changed */
st_clust ( fp - > obj . fs , dir , fp - > obj . sclust ) ; /* Update file allocation information */
st_dword ( dir + DIR_FileSize , ( DWORD ) fp - > obj . objsize ) ; /* Update file size */
st_dword ( dir + DIR_ModTime , tm ) ; /* Update modified time */
st_word ( dir + DIR_LstAccDate , 0 ) ;
fs - > wflag = 1 ;
res = sync_fs ( fs ) ; /* Restore it to the directory */
fp - > flag & = ( BYTE ) ~ FA_MODIFIED ;
}
}
}
}
LEAVE_FF ( fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* Close File */
/*-----------------------------------------------------------------------*/
FRESULT f_close (
FIL * fp /* Open file to be closed */
)
{
FRESULT res = 0 ;
FATFS * fs = NULL ;
res = f_sync ( fp ) ; /* Flush cached data */
if ( res = = FR_OK )
{
res = validate ( & fp - > obj , & fs ) ; /* Lock volume */
if ( res = = FR_OK ) {
fp - > obj . fs = 0 ; /* Invalidate file object */
}
}
return res ;
}
/*-----------------------------------------------------------------------*/
/* Seek File Read/Write Pointer */
/*-----------------------------------------------------------------------*/
FRESULT f_lseek (
FIL * fp , /* Pointer to the file object */
FSIZE_t ofs /* File pointer from top of file */
)
{
FRESULT res = 0 ;
FATFS * fs = NULL ;
DWORD clst = 0 , bcs = 0 ;
LBA_t nsect = 0 ;
FSIZE_t ifptr = 0 ;
res = validate ( & fp - > obj , & fs ) ; /* Check validity of the file object */
if ( res = = FR_OK ) res = ( FRESULT ) fp - > err ;
if ( res ! = FR_OK ) LEAVE_FF ( fs , res ) ;
/* Normal Seek */
{
if ( ofs > fp - > obj . objsize & & ( FF_FS_READONLY | | ! ( fp - > flag & FA_WRITE ) ) ) { /* In read-only mode, clip offset with the file size */
ofs = fp - > obj . objsize ;
}
ifptr = fp - > fptr ;
fp - > fptr = nsect = 0 ;
if ( ofs > 0 ) {
bcs = ( DWORD ) fs - > csize * SS ( fs ) ; /* Cluster size (byte) */
if ( ifptr > 0 & &
( ofs - 1 ) / bcs > = ( ifptr - 1 ) / bcs ) { /* When seek to same or following cluster, */
fp - > fptr = ( ifptr - 1 ) & ~ ( FSIZE_t ) ( bcs - 1 ) ; /* start from the current cluster */
ofs - = fp - > fptr ;
clst = fp - > clust ;
} else { /* When seek to back cluster, */
clst = fp - > obj . sclust ; /* start from the first cluster */
if ( clst = = 0 ) { /* If no cluster chain, create a new chain */
clst = create_chain ( & fp - > obj , 0 ) ;
if ( clst = = 1 ) ABORT ( fs , FR_INT_ERR ) ;
if ( clst = = 0xFFFFFFFF ) ABORT ( fs , FR_DISK_ERR ) ;
fp - > obj . sclust = clst ;
}
fp - > clust = clst ;
}
if ( clst ! = 0 ) {
while ( ofs > bcs ) { /* Cluster following loop */
ofs - = bcs ; fp - > fptr + = bcs ;
if ( fp - > flag & FA_WRITE ) { /* Check if in write mode or not */
if ( FF_FS_EXFAT & & fp - > fptr > fp - > obj . objsize ) { /* No FAT chain object needs correct objsize to generate FAT value */
fp - > obj . objsize = fp - > fptr ;
fp - > flag | = FA_MODIFIED ;
}
clst = create_chain ( & fp - > obj , clst ) ; /* Follow chain with forceed stretch */
if ( clst = = 0 ) { /* Clip file size in case of disk full */
ofs = 0 ; break ;
}
} else
{
clst = get_fat ( & fp - > obj , clst ) ; /* Follow cluster chain if not in write mode */
}
if ( clst = = 0xFFFFFFFF ) ABORT ( fs , FR_DISK_ERR ) ;
if ( clst < = 1 | | clst > = fs - > n_fatent ) ABORT ( fs , FR_INT_ERR ) ;
fp - > clust = clst ;
}
fp - > fptr + = ofs ;
if ( ofs % SS ( fs ) ) {
nsect = clst2sect ( fs , clst ) ; /* Current sector */
if ( nsect = = 0 ) ABORT ( fs , FR_INT_ERR ) ;
nsect + = ( DWORD ) ( ofs / SS ( fs ) ) ;
}
}
}
if ( ! FF_FS_READONLY & & fp - > fptr > fp - > obj . objsize ) { /* Set file change flag if the file size is extended */
fp - > obj . objsize = fp - > fptr ;
fp - > flag | = FA_MODIFIED ;
}
if ( fp - > fptr % SS ( fs ) & & nsect ! = fp - > sect ) { /* Fill sector cache if needed */
if ( fp - > flag & FA_DIRTY ) { /* Write-back dirty sector cache */
if ( disk_write ( fs - > pdrv , fp - > buf , fp - > sect , 1 ) ! = RES_OK ) ABORT ( fs , FR_DISK_ERR ) ;
fp - > flag & = ( BYTE ) ~ FA_DIRTY ;
}
if ( disk_read ( fs - > pdrv , fp - > buf , nsect , 1 ) ! = RES_OK ) ABORT ( fs , FR_DISK_ERR ) ; /* Fill sector cache */
fp - > sect = nsect ;
}
}
LEAVE_FF ( fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* 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 = 0 ;
FATFS * fs = NULL ;
DEF_NAMBUF
if ( ! dp ) return FR_INVALID_OBJECT ;
/* Get logical drive */
res = mount_volume ( & path , & fs , 0 ) ;
if ( res = = FR_OK ) {
dp - > obj . fs = fs ;
INIT_NAMBUF ( fs ) ;
res = follow_path ( dp , path ) ; /* Follow the path to the directory */
if ( res = = FR_OK ) { /* Follow completed */
if ( ! ( dp - > fn [ NSFLAG ] & NS_NONAME ) ) { /* It is not the origin directory itself */
if ( dp - > obj . attr & AM_DIR ) { /* This object is a sub-directory */
{
dp - > obj . sclust = ld_clust ( fs , dp - > dir ) ; /* Get object allocation info */
}
} else { /* This object is a file */
res = FR_NO_PATH ;
}
}
if ( res = = FR_OK ) {
dp - > obj . id = fs - > id ;
res = dir_sdi ( dp , 0 ) ; /* Rewind directory */
}
}
FREE_NAMBUF ( ) ;
if ( res = = FR_NO_FILE ) res = FR_NO_PATH ;
}
if ( res ! = FR_OK ) dp - > obj . fs = 0 ; /* Invalidate the directory object if function failed */
LEAVE_FF ( fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* Close Directory */
/*-----------------------------------------------------------------------*/
FRESULT f_closedir (
DIR * dp /* Pointer to the directory object to be closed */
)
{
FRESULT res = 0 ;
FATFS * fs = NULL ;
res = validate ( & dp - > obj , & fs ) ; /* Check validity of the file object */
if ( res = = FR_OK ) {
dp - > obj . fs = 0 ; /* Invalidate directory object */
}
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 = 0 ;
FATFS * fs = NULL ;
DEF_NAMBUF
res = validate ( & dp - > obj , & fs ) ; /* Check validity of the directory object */
if ( res = = FR_OK ) {
if ( ! fno ) {
res = dir_sdi ( dp , 0 ) ; /* Rewind the directory object */
} else {
INIT_NAMBUF ( fs ) ;
res = DIR_READ_FILE ( dp ) ; /* Read an item */
if ( res = = FR_NO_FILE ) res = FR_OK ; /* Ignore end of directory */
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 ) res = FR_OK ; /* Ignore end of directory now */
}
FREE_NAMBUF ( ) ;
}
}
LEAVE_FF ( fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* Get File Status */
/*-----------------------------------------------------------------------*/
FRESULT f_stat (
const TCHAR * path , /* Pointer to the file path */
FILINFO * fno /* Pointer to file information to return */
)
{
FRESULT res = 0 ;
DIR dj = { 0 } ;
DEF_NAMBUF
/* Get logical drive */
res = mount_volume ( & path , & dj . obj . fs , 0 ) ;
if ( res = = FR_OK ) {
INIT_NAMBUF ( dj . obj . fs ) ;
res = follow_path ( & dj , path ) ; /* Follow the file path */
if ( res = = FR_OK ) { /* Follow completed */
if ( dj . fn [ NSFLAG ] & NS_NONAME ) { /* It is origin directory */
res = FR_INVALID_NAME ;
} else { /* Found an object */
if ( fno ) get_fileinfo ( & dj , fno ) ;
}
}
FREE_NAMBUF ( ) ;
}
LEAVE_FF ( dj . obj . fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* Get Number of Free Clusters */
/*-----------------------------------------------------------------------*/
FRESULT f_getfree (
const TCHAR * path , /* Logical drive number */
DWORD * nclst , /* Pointer to a variable to return number of free clusters */
FATFS * * fatfs /* Pointer to return pointer to corresponding filesystem object */
)
{
FRESULT res = 0 ;
FATFS * fs = NULL ;
DWORD nfree = 0 , clst = 0 , stat = 0 ;
LBA_t sect = 0 ;
UINT i = 0 ;
FFOBJID obj = { 0 } ;
/* Get logical drive */
res = mount_volume ( & path , & fs , 0 ) ;
if ( res = = FR_OK ) {
* fatfs = fs ; /* Return ptr to the fs object */
/* If free_clst is valid, return it without full FAT scan */
if ( fs - > free_clst < = fs - > n_fatent - 2 ) {
* nclst = fs - > free_clst ;
} else {
/* Scan FAT to obtain number of free clusters */
nfree = 0 ;
if ( fs - > fs_type = = FS_FAT12 ) { /* FAT12: Scan bit field FAT entries */
clst = 2 ; obj . fs = fs ;
do {
stat = get_fat ( & obj , clst ) ;
if ( stat = = 0xFFFFFFFF ) {
res = FR_DISK_ERR ; break ;
}
if ( stat = = 1 ) {
res = FR_INT_ERR ; break ;
}
if ( stat = = 0 ) nfree + + ;
} while ( + + clst < fs - > n_fatent ) ;
} else {
{ /* FAT16/32: Scan WORD/DWORD FAT entries */
clst = fs - > n_fatent ; /* Number of entries */
sect = fs - > fatbase ; /* Top of the FAT */
i = 0 ; /* Offset in the sector */
do { /* Counts numbuer of entries with zero in the FAT */
if ( i = = 0 ) { /* New sector? */
res = move_window ( fs , sect + + ) ;
if ( res ! = FR_OK ) break ;
}
if ( fs - > fs_type = = FS_FAT16 ) {
if ( ld_word ( fs - > win + i ) = = 0 ) nfree + + ;
i + = 2 ;
} else {
if ( ( ld_dword ( fs - > win + i ) & 0x0FFFFFFF ) = = 0 ) nfree + + ;
i + = 4 ;
}
i % = SS ( fs ) ;
} while ( - - clst ) ;
}
}
if ( res = = FR_OK ) { /* Update parameters if succeeded */
* nclst = nfree ; /* Return the free clusters */
fs - > free_clst = nfree ; /* Now free_clst is valid */
fs - > fsi_flag | = 1 ; /* FAT32: FSInfo is to be updated */
}
}
}
LEAVE_FF ( fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* Truncate File */
/*-----------------------------------------------------------------------*/
FRESULT f_truncate (
FIL * fp /* Pointer to the file object */
)
{
FRESULT res = 0 ;
FATFS * fs = NULL ;
DWORD ncl = 0 ;
res = validate ( & fp - > obj , & fs ) ; /* Check validity of the file object */
if ( res ! = FR_OK | | ( res = ( FRESULT ) fp - > err ) ! = FR_OK ) LEAVE_FF ( fs , res ) ;
if ( ! ( fp - > flag & FA_WRITE ) ) LEAVE_FF ( fs , FR_DENIED ) ; /* Check access mode */
if ( fp - > fptr < fp - > obj . objsize ) { /* Process when fptr is not on the eof */
if ( fp - > fptr = = 0 ) { /* When set file size to zero, remove entire cluster chain */
res = remove_chain ( & fp - > obj , fp - > obj . sclust , 0 ) ;
fp - > obj . sclust = 0 ;
} else { /* When truncate a part of the file, remove remaining clusters */
ncl = get_fat ( & fp - > obj , fp - > clust ) ;
res = FR_OK ;
if ( ncl = = 0xFFFFFFFF ) res = FR_DISK_ERR ;
if ( ncl = = 1 ) res = FR_INT_ERR ;
if ( res = = FR_OK & & ncl < fs - > n_fatent ) {
res = remove_chain ( & fp - > obj , ncl , fp - > clust ) ;
}
}
fp - > obj . objsize = fp - > fptr ; /* Set file size to current read/write point */
fp - > flag | = FA_MODIFIED ;
if ( res = = FR_OK & & ( fp - > flag & FA_DIRTY ) ) {
if ( disk_write ( fs - > pdrv , fp - > buf , fp - > sect , 1 ) ! = RES_OK ) {
res = FR_DISK_ERR ;
} else {
fp - > flag & = ( BYTE ) ~ FA_DIRTY ;
}
}
if ( res ! = FR_OK ) ABORT ( fs , res ) ;
}
LEAVE_FF ( fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* Delete a File/Directory */
/*-----------------------------------------------------------------------*/
FRESULT f_unlink (
const TCHAR * path /* Pointer to the file or directory path */
)
{
FRESULT res = 0 ;
FATFS * fs = NULL ;
DIR dj = { 0 } , sdj = { 0 } ;
DWORD dclst = 0 ;
DEF_NAMBUF
/* Get logical drive */
res = mount_volume ( & path , & fs , FA_WRITE ) ;
if ( res = = FR_OK ) {
dj . obj . fs = fs ;
INIT_NAMBUF ( fs ) ;
res = follow_path ( & dj , path ) ; /* Follow the file path */
if ( FF_FS_RPATH & & res = = FR_OK & & ( dj . fn [ NSFLAG ] & NS_DOT ) ) {
res = FR_INVALID_NAME ; /* Cannot remove dot entry */
}
if ( res = = FR_OK ) { /* The object is accessible */
if ( dj . fn [ NSFLAG ] & NS_NONAME ) {
res = FR_INVALID_NAME ; /* Cannot remove the origin directory */
} else {
if ( dj . obj . attr & AM_RDO ) {
res = FR_DENIED ; /* Cannot remove R/O object */
}
}
if ( res = = FR_OK ) {
{
dclst = ld_clust ( fs , dj . dir ) ;
}
if ( dj . obj . attr & AM_DIR ) { /* Is it a sub-directory? */
{
sdj . obj . fs = fs ; /* Open the sub-directory */
sdj . obj . sclust = dclst ;
res = dir_sdi ( & sdj , 0 ) ;
if ( res = = FR_OK ) {
res = DIR_READ_FILE ( & sdj ) ; /* Test if the directory is empty */
if ( res = = FR_OK ) res = FR_DENIED ; /* Not empty? */
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 & & dclst ! = 0 ) { /* Remove the cluster chain if exist */
res = remove_chain ( & dj . obj , dclst , 0 ) ;
}
if ( res = = FR_OK ) res = sync_fs ( fs ) ;
}
}
FREE_NAMBUF ( ) ;
}
LEAVE_FF ( fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* Create a Directory */
/*-----------------------------------------------------------------------*/
FRESULT f_mkdir (
const TCHAR * path /* Pointer to the directory path */
)
{
FRESULT res = 0 ;
FATFS * fs = NULL ;
DIR dj = { 0 } ;
FFOBJID sobj = { 0 } ;
DWORD dcl = 0 , pcl = 0 , tm = 0 ;
DEF_NAMBUF
res = mount_volume ( & path , & fs , FA_WRITE ) ; /* Get logical drive */
if ( res = = FR_OK ) {
dj . obj . fs = fs ;
INIT_NAMBUF ( fs ) ;
res = follow_path ( & dj , path ) ; /* Follow the file path */
if ( res = = FR_OK ) res = FR_EXIST ; /* Name collision? */
if ( FF_FS_RPATH & & res = = FR_NO_FILE & & ( dj . fn [ NSFLAG ] & NS_DOT ) ) { /* Invalid name? */
res = FR_INVALID_NAME ;
}
if ( res = = FR_NO_FILE ) { /* It is clear to create a new directory */
sobj . fs = fs ; /* New object id to create a new chain */
dcl = create_chain ( & sobj , 0 ) ; /* Allocate a cluster for the new directory */
res = FR_OK ;
if ( dcl = = 0 ) res = FR_DENIED ; /* No space to allocate a new cluster? */
if ( dcl = = 1 ) res = FR_INT_ERR ; /* Any insanity? */
if ( dcl = = 0xFFFFFFFF ) res = FR_DISK_ERR ; /* Disk error? */
tm = GET_FATTIME ( ) ;
if ( res = = FR_OK ) {
res = dir_clear ( fs , dcl ) ; /* Clean up the new table */
if ( res = = FR_OK ) {
if ( ! FF_FS_EXFAT | | fs - > fs_type ! = FS_EXFAT ) { /* Create dot entries (FAT only) */
memset ( fs - > win + DIR_Name , ' ' , 11 ) ; /* Create "." entry */
fs - > win [ DIR_Name ] = ' . ' ;
fs - > win [ DIR_Attr ] = AM_DIR ;
st_dword ( fs - > win + DIR_ModTime , tm ) ;
st_clust ( fs , fs - > win , dcl ) ;
memcpy ( fs - > win + SZDIRE , fs - > win , SZDIRE ) ; /* Create ".." entry */
fs - > win [ SZDIRE + 1 ] = ' . ' ; pcl = dj . obj . sclust ;
st_clust ( fs , fs - > win + SZDIRE , pcl ) ;
fs - > wflag = 1 ;
}
res = dir_register ( & dj ) ; /* Register the object to the parent directory */
}
}
if ( res = = FR_OK ) {
{
st_dword ( dj . dir + DIR_ModTime , tm ) ; /* Created time */
st_clust ( fs , dj . dir , dcl ) ; /* Table start cluster */
dj . dir [ DIR_Attr ] = AM_DIR ; /* Attribute */
fs - > wflag = 1 ;
}
if ( res = = FR_OK ) {
res = sync_fs ( fs ) ;
}
} else {
remove_chain ( & sobj , dcl , 0 ) ; /* Could not register, remove the allocated cluster */
}
}
FREE_NAMBUF ( ) ;
}
LEAVE_FF ( fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* Rename a File/Directory */
/*-----------------------------------------------------------------------*/
FRESULT f_rename (
const TCHAR * path_old , /* Pointer to the object name to be renamed */
const TCHAR * path_new /* Pointer to the new name */
)
{
FRESULT res = 0 ;
FATFS * fs = NULL ;
DIR djo = { 0 } , djn = { 0 } ;
BYTE buf [ ( FF_FS_EXFAT ) ? ( SZDIRE * 2 ) : ( SZDIRE ) ] = { 0 } , * dir = NULL ;
LBA_t sect = 0 ;
DEF_NAMBUF
get_ldnumber ( & path_new ) ; /* Snip the drive number of new name off */
res = mount_volume ( & path_old , & fs , FA_WRITE ) ; /* Get logical drive of the old object */
if ( res = = FR_OK ) {
djo . obj . fs = fs ;
INIT_NAMBUF ( fs ) ;
res = follow_path ( & djo , path_old ) ; /* Check old object */
if ( res = = FR_OK & & ( djo . fn [ NSFLAG ] & ( NS_DOT | NS_NONAME ) ) ) res = FR_INVALID_NAME ; /* Check validity of name */
if ( res = = FR_OK ) { /* Object to be renamed is found */
{ /* At FAT/FAT32 volume */
memcpy ( buf , djo . dir , SZDIRE ) ; /* Save directory entry of the object */
memcpy ( & djn , & djo , sizeof ( DIR ) ) ; /* Duplicate the directory object */
res = follow_path ( & djn , path_new ) ; /* Make sure if new object name is not in use */
if ( res = = FR_OK ) { /* Is new name already in use by any other object? */
res = ( djn . obj . sclust = = djo . obj . sclust & & djn . dptr = = djo . dptr ) ? FR_NO_FILE : FR_EXIST ;
}
if ( res = = FR_NO_FILE ) { /* It is a valid path and no name collision */
res = dir_register ( & djn ) ; /* Register the new entry */
if ( res = = FR_OK ) {
dir = djn . dir ; /* Copy directory entry of the object except name */
memcpy ( dir + 13 , buf + 13 , SZDIRE - 13 ) ;
dir [ DIR_Attr ] = buf [ DIR_Attr ] ;
if ( ! ( dir [ DIR_Attr ] & AM_DIR ) ) dir [ DIR_Attr ] | = AM_ARC ; /* Set archive attribute if it is a file */
fs - > wflag = 1 ;
if ( ( dir [ DIR_Attr ] & AM_DIR ) & & djo . obj . sclust ! = djn . obj . sclust ) { /* Update .. entry in the sub-directory if needed */
sect = clst2sect ( fs , ld_clust ( fs , dir ) ) ;
if ( sect = = 0 ) {
res = FR_INT_ERR ;
} else {
/* Start of critical section where an interruption can cause a cross-link */
res = move_window ( fs , sect ) ;
dir = fs - > win + SZDIRE * 1 ; /* Ptr to .. entry */
if ( res = = FR_OK & & dir [ 1 ] = = ' . ' ) {
st_clust ( fs , dir , djn . obj . sclust ) ;
fs - > wflag = 1 ;
}
}
}
}
}
}
if ( res = = FR_OK ) {
res = dir_remove ( & djo ) ; /* Remove old entry */
if ( res = = FR_OK ) {
res = sync_fs ( fs ) ;
}
}
/* End of the critical section */
}
FREE_NAMBUF ( ) ;
}
LEAVE_FF ( fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* Get Volume Label */
/*-----------------------------------------------------------------------*/
FRESULT f_getlabel (
const TCHAR * path , /* Logical drive number */
TCHAR * label , /* Buffer to store the volume label */
DWORD * vsn /* Variable to store the volume serial number */
)
{
FRESULT res = 0 ;
FATFS * fs = NULL ;
DIR dj = { 0 } ;
UINT si = 0 , di = 0 ;
WCHAR wc = 0 ;
/* Get logical drive */
res = mount_volume ( & path , & fs , 0 ) ;
/* Get volume label */
if ( res = = FR_OK & & label ) {
dj . obj . fs = fs ; dj . obj . sclust = 0 ; /* Open root directory */
res = dir_sdi ( & dj , 0 ) ;
if ( res = = FR_OK ) {
res = DIR_READ_LABEL ( & dj ) ; /* Find a volume label entry */
if ( res = = FR_OK ) {
{
si = di = 0 ; /* Extract volume label from AM_VOL entry */
while ( si < 11 ) {
wc = dj . dir [ si + + ] ;
if ( dbc_1st ( ( BYTE ) wc ) & & si < 11 ) wc = wc < < 8 | dj . dir [ si + + ] ; /* Is it a DBC? */
wc = ff_oem2uni ( wc , CODEPAGE ) ; /* Convert it into Unicode */
if ( wc = = 0 ) { /* Invalid char in current code page? */
di = 0 ; break ;
}
di + = put_utf ( wc , & label [ di ] , 4 ) ; /* Store it in Unicode */
}
do { /* Truncate trailing spaces */
label [ di ] = 0 ;
if ( di = = 0 ) break ;
} while ( label [ - - di ] = = ' ' ) ;
}
}
}
if ( res = = FR_NO_FILE ) { /* No label entry and return nul string */
label [ 0 ] = 0 ;
res = FR_OK ;
}
}
/* Get volume serial number */
if ( res = = FR_OK & & vsn ) {
res = move_window ( fs , fs - > volbase ) ;
if ( res = = FR_OK ) {
switch ( fs - > fs_type ) {
case FS_EXFAT :
di = BPB_VolIDEx ;
break ;
case FS_FAT32 :
di = BS_VolID32 ;
break ;
default :
di = BS_VolID ;
}
* vsn = ld_dword ( fs - > win + di ) ;
}
}
LEAVE_FF ( fs , res ) ;
}
/*-----------------------------------------------------------------------*/
/* Set Volume Label */
/*-----------------------------------------------------------------------*/
FRESULT f_setlabel (
const TCHAR * label /* Volume label to set with heading logical drive number */
)
{
FRESULT res = 0 ;
FATFS * fs = NULL ;
DIR dj = { 0 } ;
BYTE dirvn [ 22 ] = { 0 } ;
UINT di = 0 ;
WCHAR wc = 0 ;
static const char badchr [ 18 ] = " +.,;=[] " " /*:<>| \\ \" \ ? \x7F " ; /* [0..16] for FAT, [7..16] for exFAT */
DWORD dc = 0 ;
/* Get logical drive */
res = mount_volume ( & label , & fs , FA_WRITE ) ;
if ( res ! = FR_OK ) LEAVE_FF ( fs , res ) ;
{ /* On the FAT/FAT32 volume */
memset ( dirvn , ' ' , 11 ) ;
di = 0 ;
while ( ( UINT ) * label > = ' ' ) { /* Create volume label */
dc = tchar2uni ( & label ) ;
wc = ( dc < 0x10000 ) ? ff_uni2oem ( ff_wtoupper ( dc ) , CODEPAGE ) : 0 ;
if ( wc = = 0 | | strchr ( & badchr [ 0 ] , ( int ) wc ) | | di > = ( UINT ) ( ( wc > = 0x100 ) ? 10 : 11 ) ) { /* Reject invalid characters for volume label */
LEAVE_FF ( fs , FR_INVALID_NAME ) ;
}
if ( wc > = 0x100 ) dirvn [ di + + ] = ( BYTE ) ( wc > > 8 ) ;
dirvn [ di + + ] = ( BYTE ) wc ;
}
if ( dirvn [ 0 ] = = DDEM ) LEAVE_FF ( fs , FR_INVALID_NAME ) ; /* Reject illegal name (heading DDEM) */
while ( di & & dirvn [ di - 1 ] = = ' ' ) di - - ; /* Snip trailing spaces */
}
/* Set volume label */
dj . obj . fs = fs ; dj . obj . sclust = 0 ; /* Open root directory */
res = dir_sdi ( & dj , 0 ) ;
if ( res = = FR_OK ) {
res = DIR_READ_LABEL ( & dj ) ; /* Get volume label entry */
if ( res = = FR_OK ) {
if ( FF_FS_EXFAT & & fs - > fs_type = = FS_EXFAT ) {
dj . dir [ XDIR_NumLabel ] = ( BYTE ) di ; /* Change the volume label */
memcpy ( dj . dir + XDIR_Label , dirvn , 22 ) ;
} else {
if ( di ! = 0 ) {
memcpy ( dj . dir , dirvn , 11 ) ; /* Change the volume label */
} else {
dj . dir [ DIR_Name ] = DDEM ; /* Remove the volume label */
}
}
fs - > wflag = 1 ;
res = sync_fs ( fs ) ;
} else { /* No volume label entry or an error */
if ( res = = FR_NO_FILE ) {
res = FR_OK ;
if ( di ! = 0 ) { /* Create a volume label entry */
res = dir_alloc ( & dj , 1 ) ; /* Allocate an entry */
if ( res = = FR_OK ) {
memset ( dj . dir , 0 , SZDIRE ) ; /* Clean the entry */
if ( FF_FS_EXFAT & & fs - > fs_type = = FS_EXFAT ) {
dj . dir [ XDIR_Type ] = ET_VLABEL ; /* Create volume label entry */
dj . dir [ XDIR_NumLabel ] = ( BYTE ) di ;
memcpy ( dj . dir + XDIR_Label , dirvn , 22 ) ;
} else {
dj . dir [ DIR_Attr ] = AM_VOL ; /* Create volume label entry */
memcpy ( dj . dir , dirvn , 11 ) ;
}
fs - > wflag = 1 ;
res = sync_fs ( fs ) ;
}
}
}
}
}
LEAVE_FF ( fs , res ) ;
}
# if !FF_FS_READONLY && FF_USE_MKFS
/*-----------------------------------------------------------------------*/
/* Create FAT/exFAT volume (with sub-functions) */
/*-----------------------------------------------------------------------*/
# define N_SEC_TRACK 63 /* Sectors per track for determination of drive CHS */
# define GPT_ALIGN 0x100000 /* Alignment of partitions in GPT [byte] (>=128KB) */
# define GPT_ITEMS 128 /* Number of GPT table size (>=128, sector aligned) */
/* Create partitions on the physical drive in format of MBR or GPT */
static FRESULT create_partition (
BYTE drv , /* Physical drive number */
const LBA_t plst [ ] , /* Partition list */
BYTE sys , /* System ID for each partition (for only MBR) */
BYTE * buf /* Working buffer for a sector */
)
{
UINT i = 0 , cy = 0 ;
LBA_t sz_drv = 0 ;
DWORD sz_drv32 = 0 , nxt_alloc32 = 0 , sz_part32 = 0 ;
BYTE * pte = NULL ;
BYTE hd = 0 , n_hd = 0 , sc = 0 , n_sc = 0 ;
/* Get physical drive size */
if ( disk_ioctl ( drv , GET_SECTOR_COUNT , & sz_drv ) ! = RES_OK ) return FR_DISK_ERR ;
{ /* Create partitions in MBR format */
sz_drv32 = ( DWORD ) sz_drv ;
n_sc = N_SEC_TRACK ; /* Determine drive CHS without any consideration of the drive geometry */
for ( n_hd = 8 ; n_hd ! = 0 & & sz_drv32 / n_hd / n_sc > 1024 ; n_hd * = 2 ) ;
if ( n_hd = = 0 ) n_hd = 255 ; /* Number of heads needs to be <256 */
memset ( buf , 0 , FF_MAX_SS ) ; /* Clear MBR */
pte = buf + MBR_Table ; /* Partition table in the MBR */
for ( i = 0 , nxt_alloc32 = n_sc ; i < 4 & & nxt_alloc32 ! = 0 & & nxt_alloc32 < sz_drv32 ; i + + , nxt_alloc32 + = sz_part32 ) {
sz_part32 = ( DWORD ) plst [ i ] ; /* Get partition size */
if ( sz_part32 < = 100 ) sz_part32 = ( sz_part32 = = 100 ) ? sz_drv32 : sz_drv32 / 100 * sz_part32 ; /* Size in percentage? */
if ( nxt_alloc32 + sz_part32 > sz_drv32 | | nxt_alloc32 + sz_part32 < nxt_alloc32 ) sz_part32 = sz_drv32 - nxt_alloc32 ; /* Clip at drive size */
if ( sz_part32 = = 0 ) break ; /* End of table or no sector to allocate? */
st_dword ( pte + PTE_StLba , nxt_alloc32 ) ; /* Start LBA */
st_dword ( pte + PTE_SizLba , sz_part32 ) ; /* Number of sectors */
pte [ PTE_System ] = sys ; /* System type */
cy = ( UINT ) ( nxt_alloc32 / n_sc / n_hd ) ; /* Start cylinder */
hd = ( BYTE ) ( nxt_alloc32 / n_sc % n_hd ) ; /* Start head */
sc = ( BYTE ) ( nxt_alloc32 % n_sc + 1 ) ; /* Start sector */
pte [ PTE_StHead ] = hd ;
pte [ PTE_StSec ] = ( BYTE ) ( ( cy > > 2 & 0xC0 ) | sc ) ;
pte [ PTE_StCyl ] = ( BYTE ) cy ;
cy = ( UINT ) ( ( nxt_alloc32 + sz_part32 - 1 ) / n_sc / n_hd ) ; /* End cylinder */
hd = ( BYTE ) ( ( nxt_alloc32 + sz_part32 - 1 ) / n_sc % n_hd ) ; /* End head */
sc = ( BYTE ) ( ( nxt_alloc32 + sz_part32 - 1 ) % n_sc + 1 ) ; /* End sector */
pte [ PTE_EdHead ] = hd ;
pte [ PTE_EdSec ] = ( BYTE ) ( ( cy > > 2 & 0xC0 ) | sc ) ;
pte [ PTE_EdCyl ] = ( BYTE ) cy ;
pte + = SZ_PTE ; /* Next entry */
}
st_word ( buf + BS_55AA , 0xAA55 ) ; /* MBR signature */
if ( disk_write ( drv , buf , 0 , 1 ) ! = RES_OK ) return FR_DISK_ERR ; /* Write it to the MBR */
}
return FR_OK ;
}
FRESULT f_mkfs (
const TCHAR * path , /* Logical drive number */
const MKFS_PARM * opt , /* Format options */
void * work , /* Pointer to working buffer (null: use len bytes of heap memory) */
UINT len /* Size of working buffer [byte] */
)
{
static const WORD cst [ ] = { 1 , 4 , 16 , 64 , 256 , 512 , 0 } ; /* Cluster size boundary for FAT volume (4Ks unit) */
static const WORD cst32 [ ] = { 1 , 2 , 4 , 8 , 16 , 32 , 0 } ; /* Cluster size boundary for FAT32 volume (128Ks unit) */
static const MKFS_PARM defopt = { FM_ANY , 0 , 0 , 0 , 0 } ; /* Default parameter */
BYTE fsopt = 0 , fsty = 0 , sys = 0 , pdrv = 0 , ipart = 0 ;
BYTE * buf = NULL ;
BYTE * pte = NULL ;
WORD ss = 0 ; /* Sector size */
DWORD sz_buf = 0 , sz_blk = 0 , n_clst = 0 , pau = 0 , nsect = 0 , n = 0 , vsn = 0 ;
LBA_t sz_vol , b_vol , b_fat , b_data ; /* Size of volume, Base LBA of volume, fat, data */
LBA_t sect = 0 , lba [ 2 ] = { 0 } ;
DWORD sz_rsv , sz_fat , sz_dir , sz_au ; /* Size of reserved, fat, dir, data, cluster */
UINT n_fat , n_root , i ; /* Index, Number of FATs and Number of roor dir entries */
int vol = 0 ;
DSTATUS ds = 0 ;
FRESULT res = 0 ;
/* Check mounted drive and clear work area */
vol = get_ldnumber ( & path ) ; /* Get target logical drive */
if ( vol < 0 ) return FR_INVALID_DRIVE ;
if ( FatFs [ vol ] ) FatFs [ vol ] - > fs_type = 0 ; /* Clear the fs object if mounted */
pdrv = LD2PD ( vol ) ; /* Hosting physical drive */
ipart = LD2PT ( vol ) ; /* Hosting partition (0:create as new, 1..:existing partition) */
/* Initialize the hosting physical drive */
ds = disk_initialize ( pdrv ) ;
if ( ds & STA_NOINIT ) return FR_NOT_READY ;
if ( ds & STA_PROTECT ) return FR_WRITE_PROTECTED ;
/* Get physical drive parameters (sz_drv, sz_blk and ss) */
if ( ! opt ) opt = & defopt ; /* Use default parameter if it is not given */
sz_blk = opt - > align ;
if ( sz_blk = = 0 ) disk_ioctl ( pdrv , GET_BLOCK_SIZE , & sz_blk ) ; /* Block size from the paramter or lower layer */
if ( sz_blk = = 0 | | sz_blk > 0x8000 | | ( sz_blk & ( sz_blk - 1 ) ) ) sz_blk = 1 ; /* Use default if the block size is invalid */
# if FF_MAX_SS != FF_MIN_SS
if ( disk_ioctl ( pdrv , GET_SECTOR_SIZE , & ss ) ! = RES_OK ) return FR_DISK_ERR ;
if ( ss > FF_MAX_SS | | ss < FF_MIN_SS | | ( ss & ( ss - 1 ) ) ) return FR_DISK_ERR ;
# else
ss = FF_MAX_SS ;
# endif
/* Options for FAT sub-type and FAT parameters */
fsopt = opt - > fmt & ( FM_ANY | FM_SFD ) ;
n_fat = ( opt - > n_fat > = 1 & & opt - > n_fat < = 2 ) ? opt - > n_fat : 1 ;
n_root = ( opt - > n_root > = 1 & & opt - > n_root < = 32768 & & ( opt - > n_root % ( ss / SZDIRE ) ) = = 0 ) ? opt - > n_root : 512 ;
sz_au = ( opt - > au_size < = 0x1000000 & & ( opt - > au_size & ( opt - > au_size - 1 ) ) = = 0 ) ? opt - > au_size : 0 ;
sz_au / = ss ; /* Byte --> Sector */
/* Get working buffer */
sz_buf = len / ss ; /* Size of working buffer [sector] */
if ( sz_buf = = 0 ) return FR_NOT_ENOUGH_CORE ;
buf = ( BYTE * ) work ; /* Working buffer */
if ( ! buf ) return FR_NOT_ENOUGH_CORE ;
/* Determine where the volume to be located (b_vol, sz_vol) */
b_vol = sz_vol = 0 ;
if ( FF_MULTI_PARTITION & & ipart ! = 0 ) { /* Is the volume associated with any specific partition? */
/* Get partition location from the existing partition table */
if ( disk_read ( pdrv , buf , 0 , 1 ) ! = RES_OK ) LEAVE_MKFS ( FR_DISK_ERR ) ; /* Load MBR */
if ( ld_word ( buf + BS_55AA ) ! = 0xAA55 ) LEAVE_MKFS ( FR_MKFS_ABORTED ) ; /* Check if MBR is valid */
{ /* Get the partition location from MBR partition table */
pte = buf + ( MBR_Table + ( ipart - 1 ) * SZ_PTE ) ;
if ( ipart > 4 | | pte [ PTE_System ] = = 0 ) LEAVE_MKFS ( FR_MKFS_ABORTED ) ; /* No partition? */
b_vol = ld_dword ( pte + PTE_StLba ) ; /* Get volume start sector */
sz_vol = ld_dword ( pte + PTE_SizLba ) ; /* Get volume size */
}
} else { /* The volume is associated with a physical drive */
if ( disk_ioctl ( pdrv , GET_SECTOR_COUNT , & sz_vol ) ! = RES_OK ) LEAVE_MKFS ( FR_DISK_ERR ) ;
if ( ! ( fsopt & FM_SFD ) ) { /* To be partitioned? */
/* Create a single-partition on the drive in this function */
{ /* Partitioning is in MBR */
if ( sz_vol > N_SEC_TRACK ) {
b_vol = N_SEC_TRACK ; sz_vol - = b_vol ; /* Estimated partition offset and size */
}
}
}
}
if ( sz_vol < 128 ) LEAVE_MKFS ( FR_MKFS_ABORTED ) ; /* Check if volume size is >=128s */
/* Now start to create an FAT volume at b_vol and sz_vol */
do { /* Pre-determine the FAT type */
if ( FF_FS_EXFAT & & ( fsopt & FM_EXFAT ) ) { /* exFAT possible? */
if ( ( fsopt & FM_ANY ) = = FM_EXFAT | | sz_vol > = 0x4000000 | | sz_au > 128 ) { /* exFAT only, vol >= 64MS or sz_au > 128S ? */
fsty = FS_EXFAT ; break ;
}
}
if ( sz_au > 128 ) sz_au = 128 ; /* Invalid AU for FAT/FAT32? */
if ( fsopt & FM_FAT32 ) { /* FAT32 possible? */
if ( ! ( fsopt & FM_FAT ) ) { /* no-FAT? */
fsty = FS_FAT32 ; break ;
}
}
if ( ! ( fsopt & FM_FAT ) ) LEAVE_MKFS ( FR_INVALID_PARAMETER ) ; /* no-FAT? */
fsty = FS_FAT16 ;
} while ( 0 ) ;
vsn = ( DWORD ) sz_vol + GET_FATTIME ( ) ; /* VSN generated from current time and partitiion size */
{ /* Create an FAT/FAT32 volume */
do {
pau = sz_au ;
/* Pre-determine number of clusters and FAT sub-type */
if ( fsty = = FS_FAT32 ) { /* FAT32 volume */
if ( pau = = 0 ) { /* AU auto-selection */
n = ( DWORD ) sz_vol / 0x20000 ; /* Volume size in unit of 128KS */
for ( i = 0 , pau = 1 ; cst32 [ i ] & & cst32 [ i ] < = n ; i + + , pau < < = 1 ) ; /* Get from table */
}
n_clst = ( DWORD ) sz_vol / pau ; /* Number of clusters */
sz_fat = ( n_clst * 4 + 8 + ss - 1 ) / ss ; /* FAT size [sector] */
sz_rsv = 32 ; /* Number of reserved sectors */
sz_dir = 0 ; /* No static directory */
if ( n_clst < = MAX_FAT16 | | n_clst > MAX_FAT32 ) LEAVE_MKFS ( FR_MKFS_ABORTED ) ;
} else { /* FAT volume */
if ( pau = = 0 ) { /* au auto-selection */
n = ( DWORD ) sz_vol / 0x1000 ; /* Volume size in unit of 4KS */
for ( i = 0 , pau = 1 ; cst [ i ] & & cst [ i ] < = n ; i + + , pau < < = 1 ) ; /* Get from table */
}
n_clst = ( DWORD ) sz_vol / pau ;
if ( n_clst > MAX_FAT12 ) {
n = n_clst * 2 + 4 ; /* FAT size [byte] */
} else {
fsty = FS_FAT12 ;
n = ( n_clst * 3 + 1 ) / 2 + 3 ; /* FAT size [byte] */
}
sz_fat = ( n + ss - 1 ) / ss ; /* FAT size [sector] */
sz_rsv = 1 ; /* Number of reserved sectors */
sz_dir = ( DWORD ) n_root * SZDIRE / ss ; /* Root dir size [sector] */
}
b_fat = b_vol + sz_rsv ; /* FAT base */
b_data = b_fat + sz_fat * n_fat + sz_dir ; /* Data base */
/* Align data area to erase block boundary (for flash memory media) */
n = ( DWORD ) ( ( ( b_data + sz_blk - 1 ) & ~ ( sz_blk - 1 ) ) - b_data ) ; /* Sectors to next nearest from current data base */
if ( fsty = = FS_FAT32 ) { /* FAT32: Move FAT */
sz_rsv + = n ; b_fat + = n ;
} else { /* FAT: Expand FAT */
if ( n % n_fat ) { /* Adjust fractional error if needed */
n - - ; sz_rsv + + ; b_fat + + ;
}
sz_fat + = n / n_fat ;
}
/* Determine number of clusters and final check of validity of the FAT sub-type */
if ( sz_vol < b_data + pau * 16 - b_vol ) LEAVE_MKFS ( FR_MKFS_ABORTED ) ; /* Too small volume? */
n_clst = ( ( DWORD ) sz_vol - sz_rsv - sz_fat * n_fat - sz_dir ) / pau ;
if ( fsty = = FS_FAT32 ) {
if ( n_clst < = MAX_FAT16 ) { /* Too few clusters for FAT32? */
if ( sz_au = = 0 & & ( sz_au = pau / 2 ) ! = 0 ) continue ; /* Adjust cluster size and retry */
LEAVE_MKFS ( FR_MKFS_ABORTED ) ;
}
}
if ( fsty = = FS_FAT16 ) {
if ( n_clst > MAX_FAT16 ) { /* Too many clusters for FAT16 */
if ( sz_au = = 0 & & ( pau * 2 ) < = 64 ) {
sz_au = pau * 2 ; continue ; /* Adjust cluster size and retry */
}
if ( ( fsopt & FM_FAT32 ) ) {
fsty = FS_FAT32 ; continue ; /* Switch type to FAT32 and retry */
}
if ( sz_au = = 0 & & ( sz_au = pau * 2 ) < = 128 ) continue ; /* Adjust cluster size and retry */
LEAVE_MKFS ( FR_MKFS_ABORTED ) ;
}
if ( n_clst < = MAX_FAT12 ) { /* Too few clusters for FAT16 */
if ( sz_au = = 0 & & ( sz_au = pau * 2 ) < = 128 ) continue ; /* Adjust cluster size and retry */
LEAVE_MKFS ( FR_MKFS_ABORTED ) ;
}
}
if ( fsty = = FS_FAT12 & & n_clst > MAX_FAT12 ) LEAVE_MKFS ( FR_MKFS_ABORTED ) ; /* Too many clusters for FAT12 */
/* Ok, it is the valid cluster configuration */
break ;
} while ( 1 ) ;
/* Create FAT VBR */
memset ( buf , 0 , ss ) ;
memcpy ( buf + BS_JmpBoot , " \xEB \xFE \x90 " " MSDOS5.0 " , 11 ) ; /* Boot jump code (x86), OEM name */
st_word ( buf + BPB_BytsPerSec , ss ) ; /* Sector size [byte] */
buf [ BPB_SecPerClus ] = ( BYTE ) pau ; /* Cluster size [sector] */
st_word ( buf + BPB_RsvdSecCnt , ( WORD ) sz_rsv ) ; /* Size of reserved area */
buf [ BPB_NumFATs ] = ( BYTE ) n_fat ; /* Number of FATs */
st_word ( buf + BPB_RootEntCnt , ( WORD ) ( ( fsty = = FS_FAT32 ) ? 0 : n_root ) ) ; /* Number of root directory entries */
if ( sz_vol < 0x10000 ) {
st_word ( buf + BPB_TotSec16 , ( WORD ) sz_vol ) ; /* Volume size in 16-bit LBA */
} else {
st_dword ( buf + BPB_TotSec32 , ( DWORD ) sz_vol ) ; /* Volume size in 32-bit LBA */
}
buf [ BPB_Media ] = 0xF8 ; /* Media descriptor byte */
st_word ( buf + BPB_SecPerTrk , 63 ) ; /* Number of sectors per track (for int13) */
st_word ( buf + BPB_NumHeads , 255 ) ; /* Number of heads (for int13) */
st_dword ( buf + BPB_HiddSec , ( DWORD ) b_vol ) ; /* Volume offset in the physical drive [sector] */
if ( fsty = = FS_FAT32 ) {
st_dword ( buf + BS_VolID32 , vsn ) ; /* VSN */
st_dword ( buf + BPB_FATSz32 , sz_fat ) ; /* FAT size [sector] */
st_dword ( buf + BPB_RootClus32 , 2 ) ; /* Root directory cluster # (2) */
st_word ( buf + BPB_FSInfo32 , 1 ) ; /* Offset of FSINFO sector (VBR + 1) */
st_word ( buf + BPB_BkBootSec32 , 6 ) ; /* Offset of backup VBR (VBR + 6) */
buf [ BS_DrvNum32 ] = 0x80 ; /* Drive number (for int13) */
buf [ BS_BootSig32 ] = 0x29 ; /* Extended boot signature */
memcpy ( buf + BS_VolLab32 , " NO NAME " " FAT32 " , 19 ) ; /* Volume label, FAT signature */
} else {
st_dword ( buf + BS_VolID , vsn ) ; /* VSN */
st_word ( buf + BPB_FATSz16 , ( WORD ) sz_fat ) ; /* FAT size [sector] */
buf [ BS_DrvNum ] = 0x80 ; /* Drive number (for int13) */
buf [ BS_BootSig ] = 0x29 ; /* Extended boot signature */
memcpy ( buf + BS_VolLab , " NO NAME " " FAT " , 19 ) ; /* Volume label, FAT signature */
}
st_word ( buf + BS_55AA , 0xAA55 ) ; /* Signature (offset is fixed here regardless of sector size) */
if ( disk_write ( pdrv , buf , b_vol , 1 ) ! = RES_OK ) LEAVE_MKFS ( FR_DISK_ERR ) ; /* Write it to the VBR sector */
/* Create FSINFO record if needed */
if ( fsty = = FS_FAT32 ) {
disk_write ( pdrv , buf , b_vol + 6 , 1 ) ; /* Write backup VBR (VBR + 6) */
memset ( buf , 0 , ss ) ;
st_dword ( buf + FSI_LeadSig , 0x41615252 ) ;
st_dword ( buf + FSI_StrucSig , 0x61417272 ) ;
st_dword ( buf + FSI_Free_Count , n_clst - 1 ) ; /* Number of free clusters */
st_dword ( buf + FSI_Nxt_Free , 2 ) ; /* Last allocated cluster# */
st_word ( buf + BS_55AA , 0xAA55 ) ;
disk_write ( pdrv , buf , b_vol + 7 , 1 ) ; /* Write backup FSINFO (VBR + 7) */
disk_write ( pdrv , buf , b_vol + 1 , 1 ) ; /* Write original FSINFO (VBR + 1) */
}
/* Initialize FAT area */
memset ( buf , 0 , sz_buf * ss ) ;
sect = b_fat ; /* FAT start sector */
for ( i = 0 ; i < n_fat ; i + + ) { /* Initialize FATs each */
if ( fsty = = FS_FAT32 ) {
st_dword ( buf + 0 , 0xFFFFFFF8 ) ; /* FAT[0] */
st_dword ( buf + 4 , 0xFFFFFFFF ) ; /* FAT[1] */
st_dword ( buf + 8 , 0x0FFFFFFF ) ; /* FAT[2] (root directory) */
} else {
st_dword ( buf + 0 , ( fsty = = FS_FAT12 ) ? 0xFFFFF8 : 0xFFFFFFF8 ) ; /* FAT[0] and FAT[1] */
}
nsect = sz_fat ; /* Number of FAT sectors */
do { /* Fill FAT sectors */
n = ( nsect > sz_buf ) ? sz_buf : nsect ;
if ( disk_write ( pdrv , buf , sect , ( UINT ) n ) ! = RES_OK ) LEAVE_MKFS ( FR_DISK_ERR ) ;
memset ( buf , 0 , ss ) ; /* Rest of FAT all are cleared */
sect + = n ; nsect - = n ;
} while ( nsect ) ;
}
/* Initialize root directory (fill with zero) */
nsect = ( fsty = = FS_FAT32 ) ? pau : sz_dir ; /* Number of root directory sectors */
do {
n = ( nsect > sz_buf ) ? sz_buf : nsect ;
if ( disk_write ( pdrv , buf , sect , ( UINT ) n ) ! = RES_OK ) LEAVE_MKFS ( FR_DISK_ERR ) ;
sect + = n ; nsect - = n ;
} while ( nsect ) ;
}
/* A FAT volume has been created here */
/* Determine system ID in the MBR partition table */
if ( FF_FS_EXFAT & & fsty = = FS_EXFAT ) {
sys = 0x07 ; /* exFAT */
} else if ( fsty = = FS_FAT32 ) {
sys = 0x0C ; /* FAT32X */
} else if ( sz_vol > = 0x10000 ) {
sys = 0x06 ; /* FAT12/16 (large) */
} else if ( fsty = = FS_FAT16 ) {
sys = 0x04 ; /* FAT16 */
} else {
sys = 0x01 ; /* FAT12 */
}
/* Update partition information */
if ( FF_MULTI_PARTITION & & ipart ! = 0 ) { /* Volume is in the existing partition */
if ( ! FF_LBA64 | | ! ( fsopt & 0x80 ) ) { /* Is the partition in MBR? */
/* Update system ID in the partition table */
if ( disk_read ( pdrv , buf , 0 , 1 ) ! = RES_OK ) LEAVE_MKFS ( FR_DISK_ERR ) ; /* Read the MBR */
buf [ MBR_Table + ( ipart - 1 ) * SZ_PTE + PTE_System ] = sys ; /* Set system ID */
if ( disk_write ( pdrv , buf , 0 , 1 ) ! = RES_OK ) LEAVE_MKFS ( FR_DISK_ERR ) ; /* Write it back to the MBR */
}
} else { /* Volume as a new single partition */
if ( ! ( fsopt & FM_SFD ) ) { /* Create partition table if not in SFD format */
lba [ 0 ] = sz_vol ; lba [ 1 ] = 0 ;
res = create_partition ( pdrv , lba , sys , buf ) ;
if ( res ! = FR_OK ) LEAVE_MKFS ( res ) ;
}
}
if ( disk_ioctl ( pdrv , CTRL_SYNC , 0 ) ! = RES_OK ) LEAVE_MKFS ( FR_DISK_ERR ) ;
LEAVE_MKFS ( FR_OK ) ;
}
# endif /* !FF_FS_READONLY && FF_USE_MKFS */