OpenCores
URL https://opencores.org/ocsvn/ion/ion/trunk

Subversion Repositories ion

[/] [ion/] [trunk/] [src/] [common/] [fatfs/] [ff.c] - Blame information for rev 243

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 243 ja_rd
/*----------------------------------------------------------------------------/
2
/  FatFs - FAT file system module  R0.09a                 (C)ChaN, 2012
3
/-----------------------------------------------------------------------------/
4
/ FatFs module is a generic FAT file system module for small embedded systems.
5
/ This is a free software that opened for education, research and commercial
6
/ developments under license policy of following terms.
7
/
8
/  Copyright (C) 2012, ChaN, all right reserved.
9
/
10
/ * The FatFs module is a free software and there is NO WARRANTY.
11
/ * No restriction on use. You can use, modify and redistribute it for
12
/   personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
13
/ * Redistributions of source code must retain the above copyright notice.
14
/
15
/-----------------------------------------------------------------------------/
16
/ Feb 26,'06 R0.00  Prototype.
17
/
18
/ Apr 29,'06 R0.01  First stable version.
19
/
20
/ Jun 01,'06 R0.02  Added FAT12 support.
21
/                   Removed unbuffered mode.
22
/                   Fixed a problem on small (<32M) partition.
23
/ Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM).
24
/
25
/ Sep 22,'06 R0.03  Added f_rename().
26
/                   Changed option _FS_MINIMUM to _FS_MINIMIZE.
27
/ Dec 11,'06 R0.03a Improved cluster scan algorithm to write files fast.
28
/                   Fixed f_mkdir() creates incorrect directory on FAT32.
29
/
30
/ Feb 04,'07 R0.04  Supported multiple drive system.
31
/                   Changed some interfaces for multiple drive system.
32
/                   Changed f_mountdrv() to f_mount().
33
/                   Added f_mkfs().
34
/ Apr 01,'07 R0.04a Supported multiple partitions on a physical drive.
35
/                   Added a capability of extending file size to f_lseek().
36
/                   Added minimization level 3.
37
/                   Fixed an endian sensitive code in f_mkfs().
38
/ May 05,'07 R0.04b Added a configuration option _USE_NTFLAG.
39
/                   Added FSInfo support.
40
/                   Fixed DBCS name can result FR_INVALID_NAME.
41
/                   Fixed short seek (<= csize) collapses the file object.
42
/
43
/ Aug 25,'07 R0.05  Changed arguments of f_read(), f_write() and f_mkfs().
44
/                   Fixed f_mkfs() on FAT32 creates incorrect FSInfo.
45
/                   Fixed f_mkdir() on FAT32 creates incorrect directory.
46
/ Feb 03,'08 R0.05a Added f_truncate() and f_utime().
47
/                   Fixed off by one error at FAT sub-type determination.
48
/                   Fixed btr in f_read() can be mistruncated.
49
/                   Fixed cached sector is not flushed when create and close without write.
50
/
51
/ Apr 01,'08 R0.06  Added fputc(), fputs(), fprintf() and fgets().
52
/                   Improved performance of f_lseek() on moving to the same or following cluster.
53
/
54
/ Apr 01,'09 R0.07  Merged Tiny-FatFs as a configuration option. (_FS_TINY)
55
/                   Added long file name feature.
56
/                   Added multiple code page feature.
57
/                   Added re-entrancy for multitask operation.
58
/                   Added auto cluster size selection to f_mkfs().
59
/                   Added rewind option to f_readdir().
60
/                   Changed result code of critical errors.
61
/                   Renamed string functions to avoid name collision.
62
/ Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg.
63
/                   Added multiple sector size feature.
64
/ Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error.
65
/                   Fixed wrong cache control in f_lseek().
66
/                   Added relative path feature.
67
/                   Added f_chdir() and f_chdrive().
68
/                   Added proper case conversion to extended char.
69
/ Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h.
70
/                   Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH.
71
/                   Fixed name matching error on the 13 char boundary.
72
/                   Added a configuration option, _LFN_UNICODE.
73
/                   Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.
74
/
75
/ May 15,'10 R0.08  Added a memory configuration option. (_USE_LFN = 3)
76
/                   Added file lock feature. (_FS_SHARE)
77
/                   Added fast seek feature. (_USE_FASTSEEK)
78
/                   Changed some types on the API, XCHAR->TCHAR.
79
/                   Changed fname member in the FILINFO structure on Unicode cfg.
80
/                   String functions support UTF-8 encoding files on Unicode cfg.
81
/ Aug 16,'10 R0.08a Added f_getcwd(). (_FS_RPATH = 2)
82
/                   Added sector erase feature. (_USE_ERASE)
83
/                   Moved file lock semaphore table from fs object to the bss.
84
/                   Fixed a wrong directory entry is created on non-LFN cfg when the given name contains ';'.
85
/                   Fixed f_mkfs() creates wrong FAT32 volume.
86
/ Jan 15,'11 R0.08b Fast seek feature is also applied to f_read() and f_write().
87
/                   f_lseek() reports required table size on creating CLMP.
88
/                   Extended format syntax of f_printf function.
89
/                   Ignores duplicated directory separators in given path name.
90
/
91
/ Sep 06,'11 R0.09  f_mkfs() supports multiple partition to finish the multiple partition feature.
92
/                   Added f_fdisk(). (_MULTI_PARTITION = 2)
93
/ Aug 27,'12 R0.09a Fixed assertion failure due to OS/2 EA on FAT12/16 volume.
94
/                   Changed f_open() and f_opendir reject null object pointer to avoid crash.
95
/                   Changed option name _FS_SHARE to _FS_LOCK.
96
/---------------------------------------------------------------------------*/
97
 
98
#include <stdio.h>
99
#include "ff.h"                 /* FatFs configurations and declarations */
100
#include "diskio.h"             /* Declarations of low level disk I/O functions */
101
 
102
 
103
/*--------------------------------------------------------------------------
104
 
105
   Module Private Definitions
106
 
107
---------------------------------------------------------------------------*/
108
 
109
#if _FATFS != 4004      /* Revision ID */
110
#error Wrong include file (ff.h).
111
#endif
112
 
113
 
114
/* Definitions on sector size */
115
#if _MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096
116
#error Wrong sector size.
117
#endif
118
#if _MAX_SS != 512
119
#define SS(fs)  ((fs)->ssize)   /* Variable sector size */
120
#else
121
#define SS(fs)  512U                    /* Fixed sector size */
122
#endif
123
 
124
 
125
/* Reentrancy related */
126
#if _FS_REENTRANT
127
#if _USE_LFN == 1
128
#error Static LFN work area must not be used in re-entrant configuration.
129
#endif
130
#define ENTER_FF(fs)            { if (!lock_fs(fs)) return FR_TIMEOUT; }
131
#define LEAVE_FF(fs, res)       { unlock_fs(fs, res); return res; }
132
#else
133
#define ENTER_FF(fs)
134
#define LEAVE_FF(fs, res)       return res
135
#endif
136
 
137
#define ABORT(fs, res)          { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); }
138
 
139
 
140
/* File access control feature */
141
#if _FS_LOCK
142
#if _FS_READONLY
143
#error _FS_LOCK must be 0 on read-only cfg.
144
#endif
145
typedef struct {
146
        FATFS *fs;                              /* File ID 1, volume (NULL:blank entry) */
147
        DWORD clu;                              /* File ID 2, directory */
148
        WORD idx;                               /* File ID 3, directory index */
149
        WORD ctr;                               /* File open counter, 0:none, 0x01..0xFF:read open count, 0x100:write mode */
150
} FILESEM;
151
#endif
152
 
153
 
154
 
155
/* DBCS code ranges and SBCS extend char conversion table */
156
 
157
#if _CODE_PAGE == 932   /* Japanese Shift-JIS */
158
#define _DF1S   0x81    /* DBC 1st byte range 1 start */
159
#define _DF1E   0x9F    /* DBC 1st byte range 1 end */
160
#define _DF2S   0xE0    /* DBC 1st byte range 2 start */
161
#define _DF2E   0xFC    /* DBC 1st byte range 2 end */
162
#define _DS1S   0x40    /* DBC 2nd byte range 1 start */
163
#define _DS1E   0x7E    /* DBC 2nd byte range 1 end */
164
#define _DS2S   0x80    /* DBC 2nd byte range 2 start */
165
#define _DS2E   0xFC    /* DBC 2nd byte range 2 end */
166
 
167
#elif _CODE_PAGE == 936 /* Simplified Chinese GBK */
168
#define _DF1S   0x81
169
#define _DF1E   0xFE
170
#define _DS1S   0x40
171
#define _DS1E   0x7E
172
#define _DS2S   0x80
173
#define _DS2E   0xFE
174
 
175
#elif _CODE_PAGE == 949 /* Korean */
176
#define _DF1S   0x81
177
#define _DF1E   0xFE
178
#define _DS1S   0x41
179
#define _DS1E   0x5A
180
#define _DS2S   0x61
181
#define _DS2E   0x7A
182
#define _DS3S   0x81
183
#define _DS3E   0xFE
184
 
185
#elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */
186
#define _DF1S   0x81
187
#define _DF1E   0xFE
188
#define _DS1S   0x40
189
#define _DS1E   0x7E
190
#define _DS2S   0xA1
191
#define _DS2E   0xFE
192
 
193
#elif _CODE_PAGE == 437 /* U.S. (OEM) */
194
#define _DF1S   0
195
#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
196
                                0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
197
                                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, \
198
                                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}
199
 
200
#elif _CODE_PAGE == 720 /* Arabic (OEM) */
201
#define _DF1S   0
202
#define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
203
                                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, \
204
                                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, \
205
                                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}
206
 
207
#elif _CODE_PAGE == 737 /* Greek (OEM) */
208
#define _DF1S   0
209
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \
210
                                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, \
211
                                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, \
212
                                0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
213
 
214
#elif _CODE_PAGE == 775 /* Baltic (OEM) */
215
#define _DF1S   0
216
#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
217
                                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, \
218
                                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, \
219
                                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}
220
 
221
#elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */
222
#define _DF1S   0
223
#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
224
                                0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
225
                                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
226
                                0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
227
 
228
#elif _CODE_PAGE == 852 /* Latin 2 (OEM) */
229
#define _DF1S   0
230
#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \
231
                                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, \
232
                                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, \
233
                                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}
234
 
235
#elif _CODE_PAGE == 855 /* Cyrillic (OEM) */
236
#define _DF1S   0
237
#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \
238
                                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, \
239
                                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, \
240
                                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}
241
 
242
#elif _CODE_PAGE == 857 /* Turkish (OEM) */
243
#define _DF1S   0
244
#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \
245
                                0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
246
                                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
247
                                0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
248
 
249
#elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */
250
#define _DF1S   0
251
#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
252
                                0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
253
                                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
254
                                0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
255
 
256
#elif _CODE_PAGE == 862 /* Hebrew (OEM) */
257
#define _DF1S   0
258
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
259
                                0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
260
                                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, \
261
                                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}
262
 
263
#elif _CODE_PAGE == 866 /* Russian (OEM) */
264
#define _DF1S   0
265
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
266
                                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, \
267
                                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, \
268
                                0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
269
 
270
#elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */
271
#define _DF1S   0
272
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
273
                                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, \
274
                                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, \
275
                                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}
276
 
277
#elif _CODE_PAGE == 1250 /* Central Europe (Windows) */
278
#define _DF1S   0
279
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \
280
                                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \
281
                                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, \
282
                                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}
283
 
284
#elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */
285
#define _DF1S   0
286
#define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \
287
                                0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \
288
                                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, \
289
                                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}
290
 
291
#elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */
292
#define _DF1S   0
293
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \
294
                                0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
295
                                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, \
296
                                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}
297
 
298
#elif _CODE_PAGE == 1253 /* Greek (Windows) */
299
#define _DF1S   0
300
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
301
                                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, \
302
                                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \
303
                                0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF}
304
 
305
#elif _CODE_PAGE == 1254 /* Turkish (Windows) */
306
#define _DF1S   0
307
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \
308
                                0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
309
                                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, \
310
                                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}
311
 
312
#elif _CODE_PAGE == 1255 /* Hebrew (Windows) */
313
#define _DF1S   0
314
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
315
                                0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
316
                                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, \
317
                                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}
318
 
319
#elif _CODE_PAGE == 1256 /* Arabic (Windows) */
320
#define _DF1S   0
321
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \
322
                                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, \
323
                                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, \
324
                                0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF}
325
 
326
#elif _CODE_PAGE == 1257 /* Baltic (Windows) */
327
#define _DF1S   0
328
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
329
                                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \
330
                                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, \
331
                                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}
332
 
333
#elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */
334
#define _DF1S   0
335
#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \
336
                                0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
337
                                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, \
338
                                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F}
339
 
340
#elif _CODE_PAGE == 1   /* ASCII (for only non-LFN cfg) */
341
#if _USE_LFN
342
#error Cannot use LFN feature without valid code page.
343
#endif
344
#define _DF1S   0
345
 
346
#else
347
#error Unknown code page
348
 
349
#endif
350
 
351
 
352
/* Character code support macros */
353
#define IsUpper(c)      (((c)>='A')&&((c)<='Z'))
354
#define IsLower(c)      (((c)>='a')&&((c)<='z'))
355
#define IsDigit(c)      (((c)>='0')&&((c)<='9'))
356
 
357
#if _DF1S               /* Code page is DBCS */
358
 
359
#ifdef _DF2S    /* Two 1st byte areas */
360
#define IsDBCS1(c)      (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E))
361
#else                   /* One 1st byte area */
362
#define IsDBCS1(c)      ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E)
363
#endif
364
 
365
#ifdef _DS3S    /* Three 2nd byte areas */
366
#define IsDBCS2(c)      (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E))
367
#else                   /* Two 2nd byte areas */
368
#define IsDBCS2(c)      (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E))
369
#endif
370
 
371
#else                   /* Code page is SBCS */
372
 
373
#define IsDBCS1(c)      0
374
#define IsDBCS2(c)      0
375
 
376
#endif /* _DF1S */
377
 
378
 
379
/* Name status flags */
380
#define NS                      11              /* Index of name status byte in fn[] */
381
#define NS_LOSS         0x01    /* Out of 8.3 format */
382
#define NS_LFN          0x02    /* Force to create LFN entry */
383
#define NS_LAST         0x04    /* Last segment */
384
#define NS_BODY         0x08    /* Lower case flag (body) */
385
#define NS_EXT          0x10    /* Lower case flag (ext) */
386
#define NS_DOT          0x20    /* Dot entry */
387
 
388
 
389
/* FAT sub-type boundaries */
390
/* Note that the FAT spec by Microsoft says 4085 but Windows works with 4087! */
391
#define MIN_FAT16       4086    /* Minimum number of clusters for FAT16 */
392
#define MIN_FAT32       65526   /* Minimum number of clusters for FAT32 */
393
 
394
 
395
/* FatFs refers the members in the FAT structures as byte array instead of
396
/ structure member because the structure is not binary compatible between
397
/ different platforms */
398
 
399
#define BS_jmpBoot                      0        /* Jump instruction (3) */
400
#define BS_OEMName                      3       /* OEM name (8) */
401
#define BPB_BytsPerSec          11      /* Sector size [byte] (2) */
402
#define BPB_SecPerClus          13      /* Cluster size [sector] (1) */
403
#define BPB_RsvdSecCnt          14      /* Size of reserved area [sector] (2) */
404
#define BPB_NumFATs                     16      /* Number of FAT copies (1) */
405
#define BPB_RootEntCnt          17      /* Number of root dir entries for FAT12/16 (2) */
406
#define BPB_TotSec16            19      /* Volume size [sector] (2) */
407
#define BPB_Media                       21      /* Media descriptor (1) */
408
#define BPB_FATSz16                     22      /* FAT size [sector] (2) */
409
#define BPB_SecPerTrk           24      /* Track size [sector] (2) */
410
#define BPB_NumHeads            26      /* Number of heads (2) */
411
#define BPB_HiddSec                     28      /* Number of special hidden sectors (4) */
412
#define BPB_TotSec32            32      /* Volume size [sector] (4) */
413
#define BS_DrvNum                       36      /* Physical drive number (2) */
414
#define BS_BootSig                      38      /* Extended boot signature (1) */
415
#define BS_VolID                        39      /* Volume serial number (4) */
416
#define BS_VolLab                       43      /* Volume label (8) */
417
#define BS_FilSysType           54      /* File system type (1) */
418
#define BPB_FATSz32                     36      /* FAT size [sector] (4) */
419
#define BPB_ExtFlags            40      /* Extended flags (2) */
420
#define BPB_FSVer                       42      /* File system version (2) */
421
#define BPB_RootClus            44      /* Root dir first cluster (4) */
422
#define BPB_FSInfo                      48      /* Offset of FSInfo sector (2) */
423
#define BPB_BkBootSec           50      /* Offset of backup boot sector (2) */
424
#define BS_DrvNum32                     64      /* Physical drive number (2) */
425
#define BS_BootSig32            66      /* Extended boot signature (1) */
426
#define BS_VolID32                      67      /* Volume serial number (4) */
427
#define BS_VolLab32                     71      /* Volume label (8) */
428
#define BS_FilSysType32         82      /* File system type (1) */
429
#define FSI_LeadSig                     0        /* FSI: Leading signature (4) */
430
#define FSI_StrucSig            484     /* FSI: Structure signature (4) */
431
#define FSI_Free_Count          488     /* FSI: Number of free clusters (4) */
432
#define FSI_Nxt_Free            492     /* FSI: Last allocated cluster (4) */
433
#define MBR_Table                       446     /* MBR: Partition table offset (2) */
434
#define SZ_PTE                          16      /* MBR: Size of a partition table entry */
435
#define BS_55AA                         510     /* Boot sector signature (2) */
436
 
437
#define DIR_Name                        0        /* Short file name (11) */
438
#define DIR_Attr                        11      /* Attribute (1) */
439
#define DIR_NTres                       12      /* NT flag (1) */
440
#define DIR_CrtTimeTenth        13      /* Created time sub-second (1) */
441
#define DIR_CrtTime                     14      /* Created time (2) */
442
#define DIR_CrtDate                     16      /* Created date (2) */
443
#define DIR_LstAccDate          18      /* Last accessed date (2) */
444
#define DIR_FstClusHI           20      /* Higher 16-bit of first cluster (2) */
445
#define DIR_WrtTime                     22      /* Modified time (2) */
446
#define DIR_WrtDate                     24      /* Modified date (2) */
447
#define DIR_FstClusLO           26      /* Lower 16-bit of first cluster (2) */
448
#define DIR_FileSize            28      /* File size (4) */
449
#define LDIR_Ord                        0        /* LFN entry order and LLE flag (1) */
450
#define LDIR_Attr                       11      /* LFN attribute (1) */
451
#define LDIR_Type                       12      /* LFN type (1) */
452
#define LDIR_Chksum                     13      /* Sum of corresponding SFN entry */
453
#define LDIR_FstClusLO          26      /* Filled by zero (0) */
454
#define SZ_DIR                          32              /* Size of a directory entry */
455
#define LLE                                     0x40    /* Last long entry flag in LDIR_Ord */
456
#define DDE                                     0xE5    /* Deleted directory entry mark in DIR_Name[0] */
457
#define NDDE                            0x05    /* Replacement of the character collides with DDE */
458
 
459
 
460
/*------------------------------------------------------------*/
461
/* Module private work area                                   */
462
/*------------------------------------------------------------*/
463
/* Note that uninitialized variables with static duration are
464
/  zeroed/nulled at start-up. If not, the compiler or start-up
465
/  routine is out of ANSI-C standard.
466
*/
467
 
468
#if _VOLUMES
469
static
470
FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */
471
#else
472
#error Number of volumes must not be 0.
473
#endif
474
 
475
static
476
WORD Fsid;                              /* File system mount ID */
477
 
478
#if _FS_RPATH
479
static
480
BYTE CurrVol;                   /* Current drive */
481
#endif
482
 
483
#if _FS_LOCK
484
static
485
FILESEM Files[_FS_LOCK];        /* File lock semaphores */
486
#endif
487
 
488
#if _USE_LFN == 0                       /* No LFN feature */
489
#define DEF_NAMEBUF                     BYTE sfn[12]
490
#define INIT_BUF(dobj)          (dobj).fn = sfn
491
#define FREE_BUF()
492
 
493
#elif _USE_LFN == 1                     /* LFN feature with static working buffer */
494
static WCHAR LfnBuf[_MAX_LFN+1];
495
#define DEF_NAMEBUF                     BYTE sfn[12]
496
#define INIT_BUF(dobj)          { (dobj).fn = sfn; (dobj).lfn = LfnBuf; }
497
#define FREE_BUF()
498
 
499
#elif _USE_LFN == 2             /* LFN feature with dynamic working buffer on the stack */
500
#define DEF_NAMEBUF                     BYTE sfn[12]; WCHAR lbuf[_MAX_LFN+1]
501
#define INIT_BUF(dobj)          { (dobj).fn = sfn; (dobj).lfn = lbuf; }
502
#define FREE_BUF()
503
 
504
#elif _USE_LFN == 3             /* LFN feature with dynamic working buffer on the heap */
505
#define DEF_NAMEBUF                     BYTE sfn[12]; WCHAR *lfn
506
#define INIT_BUF(dobj)          { lfn = ff_memalloc((_MAX_LFN + 1) * 2); \
507
                                                          if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); \
508
                                                          (dobj).lfn = lfn;     (dobj).fn = sfn; }
509
#define FREE_BUF()                      ff_memfree(lfn)
510
 
511
#else
512
#error Wrong LFN configuration.
513
#endif
514
 
515
 
516
 
517
 
518
/*--------------------------------------------------------------------------
519
 
520
   Module Private Functions
521
 
522
---------------------------------------------------------------------------*/
523
 
524
 
525
/*-----------------------------------------------------------------------*/
526
/* String functions                                                      */
527
/*-----------------------------------------------------------------------*/
528
 
529
/* Copy memory to memory */
530
static
531
void mem_cpy (void* dst, const void* src, UINT cnt) {
532
        BYTE *d = (BYTE*)dst;
533
        const BYTE *s = (const BYTE*)src;
534
 
535
#if _WORD_ACCESS == 1
536
        while (cnt >= sizeof (int)) {
537
                *(int*)d = *(int*)s;
538
                d += sizeof (int); s += sizeof (int);
539
                cnt -= sizeof (int);
540
        }
541
#endif
542
        while (cnt--)
543
                *d++ = *s++;
544
}
545
 
546
/* Fill memory */
547
static
548
void mem_set (void* dst, int val, UINT cnt) {
549
        BYTE *d = (BYTE*)dst;
550
 
551
        while (cnt--)
552
                *d++ = (BYTE)val;
553
}
554
 
555
/* Compare memory to memory */
556
static
557
int mem_cmp (const void* dst, const void* src, UINT cnt) {
558
        const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src;
559
        int r = 0;
560
 
561
        while (cnt-- && (r = *d++ - *s++) == 0) ;
562
        return r;
563
}
564
 
565
/* Check if chr is contained in the string */
566
static
567
int chk_chr (const char* str, int chr) {
568
        while (*str && *str != chr) str++;
569
        return *str;
570
}
571
 
572
 
573
 
574
/*-----------------------------------------------------------------------*/
575
/* Request/Release grant to access the volume                            */
576
/*-----------------------------------------------------------------------*/
577
#if _FS_REENTRANT
578
 
579
static
580
int lock_fs (
581
        FATFS *fs               /* File system object */
582
)
583
{
584
        return ff_req_grant(fs->sobj);
585
}
586
 
587
 
588
static
589
void unlock_fs (
590
        FATFS *fs,              /* File system object */
591
        FRESULT res             /* Result code to be returned */
592
)
593
{
594
        if (fs &&
595
                res != FR_NOT_ENABLED &&
596
                res != FR_INVALID_DRIVE &&
597
                res != FR_INVALID_OBJECT &&
598
                res != FR_TIMEOUT) {
599
                ff_rel_grant(fs->sobj);
600
        }
601
}
602
#endif
603
 
604
 
605
 
606
/*-----------------------------------------------------------------------*/
607
/* File lock control functions                                           */
608
/*-----------------------------------------------------------------------*/
609
#if _FS_LOCK
610
 
611
static
612
FRESULT chk_lock (      /* Check if the file can be accessed */
613
        DIR* dj,                /* Directory object pointing the file to be checked */
614
        int acc                 /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
615
)
616
{
617
        UINT i, be;
618
 
619
        /* Search file semaphore table */
620
        for (i = be = 0; i < _FS_LOCK; i++) {
621
                if (Files[i].fs) {      /* Existing entry */
622
                        if (Files[i].fs == dj->fs &&            /* Check if the file matched with an open file */
623
                                Files[i].clu == dj->sclust &&
624
                                Files[i].idx == dj->index) break;
625
                } else {                        /* Blank entry */
626
                        be++;
627
                }
628
        }
629
        if (i == _FS_LOCK)      /* The file is not opened */
630
                return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES;       /* Is there a blank entry for new file? */
631
 
632
        /* The file has been opened. Reject any open against writing file and all write mode open */
633
        return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK;
634
}
635
 
636
 
637
static
638
int enq_lock (void)     /* Check if an entry is available for a new file */
639
{
640
        UINT i;
641
 
642
        for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ;
643
        return (i == _FS_LOCK) ? 0 : 1;
644
}
645
 
646
 
647
static
648
UINT inc_lock ( /* Increment file open counter and returns its index (0:int error) */
649
        DIR* dj,        /* Directory object pointing the file to register or increment */
650
        int acc         /* Desired access mode (0:Read, !0:Write) */
651
)
652
{
653
        UINT i;
654
 
655
 
656
        for (i = 0; i < _FS_LOCK; i++) { /* Find the file */
657
                if (Files[i].fs == dj->fs &&
658
                        Files[i].clu == dj->sclust &&
659
                        Files[i].idx == dj->index) break;
660
        }
661
 
662
        if (i == _FS_LOCK) {                            /* Not opened. Register it as new. */
663
                for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ;
664
                if (i == _FS_LOCK) return 0;     /* No space to register (int err) */
665
                Files[i].fs = dj->fs;
666
                Files[i].clu = dj->sclust;
667
                Files[i].idx = dj->index;
668
                Files[i].ctr = 0;
669
        }
670
 
671
        if (acc && Files[i].ctr) return 0;       /* Access violation (int err) */
672
 
673
        Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1;  /* Set semaphore value */
674
 
675
        return i + 1;
676
}
677
 
678
 
679
static
680
FRESULT dec_lock (      /* Decrement file open counter */
681
        UINT i                  /* Semaphore index */
682
)
683
{
684
        WORD n;
685
        FRESULT res;
686
 
687
 
688
        if (--i < _FS_LOCK) {
689
                n = Files[i].ctr;
690
                if (n == 0x100) n = 0;
691
                if (n) n--;
692
                Files[i].ctr = n;
693
                if (!n) Files[i].fs = 0;
694
                res = FR_OK;
695
        } else {
696
                res = FR_INT_ERR;
697
        }
698
        return res;
699
}
700
 
701
 
702
static
703
void clear_lock (       /* Clear lock entries of the volume */
704
        FATFS *fs
705
)
706
{
707
        UINT i;
708
 
709
        for (i = 0; i < _FS_LOCK; i++) {
710
                if (Files[i].fs == fs) Files[i].fs = 0;
711
        }
712
}
713
#endif
714
 
715
 
716
 
717
/*-----------------------------------------------------------------------*/
718
/* Change window offset                                                  */
719
/*-----------------------------------------------------------------------*/
720
 
721
static
722
FRESULT move_window (
723
        FATFS *fs,              /* File system object */
724
        DWORD sector    /* Sector number to make appearance in the fs->win[] */
725
)                                       /* Move to zero only writes back dirty window */
726
{
727
        DWORD wsect;
728
 
729
 
730
        wsect = fs->winsect;
731
        if (wsect != sector) {  /* Changed current window */
732
#if !_FS_READONLY
733
                if (fs->wflag) {        /* Write back dirty window if needed */
734
                        if (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK)
735
                                return FR_DISK_ERR;
736
                        fs->wflag = 0;
737
                        if (wsect < (fs->fatbase + fs->fsize)) {        /* In FAT area */
738
                                BYTE nf;
739
                                for (nf = fs->n_fats; nf > 1; nf--) {   /* Reflect the change to all FAT copies */
740
                                        wsect += fs->fsize;
741
                                        disk_write(fs->drv, fs->win, wsect, 1);
742
                                }
743
                        }
744
                }
745
#endif
746
                if (sector) {
747
                        if (disk_read(fs->drv, fs->win, sector, 1) != RES_OK)
748
                                return FR_DISK_ERR;
749
                        fs->winsect = sector;
750
                }
751
        }
752
 
753
        return FR_OK;
754
}
755
 
756
 
757
 
758
 
759
/*-----------------------------------------------------------------------*/
760
/* Clean-up cached data                                                  */
761
/*-----------------------------------------------------------------------*/
762
#if !_FS_READONLY
763
static
764
FRESULT sync (  /* FR_OK: successful, FR_DISK_ERR: failed */
765
        FATFS *fs       /* File system object */
766
)
767
{
768
        FRESULT res;
769
 
770
 
771
        res = move_window(fs, 0);
772
        if (res == FR_OK) {
773
                /* Update FSInfo sector if needed */
774
                if (fs->fs_type == FS_FAT32 && fs->fsi_flag) {
775
                        fs->winsect = 0;
776
                        /* Create FSInfo structure */
777
                        mem_set(fs->win, 0, 512);
778
                        ST_WORD(fs->win+BS_55AA, 0xAA55);
779
                        ST_DWORD(fs->win+FSI_LeadSig, 0x41615252);
780
                        ST_DWORD(fs->win+FSI_StrucSig, 0x61417272);
781
                        ST_DWORD(fs->win+FSI_Free_Count, fs->free_clust);
782
                        ST_DWORD(fs->win+FSI_Nxt_Free, fs->last_clust);
783
                        /* Write it into the FSInfo sector */
784
                        disk_write(fs->drv, fs->win, fs->fsi_sector, 1);
785
                        fs->fsi_flag = 0;
786
                }
787
                /* Make sure that no pending write process in the physical drive */
788
                if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK)
789
                        res = FR_DISK_ERR;
790
        }
791
 
792
        return res;
793
}
794
#endif
795
 
796
 
797
 
798
 
799
/*-----------------------------------------------------------------------*/
800
/* Get sector# from cluster#                                             */
801
/*-----------------------------------------------------------------------*/
802
 
803
 
804
DWORD clust2sect (      /* !=0: Sector number, 0: Failed - invalid cluster# */
805
        FATFS *fs,              /* File system object */
806
        DWORD clst              /* Cluster# to be converted */
807
)
808
{
809
        clst -= 2;
810
        if (clst >= (fs->n_fatent - 2)) return 0;                /* Invalid cluster# */
811
        return clst * fs->csize + fs->database;
812
}
813
 
814
 
815
 
816
 
817
/*-----------------------------------------------------------------------*/
818
/* FAT access - Read value of a FAT entry                                */
819
/*-----------------------------------------------------------------------*/
820
 
821
 
822
DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, Else:Cluster status */
823
        FATFS *fs,      /* File system object */
824
        DWORD clst      /* Cluster# to get the link information */
825
)
826
{
827
        UINT wc, bc;
828
        BYTE *p;
829
 
830
 
831
        if (clst < 2 || clst >= fs->n_fatent)   /* Check range */
832
                return 1;
833
 
834
        switch (fs->fs_type) {
835
        case FS_FAT12 :
836
                bc = (UINT)clst; bc += bc / 2;
837
                if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break;
838
                wc = fs->win[bc % SS(fs)]; bc++;
839
                if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break;
840
                wc |= fs->win[bc % SS(fs)] << 8;
841
                return (clst & 1) ? (wc >> 4) : (wc & 0xFFF);
842
 
843
        case FS_FAT16 :
844
                if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)))) break;
845
                p = &fs->win[clst * 2 % SS(fs)];
846
                return LD_WORD(p);
847
 
848
        case FS_FAT32 :
849
                if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)))) break;
850
                p = &fs->win[clst * 4 % SS(fs)];
851
                return LD_DWORD(p) & 0x0FFFFFFF;
852
        }
853
 
854
        return 0xFFFFFFFF;      /* An error occurred at the disk I/O layer */
855
}
856
 
857
 
858
 
859
 
860
/*-----------------------------------------------------------------------*/
861
/* FAT access - Change value of a FAT entry                              */
862
/*-----------------------------------------------------------------------*/
863
#if !_FS_READONLY
864
 
865
FRESULT put_fat (
866
        FATFS *fs,      /* File system object */
867
        DWORD clst,     /* Cluster# to be changed in range of 2 to fs->n_fatent - 1 */
868
        DWORD val       /* New value to mark the cluster */
869
)
870
{
871
        UINT bc;
872
        BYTE *p;
873
        FRESULT res;
874
 
875
 
876
        if (clst < 2 || clst >= fs->n_fatent) { /* Check range */
877
                res = FR_INT_ERR;
878
 
879
        } else {
880
                switch (fs->fs_type) {
881
                case FS_FAT12 :
882
                        bc = (UINT)clst; bc += bc / 2;
883
                        res = move_window(fs, fs->fatbase + (bc / SS(fs)));
884
                        if (res != FR_OK) break;
885
                        p = &fs->win[bc % SS(fs)];
886
                        *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;
887
                        bc++;
888
                        fs->wflag = 1;
889
                        res = move_window(fs, fs->fatbase + (bc / SS(fs)));
890
                        if (res != FR_OK) break;
891
                        p = &fs->win[bc % SS(fs)];
892
                        *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));
893
                        break;
894
 
895
                case FS_FAT16 :
896
                        res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)));
897
                        if (res != FR_OK) break;
898
                        p = &fs->win[clst * 2 % SS(fs)];
899
                        ST_WORD(p, (WORD)val);
900
                        break;
901
 
902
                case FS_FAT32 :
903
                        res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));
904
                        if (res != FR_OK) break;
905
                        p = &fs->win[clst * 4 % SS(fs)];
906
                        val |= LD_DWORD(p) & 0xF0000000;
907
                        ST_DWORD(p, val);
908
                        break;
909
 
910
                default :
911
                        res = FR_INT_ERR;
912
                }
913
                fs->wflag = 1;
914
        }
915
 
916
        return res;
917
}
918
#endif /* !_FS_READONLY */
919
 
920
 
921
 
922
 
923
/*-----------------------------------------------------------------------*/
924
/* FAT handling - Remove a cluster chain                                 */
925
/*-----------------------------------------------------------------------*/
926
#if !_FS_READONLY
927
static
928
FRESULT remove_chain (
929
        FATFS *fs,                      /* File system object */
930
        DWORD clst                      /* Cluster# to remove a chain from */
931
)
932
{
933
        FRESULT res;
934
        DWORD nxt;
935
#if _USE_ERASE
936
        DWORD scl = clst, ecl = clst, rt[2];
937
#endif
938
 
939
        if (clst < 2 || clst >= fs->n_fatent) { /* Check range */
940
                res = FR_INT_ERR;
941
 
942
        } else {
943
                res = FR_OK;
944
                while (clst < fs->n_fatent) {                   /* Not a last link? */
945
                        nxt = get_fat(fs, clst);                        /* Get cluster status */
946
                        if (nxt == 0) break;                             /* Empty cluster? */
947
                        if (nxt == 1) { res = FR_INT_ERR; break; }      /* Internal error? */
948
                        if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }    /* Disk error? */
949
                        res = put_fat(fs, clst, 0);                      /* Mark the cluster "empty" */
950
                        if (res != FR_OK) break;
951
                        if (fs->free_clust != 0xFFFFFFFF) {     /* Update FSInfo */
952
                                fs->free_clust++;
953
                                fs->fsi_flag = 1;
954
                        }
955
#if _USE_ERASE
956
                        if (ecl + 1 == nxt) {   /* Is next cluster contiguous? */
957
                                ecl = nxt;
958
                        } else {                                /* End of contiguous clusters */
959
                                rt[0] = clust2sect(fs, scl);                                     /* Start sector */
960
                                rt[1] = clust2sect(fs, ecl) + fs->csize - 1;    /* End sector */
961
                                disk_ioctl(fs->drv, CTRL_ERASE_SECTOR, rt);             /* Erase the block */
962
                                scl = ecl = nxt;
963
                        }
964
#endif
965
                        clst = nxt;     /* Next cluster */
966
                }
967
        }
968
 
969
        return res;
970
}
971
#endif
972
 
973
 
974
 
975
 
976
/*-----------------------------------------------------------------------*/
977
/* FAT handling - Stretch or Create a cluster chain                      */
978
/*-----------------------------------------------------------------------*/
979
#if !_FS_READONLY
980
static
981
DWORD create_chain (    /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
982
        FATFS *fs,                      /* File system object */
983
        DWORD clst                      /* Cluster# to stretch. 0 means create a new chain. */
984
)
985
{
986
        DWORD cs, ncl, scl;
987
        FRESULT res;
988
 
989
 
990
        if (clst == 0) {         /* Create a new chain */
991
                scl = fs->last_clust;                   /* Get suggested start point */
992
                if (!scl || scl >= fs->n_fatent) scl = 1;
993
        }
994
        else {                                  /* Stretch the current chain */
995
                cs = get_fat(fs, clst);                 /* Check the cluster status */
996
                if (cs < 2) return 1;                   /* It is an invalid cluster */
997
                if (cs < fs->n_fatent) return cs;       /* It is already followed by next cluster */
998
                scl = clst;
999
        }
1000
 
1001
        ncl = scl;                              /* Start cluster */
1002
        for (;;) {
1003
                ncl++;                                                  /* Next cluster */
1004
                if (ncl >= fs->n_fatent) {              /* Wrap around */
1005
                        ncl = 2;
1006
                        if (ncl > scl) return 0; /* No free cluster */
1007
                }
1008
                cs = get_fat(fs, ncl);                  /* Get the cluster status */
1009
                if (cs == 0) break;                              /* Found a free cluster */
1010
                if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */
1011
                        return cs;
1012
                if (ncl == scl) return 0;                /* No free cluster */
1013
        }
1014
 
1015
        res = put_fat(fs, ncl, 0x0FFFFFFF);     /* Mark the new cluster "last link" */
1016
        if (res == FR_OK && clst != 0) {
1017
                res = put_fat(fs, clst, ncl);   /* Link it to the previous one if needed */
1018
        }
1019
        if (res == FR_OK) {
1020
                fs->last_clust = ncl;                   /* Update FSINFO */
1021
                if (fs->free_clust != 0xFFFFFFFF) {
1022
                        fs->free_clust--;
1023
                        fs->fsi_flag = 1;
1024
                }
1025
        } else {
1026
                ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1;
1027
        }
1028
 
1029
        return ncl;             /* Return new cluster number or error code */
1030
}
1031
#endif /* !_FS_READONLY */
1032
 
1033
 
1034
 
1035
/*-----------------------------------------------------------------------*/
1036
/* FAT handling - Convert offset into cluster with link map table        */
1037
/*-----------------------------------------------------------------------*/
1038
 
1039
#if _USE_FASTSEEK
1040
static
1041
DWORD clmt_clust (      /* <2:Error, >=2:Cluster number */
1042
        FIL* fp,                /* Pointer to the file object */
1043
        DWORD ofs               /* File offset to be converted to cluster# */
1044
)
1045
{
1046
        DWORD cl, ncl, *tbl;
1047
 
1048
 
1049
        tbl = fp->cltbl + 1;    /* Top of CLMT */
1050
        cl = ofs / SS(fp->fs) / fp->fs->csize;  /* Cluster order from top of the file */
1051
        for (;;) {
1052
                ncl = *tbl++;                   /* Number of cluters in the fragment */
1053
                if (!ncl) return 0;              /* End of table? (error) */
1054
                if (cl < ncl) break;    /* In this fragment? */
1055
                cl -= ncl; tbl++;               /* Next fragment */
1056
        }
1057
        return cl + *tbl;       /* Return the cluster number */
1058
}
1059
#endif  /* _USE_FASTSEEK */
1060
 
1061
 
1062
 
1063
/*-----------------------------------------------------------------------*/
1064
/* Directory handling - Set directory index                              */
1065
/*-----------------------------------------------------------------------*/
1066
 
1067
static
1068
FRESULT dir_sdi (
1069
        DIR *dj,                /* Pointer to directory object */
1070
        WORD idx                /* Index of directory table */
1071
)
1072
{
1073
        DWORD clst;
1074
        WORD ic;
1075
 
1076
 
1077
        dj->index = idx;
1078
        clst = dj->sclust;
1079
        if (clst == 1 || clst >= dj->fs->n_fatent)      /* Check start cluster range */
1080
                return FR_INT_ERR;
1081
        if (!clst && dj->fs->fs_type == FS_FAT32)       /* Replace cluster# 0 with root cluster# if in FAT32 */
1082
                clst = dj->fs->dirbase;
1083
 
1084
        if (clst == 0) { /* Static table (root-dir in FAT12/16) */
1085
                dj->clust = clst;
1086
                if (idx >= dj->fs->n_rootdir)           /* Index is out of range */
1087
                        return FR_INT_ERR;
1088
                dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / SZ_DIR);       /* Sector# */
1089
        }
1090
        else {                          /* Dynamic table (sub-dirs or root-dir in FAT32) */
1091
                ic = SS(dj->fs) / SZ_DIR * dj->fs->csize;       /* Entries per cluster */
1092
                while (idx >= ic) {     /* Follow cluster chain */
1093
                        clst = get_fat(dj->fs, clst);                           /* Get next cluster */
1094
                        if (clst == 0xFFFFFFFF) return FR_DISK_ERR;     /* Disk error */
1095
                        if (clst < 2 || clst >= dj->fs->n_fatent)       /* Reached to end of table or int error */
1096
                                return FR_INT_ERR;
1097
                        idx -= ic;
1098
                }
1099
                dj->clust = clst;
1100
                dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / SZ_DIR);      /* Sector# */
1101
        }
1102
 
1103
        dj->dir = dj->fs->win + (idx % (SS(dj->fs) / SZ_DIR)) * SZ_DIR; /* Ptr to the entry in the sector */
1104
 
1105
        return FR_OK;   /* Seek succeeded */
1106
}
1107
 
1108
 
1109
 
1110
 
1111
/*-----------------------------------------------------------------------*/
1112
/* Directory handling - Move directory table index next                  */
1113
/*-----------------------------------------------------------------------*/
1114
 
1115
static
1116
FRESULT dir_next (      /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not stretch */
1117
        DIR *dj,                /* Pointer to directory object */
1118
        int stretch             /* 0: Do not stretch table, 1: Stretch table if needed */
1119
)
1120
{
1121
        DWORD clst;
1122
        WORD i;
1123
 
1124
 
1125
        stretch = stretch;              /* To suppress warning on read-only cfg. */
1126
        i = dj->index + 1;
1127
        if (!i || !dj->sect)    /* Report EOT when index has reached 65535 */
1128
                return FR_NO_FILE;
1129
 
1130
        if (!(i % (SS(dj->fs) / SZ_DIR))) {     /* Sector changed? */
1131
                dj->sect++;                                     /* Next sector */
1132
 
1133
                if (dj->clust == 0) {    /* Static table */
1134
                        if (i >= dj->fs->n_rootdir)     /* Report EOT when end of table */
1135
                                return FR_NO_FILE;
1136
                }
1137
                else {                                  /* Dynamic table */
1138
                        if (((i / (SS(dj->fs) / SZ_DIR)) & (dj->fs->csize - 1)) == 0) {  /* Cluster changed? */
1139
                                clst = get_fat(dj->fs, dj->clust);                              /* Get next cluster */
1140
                                if (clst <= 1) return FR_INT_ERR;
1141
                                if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
1142
                                if (clst >= dj->fs->n_fatent) {                                 /* When it reached end of dynamic table */
1143
#if !_FS_READONLY
1144
                                        BYTE c;
1145
                                        if (!stretch) return FR_NO_FILE;                        /* When do not stretch, report EOT */
1146
                                        clst = create_chain(dj->fs, dj->clust);         /* Stretch cluster chain */
1147
                                        if (clst == 0) return FR_DENIED;                 /* No free cluster */
1148
                                        if (clst == 1) return FR_INT_ERR;
1149
                                        if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
1150
                                        /* Clean-up stretched table */
1151
                                        if (move_window(dj->fs, 0)) return FR_DISK_ERR;  /* Flush active window */
1152
                                        mem_set(dj->fs->win, 0, SS(dj->fs));                     /* Clear window buffer */
1153
                                        dj->fs->winsect = clust2sect(dj->fs, clst);     /* Cluster start sector */
1154
                                        for (c = 0; c < dj->fs->csize; c++) {            /* Fill the new cluster with 0 */
1155
                                                dj->fs->wflag = 1;
1156
                                                if (move_window(dj->fs, 0)) return FR_DISK_ERR;
1157
                                                dj->fs->winsect++;
1158
                                        }
1159
                                        dj->fs->winsect -= c;                                           /* Rewind window address */
1160
#else
1161
                                        return FR_NO_FILE;                      /* Report EOT */
1162
#endif
1163
                                }
1164
                                dj->clust = clst;                               /* Initialize data for new cluster */
1165
                                dj->sect = clust2sect(dj->fs, clst);
1166
                        }
1167
                }
1168
        }
1169
 
1170
        dj->index = i;
1171
        dj->dir = dj->fs->win + (i % (SS(dj->fs) / SZ_DIR)) * SZ_DIR;
1172
 
1173
        return FR_OK;
1174
}
1175
 
1176
 
1177
 
1178
 
1179
/*-----------------------------------------------------------------------*/
1180
/* Directory handling - Load/Store start cluster number                  */
1181
/*-----------------------------------------------------------------------*/
1182
 
1183
static
1184
DWORD ld_clust (
1185
        FATFS *fs,      /* Pointer to the fs object */
1186
        BYTE *dir       /* Pointer to the directory entry */
1187
)
1188
{
1189
        DWORD cl;
1190
 
1191
        cl = LD_WORD(dir+DIR_FstClusLO);
1192
        if (fs->fs_type == FS_FAT32)
1193
                cl |= (DWORD)LD_WORD(dir+DIR_FstClusHI) << 16;
1194
 
1195
        return cl;
1196
}
1197
 
1198
 
1199
#if !_FS_READONLY
1200
static
1201
void st_clust (
1202
        BYTE *dir,      /* Pointer to the directory entry */
1203
        DWORD cl        /* Value to be set */
1204
)
1205
{
1206
        ST_WORD(dir+DIR_FstClusLO, cl);
1207
        ST_WORD(dir+DIR_FstClusHI, cl >> 16);
1208
}
1209
#endif
1210
 
1211
 
1212
 
1213
/*-----------------------------------------------------------------------*/
1214
/* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry   */
1215
/*-----------------------------------------------------------------------*/
1216
#if _USE_LFN
1217
static
1218
const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30};      /* Offset of LFN chars in the directory entry */
1219
 
1220
 
1221
static
1222
int cmp_lfn (                   /* 1:Matched, 0:Not matched */
1223
        WCHAR *lfnbuf,          /* Pointer to the LFN to be compared */
1224
        BYTE *dir                       /* Pointer to the directory entry containing a part of LFN */
1225
)
1226
{
1227
        UINT i, s;
1228
        WCHAR wc, uc;
1229
 
1230
 
1231
        i = ((dir[LDIR_Ord] & ~LLE) - 1) * 13;  /* Get offset in the LFN buffer */
1232
        s = 0; wc = 1;
1233
        do {
1234
                uc = LD_WORD(dir+LfnOfs[s]);    /* Pick an LFN character from the entry */
1235
                if (wc) {       /* Last char has not been processed */
1236
                        wc = ff_wtoupper(uc);           /* Convert it to upper case */
1237
                        if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++]))    /* Compare it */
1238
                                return 0;                                /* Not matched */
1239
                } else {
1240
                        if (uc != 0xFFFF) return 0;      /* Check filler */
1241
                }
1242
        } while (++s < 13);                             /* Repeat until all chars in the entry are checked */
1243
 
1244
        if ((dir[LDIR_Ord] & LLE) && wc && lfnbuf[i])   /* Last segment matched but different length */
1245
                return 0;
1246
 
1247
        return 1;                                               /* The part of LFN matched */
1248
}
1249
 
1250
 
1251
 
1252
static
1253
int pick_lfn (                  /* 1:Succeeded, 0:Buffer overflow */
1254
        WCHAR *lfnbuf,          /* Pointer to the Unicode-LFN buffer */
1255
        BYTE *dir                       /* Pointer to the directory entry */
1256
)
1257
{
1258
        UINT i, s;
1259
        WCHAR wc, uc;
1260
 
1261
 
1262
        i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13;  /* Offset in the LFN buffer */
1263
 
1264
        s = 0; wc = 1;
1265
        do {
1266
                uc = LD_WORD(dir+LfnOfs[s]);            /* Pick an LFN character from the entry */
1267
                if (wc) {       /* Last char has not been processed */
1268
                        if (i >= _MAX_LFN) return 0;     /* Buffer overflow? */
1269
                        lfnbuf[i++] = wc = uc;                  /* Store it */
1270
                } else {
1271
                        if (uc != 0xFFFF) return 0;              /* Check filler */
1272
                }
1273
        } while (++s < 13);                                             /* Read all character in the entry */
1274
 
1275
        if (dir[LDIR_Ord] & LLE) {                              /* Put terminator if it is the last LFN part */
1276
                if (i >= _MAX_LFN) return 0;             /* Buffer overflow? */
1277
                lfnbuf[i] = 0;
1278
        }
1279
 
1280
        return 1;
1281
}
1282
 
1283
 
1284
#if !_FS_READONLY
1285
static
1286
void fit_lfn (
1287
        const WCHAR *lfnbuf,    /* Pointer to the LFN buffer */
1288
        BYTE *dir,                              /* Pointer to the directory entry */
1289
        BYTE ord,                               /* LFN order (1-20) */
1290
        BYTE sum                                /* SFN sum */
1291
)
1292
{
1293
        UINT i, s;
1294
        WCHAR wc;
1295
 
1296
 
1297
        dir[LDIR_Chksum] = sum;                 /* Set check sum */
1298
        dir[LDIR_Attr] = AM_LFN;                /* Set attribute. LFN entry */
1299
        dir[LDIR_Type] = 0;
1300
        ST_WORD(dir+LDIR_FstClusLO, 0);
1301
 
1302
        i = (ord - 1) * 13;                             /* Get offset in the LFN buffer */
1303
        s = wc = 0;
1304
        do {
1305
                if (wc != 0xFFFF) wc = lfnbuf[i++];     /* Get an effective char */
1306
                ST_WORD(dir+LfnOfs[s], wc);     /* Put it */
1307
                if (!wc) wc = 0xFFFF;           /* Padding chars following last char */
1308
        } while (++s < 13);
1309
        if (wc == 0xFFFF || !lfnbuf[i]) ord |= LLE;     /* Bottom LFN part is the start of LFN sequence */
1310
        dir[LDIR_Ord] = ord;                    /* Set the LFN order */
1311
}
1312
 
1313
#endif
1314
#endif
1315
 
1316
 
1317
 
1318
/*-----------------------------------------------------------------------*/
1319
/* Create numbered name                                                  */
1320
/*-----------------------------------------------------------------------*/
1321
#if _USE_LFN
1322
void gen_numname (
1323
        BYTE *dst,                      /* Pointer to generated SFN */
1324
        const BYTE *src,        /* Pointer to source SFN to be modified */
1325
        const WCHAR *lfn,       /* Pointer to LFN */
1326
        WORD seq                        /* Sequence number */
1327
)
1328
{
1329
        BYTE ns[8], c;
1330
        UINT i, j;
1331
 
1332
 
1333
        mem_cpy(dst, src, 11);
1334
 
1335
        if (seq > 5) {  /* On many collisions, generate a hash number instead of sequential number */
1336
                do seq = (seq >> 1) + (seq << 15) + (WORD)*lfn++; while (*lfn);
1337
        }
1338
 
1339
        /* itoa (hexdecimal) */
1340
        i = 7;
1341
        do {
1342
                c = (seq % 16) + '0';
1343
                if (c > '9') c += 7;
1344
                ns[i--] = c;
1345
                seq /= 16;
1346
        } while (seq);
1347
        ns[i] = '~';
1348
 
1349
        /* Append the number */
1350
        for (j = 0; j < i && dst[j] != ' '; j++) {
1351
                if (IsDBCS1(dst[j])) {
1352
                        if (j == i - 1) break;
1353
                        j++;
1354
                }
1355
        }
1356
        do {
1357
                dst[j++] = (i < 8) ? ns[i++] : ' ';
1358
        } while (j < 8);
1359
}
1360
#endif
1361
 
1362
 
1363
 
1364
 
1365
/*-----------------------------------------------------------------------*/
1366
/* Calculate sum of an SFN                                               */
1367
/*-----------------------------------------------------------------------*/
1368
#if _USE_LFN
1369
static
1370
BYTE sum_sfn (
1371
        const BYTE *dir         /* Ptr to directory entry */
1372
)
1373
{
1374
        BYTE sum = 0;
1375
        UINT n = 11;
1376
 
1377
        do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n);
1378
        return sum;
1379
}
1380
#endif
1381
 
1382
 
1383
 
1384
 
1385
/*-----------------------------------------------------------------------*/
1386
/* Directory handling - Find an object in the directory                  */
1387
/*-----------------------------------------------------------------------*/
1388
 
1389
static
1390
FRESULT dir_find (
1391
        DIR *dj                 /* Pointer to the directory object linked to the file name */
1392
)
1393
{
1394
        FRESULT res;
1395
        BYTE c, *dir;
1396
#if _USE_LFN
1397
        BYTE a, ord, sum;
1398
#endif
1399
 
1400
        res = dir_sdi(dj, 0);                    /* Rewind directory object */
1401
        if (res != FR_OK) return res;
1402
 
1403
#if _USE_LFN
1404
        ord = sum = 0xFF;
1405
#endif
1406
        do {
1407
                res = move_window(dj->fs, dj->sect);
1408
                if (res != FR_OK) break;
1409
                dir = dj->dir;                                  /* Ptr to the directory entry of current index */
1410
                c = dir[DIR_Name];
1411
                if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */
1412
#if _USE_LFN    /* LFN configuration */
1413
                a = dir[DIR_Attr] & AM_MASK;
1414
                if (c == DDE || ((a & AM_VOL) && a != AM_LFN)) {        /* An entry without valid data */
1415
                        ord = 0xFF;
1416
                } else {
1417
                        if (a == AM_LFN) {                      /* An LFN entry is found */
1418
                                if (dj->lfn) {
1419
                                        if (c & LLE) {          /* Is it start of LFN sequence? */
1420
                                                sum = dir[LDIR_Chksum];
1421
                                                c &= ~LLE; ord = c;     /* LFN start order */
1422
                                                dj->lfn_idx = dj->index;
1423
                                        }
1424
                                        /* Check validity of the LFN entry and compare it with given name */
1425
                                        ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF;
1426
                                }
1427
                        } else {                                        /* An SFN entry is found */
1428
                                if (!ord && sum == sum_sfn(dir)) break; /* LFN matched? */
1429
                                ord = 0xFF; dj->lfn_idx = 0xFFFF;       /* Reset LFN sequence */
1430
                                if (!(dj->fn[NS] & NS_LOSS) && !mem_cmp(dir, dj->fn, 11)) break;        /* SFN matched? */
1431
                        }
1432
                }
1433
#else           /* Non LFN configuration */
1434
                if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */
1435
                        break;
1436
#endif
1437
                res = dir_next(dj, 0);           /* Next entry */
1438
        } while (res == FR_OK);
1439
 
1440
        return res;
1441
}
1442
 
1443
 
1444
 
1445
 
1446
/*-----------------------------------------------------------------------*/
1447
/* Read an object from the directory                                     */
1448
/*-----------------------------------------------------------------------*/
1449
#if _FS_MINIMIZE <= 1
1450
static
1451
FRESULT dir_read (
1452
        DIR *dj                 /* Pointer to the directory object that pointing the entry to be read */
1453
)
1454
{
1455
        FRESULT res;
1456
        BYTE c, *dir;
1457
#if _USE_LFN
1458
        BYTE a, ord = 0xFF, sum = 0xFF;
1459
#endif
1460
 
1461
        res = FR_NO_FILE;
1462
        while (dj->sect) {
1463
                res = move_window(dj->fs, dj->sect);
1464
                if (res != FR_OK) break;
1465
                dir = dj->dir;                                  /* Ptr to the directory entry of current index */
1466
                c = dir[DIR_Name];
1467
                if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */
1468
#if _USE_LFN    /* LFN configuration */
1469
                a = dir[DIR_Attr] & AM_MASK;
1470
                if (c == DDE || (!_FS_RPATH && c == '.') || ((a & AM_VOL) && a != AM_LFN)) {    /* An entry without valid data */
1471
                        ord = 0xFF;
1472
                } else {
1473
                        if (a == AM_LFN) {                      /* An LFN entry is found */
1474
                                if (c & LLE) {                  /* Is it start of LFN sequence? */
1475
                                        sum = dir[LDIR_Chksum];
1476
                                        c &= ~LLE; ord = c;
1477
                                        dj->lfn_idx = dj->index;
1478
                                }
1479
                                /* Check LFN validity and capture it */
1480
                                ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF;
1481
                        } else {                                        /* An SFN entry is found */
1482
                                if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN? */
1483
                                        dj->lfn_idx = 0xFFFF;           /* It has no LFN. */
1484
                                break;
1485
                        }
1486
                }
1487
#else           /* Non LFN configuration */
1488
                if (c != DDE && (_FS_RPATH || c != '.') && !(dir[DIR_Attr] & AM_VOL))   /* Is it a valid entry? */
1489
                        break;
1490
#endif
1491
                res = dir_next(dj, 0);                           /* Next entry */
1492
                if (res != FR_OK) break;
1493
        }
1494
 
1495
        if (res != FR_OK) dj->sect = 0;
1496
 
1497
        return res;
1498
}
1499
#endif
1500
 
1501
 
1502
 
1503
/*-----------------------------------------------------------------------*/
1504
/* Register an object to the directory                                   */
1505
/*-----------------------------------------------------------------------*/
1506
#if !_FS_READONLY
1507
static
1508
FRESULT dir_register (  /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */
1509
        DIR *dj                         /* Target directory with object name to be created */
1510
)
1511
{
1512
        FRESULT res;
1513
        BYTE c, *dir;
1514
#if _USE_LFN    /* LFN configuration */
1515
        WORD n, ne, is;
1516
        BYTE sn[12], *fn, sum;
1517
        WCHAR *lfn;
1518
 
1519
 
1520
        fn = dj->fn; lfn = dj->lfn;
1521
        mem_cpy(sn, fn, 12);
1522
 
1523
        if (_FS_RPATH && (sn[NS] & NS_DOT))             /* Cannot create dot entry */
1524
                return FR_INVALID_NAME;
1525
 
1526
        if (sn[NS] & NS_LOSS) {                 /* When LFN is out of 8.3 format, generate a numbered name */
1527
                fn[NS] = 0; dj->lfn = 0;                  /* Find only SFN */
1528
                for (n = 1; n < 100; n++) {
1529
                        gen_numname(fn, sn, lfn, n);    /* Generate a numbered name */
1530
                        res = dir_find(dj);                             /* Check if the name collides with existing SFN */
1531
                        if (res != FR_OK) break;
1532
                }
1533
                if (n == 100) return FR_DENIED;         /* Abort if too many collisions */
1534
                if (res != FR_NO_FILE) return res;      /* Abort if the result is other than 'not collided' */
1535
                fn[NS] = sn[NS]; dj->lfn = lfn;
1536
        }
1537
 
1538
        if (sn[NS] & NS_LFN) {                  /* When LFN is to be created, reserve an SFN + LFN entries. */
1539
                for (ne = 0; lfn[ne]; ne++) ;
1540
                ne = (ne + 25) / 13;
1541
        } else {                                                /* Otherwise reserve only an SFN entry. */
1542
                ne = 1;
1543
        }
1544
 
1545
        /* Reserve contiguous entries */
1546
        res = dir_sdi(dj, 0);
1547
        if (res != FR_OK) return res;
1548
        n = is = 0;
1549
        do {
1550
                res = move_window(dj->fs, dj->sect);
1551
                if (res != FR_OK) break;
1552
                c = *dj->dir;                           /* Check the entry status */
1553
                if (c == DDE || c == 0) {        /* Is it a blank entry? */
1554
                        if (n == 0) is = dj->index;      /* First index of the contiguous entry */
1555
                        if (++n == ne) break;   /* A contiguous entry that required count is found */
1556
                } else {
1557
                        n = 0;                                   /* Not a blank entry. Restart to search */
1558
                }
1559
                res = dir_next(dj, 1);          /* Next entry with table stretch */
1560
        } while (res == FR_OK);
1561
 
1562
        if (res == FR_OK && ne > 1) {   /* Initialize LFN entry if needed */
1563
                res = dir_sdi(dj, is);
1564
                if (res == FR_OK) {
1565
                        sum = sum_sfn(dj->fn);  /* Sum of the SFN tied to the LFN */
1566
                        ne--;
1567
                        do {                                    /* Store LFN entries in bottom first */
1568
                                res = move_window(dj->fs, dj->sect);
1569
                                if (res != FR_OK) break;
1570
                                fit_lfn(dj->lfn, dj->dir, (BYTE)ne, sum);
1571
                                dj->fs->wflag = 1;
1572
                                res = dir_next(dj, 0);   /* Next entry */
1573
                        } while (res == FR_OK && --ne);
1574
                }
1575
        }
1576
 
1577
#else   /* Non LFN configuration */
1578
        res = dir_sdi(dj, 0);
1579
        if (res == FR_OK) {
1580
                do {    /* Find a blank entry for the SFN */
1581
                        res = move_window(dj->fs, dj->sect);
1582
                        if (res != FR_OK) break;
1583
                        c = *dj->dir;
1584
                        if (c == DDE || c == 0) break;   /* Is it a blank entry? */
1585
                        res = dir_next(dj, 1);                  /* Next entry with table stretch */
1586
                } while (res == FR_OK);
1587
        }
1588
#endif
1589
 
1590
        if (res == FR_OK) {             /* Initialize the SFN entry */
1591
                res = move_window(dj->fs, dj->sect);
1592
                if (res == FR_OK) {
1593
                        dir = dj->dir;
1594
                        mem_set(dir, 0, SZ_DIR); /* Clean the entry */
1595
                        mem_cpy(dir, dj->fn, 11);       /* Put SFN */
1596
#if _USE_LFN
1597
                        dir[DIR_NTres] = *(dj->fn+NS) & (NS_BODY | NS_EXT);     /* Put NT flag */
1598
#endif
1599
                        dj->fs->wflag = 1;
1600
                }
1601
        }
1602
 
1603
        return res;
1604
}
1605
#endif /* !_FS_READONLY */
1606
 
1607
 
1608
 
1609
 
1610
/*-----------------------------------------------------------------------*/
1611
/* Remove an object from the directory                                   */
1612
/*-----------------------------------------------------------------------*/
1613
#if !_FS_READONLY && !_FS_MINIMIZE
1614
static
1615
FRESULT dir_remove (    /* FR_OK: Successful, FR_DISK_ERR: A disk error */
1616
        DIR *dj                         /* Directory object pointing the entry to be removed */
1617
)
1618
{
1619
        FRESULT res;
1620
#if _USE_LFN    /* LFN configuration */
1621
        WORD i;
1622
 
1623
        i = dj->index;  /* SFN index */
1624
        res = dir_sdi(dj, (WORD)((dj->lfn_idx == 0xFFFF) ? i : dj->lfn_idx));   /* Goto the SFN or top of the LFN entries */
1625
        if (res == FR_OK) {
1626
                do {
1627
                        res = move_window(dj->fs, dj->sect);
1628
                        if (res != FR_OK) break;
1629
                        *dj->dir = DDE;                 /* Mark the entry "deleted" */
1630
                        dj->fs->wflag = 1;
1631
                        if (dj->index >= i) break;      /* When reached SFN, all entries of the object has been deleted. */
1632
                        res = dir_next(dj, 0);           /* Next entry */
1633
                } while (res == FR_OK);
1634
                if (res == FR_NO_FILE) res = FR_INT_ERR;
1635
        }
1636
 
1637
#else                   /* Non LFN configuration */
1638
        res = dir_sdi(dj, dj->index);
1639
        if (res == FR_OK) {
1640
                res = move_window(dj->fs, dj->sect);
1641
                if (res == FR_OK) {
1642
                        *dj->dir = DDE;                 /* Mark the entry "deleted" */
1643
                        dj->fs->wflag = 1;
1644
                }
1645
        }
1646
#endif
1647
 
1648
        return res;
1649
}
1650
#endif /* !_FS_READONLY */
1651
 
1652
 
1653
 
1654
 
1655
/*-----------------------------------------------------------------------*/
1656
/* Pick a segment and create the object name in directory form           */
1657
/*-----------------------------------------------------------------------*/
1658
 
1659
static
1660
FRESULT create_name (
1661
        DIR *dj,                        /* Pointer to the directory object */
1662
        const TCHAR **path      /* Pointer to pointer to the segment in the path string */
1663
)
1664
{
1665
#ifdef _EXCVT
1666
        static const BYTE excvt[] = _EXCVT;     /* Upper conversion table for extended chars */
1667
#endif
1668
 
1669
#if _USE_LFN    /* LFN configuration */
1670
        BYTE b, cf;
1671
        WCHAR w, *lfn;
1672
        UINT i, ni, si, di;
1673
        const TCHAR *p;
1674
 
1675
        /* Create LFN in Unicode */
1676
        for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */
1677
        lfn = dj->lfn;
1678
        si = di = 0;
1679
        for (;;) {
1680
                w = p[si++];                                    /* Get a character */
1681
                if (w < ' ' || w == '/' || w == '\\') break;    /* Break on end of segment */
1682
                if (di >= _MAX_LFN)                             /* Reject too long name */
1683
                        return FR_INVALID_NAME;
1684
#if !_LFN_UNICODE
1685
                w &= 0xFF;
1686
                if (IsDBCS1(w)) {                               /* Check if it is a DBC 1st byte (always false on SBCS cfg) */
1687
                        b = (BYTE)p[si++];                      /* Get 2nd byte */
1688
                        if (!IsDBCS2(b))
1689
                                return FR_INVALID_NAME; /* Reject invalid sequence */
1690
                        w = (w << 8) + b;                       /* Create a DBC */
1691
                }
1692
                w = ff_convert(w, 1);                   /* Convert ANSI/OEM to Unicode */
1693
                if (!w) return FR_INVALID_NAME; /* Reject invalid code */
1694
#endif
1695
                if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal chars for LFN */
1696
                        return FR_INVALID_NAME;
1697
                lfn[di++] = w;                                  /* Store the Unicode char */
1698
        }
1699
        *path = &p[si];                                         /* Return pointer to the next segment */
1700
        cf = (w < ' ') ? NS_LAST : 0;            /* Set last segment flag if end of path */
1701
#if _FS_RPATH
1702
        if ((di == 1 && lfn[di-1] == '.') || /* Is this a dot entry? */
1703
                (di == 2 && lfn[di-1] == '.' && lfn[di-2] == '.')) {
1704
                lfn[di] = 0;
1705
                for (i = 0; i < 11; i++)
1706
                        dj->fn[i] = (i < di) ? '.' : ' ';
1707
                dj->fn[i] = cf | NS_DOT;                /* This is a dot entry */
1708
                return FR_OK;
1709
        }
1710
#endif
1711
        while (di) {                                            /* Strip trailing spaces and dots */
1712
                w = lfn[di-1];
1713
                if (w != ' ' && w != '.') break;
1714
                di--;
1715
        }
1716
        if (!di) return FR_INVALID_NAME;        /* Reject nul string */
1717
 
1718
        lfn[di] = 0;                                             /* LFN is created */
1719
 
1720
        /* Create SFN in directory form */
1721
        mem_set(dj->fn, ' ', 11);
1722
        for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ;   /* Strip leading spaces and dots */
1723
        if (si) cf |= NS_LOSS | NS_LFN;
1724
        while (di && lfn[di - 1] != '.') di--;  /* Find extension (di<=si: no extension) */
1725
 
1726
        b = i = 0; ni = 8;
1727
        for (;;) {
1728
                w = lfn[si++];                                  /* Get an LFN char */
1729
                if (!w) break;                                  /* Break on end of the LFN */
1730
                if (w == ' ' || (w == '.' && si != di)) {       /* Remove spaces and dots */
1731
                        cf |= NS_LOSS | NS_LFN; continue;
1732
                }
1733
 
1734
                if (i >= ni || si == di) {              /* Extension or end of SFN */
1735
                        if (ni == 11) {                         /* Long extension */
1736
                                cf |= NS_LOSS | NS_LFN; break;
1737
                        }
1738
                        if (si != di) cf |= NS_LOSS | NS_LFN;   /* Out of 8.3 format */
1739
                        if (si > di) break;                     /* No extension */
1740
                        si = di; i = 8; ni = 11;        /* Enter extension section */
1741
                        b <<= 2; continue;
1742
                }
1743
 
1744
                if (w >= 0x80) {                                /* Non ASCII char */
1745
#ifdef _EXCVT
1746
                        w = ff_convert(w, 0);            /* Unicode -> OEM code */
1747
                        if (w) w = excvt[w - 0x80];     /* Convert extended char to upper (SBCS) */
1748
#else
1749
                        w = ff_convert(ff_wtoupper(w), 0);       /* Upper converted Unicode -> OEM code */
1750
#endif
1751
                        cf |= NS_LFN;                           /* Force create LFN entry */
1752
                }
1753
 
1754
                if (_DF1S && w >= 0x100) {              /* Double byte char (always false on SBCS cfg) */
1755
                        if (i >= ni - 1) {
1756
                                cf |= NS_LOSS | NS_LFN; i = ni; continue;
1757
                        }
1758
                        dj->fn[i++] = (BYTE)(w >> 8);
1759
                } else {                                                /* Single byte char */
1760
                        if (!w || chk_chr("+,;=[]", w)) {       /* Replace illegal chars for SFN */
1761
                                w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */
1762
                        } else {
1763
                                if (IsUpper(w)) {               /* ASCII large capital */
1764
                                        b |= 2;
1765
                                } else {
1766
                                        if (IsLower(w)) {       /* ASCII small capital */
1767
                                                b |= 1; w -= 0x20;
1768
                                        }
1769
                                }
1770
                        }
1771
                }
1772
                dj->fn[i++] = (BYTE)w;
1773
        }
1774
 
1775
        if (dj->fn[0] == DDE) dj->fn[0] = NDDE;   /* If the first char collides with deleted mark, replace it with 0x05 */
1776
 
1777
        if (ni == 8) b <<= 2;
1778
        if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03)   /* Create LFN entry when there are composite capitals */
1779
                cf |= NS_LFN;
1780
        if (!(cf & NS_LFN)) {                                           /* When LFN is in 8.3 format without extended char, NT flags are created */
1781
                if ((b & 0x03) == 0x01) cf |= NS_EXT;   /* NT flag (Extension has only small capital) */
1782
                if ((b & 0x0C) == 0x04) cf |= NS_BODY;  /* NT flag (Filename has only small capital) */
1783
        }
1784
 
1785
        dj->fn[NS] = cf;        /* SFN is created */
1786
 
1787
        return FR_OK;
1788
 
1789
 
1790
#else   /* Non-LFN configuration */
1791
        BYTE b, c, d, *sfn;
1792
        UINT ni, si, i;
1793
        const char *p;
1794
 
1795
        /* Create file name in directory form */
1796
        for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */
1797
        sfn = dj->fn;
1798
        mem_set(sfn, ' ', 11);
1799
        si = i = b = 0; ni = 8;
1800
#if _FS_RPATH
1801
        if (p[si] == '.') { /* Is this a dot entry? */
1802
                for (;;) {
1803
                        c = (BYTE)p[si++];
1804
                        if (c != '.' || si >= 3) break;
1805
                        sfn[i++] = c;
1806
                }
1807
                if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME;
1808
                *path = &p[si];                                                                 /* Return pointer to the next segment */
1809
                sfn[NS] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT;       /* Set last segment flag if end of path */
1810
                return FR_OK;
1811
        }
1812
#endif
1813
        for (;;) {
1814
                c = (BYTE)p[si++];
1815
                if (c <= ' ' || c == '/' || c == '\\') break;   /* Break on end of segment */
1816
                if (c == '.' || i >= ni) {
1817
                        if (ni != 8 || c != '.') return FR_INVALID_NAME;
1818
                        i = 8; ni = 11;
1819
                        b <<= 2; continue;
1820
                }
1821
                if (c >= 0x80) {                                /* Extended char? */
1822
                        b |= 3;                                         /* Eliminate NT flag */
1823
#ifdef _EXCVT
1824
                        c = excvt[c - 0x80];            /* Upper conversion (SBCS) */
1825
#else
1826
#if !_DF1S      /* ASCII only cfg */
1827
                        return FR_INVALID_NAME;
1828
#endif
1829
#endif
1830
                }
1831
                if (IsDBCS1(c)) {                               /* Check if it is a DBC 1st byte (always false on SBCS cfg) */
1832
                        d = (BYTE)p[si++];                      /* Get 2nd byte */
1833
                        if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */
1834
                                return FR_INVALID_NAME;
1835
                        sfn[i++] = c;
1836
                        sfn[i++] = d;
1837
                } else {                                                /* Single byte code */
1838
                        if (chk_chr("\"*+,:;<=>\?[]|\x7F", c))  /* Reject illegal chrs for SFN */
1839
                                return FR_INVALID_NAME;
1840
                        if (IsUpper(c)) {                       /* ASCII large capital? */
1841
                                b |= 2;
1842
                        } else {
1843
                                if (IsLower(c)) {               /* ASCII small capital? */
1844
                                        b |= 1; c -= 0x20;
1845
                                }
1846
                        }
1847
                        sfn[i++] = c;
1848
                }
1849
        }
1850
        *path = &p[si];                                         /* Return pointer to the next segment */
1851
        c = (c <= ' ') ? NS_LAST : 0;            /* Set last segment flag if end of path */
1852
 
1853
        if (!i) return FR_INVALID_NAME;         /* Reject nul string */
1854
        if (sfn[0] == DDE) sfn[0] = NDDE; /* When first char collides with DDE, replace it with 0x05 */
1855
 
1856
        if (ni == 8) b <<= 2;
1857
        if ((b & 0x03) == 0x01) c |= NS_EXT;    /* NT flag (Name extension has only small capital) */
1858
        if ((b & 0x0C) == 0x04) c |= NS_BODY;   /* NT flag (Name body has only small capital) */
1859
 
1860
        sfn[NS] = c;            /* Store NT flag, File name is created */
1861
 
1862
        return FR_OK;
1863
#endif
1864
}
1865
 
1866
 
1867
 
1868
 
1869
/*-----------------------------------------------------------------------*/
1870
/* Get file information from directory entry                             */
1871
/*-----------------------------------------------------------------------*/
1872
#if _FS_MINIMIZE <= 1
1873
static
1874
void get_fileinfo (             /* No return code */
1875
        DIR *dj,                        /* Pointer to the directory object */
1876
        FILINFO *fno            /* Pointer to the file information to be filled */
1877
)
1878
{
1879
        UINT i;
1880
        BYTE nt, *dir;
1881
        TCHAR *p, c;
1882
 
1883
 
1884
        p = fno->fname;
1885
        if (dj->sect) {
1886
                dir = dj->dir;
1887
                nt = dir[DIR_NTres];            /* NT flag */
1888
                for (i = 0; i < 8; i++) {        /* Copy name body */
1889
                        c = dir[i];
1890
                        if (c == ' ') break;
1891
                        if (c == NDDE) c = (TCHAR)DDE;
1892
                        if (_USE_LFN && (nt & NS_BODY) && IsUpper(c)) c += 0x20;
1893
#if _LFN_UNICODE
1894
                        if (IsDBCS1(c) && i < 7 && IsDBCS2(dir[i+1]))
1895
                                c = (c << 8) | dir[++i];
1896
                        c = ff_convert(c, 1);
1897
                        if (!c) c = '?';
1898
#endif
1899
                        *p++ = c;
1900
                }
1901
                if (dir[8] != ' ') {            /* Copy name extension */
1902
                        *p++ = '.';
1903
                        for (i = 8; i < 11; i++) {
1904
                                c = dir[i];
1905
                                if (c == ' ') break;
1906
                                if (_USE_LFN && (nt & NS_EXT) && IsUpper(c)) c += 0x20;
1907
#if _LFN_UNICODE
1908
                                if (IsDBCS1(c) && i < 10 && IsDBCS2(dir[i+1]))
1909
                                        c = (c << 8) | dir[++i];
1910
                                c = ff_convert(c, 1);
1911
                                if (!c) c = '?';
1912
#endif
1913
                                *p++ = c;
1914
                        }
1915
                }
1916
                fno->fattrib = dir[DIR_Attr];                           /* Attribute */
1917
                fno->fsize = LD_DWORD(dir+DIR_FileSize);        /* Size */
1918
                fno->fdate = LD_WORD(dir+DIR_WrtDate);          /* Date */
1919
                fno->ftime = LD_WORD(dir+DIR_WrtTime);          /* Time */
1920
        }
1921
        *p = 0;          /* Terminate SFN str by a \0 */
1922
 
1923
#if _USE_LFN
1924
        if (fno->lfname && fno->lfsize) {
1925
                TCHAR *tp = fno->lfname;
1926
                WCHAR w, *lfn;
1927
 
1928
                i = 0;
1929
                if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */
1930
                        lfn = dj->lfn;
1931
                        while ((w = *lfn++) != 0) {                      /* Get an LFN char */
1932
#if !_LFN_UNICODE
1933
                                w = ff_convert(w, 0);                    /* Unicode -> OEM conversion */
1934
                                if (!w) { i = 0; break; }                /* Could not convert, no LFN */
1935
                                if (_DF1S && w >= 0x100)                /* Put 1st byte if it is a DBC (always false on SBCS cfg) */
1936
                                        tp[i++] = (TCHAR)(w >> 8);
1937
#endif
1938
                                if (i >= fno->lfsize - 1) { i = 0; break; }      /* Buffer overflow, no LFN */
1939
                                tp[i++] = (TCHAR)w;
1940
                        }
1941
                }
1942
                tp[i] = 0;       /* Terminate the LFN str by a \0 */
1943
        }
1944
#endif
1945
}
1946
#endif /* _FS_MINIMIZE <= 1 */
1947
 
1948
 
1949
 
1950
 
1951
/*-----------------------------------------------------------------------*/
1952
/* Follow a file path                                                    */
1953
/*-----------------------------------------------------------------------*/
1954
 
1955
static
1956
FRESULT follow_path (   /* FR_OK(0): successful, !=0: error code */
1957
        DIR *dj,                        /* Directory object to return last directory and found object */
1958
        const TCHAR *path       /* Full-path string to find a file or directory */
1959
)
1960
{
1961
        FRESULT res;
1962
        BYTE *dir, ns;
1963
 
1964
 
1965
#if _FS_RPATH
1966
        if (*path == '/' || *path == '\\') { /* There is a heading separator */
1967
                path++; dj->sclust = 0;          /* Strip it and start from the root dir */
1968
        } else {                                                        /* No heading separator */
1969
                dj->sclust = dj->fs->cdir;      /* Start from the current dir */
1970
        }
1971
#else
1972
        if (*path == '/' || *path == '\\')      /* Strip heading separator if exist */
1973
                path++;
1974
        dj->sclust = 0;                                          /* Start from the root dir */
1975
#endif
1976
 
1977
        if ((UINT)*path < ' ') {                        /* Nul path means the start directory itself */
1978
                res = dir_sdi(dj, 0);
1979
                dj->dir = 0;
1980
        } else {                                                        /* Follow path */
1981
                for (;;) {
1982
                        res = create_name(dj, &path);   /* Get a segment */
1983
                        if (res != FR_OK) break;
1984
                        res = dir_find(dj);                             /* Find it */
1985
                        ns = *(dj->fn+NS);
1986
                        if (res != FR_OK) {                             /* Failed to find the object */
1987
                                if (res != FR_NO_FILE) break;   /* Abort if any hard error occurred */
1988
                                /* Object not found */
1989
                                if (_FS_RPATH && (ns & NS_DOT)) {       /* If dot entry is not exit */
1990
                                        dj->sclust = 0; dj->dir = 0;      /* It is the root dir */
1991
                                        res = FR_OK;
1992
                                        if (!(ns & NS_LAST)) continue;
1993
                                } else {                                                        /* Could not find the object */
1994
                                        if (!(ns & NS_LAST)) res = FR_NO_PATH;
1995
                                }
1996
                                break;
1997
                        }
1998
                        if (ns & NS_LAST) break;                        /* Last segment match. Function completed. */
1999
                        dir = dj->dir;                                          /* There is next segment. Follow the sub directory */
2000
                        if (!(dir[DIR_Attr] & AM_DIR)) {        /* Cannot follow because it is a file */
2001
                                res = FR_NO_PATH; break;
2002
                        }
2003
                        dj->sclust = ld_clust(dj->fs, dir);
2004
                }
2005
        }
2006
 
2007
        return res;
2008
}
2009
 
2010
 
2011
 
2012
 
2013
/*-----------------------------------------------------------------------*/
2014
/* Load a sector and check if it is an FAT Volume Boot Record            */
2015
/*-----------------------------------------------------------------------*/
2016
 
2017
static
2018
BYTE check_fs ( /* 0:FAT-VBR, 1:Any BR but not FAT, 2:Not a BR, 3:Disk error */
2019
        FATFS *fs,      /* File system object */
2020
        DWORD sect      /* Sector# (lba) to check if it is an FAT boot record or not */
2021
)
2022
{
2023
    unsigned i;
2024
 
2025
        if (disk_read(fs->drv, fs->win, sect, 1) != RES_OK)     /* Load boot record */
2026
                return 3;
2027
        if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55)               /* Check record signature (always placed at offset 510 even if the sector size is >512) */
2028
                return 2;
2029
#if 0
2030
    printf("Boot sector read\n");
2031
    for(i=0;i<512;i++){
2032
        if((i%16)==0){
2033
            printf("%03u : ", i);
2034
        }
2035
        printf("%02x ", fs->win[i]);
2036
        if((i%16)==7){
2037
            printf(" ");
2038
        }
2039
        else if((i%16)==15){
2040
            printf("\n");
2041
        }
2042
    }
2043
#endif   
2044
 
2045
        if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */
2046
                return 0;
2047
        if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146)
2048
                return 0;
2049
 
2050
        return 1;
2051
}
2052
 
2053
 
2054
 
2055
 
2056
/*-----------------------------------------------------------------------*/
2057
/* Check if the file system object is valid or not                       */
2058
/*-----------------------------------------------------------------------*/
2059
 
2060
static
2061
FRESULT chk_mounted (   /* FR_OK(0): successful, !=0: any error occurred */
2062
        const TCHAR **path,     /* Pointer to pointer to the path name (drive number) */
2063
        FATFS **rfs,            /* Pointer to pointer to the found file system object */
2064
        BYTE wmode                      /* !=0: Check write protection for write access */
2065
)
2066
{
2067
        BYTE fmt, b, pi, *tbl;
2068
        UINT vol;
2069
        DSTATUS stat;
2070
        DWORD bsect, fasize, tsect, sysect, nclst, szbfat;
2071
        WORD nrsv;
2072
        const TCHAR *p = *path;
2073
        FATFS *fs;
2074
 
2075
 
2076
        /* Get logical drive number from the path name */
2077
        vol = p[0] - '0';                                        /* Is there a drive number? */
2078
        if (vol <= 9 && p[1] == ':') {          /* Found a drive number, get and strip it */
2079
                p += 2; *path = p;                              /* Return pointer to the path name */
2080
        } else {                                                        /* No drive number is given */
2081
#if _FS_RPATH
2082
                vol = CurrVol;                                  /* Use current drive */
2083
#else
2084
                vol = 0;                                         /* Use drive 0 */
2085
#endif
2086
        }
2087
 
2088
        /* Check if the file system object is valid or not */
2089
        *rfs = 0;
2090
        if (vol >= _VOLUMES)                            /* Is the drive number valid? */
2091
                return FR_INVALID_DRIVE;
2092
        fs = FatFs[vol];                                        /* Get corresponding file system object */
2093
        if (!fs) return FR_NOT_ENABLED;         /* Is the file system object available? */
2094
 
2095
        ENTER_FF(fs);                                           /* Lock file system */
2096
 
2097
        *rfs = fs;                                                      /* Return pointer to the corresponding file system object */
2098
        if (fs->fs_type) {                                      /* If the volume has been mounted */
2099
                stat = disk_status(fs->drv);
2100
                if (!(stat & STA_NOINIT)) {             /* and the physical drive is kept initialized (has not been changed), */
2101
                        if (!_FS_READONLY && wmode && (stat & STA_PROTECT))     /* Check write protection if needed */
2102
                                return FR_WRITE_PROTECTED;
2103
                        return FR_OK;                           /* The file system object is valid */
2104
                }
2105
        }
2106
 
2107
        /* The file system object is not valid. */
2108
        /* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */
2109
 
2110
        fs->fs_type = 0;                                 /* Clear the file system object */
2111
        fs->drv = LD2PD(vol);                           /* Bind the logical drive and a physical drive */
2112
        stat = disk_initialize(fs->drv);        /* Initialize the physical drive */
2113
        if (stat & STA_NOINIT)                          /* Check if the initialization succeeded */
2114
                return FR_NOT_READY;                    /* Failed to initialize due to no medium or hard error */
2115
        if (!_FS_READONLY && wmode && (stat & STA_PROTECT))     /* Check disk write protection if needed */
2116
                return FR_WRITE_PROTECTED;
2117
#if _MAX_SS != 512                                              /* Get disk sector size (variable sector size cfg only) */
2118
        if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &fs->ssize) != RES_OK)
2119
                return FR_DISK_ERR;
2120
#endif
2121
        /* Search FAT partition on the drive. Supports only generic partitions, FDISK and SFD. */
2122
        fmt = check_fs(fs, bsect = 0);           /* Load sector 0 and check if it is an FAT-VBR (in SFD) */
2123
 
2124
        if (LD2PT(vol) && !fmt) fmt = 1;        /* Force non-SFD if the volume is forced partition */
2125
#if 1   
2126
    /* FIXME explain this */
2127
    fmt = 1;
2128
#endif  
2129
    if (fmt == 1) {                                             /* Not an FAT-VBR, the physical drive can be partitioned */
2130
        /* Check the partition listed in the partition table */
2131
                pi = LD2PT(vol);
2132
                if (pi) pi--;
2133
                tbl = &fs->win[MBR_Table + pi * SZ_PTE];/* Partition table */
2134
                if (tbl[4]) {                                           /* Is the partition existing? */
2135
                        bsect = LD_DWORD(&tbl[8]);              /* Partition offset in LBA */
2136
                        fmt = check_fs(fs, bsect);              /* Check the partition */
2137
                }
2138
        }
2139
        if (fmt == 3) return FR_DISK_ERR;
2140
        if (fmt) return FR_NO_FILESYSTEM;               /* No FAT volume is found */
2141
 
2142
        /* An FAT volume is found. Following code initializes the file system object */
2143
 
2144
        if (LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs))          /* (BPB_BytsPerSec must be equal to the physical sector size) */
2145
                return FR_NO_FILESYSTEM;
2146
 
2147
        fasize = LD_WORD(fs->win+BPB_FATSz16);                          /* Number of sectors per FAT */
2148
        if (!fasize) fasize = LD_DWORD(fs->win+BPB_FATSz32);
2149
        fs->fsize = fasize;
2150
 
2151
        fs->n_fats = b = fs->win[BPB_NumFATs];                          /* Number of FAT copies */
2152
        if (b != 1 && b != 2) return FR_NO_FILESYSTEM;          /* (Must be 1 or 2) */
2153
        fasize *= b;                                                                            /* Number of sectors for FAT area */
2154
 
2155
        fs->csize = b = fs->win[BPB_SecPerClus];                        /* Number of sectors per cluster */
2156
        if (!b || (b & (b - 1))) return FR_NO_FILESYSTEM;       /* (Must be power of 2) */
2157
 
2158
        fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt);        /* Number of root directory entries */
2159
        if (fs->n_rootdir % (SS(fs) / SZ_DIR)) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be sector aligned) */
2160
 
2161
        tsect = LD_WORD(fs->win+BPB_TotSec16);                          /* Number of sectors on the volume */
2162
        if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32);
2163
 
2164
        nrsv = LD_WORD(fs->win+BPB_RsvdSecCnt);                         /* Number of reserved sectors */
2165
        if (!nrsv) return FR_NO_FILESYSTEM;                                     /* (BPB_RsvdSecCnt must not be 0) */
2166
 
2167
        /* Determine the FAT sub type */
2168
        sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZ_DIR);     /* RSV+FAT+DIR */
2169
        if (tsect < sysect) return FR_NO_FILESYSTEM;            /* (Invalid volume size) */
2170
        nclst = (tsect - sysect) / fs->csize;                           /* Number of clusters */
2171
        if (!nclst) return FR_NO_FILESYSTEM;                            /* (Invalid volume size) */
2172
        fmt = FS_FAT12;
2173
        if (nclst >= MIN_FAT16) fmt = FS_FAT16;
2174
        if (nclst >= MIN_FAT32) fmt = FS_FAT32;
2175
 
2176
 
2177
        /* Boundaries and Limits */
2178
        fs->n_fatent = nclst + 2;                                                       /* Number of FAT entries */
2179
        fs->database = bsect + sysect;                                          /* Data start sector */
2180
        fs->fatbase = bsect + nrsv;                                             /* FAT start sector */
2181
        if (fmt == FS_FAT32) {
2182
                if (fs->n_rootdir) return FR_NO_FILESYSTEM;             /* (BPB_RootEntCnt must be 0) */
2183
                fs->dirbase = LD_DWORD(fs->win+BPB_RootClus);   /* Root directory start cluster */
2184
                szbfat = fs->n_fatent * 4;                                              /* (Required FAT size) */
2185
        } else {
2186
                if (!fs->n_rootdir)     return FR_NO_FILESYSTEM;        /* (BPB_RootEntCnt must not be 0) */
2187
                fs->dirbase = fs->fatbase + fasize;                             /* Root directory start sector */
2188
                szbfat = (fmt == FS_FAT16) ?                                    /* (Required FAT size) */
2189
                        fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1);
2190
        }
2191
        if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs))       /* (BPB_FATSz must not be less than required) */
2192
                return FR_NO_FILESYSTEM;
2193
 
2194
#if !_FS_READONLY
2195
        /* Initialize cluster allocation information */
2196
        fs->free_clust = 0xFFFFFFFF;
2197
        fs->last_clust = 0;
2198
 
2199
        /* Get fsinfo if available */
2200
        if (fmt == FS_FAT32) {
2201
                fs->fsi_flag = 0;
2202
                fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo);
2203
                if (disk_read(fs->drv, fs->win, fs->fsi_sector, 1) == RES_OK &&
2204
                        LD_WORD(fs->win+BS_55AA) == 0xAA55 &&
2205
                        LD_DWORD(fs->win+FSI_LeadSig) == 0x41615252 &&
2206
                        LD_DWORD(fs->win+FSI_StrucSig) == 0x61417272) {
2207
                                fs->last_clust = LD_DWORD(fs->win+FSI_Nxt_Free);
2208
                                fs->free_clust = LD_DWORD(fs->win+FSI_Free_Count);
2209
                }
2210
        }
2211
#endif
2212
        fs->fs_type = fmt;              /* FAT sub-type */
2213
        fs->id = ++Fsid;                /* File system mount ID */
2214
        fs->winsect = 0;         /* Invalidate sector cache */
2215
        fs->wflag = 0;
2216
#if _FS_RPATH
2217
        fs->cdir = 0;                    /* Current directory (root dir) */
2218
#endif
2219
#if _FS_LOCK                            /* Clear file lock semaphores */
2220
        clear_lock(fs);
2221
#endif
2222
 
2223
        return FR_OK;
2224
}
2225
 
2226
 
2227
 
2228
 
2229
/*-----------------------------------------------------------------------*/
2230
/* Check if the file/dir object is valid or not                          */
2231
/*-----------------------------------------------------------------------*/
2232
 
2233
static
2234
FRESULT validate (      /* FR_OK(0): The object is valid, !=0: Invalid */
2235
        void* obj               /* Pointer to the object FIL/DIR to check validity */
2236
)
2237
{
2238
        FIL *fil;
2239
 
2240
 
2241
        fil = (FIL*)obj;        /* Assuming offset of fs and id in the FIL/DIR is identical */
2242
        if (!fil->fs || !fil->fs->fs_type || fil->fs->id != fil->id)
2243
                return FR_INVALID_OBJECT;
2244
 
2245
        ENTER_FF(fil->fs);              /* Lock file system */
2246
 
2247
        if (disk_status(fil->fs->drv) & STA_NOINIT)
2248
                return FR_NOT_READY;
2249
 
2250
        return FR_OK;
2251
}
2252
 
2253
 
2254
 
2255
 
2256
/*--------------------------------------------------------------------------
2257
 
2258
   Public Functions
2259
 
2260
--------------------------------------------------------------------------*/
2261
 
2262
 
2263
 
2264
/*-----------------------------------------------------------------------*/
2265
/* Mount/Unmount a Logical Drive                                         */
2266
/*-----------------------------------------------------------------------*/
2267
 
2268
FRESULT f_mount (
2269
        BYTE vol,               /* Logical drive number to be mounted/unmounted */
2270
        FATFS *fs               /* Pointer to new file system object (NULL for unmount)*/
2271
)
2272
{
2273
        FATFS *rfs;
2274
 
2275
 
2276
        if (vol >= _VOLUMES)            /* Check if the drive number is valid */
2277
                return FR_INVALID_DRIVE;
2278
        rfs = FatFs[vol];                       /* Get current fs object */
2279
 
2280
        if (rfs) {
2281
#if _FS_LOCK
2282
                clear_lock(rfs);
2283
#endif
2284
#if _FS_REENTRANT                               /* Discard sync object of the current volume */
2285
                if (!ff_del_syncobj(rfs->sobj)) return FR_INT_ERR;
2286
#endif
2287
                rfs->fs_type = 0;                /* Clear old fs object */
2288
        }
2289
 
2290
        if (fs) {
2291
                fs->fs_type = 0;         /* Clear new fs object */
2292
#if _FS_REENTRANT                               /* Create sync object for the new volume */
2293
                if (!ff_cre_syncobj(vol, &fs->sobj)) return FR_INT_ERR;
2294
#endif
2295
        }
2296
        FatFs[vol] = fs;                        /* Register new fs object */
2297
 
2298
        return FR_OK;
2299
}
2300
 
2301
 
2302
 
2303
 
2304
/*-----------------------------------------------------------------------*/
2305
/* Open or Create a File                                                 */
2306
/*-----------------------------------------------------------------------*/
2307
 
2308
FRESULT f_open (
2309
        FIL *fp,                        /* Pointer to the blank file object */
2310
        const TCHAR *path,      /* Pointer to the file name */
2311
        BYTE mode                       /* Access mode and file open mode flags */
2312
)
2313
{
2314
        FRESULT res;
2315
        DIR dj;
2316
        BYTE *dir;
2317
        DEF_NAMEBUF;
2318
 
2319
 
2320
        if (!fp) return FR_INVALID_OBJECT;
2321
        fp->fs = 0;                      /* Clear file object */
2322
 
2323
#if !_FS_READONLY
2324
        mode &= FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW;
2325
        res = chk_mounted(&path, &dj.fs, (BYTE)(mode & ~FA_READ));
2326
#else
2327
        mode &= FA_READ;
2328
        res = chk_mounted(&path, &dj.fs, 0);
2329
#endif
2330
        if (res == FR_OK) {
2331
                INIT_BUF(dj);
2332
                res = follow_path(&dj, path);   /* Follow the file path */
2333
                dir = dj.dir;
2334
#if !_FS_READONLY       /* R/W configuration */
2335
                if (res == FR_OK) {
2336
                        if (!dir)       /* Current dir itself */
2337
                                res = FR_INVALID_NAME;
2338
#if _FS_LOCK
2339
                        else
2340
                                res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0);
2341
#endif
2342
                }
2343
                /* Create or Open a file */
2344
                if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {
2345
                        DWORD dw, cl;
2346
 
2347
                        if (res != FR_OK) {                                     /* No file, create new */
2348
                                if (res == FR_NO_FILE)                  /* There is no file to open, create a new entry */
2349
#if _FS_LOCK
2350
                                        res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES;
2351
#else
2352
                                        res = dir_register(&dj);
2353
#endif
2354
                                mode |= FA_CREATE_ALWAYS;               /* File is created */
2355
                                dir = dj.dir;                                   /* New entry */
2356
                        }
2357
                        else {                                                          /* Any object is already existing */
2358
                                if (dir[DIR_Attr] & (AM_RDO | AM_DIR)) {        /* Cannot overwrite it (R/O or DIR) */
2359
                                        res = FR_DENIED;
2360
                                } else {
2361
                                        if (mode & FA_CREATE_NEW)       /* Cannot create as new file */
2362
                                                res = FR_EXIST;
2363
                                }
2364
                        }
2365
                        if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) {        /* Truncate it if overwrite mode */
2366
                                dw = get_fattime();                                     /* Created time */
2367
                                ST_DWORD(dir+DIR_CrtTime, dw);
2368
                                dir[DIR_Attr] = 0;                                       /* Reset attribute */
2369
                                ST_DWORD(dir+DIR_FileSize, 0);           /* size = 0 */
2370
                                cl = ld_clust(dj.fs, dir);                      /* Get start cluster */
2371
                                st_clust(dir, 0);                                        /* cluster = 0 */
2372
                                dj.fs->wflag = 1;
2373
                                if (cl) {                                                       /* Remove the cluster chain if exist */
2374
                                        dw = dj.fs->winsect;
2375
                                        res = remove_chain(dj.fs, cl);
2376
                                        if (res == FR_OK) {
2377
                                                dj.fs->last_clust = cl - 1;     /* Reuse the cluster hole */
2378
                                                res = move_window(dj.fs, dw);
2379
                                        }
2380
                                }
2381
                        }
2382
                }
2383
                else {  /* Open an existing file */
2384
                        if (res == FR_OK) {                                             /* Follow succeeded */
2385
                                if (dir[DIR_Attr] & AM_DIR) {           /* It is a directory */
2386
                                        res = FR_NO_FILE;
2387
                                } else {
2388
                                        if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */
2389
                                                res = FR_DENIED;
2390
                                }
2391
                        }
2392
                }
2393
                if (res == FR_OK) {
2394
                        if (mode & FA_CREATE_ALWAYS)                    /* Set file change flag if created or overwritten */
2395
                                mode |= FA__WRITTEN;
2396
                        fp->dir_sect = dj.fs->winsect;                  /* Pointer to the directory entry */
2397
                        fp->dir_ptr = dir;
2398
#if _FS_LOCK
2399
                        fp->lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0);
2400
                        if (!fp->lockid) res = FR_INT_ERR;
2401
#endif
2402
                }
2403
 
2404
#else                           /* R/O configuration */
2405
                if (res == FR_OK) {                                     /* Follow succeeded */
2406
                        dir = dj.dir;
2407
                        if (!dir) {                                             /* Current dir itself */
2408
                                res = FR_INVALID_NAME;
2409
                        } else {
2410
                                if (dir[DIR_Attr] & AM_DIR)     /* It is a directory */
2411
                                        res = FR_NO_FILE;
2412
                        }
2413
                }
2414
#endif
2415
                FREE_BUF();
2416
 
2417
                if (res == FR_OK) {
2418
                        fp->flag = mode;                                        /* File access mode */
2419
                        fp->sclust = ld_clust(dj.fs, dir);      /* File start cluster */
2420
                        fp->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */
2421
                        fp->fptr = 0;                                            /* File pointer */
2422
                        fp->dsect = 0;
2423
#if _USE_FASTSEEK
2424
                        fp->cltbl = 0;                                           /* Normal seek mode */
2425
#endif
2426
                        fp->fs = dj.fs; fp->id = dj.fs->id;     /* Validate file object */
2427
                }
2428
        }
2429
 
2430
        LEAVE_FF(dj.fs, res);
2431
}
2432
 
2433
 
2434
 
2435
 
2436
/*-----------------------------------------------------------------------*/
2437
/* Read File                                                             */
2438
/*-----------------------------------------------------------------------*/
2439
 
2440
FRESULT f_read (
2441
        FIL *fp,                /* Pointer to the file object */
2442
        void *buff,             /* Pointer to data buffer */
2443
        UINT btr,               /* Number of bytes to read */
2444
        UINT *br                /* Pointer to number of bytes read */
2445
)
2446
{
2447
        FRESULT res;
2448
        DWORD clst, sect, remain;
2449
        UINT rcnt, cc;
2450
        BYTE csect, *rbuff = buff;
2451
 
2452
 
2453
        *br = 0; /* Clear read byte counter */
2454
 
2455
        res = validate(fp);                                                     /* Check validity */
2456
        if (res != FR_OK) LEAVE_FF(fp->fs, res);
2457
        if (fp->flag & FA__ERROR)                                       /* Aborted file? */
2458
                LEAVE_FF(fp->fs, FR_INT_ERR);
2459
        if (!(fp->flag & FA_READ))                                      /* Check access mode */
2460
                LEAVE_FF(fp->fs, FR_DENIED);
2461
        remain = fp->fsize - fp->fptr;
2462
        if (btr > remain) btr = (UINT)remain;           /* Truncate btr by remaining bytes */
2463
 
2464
        for ( ;  btr;                                                           /* Repeat until all data read */
2465
                rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {
2466
                if ((fp->fptr % SS(fp->fs)) == 0) {              /* On the sector boundary? */
2467
                        csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1));    /* Sector offset in the cluster */
2468
                        if (!csect) {                                           /* On the cluster boundary? */
2469
                                if (fp->fptr == 0) {                     /* On the top of the file? */
2470
                                        clst = fp->sclust;                      /* Follow from the origin */
2471
                                } else {                                                /* Middle or end of the file */
2472
#if _USE_FASTSEEK
2473
                                        if (fp->cltbl)
2474
                                                clst = clmt_clust(fp, fp->fptr);        /* Get cluster# from the CLMT */
2475
                                        else
2476
#endif
2477
                                                clst = get_fat(fp->fs, fp->clust);      /* Follow cluster chain on the FAT */
2478
                                }
2479
                                if (clst < 2) ABORT(fp->fs, FR_INT_ERR);
2480
                                if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
2481
                                fp->clust = clst;                               /* Update current cluster */
2482
                        }
2483
                        sect = clust2sect(fp->fs, fp->clust);   /* Get current sector */
2484
                        if (!sect) ABORT(fp->fs, FR_INT_ERR);
2485
                        sect += csect;
2486
                        cc = btr / SS(fp->fs);                          /* When remaining bytes >= sector size, */
2487
                        if (cc) {                                                       /* Read maximum contiguous sectors directly */
2488
                                if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */
2489
                                        cc = fp->fs->csize - csect;
2490
                                if (disk_read(fp->fs->drv, rbuff, sect, (BYTE)cc) != RES_OK)
2491
                                        ABORT(fp->fs, FR_DISK_ERR);
2492
#if !_FS_READONLY && _FS_MINIMIZE <= 2                  /* Replace one of the read sectors with cached data if it contains a dirty sector */
2493
#if _FS_TINY
2494
                                if (fp->fs->wflag && fp->fs->winsect - sect < cc)
2495
                                        mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs));
2496
#else
2497
                                if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc)
2498
                                        mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs));
2499
#endif
2500
#endif
2501
                                rcnt = SS(fp->fs) * cc;                 /* Number of bytes transferred */
2502
                                continue;
2503
                        }
2504
#if !_FS_TINY
2505
                        if (fp->dsect != sect) {                        /* Load data sector if not in cache */
2506
#if !_FS_READONLY
2507
                                if (fp->flag & FA__DIRTY) {             /* Write-back dirty sector cache */
2508
                                        if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
2509
                                                ABORT(fp->fs, FR_DISK_ERR);
2510
                                        fp->flag &= ~FA__DIRTY;
2511
                                }
2512
#endif
2513
                                if (disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) /* Fill sector cache */
2514
                                        ABORT(fp->fs, FR_DISK_ERR);
2515
                        }
2516
#endif
2517
                        fp->dsect = sect;
2518
                }
2519
                rcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs));      /* Get partial sector data from sector buffer */
2520
                if (rcnt > btr) rcnt = btr;
2521
#if _FS_TINY
2522
                if (move_window(fp->fs, fp->dsect))             /* Move sector window */
2523
                        ABORT(fp->fs, FR_DISK_ERR);
2524
                mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt);      /* Pick partial sector */
2525
#else
2526
                mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt);  /* Pick partial sector */
2527
#endif
2528
        }
2529
 
2530
        LEAVE_FF(fp->fs, FR_OK);
2531
}
2532
 
2533
 
2534
 
2535
 
2536
#if !_FS_READONLY
2537
/*-----------------------------------------------------------------------*/
2538
/* Write File                                                            */
2539
/*-----------------------------------------------------------------------*/
2540
 
2541
FRESULT f_write (
2542
        FIL *fp,                        /* Pointer to the file object */
2543
        const void *buff,       /* Pointer to the data to be written */
2544
        UINT btw,                       /* Number of bytes to write */
2545
        UINT *bw                        /* Pointer to number of bytes written */
2546
)
2547
{
2548
        FRESULT res;
2549
        DWORD clst, sect;
2550
        UINT wcnt, cc;
2551
        const BYTE *wbuff = buff;
2552
        BYTE csect;
2553
 
2554
 
2555
        *bw = 0; /* Clear write byte counter */
2556
 
2557
        res = validate(fp);                                             /* Check validity */
2558
        if (res != FR_OK) LEAVE_FF(fp->fs, res);
2559
        if (fp->flag & FA__ERROR)                               /* Aborted file? */
2560
                LEAVE_FF(fp->fs, FR_INT_ERR);
2561
        if (!(fp->flag & FA_WRITE))                             /* Check access mode */
2562
                LEAVE_FF(fp->fs, FR_DENIED);
2563
        if ((DWORD)(fp->fsize + btw) < fp->fsize) btw = 0;       /* File size cannot reach 4GB */
2564
 
2565
        for ( ;  btw;                                                   /* Repeat until all data written */
2566
                wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) {
2567
                if ((fp->fptr % SS(fp->fs)) == 0) {      /* On the sector boundary? */
2568
                        csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1));    /* Sector offset in the cluster */
2569
                        if (!csect) {                                   /* On the cluster boundary? */
2570
                                if (fp->fptr == 0) {             /* On the top of the file? */
2571
                                        clst = fp->sclust;              /* Follow from the origin */
2572
                                        if (clst == 0)                   /* When no cluster is allocated, */
2573
                                                fp->sclust = clst = create_chain(fp->fs, 0);     /* Create a new cluster chain */
2574
                                } else {                                        /* Middle or end of the file */
2575
#if _USE_FASTSEEK
2576
                                        if (fp->cltbl)
2577
                                                clst = clmt_clust(fp, fp->fptr);        /* Get cluster# from the CLMT */
2578
                                        else
2579
#endif
2580
                                                clst = create_chain(fp->fs, fp->clust); /* Follow or stretch cluster chain on the FAT */
2581
                                }
2582
                                if (clst == 0) break;            /* Could not allocate a new cluster (disk full) */
2583
                                if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
2584
                                if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
2585
                                fp->clust = clst;                       /* Update current cluster */
2586
                        }
2587
#if _FS_TINY
2588
                        if (fp->fs->winsect == fp->dsect && move_window(fp->fs, 0))      /* Write-back sector cache */
2589
                                ABORT(fp->fs, FR_DISK_ERR);
2590
#else
2591
                        if (fp->flag & FA__DIRTY) {             /* Write-back sector cache */
2592
                                if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
2593
                                        ABORT(fp->fs, FR_DISK_ERR);
2594
                                fp->flag &= ~FA__DIRTY;
2595
                        }
2596
#endif
2597
                        sect = clust2sect(fp->fs, fp->clust);   /* Get current sector */
2598
                        if (!sect) ABORT(fp->fs, FR_INT_ERR);
2599
                        sect += csect;
2600
                        cc = btw / SS(fp->fs);                  /* When remaining bytes >= sector size, */
2601
                        if (cc) {                                               /* Write maximum contiguous sectors directly */
2602
                                if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */
2603
                                        cc = fp->fs->csize - csect;
2604
                                if (disk_write(fp->fs->drv, wbuff, sect, (BYTE)cc) != RES_OK)
2605
                                        ABORT(fp->fs, FR_DISK_ERR);
2606
#if _FS_TINY
2607
                                if (fp->fs->winsect - sect < cc) {      /* Refill sector cache if it gets invalidated by the direct write */
2608
                                        mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs));
2609
                                        fp->fs->wflag = 0;
2610
                                }
2611
#else
2612
                                if (fp->dsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
2613
                                        mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs));
2614
                                        fp->flag &= ~FA__DIRTY;
2615
                                }
2616
#endif
2617
                                wcnt = SS(fp->fs) * cc;         /* Number of bytes transferred */
2618
                                continue;
2619
                        }
2620
#if _FS_TINY
2621
                        if (fp->fptr >= fp->fsize) {    /* Avoid silly cache filling at growing edge */
2622
                                if (move_window(fp->fs, 0)) ABORT(fp->fs, FR_DISK_ERR);
2623
                                fp->fs->winsect = sect;
2624
                        }
2625
#else
2626
                        if (fp->dsect != sect) {                /* Fill sector cache with file data */
2627
                                if (fp->fptr < fp->fsize &&
2628
                                        disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK)
2629
                                                ABORT(fp->fs, FR_DISK_ERR);
2630
                        }
2631
#endif
2632
                        fp->dsect = sect;
2633
                }
2634
                wcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs));/* Put partial sector into file I/O buffer */
2635
                if (wcnt > btw) wcnt = btw;
2636
#if _FS_TINY
2637
                if (move_window(fp->fs, fp->dsect))     /* Move sector window */
2638
                        ABORT(fp->fs, FR_DISK_ERR);
2639
                mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt);      /* Fit partial sector */
2640
                fp->fs->wflag = 1;
2641
#else
2642
                mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt);  /* Fit partial sector */
2643
                fp->flag |= FA__DIRTY;
2644
#endif
2645
        }
2646
 
2647
        if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */
2648
        fp->flag |= FA__WRITTEN;                                                /* Set file change flag */
2649
 
2650
        LEAVE_FF(fp->fs, FR_OK);
2651
}
2652
 
2653
 
2654
 
2655
 
2656
/*-----------------------------------------------------------------------*/
2657
/* Synchronize the File Object                                           */
2658
/*-----------------------------------------------------------------------*/
2659
 
2660
FRESULT f_sync (
2661
        FIL *fp         /* Pointer to the file object */
2662
)
2663
{
2664
        FRESULT res;
2665
        DWORD tim;
2666
        BYTE *dir;
2667
 
2668
 
2669
        res = validate(fp);                                     /* Check validity of the object */
2670
        if (res == FR_OK) {
2671
                if (fp->flag & FA__WRITTEN) {   /* Has the file been written? */
2672
#if !_FS_TINY   /* Write-back dirty buffer */
2673
                        if (fp->flag & FA__DIRTY) {
2674
                                if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
2675
                                        LEAVE_FF(fp->fs, FR_DISK_ERR);
2676
                                fp->flag &= ~FA__DIRTY;
2677
                        }
2678
#endif
2679
                        /* Update the directory entry */
2680
                        res = move_window(fp->fs, fp->dir_sect);
2681
                        if (res == FR_OK) {
2682
                                dir = fp->dir_ptr;
2683
                                dir[DIR_Attr] |= AM_ARC;                                        /* Set archive bit */
2684
                                ST_DWORD(dir+DIR_FileSize, fp->fsize);          /* Update file size */
2685
                                st_clust(dir, fp->sclust);                                      /* Update start cluster */
2686
                                tim = get_fattime();                                            /* Update updated time */
2687
                                ST_DWORD(dir+DIR_WrtTime, tim);
2688
                                ST_WORD(dir+DIR_LstAccDate, 0);
2689
                                fp->flag &= ~FA__WRITTEN;
2690
                                fp->fs->wflag = 1;
2691
                                res = sync(fp->fs);
2692
                        }
2693
                }
2694
        }
2695
 
2696
        LEAVE_FF(fp->fs, res);
2697
}
2698
 
2699
#endif /* !_FS_READONLY */
2700
 
2701
 
2702
 
2703
 
2704
/*-----------------------------------------------------------------------*/
2705
/* Close File                                                            */
2706
/*-----------------------------------------------------------------------*/
2707
 
2708
FRESULT f_close (
2709
        FIL *fp         /* Pointer to the file object to be closed */
2710
)
2711
{
2712
        FRESULT res;
2713
 
2714
 
2715
#if _FS_READONLY
2716
        res = validate(fp);
2717
        {
2718
#if _FS_REENTRANT
2719
                FATFS *fs = fp->fs;
2720
#endif
2721
                if (res == FR_OK) fp->fs = 0;    /* Discard file object */
2722
                LEAVE_FF(fs, res);
2723
        }
2724
#else
2725
        res = f_sync(fp);               /* Flush cached data */
2726
#if _FS_LOCK
2727
        if (res == FR_OK) {             /* Decrement open counter */
2728
#if _FS_REENTRANT
2729
                FATFS *fs = fp->fs;;
2730
                res = validate(fp);
2731
                if (res == FR_OK) {
2732
                        res = dec_lock(fp->lockid);
2733
                        unlock_fs(fs, FR_OK);
2734
                }
2735
#else
2736
                res = dec_lock(fp->lockid);
2737
#endif
2738
        }
2739
#endif
2740
        if (res == FR_OK) fp->fs = 0;    /* Discard file object */
2741
        return res;
2742
#endif
2743
}
2744
 
2745
 
2746
 
2747
 
2748
/*-----------------------------------------------------------------------*/
2749
/* Current Drive/Directory Handlings                                     */
2750
/*-----------------------------------------------------------------------*/
2751
 
2752
#if _FS_RPATH >= 1
2753
 
2754
FRESULT f_chdrive (
2755
        BYTE drv                /* Drive number */
2756
)
2757
{
2758
        if (drv >= _VOLUMES) return FR_INVALID_DRIVE;
2759
 
2760
        CurrVol = drv;
2761
 
2762
        return FR_OK;
2763
}
2764
 
2765
 
2766
 
2767
FRESULT f_chdir (
2768
        const TCHAR *path       /* Pointer to the directory path */
2769
)
2770
{
2771
        FRESULT res;
2772
        DIR dj;
2773
        DEF_NAMEBUF;
2774
 
2775
 
2776
        res = chk_mounted(&path, &dj.fs, 0);
2777
        if (res == FR_OK) {
2778
                INIT_BUF(dj);
2779
                res = follow_path(&dj, path);           /* Follow the path */
2780
                FREE_BUF();
2781
                if (res == FR_OK) {                                     /* Follow completed */
2782
                        if (!dj.dir) {
2783
                                dj.fs->cdir = dj.sclust;        /* Start directory itself */
2784
                        } else {
2785
                                if (dj.dir[DIR_Attr] & AM_DIR)  /* Reached to the directory */
2786
                                        dj.fs->cdir = ld_clust(dj.fs, dj.dir);
2787
                                else
2788
                                        res = FR_NO_PATH;               /* Reached but a file */
2789
                        }
2790
                }
2791
                if (res == FR_NO_FILE) res = FR_NO_PATH;
2792
        }
2793
 
2794
        LEAVE_FF(dj.fs, res);
2795
}
2796
 
2797
 
2798
#if _FS_RPATH >= 2
2799
FRESULT f_getcwd (
2800
        TCHAR *path,    /* Pointer to the directory path */
2801
        UINT sz_path    /* Size of path */
2802
)
2803
{
2804
        FRESULT res;
2805
        DIR dj;
2806
        UINT i, n;
2807
        DWORD ccl;
2808
        TCHAR *tp;
2809
        FILINFO fno;
2810
        DEF_NAMEBUF;
2811
 
2812
 
2813
        *path = 0;
2814
        res = chk_mounted((const TCHAR**)&path, &dj.fs, 0);      /* Get current volume */
2815
        if (res == FR_OK) {
2816
                INIT_BUF(dj);
2817
                i = sz_path;            /* Bottom of buffer (dir stack base) */
2818
                dj.sclust = dj.fs->cdir;                        /* Start to follow upper dir from current dir */
2819
                while ((ccl = dj.sclust) != 0) { /* Repeat while current dir is a sub-dir */
2820
                        res = dir_sdi(&dj, 1);                  /* Get parent dir */
2821
                        if (res != FR_OK) break;
2822
                        res = dir_read(&dj);
2823
                        if (res != FR_OK) break;
2824
                        dj.sclust = ld_clust(dj.fs, dj.dir);    /* Goto parent dir */
2825
                        res = dir_sdi(&dj, 0);
2826
                        if (res != FR_OK) break;
2827
                        do {                                                    /* Find the entry links to the child dir */
2828
                                res = dir_read(&dj);
2829
                                if (res != FR_OK) break;
2830
                                if (ccl == ld_clust(dj.fs, dj.dir)) break;      /* Found the entry */
2831
                                res = dir_next(&dj, 0);
2832
                        } while (res == FR_OK);
2833
                        if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */
2834
                        if (res != FR_OK) break;
2835
#if _USE_LFN
2836
                        fno.lfname = path;
2837
                        fno.lfsize = i;
2838
#endif
2839
                        get_fileinfo(&dj, &fno);                /* Get the dir name and push it to the buffer */
2840
                        tp = fno.fname;
2841
                        if (_USE_LFN && *path) tp = path;
2842
                        for (n = 0; tp[n]; n++) ;
2843
                        if (i < n + 3) {
2844
                                res = FR_NOT_ENOUGH_CORE; break;
2845
                        }
2846
                        while (n) path[--i] = tp[--n];
2847
                        path[--i] = '/';
2848
                }
2849
                tp = path;
2850
                if (res == FR_OK) {
2851
                        *tp++ = '0' + CurrVol;                  /* Put drive number */
2852
                        *tp++ = ':';
2853
                        if (i == sz_path) {                             /* Root-dir */
2854
                                *tp++ = '/';
2855
                        } else {                                                /* Sub-dir */
2856
                                do              /* Add stacked path str */
2857
                                        *tp++ = path[i++];
2858
                                while (i < sz_path);
2859
                        }
2860
                }
2861
                *tp = 0;
2862
                FREE_BUF();
2863
        }
2864
 
2865
        LEAVE_FF(dj.fs, res);
2866
}
2867
#endif /* _FS_RPATH >= 2 */
2868
#endif /* _FS_RPATH >= 1 */
2869
 
2870
 
2871
 
2872
#if _FS_MINIMIZE <= 2
2873
/*-----------------------------------------------------------------------*/
2874
/* Seek File R/W Pointer                                                 */
2875
/*-----------------------------------------------------------------------*/
2876
 
2877
FRESULT f_lseek (
2878
        FIL *fp,                /* Pointer to the file object */
2879
        DWORD ofs               /* File pointer from top of file */
2880
)
2881
{
2882
        FRESULT res;
2883
 
2884
 
2885
        res = validate(fp);                                     /* Check validity of the object */
2886
        if (res != FR_OK) LEAVE_FF(fp->fs, res);
2887
        if (fp->flag & FA__ERROR)                       /* Check abort flag */
2888
                LEAVE_FF(fp->fs, FR_INT_ERR);
2889
 
2890
#if _USE_FASTSEEK
2891
        if (fp->cltbl) {        /* Fast seek */
2892
                DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl;
2893
 
2894
                if (ofs == CREATE_LINKMAP) {    /* Create CLMT */
2895
                        tbl = fp->cltbl;
2896
                        tlen = *tbl++; ulen = 2;        /* Given table size and required table size */
2897
                        cl = fp->sclust;                        /* Top of the chain */
2898
                        if (cl) {
2899
                                do {
2900
                                        /* Get a fragment */
2901
                                        tcl = cl; ncl = 0; ulen += 2;    /* Top, length and used items */
2902
                                        do {
2903
                                                pcl = cl; ncl++;
2904
                                                cl = get_fat(fp->fs, cl);
2905
                                                if (cl <= 1) ABORT(fp->fs, FR_INT_ERR);
2906
                                                if (cl == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
2907
                                        } while (cl == pcl + 1);
2908
                                        if (ulen <= tlen) {             /* Store the length and top of the fragment */
2909
                                                *tbl++ = ncl; *tbl++ = tcl;
2910
                                        }
2911
                                } while (cl < fp->fs->n_fatent);        /* Repeat until end of chain */
2912
                        }
2913
                        *fp->cltbl = ulen;      /* Number of items used */
2914
                        if (ulen <= tlen)
2915
                                *tbl = 0;                /* Terminate table */
2916
                        else
2917
                                res = FR_NOT_ENOUGH_CORE;       /* Given table size is smaller than required */
2918
 
2919
                } else {                                                /* Fast seek */
2920
                        if (ofs > fp->fsize)            /* Clip offset at the file size */
2921
                                ofs = fp->fsize;
2922
                        fp->fptr = ofs;                         /* Set file pointer */
2923
                        if (ofs) {
2924
                                fp->clust = clmt_clust(fp, ofs - 1);
2925
                                dsc = clust2sect(fp->fs, fp->clust);
2926
                                if (!dsc) ABORT(fp->fs, FR_INT_ERR);
2927
                                dsc += (ofs - 1) / SS(fp->fs) & (fp->fs->csize - 1);
2928
                                if (fp->fptr % SS(fp->fs) && dsc != fp->dsect) {        /* Refill sector cache if needed */
2929
#if !_FS_TINY
2930
#if !_FS_READONLY
2931
                                        if (fp->flag & FA__DIRTY) {             /* Write-back dirty sector cache */
2932
                                                if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
2933
                                                        ABORT(fp->fs, FR_DISK_ERR);
2934
                                                fp->flag &= ~FA__DIRTY;
2935
                                        }
2936
#endif
2937
                                        if (disk_read(fp->fs->drv, fp->buf, dsc, 1) != RES_OK)  /* Load current sector */
2938
                                                ABORT(fp->fs, FR_DISK_ERR);
2939
#endif
2940
                                        fp->dsect = dsc;
2941
                                }
2942
                        }
2943
                }
2944
        } else
2945
#endif
2946
 
2947
        /* Normal Seek */
2948
        {
2949
                DWORD clst, bcs, nsect, ifptr;
2950
 
2951
                if (ofs > fp->fsize                                     /* In read-only mode, clip offset with the file size */
2952
#if !_FS_READONLY
2953
                         && !(fp->flag & FA_WRITE)
2954
#endif
2955
                        ) ofs = fp->fsize;
2956
 
2957
                ifptr = fp->fptr;
2958
                fp->fptr = nsect = 0;
2959
                if (ofs) {
2960
                        bcs = (DWORD)fp->fs->csize * SS(fp->fs);        /* Cluster size (byte) */
2961
                        if (ifptr > 0 &&
2962
                                (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */
2963
                                fp->fptr = (ifptr - 1) & ~(bcs - 1);    /* start from the current cluster */
2964
                                ofs -= fp->fptr;
2965
                                clst = fp->clust;
2966
                        } else {                                                                        /* When seek to back cluster, */
2967
                                clst = fp->sclust;                                              /* start from the first cluster */
2968
#if !_FS_READONLY
2969
                                if (clst == 0) {                                         /* If no cluster chain, create a new chain */
2970
                                        clst = create_chain(fp->fs, 0);
2971
                                        if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
2972
                                        if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
2973
                                        fp->sclust = clst;
2974
                                }
2975
#endif
2976
                                fp->clust = clst;
2977
                        }
2978
                        if (clst != 0) {
2979
                                while (ofs > bcs) {                                             /* Cluster following loop */
2980
#if !_FS_READONLY
2981
                                        if (fp->flag & FA_WRITE) {                      /* Check if in write mode or not */
2982
                                                clst = create_chain(fp->fs, clst);      /* Force stretch if in write mode */
2983
                                                if (clst == 0) {                         /* When disk gets full, clip file size */
2984
                                                        ofs = bcs; break;
2985
                                                }
2986
                                        } else
2987
#endif
2988
                                                clst = get_fat(fp->fs, clst);   /* Follow cluster chain if not in write mode */
2989
                                        if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
2990
                                        if (clst <= 1 || clst >= fp->fs->n_fatent) ABORT(fp->fs, FR_INT_ERR);
2991
                                        fp->clust = clst;
2992
                                        fp->fptr += bcs;
2993
                                        ofs -= bcs;
2994
                                }
2995
                                fp->fptr += ofs;
2996
                                if (ofs % SS(fp->fs)) {
2997
                                        nsect = clust2sect(fp->fs, clst);       /* Current sector */
2998
                                        if (!nsect) ABORT(fp->fs, FR_INT_ERR);
2999
                                        nsect += ofs / SS(fp->fs);
3000
                                }
3001
                        }
3002
                }
3003
                if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) {      /* Fill sector cache if needed */
3004
#if !_FS_TINY
3005
#if !_FS_READONLY
3006
                        if (fp->flag & FA__DIRTY) {                     /* Write-back dirty sector cache */
3007
                                if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
3008
                                        ABORT(fp->fs, FR_DISK_ERR);
3009
                                fp->flag &= ~FA__DIRTY;
3010
                        }
3011
#endif
3012
                        if (disk_read(fp->fs->drv, fp->buf, nsect, 1) != RES_OK)        /* Fill sector cache */
3013
                                ABORT(fp->fs, FR_DISK_ERR);
3014
#endif
3015
                        fp->dsect = nsect;
3016
                }
3017
#if !_FS_READONLY
3018
                if (fp->fptr > fp->fsize) {                     /* Set file change flag if the file size is extended */
3019
                        fp->fsize = fp->fptr;
3020
                        fp->flag |= FA__WRITTEN;
3021
                }
3022
#endif
3023
        }
3024
 
3025
        LEAVE_FF(fp->fs, res);
3026
}
3027
 
3028
 
3029
 
3030
#if _FS_MINIMIZE <= 1
3031
/*-----------------------------------------------------------------------*/
3032
/* Create a Directory Object                                             */
3033
/*-----------------------------------------------------------------------*/
3034
 
3035
FRESULT f_opendir (
3036
        DIR *dj,                        /* Pointer to directory object to create */
3037
        const TCHAR *path       /* Pointer to the directory path */
3038
)
3039
{
3040
        FRESULT res;
3041
        FATFS *fs;
3042
        DEF_NAMEBUF;
3043
 
3044
 
3045
        if (!dj) return FR_INVALID_OBJECT;
3046
 
3047
        res = chk_mounted(&path, &dj->fs, 0);
3048
        fs = dj->fs;
3049
        if (res == FR_OK) {
3050
                INIT_BUF(*dj);
3051
                res = follow_path(dj, path);                    /* Follow the path to the directory */
3052
                FREE_BUF();
3053
                if (res == FR_OK) {                                             /* Follow completed */
3054
                        if (dj->dir) {                                          /* It is not the root dir */
3055
                                if (dj->dir[DIR_Attr] & AM_DIR) {       /* The object is a directory */
3056
                                        dj->sclust = ld_clust(fs, dj->dir);
3057
                                } else {                                                /* The object is not a directory */
3058
                                        res = FR_NO_PATH;
3059
                                }
3060
                        }
3061
                        if (res == FR_OK) {
3062
                                dj->id = fs->id;
3063
                                res = dir_sdi(dj, 0);                    /* Rewind dir */
3064
                        }
3065
                }
3066
                if (res == FR_NO_FILE) res = FR_NO_PATH;
3067
                if (res != FR_OK) dj->fs = 0;                    /* Invalidate the dir object if function faild */
3068
        } else {
3069
                dj->fs = 0;
3070
        }
3071
 
3072
        LEAVE_FF(fs, res);
3073
}
3074
 
3075
 
3076
 
3077
 
3078
/*-----------------------------------------------------------------------*/
3079
/* Read Directory Entry in Sequence                                      */
3080
/*-----------------------------------------------------------------------*/
3081
 
3082
FRESULT f_readdir (
3083
        DIR *dj,                        /* Pointer to the open directory object */
3084
        FILINFO *fno            /* Pointer to file information to return */
3085
)
3086
{
3087
        FRESULT res;
3088
        DEF_NAMEBUF;
3089
 
3090
        res = validate(dj);                                             /* Check validity of the object */
3091
        if (res == FR_OK) {
3092
                if (!fno) {
3093
                        res = dir_sdi(dj, 0);                    /* Rewind the directory object */
3094
                } else {
3095
                        INIT_BUF(*dj);
3096
                        res = dir_read(dj);                             /* Read an directory item */
3097
                        if (res == FR_NO_FILE) {                /* Reached end of dir */
3098
                                dj->sect = 0;
3099
                                res = FR_OK;
3100
                        }
3101
                        if (res == FR_OK) {                             /* A valid entry is found */
3102
                                get_fileinfo(dj, fno);          /* Get the object information */
3103
                                res = dir_next(dj, 0);           /* Increment index for next */
3104
                                if (res == FR_NO_FILE) {
3105
                                        dj->sect = 0;
3106
                                        res = FR_OK;
3107
                                }
3108
                        }
3109
                        FREE_BUF();
3110
                }
3111
        }
3112
 
3113
        LEAVE_FF(dj->fs, res);
3114
}
3115
 
3116
 
3117
 
3118
#if _FS_MINIMIZE == 0
3119
/*-----------------------------------------------------------------------*/
3120
/* Get File Status                                                       */
3121
/*-----------------------------------------------------------------------*/
3122
 
3123
FRESULT f_stat (
3124
        const TCHAR *path,      /* Pointer to the file path */
3125
        FILINFO *fno            /* Pointer to file information to return */
3126
)
3127
{
3128
        FRESULT res;
3129
        DIR dj;
3130
        DEF_NAMEBUF;
3131
 
3132
 
3133
        res = chk_mounted(&path, &dj.fs, 0);
3134
        if (res == FR_OK) {
3135
                INIT_BUF(dj);
3136
                res = follow_path(&dj, path);   /* Follow the file path */
3137
                if (res == FR_OK) {                             /* Follow completed */
3138
                        if (dj.dir)             /* Found an object */
3139
                                get_fileinfo(&dj, fno);
3140
                        else                    /* It is root dir */
3141
                                res = FR_INVALID_NAME;
3142
                }
3143
                FREE_BUF();
3144
        }
3145
 
3146
        LEAVE_FF(dj.fs, res);
3147
}
3148
 
3149
 
3150
 
3151
#if !_FS_READONLY
3152
/*-----------------------------------------------------------------------*/
3153
/* Get Number of Free Clusters                                           */
3154
/*-----------------------------------------------------------------------*/
3155
 
3156
FRESULT f_getfree (
3157
        const TCHAR *path,      /* Pointer to the logical drive number (root dir) */
3158
        DWORD *nclst,           /* Pointer to the variable to return number of free clusters */
3159
        FATFS **fatfs           /* Pointer to pointer to corresponding file system object to return */
3160
)
3161
{
3162
        FRESULT res;
3163
        FATFS *fs;
3164
        DWORD n, clst, sect, stat;
3165
        UINT i;
3166
        BYTE fat, *p;
3167
 
3168
 
3169
        /* Get drive number */
3170
        res = chk_mounted(&path, fatfs, 0);
3171
        fs = *fatfs;
3172
        if (res == FR_OK) {
3173
                /* If free_clust is valid, return it without full cluster scan */
3174
                if (fs->free_clust <= fs->n_fatent - 2) {
3175
                        *nclst = fs->free_clust;
3176
                } else {
3177
                        /* Get number of free clusters */
3178
                        fat = fs->fs_type;
3179
                        n = 0;
3180
                        if (fat == FS_FAT12) {
3181
                                clst = 2;
3182
                                do {
3183
                                        stat = get_fat(fs, clst);
3184
                                        if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }
3185
                                        if (stat == 1) { res = FR_INT_ERR; break; }
3186
                                        if (stat == 0) n++;
3187
                                } while (++clst < fs->n_fatent);
3188
                        } else {
3189
                                clst = fs->n_fatent;
3190
                                sect = fs->fatbase;
3191
                                i = 0; p = 0;
3192
                                do {
3193
                                        if (!i) {
3194
                                                res = move_window(fs, sect++);
3195
                                                if (res != FR_OK) break;
3196
                                                p = fs->win;
3197
                                                i = SS(fs);
3198
                                        }
3199
                                        if (fat == FS_FAT16) {
3200
                                                if (LD_WORD(p) == 0) n++;
3201
                                                p += 2; i -= 2;
3202
                                        } else {
3203
                                                if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++;
3204
                                                p += 4; i -= 4;
3205
                                        }
3206
                                } while (--clst);
3207
                        }
3208
                        fs->free_clust = n;
3209
                        if (fat == FS_FAT32) fs->fsi_flag = 1;
3210
                        *nclst = n;
3211
                }
3212
        }
3213
        LEAVE_FF(fs, res);
3214
}
3215
 
3216
 
3217
 
3218
 
3219
/*-----------------------------------------------------------------------*/
3220
/* Truncate File                                                         */
3221
/*-----------------------------------------------------------------------*/
3222
 
3223
FRESULT f_truncate (
3224
        FIL *fp         /* Pointer to the file object */
3225
)
3226
{
3227
        FRESULT res;
3228
        DWORD ncl;
3229
 
3230
 
3231
        if (!fp) return FR_INVALID_OBJECT;
3232
 
3233
        res = validate(fp);                                             /* Check validity of the object */
3234
        if (res == FR_OK) {
3235
                if (fp->flag & FA__ERROR) {                     /* Check abort flag */
3236
                        res = FR_INT_ERR;
3237
                } else {
3238
                        if (!(fp->flag & FA_WRITE))             /* Check access mode */
3239
                                res = FR_DENIED;
3240
                }
3241
        }
3242
        if (res == FR_OK) {
3243
                if (fp->fsize > fp->fptr) {
3244
                        fp->fsize = fp->fptr;   /* Set file size to current R/W point */
3245
                        fp->flag |= FA__WRITTEN;
3246
                        if (fp->fptr == 0) {     /* When set file size to zero, remove entire cluster chain */
3247
                                res = remove_chain(fp->fs, fp->sclust);
3248
                                fp->sclust = 0;
3249
                        } else {                                /* When truncate a part of the file, remove remaining clusters */
3250
                                ncl = get_fat(fp->fs, fp->clust);
3251
                                res = FR_OK;
3252
                                if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;
3253
                                if (ncl == 1) res = FR_INT_ERR;
3254
                                if (res == FR_OK && ncl < fp->fs->n_fatent) {
3255
                                        res = put_fat(fp->fs, fp->clust, 0x0FFFFFFF);
3256
                                        if (res == FR_OK) res = remove_chain(fp->fs, ncl);
3257
                                }
3258
                        }
3259
                }
3260
                if (res != FR_OK) fp->flag |= FA__ERROR;
3261
        }
3262
 
3263
        LEAVE_FF(fp->fs, res);
3264
}
3265
 
3266
 
3267
 
3268
 
3269
/*-----------------------------------------------------------------------*/
3270
/* Delete a File or Directory                                            */
3271
/*-----------------------------------------------------------------------*/
3272
 
3273
FRESULT f_unlink (
3274
        const TCHAR *path               /* Pointer to the file or directory path */
3275
)
3276
{
3277
        FRESULT res;
3278
        DIR dj, sdj;
3279
        BYTE *dir;
3280
        DWORD dclst;
3281
        DEF_NAMEBUF;
3282
 
3283
 
3284
        res = chk_mounted(&path, &dj.fs, 1);
3285
        if (res == FR_OK) {
3286
                INIT_BUF(dj);
3287
                res = follow_path(&dj, path);           /* Follow the file path */
3288
                if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
3289
                        res = FR_INVALID_NAME;                  /* Cannot remove dot entry */
3290
#if _FS_LOCK
3291
                if (res == FR_OK) res = chk_lock(&dj, 2);       /* Cannot remove open file */
3292
#endif
3293
                if (res == FR_OK) {                                     /* The object is accessible */
3294
                        dir = dj.dir;
3295
                        if (!dir) {
3296
                                res = FR_INVALID_NAME;          /* Cannot remove the start directory */
3297
                        } else {
3298
                                if (dir[DIR_Attr] & AM_RDO)
3299
                                        res = FR_DENIED;                /* Cannot remove R/O object */
3300
                        }
3301
                        dclst = ld_clust(dj.fs, dir);
3302
                        if (res == FR_OK && (dir[DIR_Attr] & AM_DIR)) { /* Is it a sub-dir? */
3303
                                if (dclst < 2) {
3304
                                        res = FR_INT_ERR;
3305
                                } else {
3306
                                        mem_cpy(&sdj, &dj, sizeof (DIR));       /* Check if the sub-dir is empty or not */
3307
                                        sdj.sclust = dclst;
3308
                                        res = dir_sdi(&sdj, 2);         /* Exclude dot entries */
3309
                                        if (res == FR_OK) {
3310
                                                res = dir_read(&sdj);
3311
                                                if (res == FR_OK                /* Not empty dir */
3312
#if _FS_RPATH
3313
                                                || dclst == dj.fs->cdir /* Current dir */
3314
#endif
3315
                                                ) res = FR_DENIED;
3316
                                                if (res == FR_NO_FILE) res = FR_OK;     /* Empty */
3317
                                        }
3318
                                }
3319
                        }
3320
                        if (res == FR_OK) {
3321
                                res = dir_remove(&dj);          /* Remove the directory entry */
3322
                                if (res == FR_OK) {
3323
                                        if (dclst)                              /* Remove the cluster chain if exist */
3324
                                                res = remove_chain(dj.fs, dclst);
3325
                                        if (res == FR_OK) res = sync(dj.fs);
3326
                                }
3327
                        }
3328
                }
3329
                FREE_BUF();
3330
        }
3331
        LEAVE_FF(dj.fs, res);
3332
}
3333
 
3334
 
3335
 
3336
 
3337
/*-----------------------------------------------------------------------*/
3338
/* Create a Directory                                                    */
3339
/*-----------------------------------------------------------------------*/
3340
 
3341
FRESULT f_mkdir (
3342
        const TCHAR *path               /* Pointer to the directory path */
3343
)
3344
{
3345
        FRESULT res;
3346
        DIR dj;
3347
        BYTE *dir, n;
3348
        DWORD dsc, dcl, pcl, tim = get_fattime();
3349
        DEF_NAMEBUF;
3350
 
3351
 
3352
        res = chk_mounted(&path, &dj.fs, 1);
3353
        if (res == FR_OK) {
3354
                INIT_BUF(dj);
3355
                res = follow_path(&dj, path);                   /* Follow the file path */
3356
                if (res == FR_OK) res = FR_EXIST;               /* Any object with same name is already existing */
3357
                if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT))
3358
                        res = FR_INVALID_NAME;
3359
                if (res == FR_NO_FILE) {                                /* Can create a new directory */
3360
                        dcl = create_chain(dj.fs, 0);            /* Allocate a cluster for the new directory table */
3361
                        res = FR_OK;
3362
                        if (dcl == 0) res = FR_DENIED;           /* No space to allocate a new cluster */
3363
                        if (dcl == 1) res = FR_INT_ERR;
3364
                        if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR;
3365
                        if (res == FR_OK)                                       /* Flush FAT */
3366
                                res = move_window(dj.fs, 0);
3367
                        if (res == FR_OK) {                                     /* Initialize the new directory table */
3368
                                dsc = clust2sect(dj.fs, dcl);
3369
                                dir = dj.fs->win;
3370
                                mem_set(dir, 0, SS(dj.fs));
3371
                                mem_set(dir+DIR_Name, ' ', 8+3);        /* Create "." entry */
3372
                                dir[DIR_Name] = '.';
3373
                                dir[DIR_Attr] = AM_DIR;
3374
                                ST_DWORD(dir+DIR_WrtTime, tim);
3375
                                st_clust(dir, dcl);
3376
                                mem_cpy(dir+SZ_DIR, dir, SZ_DIR);       /* Create ".." entry */
3377
                                dir[33] = '.'; pcl = dj.sclust;
3378
                                if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase)
3379
                                        pcl = 0;
3380
                                st_clust(dir+SZ_DIR, pcl);
3381
                                for (n = dj.fs->csize; n; n--) {        /* Write dot entries and clear following sectors */
3382
                                        dj.fs->winsect = dsc++;
3383
                                        dj.fs->wflag = 1;
3384
                                        res = move_window(dj.fs, 0);
3385
                                        if (res != FR_OK) break;
3386
                                        mem_set(dir, 0, SS(dj.fs));
3387
                                }
3388
                        }
3389
                        if (res == FR_OK) res = dir_register(&dj);      /* Register the object to the directoy */
3390
                        if (res != FR_OK) {
3391
                                remove_chain(dj.fs, dcl);                       /* Could not register, remove cluster chain */
3392
                        } else {
3393
                                dir = dj.dir;
3394
                                dir[DIR_Attr] = AM_DIR;                         /* Attribute */
3395
                                ST_DWORD(dir+DIR_WrtTime, tim);         /* Created time */
3396
                                st_clust(dir, dcl);                                     /* Table start cluster */
3397
                                dj.fs->wflag = 1;
3398
                                res = sync(dj.fs);
3399
                        }
3400
                }
3401
                FREE_BUF();
3402
        }
3403
 
3404
        LEAVE_FF(dj.fs, res);
3405
}
3406
 
3407
 
3408
 
3409
 
3410
/*-----------------------------------------------------------------------*/
3411
/* Change Attribute                                                      */
3412
/*-----------------------------------------------------------------------*/
3413
 
3414
FRESULT f_chmod (
3415
        const TCHAR *path,      /* Pointer to the file path */
3416
        BYTE value,                     /* Attribute bits */
3417
        BYTE mask                       /* Attribute mask to change */
3418
)
3419
{
3420
        FRESULT res;
3421
        DIR dj;
3422
        BYTE *dir;
3423
        DEF_NAMEBUF;
3424
 
3425
 
3426
        res = chk_mounted(&path, &dj.fs, 1);
3427
        if (res == FR_OK) {
3428
                INIT_BUF(dj);
3429
                res = follow_path(&dj, path);           /* Follow the file path */
3430
                FREE_BUF();
3431
                if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
3432
                        res = FR_INVALID_NAME;
3433
                if (res == FR_OK) {
3434
                        dir = dj.dir;
3435
                        if (!dir) {                                             /* Is it a root directory? */
3436
                                res = FR_INVALID_NAME;
3437
                        } else {                                                /* File or sub directory */
3438
                                mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC;    /* Valid attribute mask */
3439
                                dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */
3440
                                dj.fs->wflag = 1;
3441
                                res = sync(dj.fs);
3442
                        }
3443
                }
3444
        }
3445
 
3446
        LEAVE_FF(dj.fs, res);
3447
}
3448
 
3449
 
3450
 
3451
 
3452
/*-----------------------------------------------------------------------*/
3453
/* Change Timestamp                                                      */
3454
/*-----------------------------------------------------------------------*/
3455
 
3456
FRESULT f_utime (
3457
        const TCHAR *path,      /* Pointer to the file/directory name */
3458
        const FILINFO *fno      /* Pointer to the time stamp to be set */
3459
)
3460
{
3461
        FRESULT res;
3462
        DIR dj;
3463
        BYTE *dir;
3464
        DEF_NAMEBUF;
3465
 
3466
 
3467
        res = chk_mounted(&path, &dj.fs, 1);
3468
        if (res == FR_OK) {
3469
                INIT_BUF(dj);
3470
                res = follow_path(&dj, path);   /* Follow the file path */
3471
                FREE_BUF();
3472
                if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
3473
                        res = FR_INVALID_NAME;
3474
                if (res == FR_OK) {
3475
                        dir = dj.dir;
3476
                        if (!dir) {                                     /* Root directory */
3477
                                res = FR_INVALID_NAME;
3478
                        } else {                                        /* File or sub-directory */
3479
                                ST_WORD(dir+DIR_WrtTime, fno->ftime);
3480
                                ST_WORD(dir+DIR_WrtDate, fno->fdate);
3481
                                dj.fs->wflag = 1;
3482
                                res = sync(dj.fs);
3483
                        }
3484
                }
3485
        }
3486
 
3487
        LEAVE_FF(dj.fs, res);
3488
}
3489
 
3490
 
3491
 
3492
 
3493
/*-----------------------------------------------------------------------*/
3494
/* Rename File/Directory                                                 */
3495
/*-----------------------------------------------------------------------*/
3496
 
3497
FRESULT f_rename (
3498
        const TCHAR *path_old,  /* Pointer to the old name */
3499
        const TCHAR *path_new   /* Pointer to the new name */
3500
)
3501
{
3502
        FRESULT res;
3503
        DIR djo, djn;
3504
        BYTE buf[21], *dir;
3505
        DWORD dw;
3506
        DEF_NAMEBUF;
3507
 
3508
 
3509
        res = chk_mounted(&path_old, &djo.fs, 1);
3510
        if (res == FR_OK) {
3511
                djn.fs = djo.fs;
3512
                INIT_BUF(djo);
3513
                res = follow_path(&djo, path_old);              /* Check old object */
3514
                if (_FS_RPATH && res == FR_OK && (djo.fn[NS] & NS_DOT))
3515
                        res = FR_INVALID_NAME;
3516
#if _FS_LOCK
3517
                if (res == FR_OK) res = chk_lock(&djo, 2);
3518
#endif
3519
                if (res == FR_OK) {                                             /* Old object is found */
3520
                        if (!djo.dir) {                                         /* Is root dir? */
3521
                                res = FR_NO_FILE;
3522
                        } else {
3523
                                mem_cpy(buf, djo.dir+DIR_Attr, 21);             /* Save the object information except for name */
3524
                                mem_cpy(&djn, &djo, sizeof (DIR));              /* Check new object */
3525
                                res = follow_path(&djn, path_new);
3526
                                if (res == FR_OK) res = FR_EXIST;               /* The new object name is already existing */
3527
                                if (res == FR_NO_FILE) {                                /* Is it a valid path and no name collision? */
3528
/* Start critical section that an interruption or error can cause cross-link */
3529
                                        res = dir_register(&djn);                       /* Register the new entry */
3530
                                        if (res == FR_OK) {
3531
                                                dir = djn.dir;                                  /* Copy object information except for name */
3532
                                                mem_cpy(dir+13, buf+2, 19);
3533
                                                dir[DIR_Attr] = buf[0] | AM_ARC;
3534
                                                djo.fs->wflag = 1;
3535
                                                if (djo.sclust != djn.sclust && (dir[DIR_Attr] & AM_DIR)) {             /* Update .. entry in the directory if needed */
3536
                                                        dw = clust2sect(djo.fs, ld_clust(djo.fs, dir));
3537
                                                        if (!dw) {
3538
                                                                res = FR_INT_ERR;
3539
                                                        } else {
3540
                                                                res = move_window(djo.fs, dw);
3541
                                                                dir = djo.fs->win+SZ_DIR;       /* .. entry */
3542
                                                                if (res == FR_OK && dir[1] == '.') {
3543
                                                                        dw = (djo.fs->fs_type == FS_FAT32 && djn.sclust == djo.fs->dirbase) ? 0 : djn.sclust;
3544
                                                                        st_clust(dir, dw);
3545
                                                                        djo.fs->wflag = 1;
3546
                                                                }
3547
                                                        }
3548
                                                }
3549
                                                if (res == FR_OK) {
3550
                                                        res = dir_remove(&djo);         /* Remove old entry */
3551
                                                        if (res == FR_OK)
3552
                                                                res = sync(djo.fs);
3553
                                                }
3554
                                        }
3555
/* End critical section */
3556
                                }
3557
                        }
3558
                }
3559
                FREE_BUF();
3560
        }
3561
        LEAVE_FF(djo.fs, res);
3562
}
3563
 
3564
#endif /* !_FS_READONLY */
3565
#endif /* _FS_MINIMIZE == 0 */
3566
#endif /* _FS_MINIMIZE <= 1 */
3567
#endif /* _FS_MINIMIZE <= 2 */
3568
 
3569
 
3570
 
3571
/*-----------------------------------------------------------------------*/
3572
/* Forward data to the stream directly (available on only tiny cfg)      */
3573
/*-----------------------------------------------------------------------*/
3574
#if _USE_FORWARD && _FS_TINY
3575
 
3576
FRESULT f_forward (
3577
        FIL *fp,                                                /* Pointer to the file object */
3578
        UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */
3579
        UINT btr,                                               /* Number of bytes to forward */
3580
        UINT *bf                                                /* Pointer to number of bytes forwarded */
3581
)
3582
{
3583
        FRESULT res;
3584
        DWORD remain, clst, sect;
3585
        UINT rcnt;
3586
        BYTE csect;
3587
 
3588
 
3589
        *bf = 0; /* Clear transfer byte counter */
3590
 
3591
        if (!fp) return FR_INVALID_OBJECT;
3592
 
3593
        res = validate(fp);                                                             /* Check validity of the object */
3594
        if (res != FR_OK) LEAVE_FF(fp->fs, res);
3595
        if (fp->flag & FA__ERROR)                                               /* Check error flag */
3596
                LEAVE_FF(fp->fs, FR_INT_ERR);
3597
        if (!(fp->flag & FA_READ))                                              /* Check access mode */
3598
                LEAVE_FF(fp->fs, FR_DENIED);
3599
 
3600
        remain = fp->fsize - fp->fptr;
3601
        if (btr > remain) btr = (UINT)remain;                   /* Truncate btr by remaining bytes */
3602
 
3603
        for ( ;  btr && (*func)(0, 0);                                    /* Repeat until all data transferred or stream becomes busy */
3604
                fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) {
3605
                csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1));    /* Sector offset in the cluster */
3606
                if ((fp->fptr % SS(fp->fs)) == 0) {                      /* On the sector boundary? */
3607
                        if (!csect) {                                                   /* On the cluster boundary? */
3608
                                clst = (fp->fptr == 0) ?                 /* On the top of the file? */
3609
                                        fp->sclust : get_fat(fp->fs, fp->clust);
3610
                                if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
3611
                                if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
3612
                                fp->clust = clst;                                       /* Update current cluster */
3613
                        }
3614
                }
3615
                sect = clust2sect(fp->fs, fp->clust);           /* Get current data sector */
3616
                if (!sect) ABORT(fp->fs, FR_INT_ERR);
3617
                sect += csect;
3618
                if (move_window(fp->fs, sect))                          /* Move sector window */
3619
                        ABORT(fp->fs, FR_DISK_ERR);
3620
                fp->dsect = sect;
3621
                rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs));      /* Forward data from sector window */
3622
                if (rcnt > btr) rcnt = btr;
3623
                rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt);
3624
                if (!rcnt) ABORT(fp->fs, FR_INT_ERR);
3625
        }
3626
 
3627
        LEAVE_FF(fp->fs, FR_OK);
3628
}
3629
#endif /* _USE_FORWARD */
3630
 
3631
 
3632
 
3633
#if _USE_MKFS && !_FS_READONLY
3634
/*-----------------------------------------------------------------------*/
3635
/* Create File System on the Drive                                       */
3636
/*-----------------------------------------------------------------------*/
3637
#define N_ROOTDIR       512             /* Number of root dir entries for FAT12/16 */
3638
#define N_FATS          1               /* Number of FAT copies (1 or 2) */
3639
 
3640
 
3641
FRESULT f_mkfs (
3642
        BYTE drv,               /* Logical drive number */
3643
        BYTE sfd,               /* Partitioning rule 0:FDISK, 1:SFD */
3644
        UINT au                 /* Allocation unit size [bytes] */
3645
)
3646
{
3647
        static const WORD vst[] = { 1024,   512,  256,  128,   64,    32,   16,    8,    4,    2,   0};
3648
        static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512};
3649
        BYTE fmt, md, sys, *tbl, pdrv, part;
3650
        DWORD n_clst, vs, n, wsect;
3651
        UINT i;
3652
        DWORD b_vol, b_fat, b_dir, b_data;      /* LBA */
3653
        DWORD n_vol, n_rsv, n_fat, n_dir;       /* Size */
3654
        FATFS *fs;
3655
        DSTATUS stat;
3656
 
3657
 
3658
        /* Check mounted drive and clear work area */
3659
        if (drv >= _VOLUMES) return FR_INVALID_DRIVE;
3660
        if (sfd > 1) return FR_INVALID_PARAMETER;
3661
        if (au & (au - 1)) return FR_INVALID_PARAMETER;
3662
        fs = FatFs[drv];
3663
        if (!fs) return FR_NOT_ENABLED;
3664
        fs->fs_type = 0;
3665
        pdrv = LD2PD(drv);      /* Physical drive */
3666
        part = LD2PT(drv);      /* Partition (0:auto detect, 1-4:get from partition table)*/
3667
 
3668
        /* Get disk statics */
3669
        stat = disk_initialize(pdrv);
3670
        if (stat & STA_NOINIT) return FR_NOT_READY;
3671
        if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
3672
#if _MAX_SS != 512                                      /* Get disk sector size */
3673
        if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS)
3674
                return FR_DISK_ERR;
3675
#endif
3676
        if (_MULTI_PARTITION && part) {
3677
                /* Get partition information from partition table in the MBR */
3678
                if (disk_read(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR;
3679
                if (LD_WORD(fs->win+BS_55AA) != 0xAA55) return FR_MKFS_ABORTED;
3680
                tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE];
3681
                if (!tbl[4]) return FR_MKFS_ABORTED;    /* No partition? */
3682
                b_vol = LD_DWORD(tbl+8);        /* Volume start sector */
3683
                n_vol = LD_DWORD(tbl+12);       /* Volume size */
3684
        } else {
3685
                /* Create a partition in this function */
3686
                if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128)
3687
                        return FR_DISK_ERR;
3688
                b_vol = (sfd) ? 0 : 63;          /* Volume start sector */
3689
                n_vol -= b_vol;                         /* Volume size */
3690
        }
3691
 
3692
        if (!au) {                              /* AU auto selection */
3693
                vs = n_vol / (2000 / (SS(fs) / 512));
3694
                for (i = 0; vs < vst[i]; i++) ;
3695
                au = cst[i];
3696
        }
3697
        au /= SS(fs);           /* Number of sectors per cluster */
3698
        if (au == 0) au = 1;
3699
        if (au > 128) au = 128;
3700
 
3701
        /* Pre-compute number of clusters and FAT sub-type */
3702
        n_clst = n_vol / au;
3703
        fmt = FS_FAT12;
3704
        if (n_clst >= MIN_FAT16) fmt = FS_FAT16;
3705
        if (n_clst >= MIN_FAT32) fmt = FS_FAT32;
3706
 
3707
        /* Determine offset and size of FAT structure */
3708
        if (fmt == FS_FAT32) {
3709
                n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs);
3710
                n_rsv = 32;
3711
                n_dir = 0;
3712
        } else {
3713
                n_fat = (fmt == FS_FAT12) ? (n_clst * 3 + 1) / 2 + 3 : (n_clst * 2) + 4;
3714
                n_fat = (n_fat + SS(fs) - 1) / SS(fs);
3715
                n_rsv = 1;
3716
                n_dir = (DWORD)N_ROOTDIR * SZ_DIR / SS(fs);
3717
        }
3718
        b_fat = b_vol + n_rsv;                          /* FAT area start sector */
3719
        b_dir = b_fat + n_fat * N_FATS;         /* Directory area start sector */
3720
        b_data = b_dir + n_dir;                         /* Data area start sector */
3721
        if (n_vol < b_data + au - b_vol) return FR_MKFS_ABORTED;        /* Too small volume */
3722
 
3723
        /* Align data start sector to erase block boundary (for flash memory media) */
3724
        if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &n) != RES_OK || !n || n > 32768) n = 1;
3725
        n = (b_data + n - 1) & ~(n - 1);        /* Next nearest erase block from current data start */
3726
        n = (n - b_data) / N_FATS;
3727
        if (fmt == FS_FAT32) {          /* FAT32: Move FAT offset */
3728
                n_rsv += n;
3729
                b_fat += n;
3730
        } else {                                        /* FAT12/16: Expand FAT size */
3731
                n_fat += n;
3732
        }
3733
 
3734
        /* Determine number of clusters and final check of validity of the FAT sub-type */
3735
        n_clst = (n_vol - n_rsv - n_fat * N_FATS - n_dir) / au;
3736
        if (   (fmt == FS_FAT16 && n_clst < MIN_FAT16)
3737
                || (fmt == FS_FAT32 && n_clst < MIN_FAT32))
3738
                return FR_MKFS_ABORTED;
3739
 
3740
        switch (fmt) {  /* Determine system ID for partition table */
3741
        case FS_FAT12:  sys = 0x01; break;
3742
        case FS_FAT16:  sys = (n_vol < 0x10000) ? 0x04 : 0x06; break;
3743
        default:                sys = 0x0C;
3744
        }
3745
 
3746
        if (_MULTI_PARTITION && part) {
3747
                /* Update system ID in the partition table */
3748
                tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE];
3749
                tbl[4] = sys;
3750
                if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR;
3751
                md = 0xF8;
3752
        } else {
3753
                if (sfd) {      /* No partition table (SFD) */
3754
                        md = 0xF0;
3755
                } else {        /* Create partition table (FDISK) */
3756
                        mem_set(fs->win, 0, SS(fs));
3757
                        tbl = fs->win+MBR_Table;        /* Create partition table for single partition in the drive */
3758
                        tbl[1] = 1;                                             /* Partition start head */
3759
                        tbl[2] = 1;                                             /* Partition start sector */
3760
                        tbl[3] = 0;                                              /* Partition start cylinder */
3761
                        tbl[4] = sys;                                   /* System type */
3762
                        tbl[5] = 254;                                   /* Partition end head */
3763
                        n = (b_vol + n_vol) / 63 / 255;
3764
                        tbl[6] = (BYTE)((n >> 2) | 63); /* Partition end sector */
3765
                        tbl[7] = (BYTE)n;                               /* End cylinder */
3766
                        ST_DWORD(tbl+8, 63);                    /* Partition start in LBA */
3767
                        ST_DWORD(tbl+12, n_vol);                /* Partition size in LBA */
3768
                        ST_WORD(fs->win+BS_55AA, 0xAA55);       /* MBR signature */
3769
                        if (disk_write(pdrv, fs->win, 0, 1) != RES_OK)   /* Write it to the MBR sector */
3770
                                return FR_DISK_ERR;
3771
                        md = 0xF8;
3772
                }
3773
        }
3774
 
3775
        /* Create BPB in the VBR */
3776
        tbl = fs->win;                                                  /* Clear sector */
3777
        mem_set(tbl, 0, SS(fs));
3778
        mem_cpy(tbl, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code, OEM name */
3779
        i = SS(fs);                                                             /* Sector size */
3780
        ST_WORD(tbl+BPB_BytsPerSec, i);
3781
        tbl[BPB_SecPerClus] = (BYTE)au;                 /* Sectors per cluster */
3782
        ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv);             /* Reserved sectors */
3783
        tbl[BPB_NumFATs] = N_FATS;                              /* Number of FATs */
3784
        i = (fmt == FS_FAT32) ? 0 : N_ROOTDIR;   /* Number of rootdir entries */
3785
        ST_WORD(tbl+BPB_RootEntCnt, i);
3786
        if (n_vol < 0x10000) {                                  /* Number of total sectors */
3787
                ST_WORD(tbl+BPB_TotSec16, n_vol);
3788
        } else {
3789
                ST_DWORD(tbl+BPB_TotSec32, n_vol);
3790
        }
3791
        tbl[BPB_Media] = md;                                    /* Media descriptor */
3792
        ST_WORD(tbl+BPB_SecPerTrk, 63);                 /* Number of sectors per track */
3793
        ST_WORD(tbl+BPB_NumHeads, 255);                 /* Number of heads */
3794
        ST_DWORD(tbl+BPB_HiddSec, b_vol);               /* Hidden sectors */
3795
        n = get_fattime();                                              /* Use current time as VSN */
3796
        if (fmt == FS_FAT32) {
3797
                ST_DWORD(tbl+BS_VolID32, n);            /* VSN */
3798
                ST_DWORD(tbl+BPB_FATSz32, n_fat);       /* Number of sectors per FAT */
3799
                ST_DWORD(tbl+BPB_RootClus, 2);          /* Root directory start cluster (2) */
3800
                ST_WORD(tbl+BPB_FSInfo, 1);                     /* FSInfo record offset (VBR+1) */
3801
                ST_WORD(tbl+BPB_BkBootSec, 6);          /* Backup boot record offset (VBR+6) */
3802
                tbl[BS_DrvNum32] = 0x80;                        /* Drive number */
3803
                tbl[BS_BootSig32] = 0x29;                       /* Extended boot signature */
3804
                mem_cpy(tbl+BS_VolLab32, "NO NAME    " "FAT32   ", 19); /* Volume label, FAT signature */
3805
        } else {
3806
                ST_DWORD(tbl+BS_VolID, n);                      /* VSN */
3807
                ST_WORD(tbl+BPB_FATSz16, n_fat);        /* Number of sectors per FAT */
3808
                tbl[BS_DrvNum] = 0x80;                          /* Drive number */
3809
                tbl[BS_BootSig] = 0x29;                         /* Extended boot signature */
3810
                mem_cpy(tbl+BS_VolLab, "NO NAME    " "FAT     ", 19);   /* Volume label, FAT signature */
3811
        }
3812
        ST_WORD(tbl+BS_55AA, 0xAA55);                   /* Signature (Offset is fixed here regardless of sector size) */
3813
        if (disk_write(pdrv, tbl, b_vol, 1) != RES_OK)  /* Write it to the VBR sector */
3814
                return FR_DISK_ERR;
3815
        if (fmt == FS_FAT32)                                                    /* Write backup VBR if needed (VBR+6) */
3816
                disk_write(pdrv, tbl, b_vol + 6, 1);
3817
 
3818
        /* Initialize FAT area */
3819
        wsect = b_fat;
3820
        for (i = 0; i < N_FATS; i++) {           /* Initialize each FAT copy */
3821
                mem_set(tbl, 0, SS(fs));                 /* 1st sector of the FAT  */
3822
                n = md;                                                         /* Media descriptor byte */
3823
                if (fmt != FS_FAT32) {
3824
                        n |= (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00;
3825
                        ST_DWORD(tbl+0, n);                              /* Reserve cluster #0-1 (FAT12/16) */
3826
                } else {
3827
                        n |= 0xFFFFFF00;
3828
                        ST_DWORD(tbl+0, n);                              /* Reserve cluster #0-1 (FAT32) */
3829
                        ST_DWORD(tbl+4, 0xFFFFFFFF);
3830
                        ST_DWORD(tbl+8, 0x0FFFFFFF);    /* Reserve cluster #2 for root dir */
3831
                }
3832
                if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK)
3833
                        return FR_DISK_ERR;
3834
                mem_set(tbl, 0, SS(fs));                 /* Fill following FAT entries with zero */
3835
                for (n = 1; n < n_fat; n++) {           /* This loop may take a time on FAT32 volume due to many single sector writes */
3836
                        if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK)
3837
                                return FR_DISK_ERR;
3838
                }
3839
        }
3840
 
3841
        /* Initialize root directory */
3842
        i = (fmt == FS_FAT32) ? au : n_dir;
3843
        do {
3844
                if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK)
3845
                        return FR_DISK_ERR;
3846
        } while (--i);
3847
 
3848
#if _USE_ERASE  /* Erase data area if needed */
3849
        {
3850
                DWORD eb[2];
3851
 
3852
                eb[0] = wsect; eb[1] = wsect + (n_clst - ((fmt == FS_FAT32) ? 1 : 0)) * au - 1;
3853
                disk_ioctl(pdrv, CTRL_ERASE_SECTOR, eb);
3854
        }
3855
#endif
3856
 
3857
        /* Create FSInfo if needed */
3858
        if (fmt == FS_FAT32) {
3859
                ST_DWORD(tbl+FSI_LeadSig, 0x41615252);
3860
                ST_DWORD(tbl+FSI_StrucSig, 0x61417272);
3861
                ST_DWORD(tbl+FSI_Free_Count, n_clst - 1);       /* Number of free clusters */
3862
                ST_DWORD(tbl+FSI_Nxt_Free, 2);                          /* Last allocated cluster# */
3863
                ST_WORD(tbl+BS_55AA, 0xAA55);
3864
                disk_write(pdrv, tbl, b_vol + 1, 1);    /* Write original (VBR+1) */
3865
                disk_write(pdrv, tbl, b_vol + 7, 1);    /* Write backup (VBR+7) */
3866
        }
3867
 
3868
        return (disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR;
3869
}
3870
 
3871
 
3872
#if _MULTI_PARTITION == 2
3873
/*-----------------------------------------------------------------------*/
3874
/* Divide Physical Drive                                                 */
3875
/*-----------------------------------------------------------------------*/
3876
 
3877
FRESULT f_fdisk (
3878
        BYTE pdrv,                      /* Physical drive number */
3879
        const DWORD szt[],      /* Pointer to the size table for each partitions */
3880
        void* work                      /* Pointer to the working buffer */
3881
)
3882
{
3883
        UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl;
3884
        BYTE s_hd, e_hd, *p, *buf = (BYTE*)work;
3885
        DSTATUS stat;
3886
        DWORD sz_disk, sz_part, s_part;
3887
 
3888
 
3889
        stat = disk_initialize(pdrv);
3890
        if (stat & STA_NOINIT) return FR_NOT_READY;
3891
        if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
3892
        if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR;
3893
 
3894
        /* Determine CHS in the table regardless of the drive geometry */
3895
        for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ;
3896
        if (n == 256) n--;
3897
        e_hd = n - 1;
3898
        sz_cyl = 63 * n;
3899
        tot_cyl = sz_disk / sz_cyl;
3900
 
3901
        /* Create partition table */
3902
        mem_set(buf, 0, _MAX_SS);
3903
        p = buf + MBR_Table; b_cyl = 0;
3904
        for (i = 0; i < 4; i++, p += SZ_PTE) {
3905
                p_cyl = (szt[i] <= 100) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl;
3906
                if (!p_cyl) continue;
3907
                s_part = (DWORD)sz_cyl * b_cyl;
3908
                sz_part = (DWORD)sz_cyl * p_cyl;
3909
                if (i == 0) {    /* Exclude first track of cylinder 0 */
3910
                        s_hd = 1;
3911
                        s_part += 63; sz_part -= 63;
3912
                } else {
3913
                        s_hd = 0;
3914
                }
3915
                e_cyl = b_cyl + p_cyl - 1;
3916
                if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER;
3917
 
3918
                /* Set partition table */
3919
                p[1] = s_hd;                                            /* Start head */
3920
                p[2] = (BYTE)((b_cyl >> 2) + 1);        /* Start sector */
3921
                p[3] = (BYTE)b_cyl;                                     /* Start cylinder */
3922
                p[4] = 0x06;                                            /* System type (temporary setting) */
3923
                p[5] = e_hd;                                            /* End head */
3924
                p[6] = (BYTE)((e_cyl >> 2) + 63);       /* End sector */
3925
                p[7] = (BYTE)e_cyl;                                     /* End cylinder */
3926
                ST_DWORD(p + 8, s_part);                        /* Start sector in LBA */
3927
                ST_DWORD(p + 12, sz_part);                      /* Partition size */
3928
 
3929
                /* Next partition */
3930
                b_cyl += p_cyl;
3931
        }
3932
        ST_WORD(p, 0xAA55);
3933
 
3934
        /* Write it to the MBR */
3935
        return (disk_write(pdrv, buf, 0, 1) || disk_ioctl(pdrv, CTRL_SYNC, 0)) ? FR_DISK_ERR : FR_OK;
3936
}
3937
 
3938
 
3939
#endif /* _MULTI_PARTITION == 2 */
3940
#endif /* _USE_MKFS && !_FS_READONLY */
3941
 
3942
 
3943
 
3944
 
3945
#if _USE_STRFUNC
3946
/*-----------------------------------------------------------------------*/
3947
/* Get a string from the file                                            */
3948
/*-----------------------------------------------------------------------*/
3949
TCHAR* f_gets (
3950
        TCHAR* buff,    /* Pointer to the string buffer to read */
3951
        int len,                /* Size of string buffer (characters) */
3952
        FIL* fil                /* Pointer to the file object */
3953
)
3954
{
3955
        int n = 0;
3956
        TCHAR c, *p = buff;
3957
        BYTE s[2];
3958
        UINT rc;
3959
 
3960
 
3961
        while (n < len - 1) {                   /* Read bytes until buffer gets filled */
3962
                f_read(fil, s, 1, &rc);
3963
                if (rc != 1) break;                     /* Break on EOF or error */
3964
                c = s[0];
3965
#if _LFN_UNICODE                                        /* Read a character in UTF-8 encoding */
3966
                if (c >= 0x80) {
3967
                        if (c < 0xC0) continue; /* Skip stray trailer */
3968
                        if (c < 0xE0) {                 /* Two-byte sequence */
3969
                                f_read(fil, s, 1, &rc);
3970
                                if (rc != 1) break;
3971
                                c = ((c & 0x1F) << 6) | (s[0] & 0x3F);
3972
                                if (c < 0x80) c = '?';
3973
                        } else {
3974
                                if (c < 0xF0) {         /* Three-byte sequence */
3975
                                        f_read(fil, s, 2, &rc);
3976
                                        if (rc != 2) break;
3977
                                        c = (c << 12) | ((s[0] & 0x3F) << 6) | (s[1] & 0x3F);
3978
                                        if (c < 0x800) c = '?';
3979
                                } else {                        /* Reject four-byte sequence */
3980
                                        c = '?';
3981
                                }
3982
                        }
3983
                }
3984
#endif
3985
#if _USE_STRFUNC >= 2
3986
                if (c == '\r') continue;        /* Strip '\r' */
3987
#endif
3988
                *p++ = c;
3989
                n++;
3990
                if (c == '\n') break;           /* Break on EOL */
3991
        }
3992
        *p = 0;
3993
        return n ? buff : 0;                     /* When no data read (eof or error), return with error. */
3994
}
3995
 
3996
 
3997
 
3998
#if !_FS_READONLY
3999
#include <stdarg.h>
4000
/*-----------------------------------------------------------------------*/
4001
/* Put a character to the file                                           */
4002
/*-----------------------------------------------------------------------*/
4003
int f_putc (
4004
        TCHAR c,        /* A character to be output */
4005
        FIL* fil        /* Pointer to the file object */
4006
)
4007
{
4008
        UINT bw, btw;
4009
        BYTE s[3];
4010
 
4011
 
4012
#if _USE_STRFUNC >= 2
4013
        if (c == '\n') f_putc ('\r', fil);      /* LF -> CRLF conversion */
4014
#endif
4015
 
4016
#if _LFN_UNICODE        /* Write the character in UTF-8 encoding */
4017
        if (c < 0x80) {                 /* 7-bit */
4018
                s[0] = (BYTE)c;
4019
                btw = 1;
4020
        } else {
4021
                if (c < 0x800) {        /* 11-bit */
4022
                        s[0] = (BYTE)(0xC0 | (c >> 6));
4023
                        s[1] = (BYTE)(0x80 | (c & 0x3F));
4024
                        btw = 2;
4025
                } else {                        /* 16-bit */
4026
                        s[0] = (BYTE)(0xE0 | (c >> 12));
4027
                        s[1] = (BYTE)(0x80 | ((c >> 6) & 0x3F));
4028
                        s[2] = (BYTE)(0x80 | (c & 0x3F));
4029
                        btw = 3;
4030
                }
4031
        }
4032
#else                           /* Write the character without conversion */
4033
        s[0] = (BYTE)c;
4034
        btw = 1;
4035
#endif
4036
        f_write(fil, s, btw, &bw);              /* Write the char to the file */
4037
        return (bw == btw) ? 1 : EOF;   /* Return the result */
4038
}
4039
 
4040
 
4041
 
4042
 
4043
/*-----------------------------------------------------------------------*/
4044
/* Put a string to the file                                              */
4045
/*-----------------------------------------------------------------------*/
4046
int f_puts (
4047
        const TCHAR* str,       /* Pointer to the string to be output */
4048
        FIL* fil                        /* Pointer to the file object */
4049
)
4050
{
4051
        int n;
4052
 
4053
 
4054
        for (n = 0; *str; str++, n++) {
4055
                if (f_putc(*str, fil) == EOF) return EOF;
4056
        }
4057
        return n;
4058
}
4059
 
4060
 
4061
 
4062
 
4063
/*-----------------------------------------------------------------------*/
4064
/* Put a formatted string to the file                                    */
4065
/*-----------------------------------------------------------------------*/
4066
int f_printf (
4067
        FIL* fil,                       /* Pointer to the file object */
4068
        const TCHAR* str,       /* Pointer to the format string */
4069
        ...                                     /* Optional arguments... */
4070
)
4071
{
4072
        va_list arp;
4073
        BYTE f, r;
4074
        UINT i, j, w;
4075
        ULONG v;
4076
        TCHAR c, d, s[16], *p;
4077
        int res, chc, cc;
4078
 
4079
 
4080
        va_start(arp, str);
4081
 
4082
        for (cc = res = 0; cc != EOF; res += cc) {
4083
                c = *str++;
4084
                if (c == 0) break;                       /* End of string */
4085
                if (c != '%') {                         /* Non escape character */
4086
                        cc = f_putc(c, fil);
4087
                        if (cc != EOF) cc = 1;
4088
                        continue;
4089
                }
4090
                w = f = 0;
4091
                c = *str++;
4092
                if (c == '0') {                         /* Flag: '0' padding */
4093
                        f = 1; c = *str++;
4094
                } else {
4095
                        if (c == '-') {                 /* Flag: left justified */
4096
                                f = 2; c = *str++;
4097
                        }
4098
                }
4099
                while (IsDigit(c)) {            /* Precision */
4100
                        w = w * 10 + c - '0';
4101
                        c = *str++;
4102
                }
4103
                if (c == 'l' || c == 'L') {     /* Prefix: Size is long int */
4104
                        f |= 4; c = *str++;
4105
                }
4106
                if (!c) break;
4107
                d = c;
4108
                if (IsLower(d)) d -= 0x20;
4109
                switch (d) {                            /* Type is... */
4110
                case 'S' :                                      /* String */
4111
                        p = va_arg(arp, TCHAR*);
4112
                        for (j = 0; p[j]; j++) ;
4113
                        chc = 0;
4114
                        if (!(f & 2)) {
4115
                                while (j++ < w) chc += (cc = f_putc(' ', fil));
4116
                        }
4117
                        chc += (cc = f_puts(p, fil));
4118
                        while (j++ < w) chc += (cc = f_putc(' ', fil));
4119
                        if (cc != EOF) cc = chc;
4120
                        continue;
4121
                case 'C' :                                      /* Character */
4122
                        cc = f_putc((TCHAR)va_arg(arp, int), fil); continue;
4123
                case 'B' :                                      /* Binary */
4124
                        r = 2; break;
4125
                case 'O' :                                      /* Octal */
4126
                        r = 8; break;
4127
                case 'D' :                                      /* Signed decimal */
4128
                case 'U' :                                      /* Unsigned decimal */
4129
                        r = 10; break;
4130
                case 'X' :                                      /* Hexdecimal */
4131
                        r = 16; break;
4132
                default:                                        /* Unknown type (pass-through) */
4133
                        cc = f_putc(c, fil); continue;
4134
                }
4135
 
4136
                /* Get an argument and put it in numeral */
4137
                v = (f & 4) ? (ULONG)va_arg(arp, long) : ((d == 'D') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int));
4138
                if (d == 'D' && (v & 0x80000000)) {
4139
                        v = 0 - v;
4140
                        f |= 8;
4141
                }
4142
                i = 0;
4143
                do {
4144
                        d = (TCHAR)(v % r); v /= r;
4145
                        if (d > 9) d += (c == 'x') ? 0x27 : 0x07;
4146
                        s[i++] = d + '0';
4147
                } while (v && i < sizeof s / sizeof s[0]);
4148
                if (f & 8) s[i++] = '-';
4149
                j = i; d = (f & 1) ? '0' : ' ';
4150
                res = 0;
4151
                while (!(f & 2) && j++ < w) res += (cc = f_putc(d, fil));
4152
                do res += (cc = f_putc(s[--i], fil)); while(i);
4153
                while (j++ < w) res += (cc = f_putc(' ', fil));
4154
                if (cc != EOF) cc = res;
4155
        }
4156
 
4157
        va_end(arp);
4158
        return (cc == EOF) ? cc : res;
4159
}
4160
 
4161
#endif /* !_FS_READONLY */
4162
#endif /* _USE_STRFUNC */

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.