From 4f7376e6aa57973a38d503ed2bfaa1831da556d9 Mon Sep 17 00:00:00 2001 From: savelij13 Date: Thu, 11 Sep 2025 08:52:19 +0300 Subject: [PATCH] fatfs v0.01 Apr 29, 2006: - First stable version --- doc/00index_e.html | 99 ++++ doc/00index_j.html | 98 ++++ doc/css_e.css | 51 ++ doc/css_j.css | 54 ++ doc/en/chmod.html | 88 +++ doc/en/close.html | 59 ++ doc/en/dinit.html | 32 ++ doc/en/dread.html | 54 ++ doc/en/dstat.html | 35 ++ doc/en/dwrite.html | 62 +++ doc/en/fattime.html | 49 ++ doc/en/getfree.html | 78 +++ doc/en/lseek.html | 82 +++ doc/en/mkdir.html | 80 +++ doc/en/mountdrv.html | 56 ++ doc/en/open.html | 138 +++++ doc/en/opendir.html | 70 +++ doc/en/read.html | 72 +++ doc/en/readdir.html | 88 +++ doc/en/sdir.html | 37 ++ doc/en/sfatfs.html | 58 ++ doc/en/sfile.html | 50 ++ doc/en/sfileinfo.html | 42 ++ doc/en/stat.html | 70 +++ doc/en/sync.html | 59 ++ doc/en/unlink.html | 62 +++ doc/en/write.html | 72 +++ doc/ja/chmod.html | 88 +++ doc/ja/close.html | 59 ++ doc/ja/dinit.html | 32 ++ doc/ja/dread.html | 54 ++ doc/ja/dstat.html | 35 ++ doc/ja/dwrite.html | 62 +++ doc/ja/fattime.html | 49 ++ doc/ja/getfree.html | 78 +++ doc/ja/lseek.html | 82 +++ doc/ja/mkdir.html | 80 +++ doc/ja/mountdrv.html | 57 ++ doc/ja/open.html | 141 +++++ doc/ja/opendir.html | 70 +++ doc/ja/read.html | 72 +++ doc/ja/readdir.html | 88 +++ doc/ja/sdir.html | 37 ++ doc/ja/sfatfs.html | 58 ++ doc/ja/sfile.html | 49 ++ doc/ja/sfileinfo.html | 42 ++ doc/ja/stat.html | 70 +++ doc/ja/sync.html | 60 ++ doc/ja/unlink.html | 67 +++ doc/ja/write.html | 72 +++ doc/layers.png | Bin 0 -> 1837 bytes doc/rw_ata.jpeg | Bin 0 -> 66954 bytes doc/rw_cfc.jpeg | Bin 0 -> 32242 bytes doc/rw_mmc.jpeg | Bin 0 -> 29612 bytes doc/rwtest.png | Bin 0 -> 19261 bytes src/00readme.txt | 60 ++ src/diskio.h | 57 ++ src/ff.c | 1202 +++++++++++++++++++++++++++++++++++++++++ src/ff.h | 186 +++++++ src/integer.h | 12 + src/tff.c | 1141 ++++++++++++++++++++++++++++++++++++++ src/tff.h | 181 +++++++ 62 files changed, 6136 insertions(+) create mode 100644 doc/00index_e.html create mode 100644 doc/00index_j.html create mode 100644 doc/css_e.css create mode 100644 doc/css_j.css create mode 100644 doc/en/chmod.html create mode 100644 doc/en/close.html create mode 100644 doc/en/dinit.html create mode 100644 doc/en/dread.html create mode 100644 doc/en/dstat.html create mode 100644 doc/en/dwrite.html create mode 100644 doc/en/fattime.html create mode 100644 doc/en/getfree.html create mode 100644 doc/en/lseek.html create mode 100644 doc/en/mkdir.html create mode 100644 doc/en/mountdrv.html create mode 100644 doc/en/open.html create mode 100644 doc/en/opendir.html create mode 100644 doc/en/read.html create mode 100644 doc/en/readdir.html create mode 100644 doc/en/sdir.html create mode 100644 doc/en/sfatfs.html create mode 100644 doc/en/sfile.html create mode 100644 doc/en/sfileinfo.html create mode 100644 doc/en/stat.html create mode 100644 doc/en/sync.html create mode 100644 doc/en/unlink.html create mode 100644 doc/en/write.html create mode 100644 doc/ja/chmod.html create mode 100644 doc/ja/close.html create mode 100644 doc/ja/dinit.html create mode 100644 doc/ja/dread.html create mode 100644 doc/ja/dstat.html create mode 100644 doc/ja/dwrite.html create mode 100644 doc/ja/fattime.html create mode 100644 doc/ja/getfree.html create mode 100644 doc/ja/lseek.html create mode 100644 doc/ja/mkdir.html create mode 100644 doc/ja/mountdrv.html create mode 100644 doc/ja/open.html create mode 100644 doc/ja/opendir.html create mode 100644 doc/ja/read.html create mode 100644 doc/ja/readdir.html create mode 100644 doc/ja/sdir.html create mode 100644 doc/ja/sfatfs.html create mode 100644 doc/ja/sfile.html create mode 100644 doc/ja/sfileinfo.html create mode 100644 doc/ja/stat.html create mode 100644 doc/ja/sync.html create mode 100644 doc/ja/unlink.html create mode 100644 doc/ja/write.html create mode 100644 doc/layers.png create mode 100644 doc/rw_ata.jpeg create mode 100644 doc/rw_cfc.jpeg create mode 100644 doc/rw_mmc.jpeg create mode 100644 doc/rwtest.png create mode 100644 src/00readme.txt create mode 100644 src/diskio.h create mode 100644 src/ff.c create mode 100644 src/ff.h create mode 100644 src/integer.h create mode 100644 src/tff.c create mode 100644 src/tff.h diff --git a/doc/00index_e.html b/doc/00index_e.html new file mode 100644 index 0000000..7ca5d00 --- /dev/null +++ b/doc/00index_e.html @@ -0,0 +1,99 @@ + + + + + + + +FatFs - FAT Files System Module + + + +

FAT File System Module

+
+ +
+ +

FatFs module is an experimental project to implement a FAT file system to small embdded system. Because the module is written in compliance with ANSI C, it can be used for most 8/16 bit microcontrollers, such as PIC, AVR, H8, Z80 and etc..., without any modification. To use the FatFs module, low level disk I/O functions for each recording media must be provided by user. I created two modules in different configurations in consideration of various use. For read only applications, wriiting codes can also be eliminated to reduce the code size.

+ +

Features of FatFs Module

+ +

Features of Tiny-FatFs Module

+ +
+ + +
+

Application Interface

+

FatFs module and Tiny-FatFs module provide following functions.

+ +
+ + +
+

Disk I/O Interface

+

FatFs module and Tiny-FatFs module require following functions to lower layer to read/write to physical disk and to get current time.

+ +
+ + +
+

Resources

+

The FatFs module is opened for education, reserch and development. You can use, modify and republish it for personal, non-profit or profit use without any limitation under your responsibility.

+
+ +
+ + +
+ + diff --git a/doc/00index_j.html b/doc/00index_j.html new file mode 100644 index 0000000..8ef4a2d --- /dev/null +++ b/doc/00index_j.html @@ -0,0 +1,98 @@ + + + + + + + +FatFs - FAT Files System Module + + + +

FATファイルシステム・モジュール

+
+ +
+ +

小規模な組み込みシステム向けの汎用FATファイルシステム・モジュールです。ANSI C準拠でハードウェア・プラットフォームには依存しないので、必要なワークエリアが確保できれば、PIC, AVR, H8, Z80などほとんどのマイコンでそのまま使用可能です。実際に使用するには、各記録メディア(CFC, MMC, SDC, ATA, USB, SCSI等)に対応したディスクI/Oモジュールを用意する必要があります。いろいろな使用形態を考慮して、高速版(FatFs)と省メモリ版(Tiny-FatFs)の2通りを作成してみました。読み出し専用アプリケーションではさらに書き込み関係のコードを削減した構成もできます。

+

FatFsモジュールの特徴

+ +

Tiny-FatFsモジュールの特徴

+ +
+ + +
+

上位レイヤI/F

+

FatFsモジュールとTiny-FatFsモジュールは、次のファイル操作関数を提供しています。

+ +
+ + +
+

下位レイヤI/F

+

FatFsモジュールとTiny-FatFsモジュールは、物理ドライブへのアクセスや現在時刻を得るため、下位レイヤに次のインターフェースを要求します。

+ +
+ + +
+

資料

+

FatFsモジュールとTiny-FatFsモジュールは教育・研究・開発用に公開しています。どのような目的(個人・非商用・商用)の利用でも使用・改変・配布について一切の制限はありませんが、全て利用者の責任の下での利用とします。

+
+ +
+ + +
+ + diff --git a/doc/css_e.css b/doc/css_e.css new file mode 100644 index 0000000..7a926ef --- /dev/null +++ b/doc/css_e.css @@ -0,0 +1,51 @@ +* {margin: 0; padding: 0; border-width: 0;} +body {margin: 8px; background-color: #e0ffff; font-color: black; line-height: 133%; max-width: 1024px;} +a:link {color: blue;} +a:visited {color: darkmagenta;} +a:hover {background-color: #a0ffff;} +a:active {color: darkmagenta; position: relative; top: 1px; left: 1px;} +abbr {border-width: 1px;} + +p {margin: 0 0 0.3em 1em;} +em {} +strong {} +pre {margin: 1em; line-height: 1.2em; background-color: white;} +tt {margin: 0 0.2em;} +ol {margin: 0 2em;} +ul {margin: 0 2em;} +dl {margin: 0 1em;} +dt {font-family: monospace;} +dl.par dt {margin: 0.5em 0 0 0 ; font-style: italic; } +dl.ret dt {margin: 0.5em 0 0 0 ; font-weight: bold;} +dd {margin: 0 2em;} +hr {border-width: 1px; margin: 1em;} +div.abst {font-family: sans-serif;} +div.para {clear: both; font-family: serif;} +.equ {text-indent: 0; margin: 1em 2em 1em;} +.indent {margin-left: 2em;} +.rset {float: right; margin: 0 0 0.5em 0.5em;} +.lset {float: left; margin: 0 0.5em 0.5em 0;} +ul.flat li {list-style-type: none; margin: 0;} +a.imglnk img {border: 1px solid;} +.iequ {white-space: nowrap; font-weight: bold;} +.clr {clear: both;} +.it {font-style: italic;} + +h1 {line-height: 1em; font-size: 2em; font-family: sans-serif; padding: 0.3em 0 0.3em;} +p.hdd {float: right; text-align: right; margin-top: 0.5em;} +hr.hds {clear: both; margin-bottom: 1em;} + +h2 {font-size: 1.5em; font-family: sans-serif; margin: 0 0 0.5em;} +h3 {font-size: 1.5em; font-family: sans-serif; margin: 2em 0 0.5em;} +h4 {font-size: 1.2em; font-family: sans-serif; margin: 1.5em 0 0.2em;} +h5 {font-size: 1em; font-family: sans-serif; margin: 0.5em 0 0em;} +small {font-size: 80%;} +.indent {margin-left: 2em;} + +/* Tables */ +table.lst {margin: 4px; border-collapse: collapse; border-style: solid; border-width: 2px; border-color: black; } +table.lst th {background-color: white; border-style: solid; border-width: 1px 1px 2px; border-color: black; padding: 0 3px; vertical-align: top; white-space: nowrap;} +table.lst td {background-color: white; border-style: solid; border-width: 1px; border-color: black; padding: 0 3px; vertical-align: top; line-height: 1.3em;} +table.lst td:first-child {font-family: monospace;} + +p.foot {clear: both; text-indent: 0; margin: 1em 0.5em 1em;} diff --git a/doc/css_j.css b/doc/css_j.css new file mode 100644 index 0000000..8299ab3 --- /dev/null +++ b/doc/css_j.css @@ -0,0 +1,54 @@ +@charset "Shift_JIS"; +/* Common style sheet for Tech Notes */ + +* {margin: 0; padding: 0; border-width: 0;} +body {margin: 8px; background-color: #e0ffff; font-color: black; line-height: 133%; letter-spacing: 1px; max-width: 1024px;} +a:link {color: blue;} +a:visited {color: darkmagenta;} +a:hover {background-color: #a0ffff;} +a:active {color: darkmagenta; position: relative; top: 1px; left: 1px;} +abbr {border-width: 1px;} + +p {text-indent: 1em; margin: 0 0 0.3em 0.5em;} +em {} +strong {} +pre {margin: 1em; line-height: 1.2em; letter-spacing: 0; background-color: white;} +tt {margin: 0 0.2em; letter-spacing: 0;} +ol {margin: 0 2em;} +ul {margin: 0 2em;} +dl {margin: 0 1em;} +dt {font-family: monospace;} +dl.par dt {margin: 0.5em 0 0 0 ; font-style: italic; letter-spacing: 0;} +dl.ret dt {margin: 0.5em 0 0 0 ; font-family: monospace; letter-spacing: 0; font-weight: bold;} +dd {margin: 0 2em;} +hr {border-width: 1px; margin: 1em;} +div.abst {font-family: "MS Pゴシック",sans-serif;} +div.para {clear: both; font-family: "MS P明朝",serif;} +.equ {text-indent: 0; margin: 1em 2em 1em;} +.indent {margin-left: 2em;} +.rset {float: right; margin: 0 0 0.5em 0.5em;} +.lset {float: left; margin: 0 0.5em 0.5em 0;} +ul.flat li {list-style-type: none; margin: 0;} +a.imglnk img {border: 1px solid;} +.iequ {white-space: nowrap; font-weight: bold;} +.clr {clear: both;} +.it {font-style: italic;} + +h1 {line-height: 1em; font-size: 2em; font-family: sans-serif; padding: 0.3em 0 0.3em;} +p.hdd {float: right; text-align: right; margin-top: 0.5em;} +hr.hds {clear: both; margin-bottom: 1em;} + +h2 {font-size: 1.5em; font-family: sans-serif; margin: 0 0 0.5em;} +h3 {font-size: 1.5em; font-family: sans-serif; margin: 2em 0 0.5em;} +h4 {font-size: 1.2em; font-family: sans-serif; margin: 1.5em 0 0.2em;} +h5 {font-size: 1em; font-family: sans-serif; margin: 0.5em 0 0em;} +small {font-size: 80%;} +.indent {margin-left: 2em;} + +/* Tables */ +table.lst {margin: 4px; border-collapse: collapse; border-style: solid; border-width: 2px; border-color: black; letter-spacing: 0;} +table.lst th {background-color: white; border-style: solid; border-width: 1px 1px 2px; border-color: black; padding: 0 3px; vertical-align: top;} +table.lst td {background-color: white; border-style: solid; border-width: 1px; border-color: black; padding: 0 3px; vertical-align: top; line-height: 1.3em;} +table.lst td:first-child {font-family: monospace; white-space: nowrap;} + +p.foot {clear: both; text-indent: 0; margin: 1em 0.5em 1em;} diff --git a/doc/en/chmod.html b/doc/en/chmod.html new file mode 100644 index 0000000..b715378 --- /dev/null +++ b/doc/en/chmod.html @@ -0,0 +1,88 @@ + + + + + + +FatFs - f_chmod + + + + +
+

f_chmod

+

The f_chmod changes the attribute of a file or directory.

+
+FRESULT f_chmod (
+  const char* FileName, // Pointer to the file or directory
+  BYTE Attribute,       // Attribute flags
+  BYTE AttributeMask    // Attribute masks
+);
+
+
+ +
+

Parameter

+
+
FileName
+
Pointer to the null-terminated string that specifies a file or directory to be changed
+
Attribute
+
Attribute flags to be set in one or more combination of the following flags. The specified flags are set and others are cleard.
+ + + + + + +
AttributeDescription
AR_RDORead only
AR_ARCArchive
AR_SYSSystem
AR_HIDHidden
+
+
AttributeMask
+
Attribute mask that specifies which attribute is changed. The specified aattributes are set or cleard.
+
+
+ + +
+

Return Values

+
+
FR_OK
+
The function succeeded.
+
FR_NOFILE
+
Could not find the file.
+
FR_NOPATH
+
Could not find the path.
+
FR_INVALID_NAME
+
The file name is invalid.
+
FR_NOT_READY
+
The disk drive cannot work due to no medium in the drive or any other reason.
+
FR_WRITE_PROTECTED
+
The medium is in write protected.
+
FR_RW_ERROR
+
Any error occured in low level disk I/O.
+
FR_INCORRECT_DISK_CHANGE
+
Incorrect disk removal, such as a medium change during any file opend, has been occured.
+
FR_NOT_ENABLED
+
FatFs module has not been enabled.
+
FR_NO_FILESYSTEM
+
There is no valid FAT partition on the disk.
+
+
+ + +
+

Description

+

The f_chmod changes the attribute of a file or directory. This function is not supported in read-only configuration.

+
+ + +
+

Sample Code

+
+    // Set read-only flag, clear archive flag and others are not changed.
+    f_chmod("/file.txt", AR_RDO, AR_RDO | AR_ARC);
+
+
+ +

Return

+ + diff --git a/doc/en/close.html b/doc/en/close.html new file mode 100644 index 0000000..f0ce408 --- /dev/null +++ b/doc/en/close.html @@ -0,0 +1,59 @@ + + + + + + +FatFs - f_close + + + + +
+

f_close

+

Function f_close closes a file.

+
+FRESULT f_close (
+  FIL* FileObject     // Pointer to the file object structure
+);
+
+
+ +
+

Parameter

+
+
FileObject
+
Pointer to the file object structure to be closed.
+
+
+ + +
+

Return Values

+
+
FR_OK
+
The file has been closed successfuly.
+
FR_RW_ERROR
+
Any error occured in low level disk I/O.
+
FR_INCORRECT_DISK_CHANGE
+
Incorrect disk removal, such as a medium change during any file opend, has been occured.
+
FR_NOT_ENABLED
+
FatFs module is not enabled.
+
+
+ + +
+

Description

+

Function f_close closes a file. When the function succeeded, the file object structure can be discarded.

+
+ + +
+

References

+

f_open, f_read, f_write, f_sync, FIL, FATFS

+
+ +

Return

+ + diff --git a/doc/en/dinit.html b/doc/en/dinit.html new file mode 100644 index 0000000..74ff0e4 --- /dev/null +++ b/doc/en/dinit.html @@ -0,0 +1,32 @@ + + + + + + +FatFs - disk_initialize + + + + +
+

disk_initialize

+

The disk_initialize initializes the disk drive.

+
+DSTATUS disk_initialize ();
+
+
+ +
+

Return Values

+

This function returns a disk status. For details of the disk status, refer to the disk_status().

+
+ +
+

Description

+

The disk_initialize initializes the disk drive. When the function succeeded, STA_NOINIT flag in the return value will be cleard.

+
+ +

Return

+ + diff --git a/doc/en/dread.html b/doc/en/dread.html new file mode 100644 index 0000000..2eaf97b --- /dev/null +++ b/doc/en/dread.html @@ -0,0 +1,54 @@ + + + + + + +FatFs - disk_read + + + + +
+

disk_read

+

The disk_read reads sector(s) from the disk drive.

+
+DRESULT disk_read (
+  BYTE* Buffer,        // Pointer to the read buffer.
+  DWORD SectorNumber,  // Sector number to read from.
+  BYTE SectorCount     // Number of sectros to read.
+);
+
+
+ +
+

Parameters

+
+
Buffer
+
Pointer to the read buffer to store the read data. SectorCount * 512 bytes is required for the size of the read buffer.
+
SectorNumber
+
Specifies the sector number in logical block address to read from.
+
SectorCount
+
Specifies number of sectors to read. The value can be 1 to 255.
+
+
+ + +
+

Return Value

+
+
RES_OK
+
The function succeeded.
+
RES_ERROR
+
Any error occured during read operation.
+
RES_PARERR
+
Invalid parameter.
+
RES_NOTRDY
+
The disk dirve has not been initialized.
+
+
+ + +

Return

+ + diff --git a/doc/en/dstat.html b/doc/en/dstat.html new file mode 100644 index 0000000..576da2d --- /dev/null +++ b/doc/en/dstat.html @@ -0,0 +1,35 @@ + + + + + + +FatFs - disk_status + + + + +
+

disk_status

+

The disk_status gets the disk status.

+
+DSTATUS disk_status ();
+
+
+ +
+

Return Values

+

The disk status is returned in combinatin of following flags.

+
+
STA_NOINIT
+
Indicates that the disk drive has not been initialiezed. This flag is set when power-on, occurrence of disk removal or disk_initialize() failed and cleared when disk_initialize() succeeded.
+
STA_NODISK
+
Indicates that no disk in the drive. It is always cleared on non-removable drive.
+
STA_PROTECTED
+
Indicates that the disk is write protected. It is always cleared on the disk that has not write protect notch.
+
+
+ +

Return

+ + diff --git a/doc/en/dwrite.html b/doc/en/dwrite.html new file mode 100644 index 0000000..ab044bb --- /dev/null +++ b/doc/en/dwrite.html @@ -0,0 +1,62 @@ + + + + + + +FatFs - disk_write + + + + +
+

disk_write

+

The disk_write writes sector(s) to the disk.

+
+DRESULT disk_write (
+  const BYTE* Buffer,  // Pointer to the data to be written.
+  DWORD SectorNumber,  // Sector number to write.
+  BYTE SectorCount     // Number of sectors to write.
+);
+
+
+ +
+

Parameters

+
+
Buffer
+
Pointer to the data to be written.
+
SectorNumber +
Specifies the sector number in logical block address to write from.
+
SectorCount
+
Specifies number of sectors to write. The value can be 1 to 255.
+
+
+ + +
+

Return Values

+
+
RES_OK
+
The function succeeded.
+
RES_ERROR
+
Any error occured during read operation.
+
RES_WRPRT
+
The disk is write protected.
+
RES_PARERR
+
Invalid parameter.
+
RES_NOTRDY
+
The disk dirve has not been initialized.
+
+
+ + +
+

Description

+

This function is not required in read only configuration.

+
+ + +

Return

+ + diff --git a/doc/en/fattime.html b/doc/en/fattime.html new file mode 100644 index 0000000..faf7dcc --- /dev/null +++ b/doc/en/fattime.html @@ -0,0 +1,49 @@ + + + + + + +FatFs - get_fattime + + + + +
+

get_fattime

+

The get_fattime gets current time.

+
+DWORD get_fattime ();
+
+
+ + +
+

Return Value

+

Currnet time is returned with packed into a DWORD value. The bit field is as follows:

+
+
bit31:25
+
Year from 1980 (0..127)
+
bit24:21
+
Month (1..12)
+
bit20:16
+
Date (1..31)
+
bit15:11
+
Hour (0..23)
+
bit10:5
+
Minute (0..59)
+
bit4:0
+
Second/2 (0..29)
+
+
+ + +
+

Description

+

The function must be return any valid time even if the system does not support a real time clock. The get_fattime is not required in read only configuration.

+
+ + +

Return

+ + diff --git a/doc/en/getfree.html b/doc/en/getfree.html new file mode 100644 index 0000000..60826f7 --- /dev/null +++ b/doc/en/getfree.html @@ -0,0 +1,78 @@ + + + + + + +FatFs - f_getfree + + + + +
+

f_getfree

+

The f_getfree gets number of the free clusters.

+
+FRESULT f_getfree (
+  DWORD* Clusters     // Pointer to the variable to store number of free clusters.
+);
+
+
+ +
+

Parameters

+
+
Clusters
+
Pointer to the DWORD variable to store number of free clusters.
+
+
+ + +
+

Return Values

+
+
FR_OK
+
The function succeeded. The *Clusters havs number of free clusters.
+
FR_NOT_READY
+
The disk drive cannot work due to no medium in the drive or any other reason.
+
FR_RW_ERROR
+
Any error occured in low level disk I/O.
+
FR_INCORRECT_DISK_CHANGE
+
Incorrect disk removal, such as a medium change during any file opend, has been occured.
+
FR_NOT_ENABLED
+
FatFs module has not been enabled.
+
FR_NO_FILESYSTEM
+
There is no valid FAT partition on the disk.
+
+
+ + +
+

Descriptions

+

The f_getfree gets number of free clusters on the drive. The FatFs.sects_clust is indicating number of sectors per cluster, so that the free space in unit of byte can be calcurated with this.

+
+ + +
+

Samples Code

+
+    DWORD clust;
+
+    // Get free clusters
+    res = f_getfree(&clust);
+    if (res) die(res);
+
+    // Get free bytes
+    printf("%lu bytes available on the disk.\n", clust * FatFs->sects_clust * 512);
+
+
+ + +
+

References

+

FATFS

+
+ +

Return

+ + diff --git a/doc/en/lseek.html b/doc/en/lseek.html new file mode 100644 index 0000000..8754204 --- /dev/null +++ b/doc/en/lseek.html @@ -0,0 +1,82 @@ + + + + + + +FatFs - f_lseek + + + + +
+

f_lseek

+

The f_lseek moves the file read/write pointer.

+
+FRESULT f_lseek (
+  FIL* FileObject,   // Pointer to the file object structure
+  DWORD Offset       // File offset in unit of byte
+);
+
+
+ +
+

Parameters

+
+
FileObject
+
Pointer to the file object structure.
+
Offset
+
Number of bytes where from start of file
+
+
+ + +
+

Return Values

+
+
FR_OK
+
The function succeeded.
+
FR_ALIGN_ERROR
+
Out of alignment. In unbuffered mode, specified offset must be aligned on 512 byte boundary.
+
FR_RW_ERROR
+
Any error occured in low level disk I/O.
+
FR_INCORRECT_DISK_CHANGE
+
Incorrect disk removal, such as a medium change during any file opend, has been occured.
+
FR_NOT_ENABLED
+
FatFs module has not been enabled.
+
+
+ + +
+

Description

+

The f_lseek moves the file read/write pointer. The offset can be specified in only origin from top of the file and cannot moved to above end of the file. When an offset above the file size was specified, the read/write pointer moves to end of the file.

+
+ + +
+

Sample Code

+
+    // Move to offset of 5000 from top of file.
+    res = f_lseek(&file, 5000);
+    if (res) die(res);
+
+    // Move to 3000 bytes front of current offset.
+    res = f_lseek(&file, file.fptr + 3000);
+    if (res) die(res);
+
+    // Move to 2000 bytes back of current offset.
+    res = f_lseek(&file, file.fptr - 2000);
+    if (res) die(res);
+
+
+ + +
+

References

+

f_open, FIL

+
+ +

Return

+ + diff --git a/doc/en/mkdir.html b/doc/en/mkdir.html new file mode 100644 index 0000000..72242ce --- /dev/null +++ b/doc/en/mkdir.html @@ -0,0 +1,80 @@ + + + + + + +FatFs - f_mkdir + + + + +
+

f_mkdir

+

The f_mkdir creates a directory.

+
+FRESULT f_mkdir (
+  const char* DirName // Pointer to the directory name
+);
+
+
+ +
+

Parameter

+
+
DirName
+
Pointer to the null-terminated string that specifies the full-path directory name to create. The directory separator is '/'. Because the FatFs module does not have a concept of current directory, a full-path name that followed from the root directory must be used. Leading space charactors are skipped if exist and heading '/' can be exist or omitted.
+
+
+ + +
+

Return Value

+
+
FR_OK
+
The function succeeded.
+
FR_NOPATH
+
Could not find the path.
+
FR_INVALID_NAME
+
The path name is invalid.
+
FR_DENIED
+
The function was denied due to any of following reasons: any file or directory that has same name is existing, cannot be created due to the directory table or disk full.
+
FR_NOT_READY
+
The disk drive cannot work due to no medium in the drive or any other reason.
+
FR_WRITE_PROTECTED
+
The medium is write protected.
+
FR_RW_ERROR
+
Any error occured in low level disk I/O.
+
FR_INCORRECT_DISK_CHANGE
+
Incorrect disk removal, such as a medium change during any file opend, has been occured.
+
FR_NOT_ENABLED
+
FatFs module has not been enabled.
+
FR_NO_FILESYSTEM
+
There is no valid FAT partition on the disk.
+
+
+ + +
+

Description

+

The f_mkdir creates an empty directory. This function is not supported in read only configuration.

+

+

+
+ + +
+

Sample Code

+
+    res = f_mkdir("/sub1");
+    if (res) die(res);
+    res = f_mkdir("/sub1/sub2");
+    if (res) die(res);
+    res = f_mkdir("/sub1/sub2/sub3");
+    if (res) die(res);
+
+
+ +

Return

+ + diff --git a/doc/en/mountdrv.html b/doc/en/mountdrv.html new file mode 100644 index 0000000..acc96b8 --- /dev/null +++ b/doc/en/mountdrv.html @@ -0,0 +1,56 @@ + + + + + + +FatFs - f_mountdrv + + + + +
+

f_mountdrv

+

The f_mountdrv forces mounted the partition.

+
+FRESULT f_mountdrv ();
+
+
+ +
+

Return Values

+
+
FR_OK
+
The function succeeded.
+
FR_NOT_READY
+
The disk drive cannot work due to no medium in the drive or any other reason.
+
FR_RW_ERROR
+
Any error occured in low level disk I/O.
+
FR_NOT_ENABLED
+
FatFs module has not been enabled.
+
FR_NO_FILESYSTEM
+
There is no valid FAT partition on the disk.
+
+
+ + +
+

Description

+

The f_mountdrv mounts a partition (initializes FATFS structure) and put all file functions can be used. The file system is mounted (initialized) automatically in accordance with the necessity when any file function is called, so that any initialization is not needed before using file functions. To terminate use of the FatFs module, close all files and clear the global variable FatFs, then the FATFS structure can be discarded. In this function, following process is executed.

+
+ +
+ + +
+

References

+

FATFS

+
+ +

Return

+ + diff --git a/doc/en/open.html b/doc/en/open.html new file mode 100644 index 0000000..e64490e --- /dev/null +++ b/doc/en/open.html @@ -0,0 +1,138 @@ + + + + + + +FatFs - f_open + + + + +
+

f_open

+

The f_open function creates or opens the file and initialize a file object structure to be used to access the file.

+
+FRESULT f_open (
+  FIL* FileObject,      // Pointer to the file object structure
+  const char* FileName, // Pointer to the file neme
+  BYTE ModeFlags        // Mode flags
+);
+
+
+ +
+

Parameters

+
+
FileObject
+
Pointer to the blank file object structure to be initialized. After the f_open function succeeded, the file can be accessed with the file object structure until it is closed. The member buffer in the structure must be initialized with the address of a 512 bytes file read/write buffer before or immediataly after the f_open function. The initialization is not needed when FA_UNBUFFERED flag has been specified.
+
FileName
+
Pointer to a null-terminated string specifies the full-path file name to create or open. The directory separator is '/'. Because the FatFs module does not have a concept of current directory, a full-path name that followed from the root directory must be used. Leading space charactors are skipped if exist and heading '/' can be exist or omitted.
+
ModeFlags
+
Specifies the type of access and open method for the file. It is specified by a combination of following flags.
+ + + + + + + + +
ValueDescription
FA_READSpecifies read access to the object. Data can be read from the file.
Combine with FA_WRITE for read-write access.
FA_WRITESpecifies write access to the object. Data can be written to the file.
Combine with FA_READ for read-write access.
FA_UNBUFFEREDThis is for only FatFs module. When not specified, the file can be read/written in
stream I/O via the file read/write buffer pointed by member 'buffer' in the file
object. When specified, file read/write buffer is not used and number of bytes
to read/write must be integer multiple of 512.
FA_OPEN_EXISTINGOpens the file. The function fails if the file does not exist.
FA_CREATE_ALWAYSCreates a new file. If the file exists, it is truncated and overwritten.
FA_OPEN_ALWAYSOpens the file, if it exists. If the file does not exist, the function creates the file.
+
+
+
+ + +
+

Return Values

+
+
FR_OK
+
The function succeeded. The FileObject structure is used for subsequent calls to refer to the file. When close the file, use function f_close().
+
FR_NOFILE
+
Could not find the file.
+
FR_NOPATH
+
Could not find the path.
+
FR_INVALID_NAME
+
The file name is invalid.
+
FR_DENIED
+
The required access was denied due to any of following reasons: write mode open of a file that has read-only attribute, file creation under existing a same name directory or read-only file, cannot be created due to the directory table or disk full.
+
FR_NOT_READY
+
The disk drive cannot work due to no medium in the drive or any other reason.
+
FR_WRITE_PROTECTED
+
Write mode open or creation under the medium is write protected.
+
FR_RW_ERROR
+
Any error occured in low level disk I/O.
+
FR_INCORRECT_DISK_CHANGE
+
Incorrect disk removal, such as a medium change during any file opend, has been occured.
+
FR_NOT_ENABLED
+
FatFs module has not been enabled.
+
FR_NO_FILESYSTEM
+
There is no valid FAT partition on the disk.
+
+
+ + +
+

Description

+

To start to use the FatFs module, prepare a work area (FATFS structure), clear the structure and store the address to the global variable 'FatFs' to allocate the work area to the FatFs module. Then the FatFs module can work.

+

Flags FA_WRITE, FA_CREATE_ALWAYS, FA_OPEN_ALWAYS are not supported in read-only configuration. Flag FA_UNBUFFERED is not supported in Tiny-FatFs.

+
+ + +
+

Sample Code

+
+void main ()
+{
+    FATFS fs;            // FatFs work area
+    FIL fsrc, fdst;      // file structures
+    BYTE fbuff[512*2];   // file r/w buffers (not required for Tiny-FatFs)
+    BYTE buffer[4096];   // file copy buffer
+    FRESULT res;         // FatFs function common result code
+    WORD br, bw;         // File R/W count
+
+
+    // Activate FatFs module
+    memset(&fs, 0, sizeof(FATFS));
+    FatFs = &fs;
+
+    // Open source file
+    fsrc.buffer = fbuff+0;	// (not required for Tiny-FatFs)
+    res = f_open(&fsrc, "/srcfile.dat", FA_OPEN_EXISTING | FA_READ);
+    if (res) die(res);
+
+    // Create destination file
+    fdst.buffer = fbuff+512;	// (not required for Tiny-FatFs)
+    res = f_open(&fdst, "/dstfile.dat", FA_CREATE_ALWAYS | FA_WRITE);
+    if (res) die(res);
+
+    // Copy source to destination
+    for (;;) {
+        res = f_read(&fsrc, buffer, sizeof(buffer), &br);
+        if (res) die(res);
+        if (br == 0) break;
+        res = f_write(&fdst, buffer, br, &bw);
+        if (res) die(res);
+        if (bw < br) break;
+    }
+
+    // Close all files
+    f_close(&fsrc);
+    f_close(&fdst);
+
+    // Deactivate FatFs module
+    FatFs = NULL;
+}
+
+
+ + +
+

References

+

f_read, f_write, f_close, FIL, FATFS

+
+ +

Return

+ + diff --git a/doc/en/opendir.html b/doc/en/opendir.html new file mode 100644 index 0000000..d0ca017 --- /dev/null +++ b/doc/en/opendir.html @@ -0,0 +1,70 @@ + + + + + + +FatFs - f_opendir + + + + +
+

f_opendir

+

The f_opendir opens a directory.

+
+FRESULT f_opendir (
+  DIR* DirObject,      // Pointer to blank directory object structure
+  const char* DirName  // Pointer to the directory name
+);
+
+
+ +
+

Parameter

+
+
DirObject
+
Pointer to the blank directory object to be initialized
+
DirName
+
Pinter to the null-terminated string that specifies the full-path directory name to be opened. The directory separator is '/'. Because the FatFs module does not have a concept of current directory, a full-path name that followed from the root directory must be used. Leading space charactors are skipped if exist and heading '/' can be exist or omitted. The target directory name cannot be followed by a '/'. When open the root directory, specify "" or "/".
+
+
+ + +
+

Return Values

+
+
FR_OK
+
The function succeeded. The DirObject structure is used for subsequent calls to read the directory items.
+
FR_NOPATH
+
Could not find the path.
+
FR_INVALID_NAME
+
The path name is invalid.
+
FR_NOT_READY
+
The disk drive cannot work due to no medium in the drive or any other reason.
+
FR_RW_ERROR
+
Any error occured in low level disk I/O.
+
FR_INCORRECT_DISK_CHANGE
+
Incorrect disk removal, such as a medium change during any file opend, has been occured.
+
FR_NOT_ENABLED
+
FatFs module has not been enabled.
+
FR_NO_FILESYSTEM
+
There is no valid FAT partition on the disk.
+
+
+ + +
+

Description

+

The f_opendir opens a directory and initializes the DirObject structure for subsequent calls. The directory object structure can be discarded at any time.

+
+ + +
+

References

+

f_readdir, DIR

+
+ +

Return

+ + diff --git a/doc/en/read.html b/doc/en/read.html new file mode 100644 index 0000000..e5e091b --- /dev/null +++ b/doc/en/read.html @@ -0,0 +1,72 @@ + + + + + + +FatFs - f_read + + + + +
+

f_read

+

The f_read reads data from a file.

+
+FRESULT f_read (
+  FIL* FileObject,    // Pointer to the file object structure
+  BYTE* Buffer,       // Pointer to the buffer to store read data
+  WORD ByteToRead,    // Number of bytes to read
+  WORD* ByteRead      // Pointer to the variable to return number of bytes read
+);
+
+
+ +
+

Parameters

+
+
FileObject
+
Pointer to the valid file object structure.
+
Buffer
+
Pointer to the buffer to store read data
+
ByteToRead
+
Number of bytes to read
+
ByteRead
+
Pointer to the WORD variable to return number of bytes read.
+ +
+ + +
+

Return Values

+
+
FR_OK
+
The function succeeded.
+
FR_DENIED
+
The function denied due to the file has been opened in write only mode.
+
FR_ALIGN_ERROR
+
The file has been opened in unbufferred mode but unaligned access was detected.
+
FR_RW_ERROR
+
Any error occured in low level disk I/O.
+
FR_NOT_READY
+
The disk drive cannot work due to no medium in the drive or any other reason.
+
FR_NOT_ENABLED
+
FatFs module has not been enabled.
+
+
+ + +
+

Description

+

The read/write pointer increases in number of bytes read. The ByteRead will be smaller than ByteToRead when the read pointer reached to end of the file or alignment error occured during the read operation. In unbufferred mode, last fractional bytes cannot be read due to FR_ALIGN_ERROR.

+
+ + +
+

References

+

f_open, f_write, f_close, FIL

+
+ +

Return

+ + diff --git a/doc/en/readdir.html b/doc/en/readdir.html new file mode 100644 index 0000000..37995ad --- /dev/null +++ b/doc/en/readdir.html @@ -0,0 +1,88 @@ + + + + + + +FatFs - f_readdir + + + + +
+

f_readdir

+

The f_readdir reads directory items.

+
+FRESULT f_readdir (
+  DIR* DirObject,    // Pointer to the directory object strcture
+  FILINFO* FileInfo  // Pointer to the blank file information structure
+);
+
+
+ +
+

Parameters

+
+
DirObject
+
Pointer to the valid directory object strcture.
+
FileInfo
+
Pointer to the file information structure to store the read item.
+
+
+ + +
+

Return Values

+
+
FR_OK
+
The function succeeded.
+
FR_NOT_READY
+
The disk drive cannot work due to no medium in the drive or any other reason.
+
FR_RW_ERROR
+
Any error occured in low level disk I/O.
+
FR_NOT_ENABLED
+
FatFs module has not been enabled.
+
+
+ + +
+

Description

+

The f_readdir reads directory items in sequence. All items in the directory can be read by calling f_readdir repeatedly. When all items have been read and no item to read, the member f_name[] in the file information structure gets a null string. For details of the file informations, refer to the FILINFO.

+
+ + +
+

Sample Code

+
+void scan_files (char* path)
+{
+    FILINFO finfo;
+    DIR dirs;
+    int i;
+
+    if (f_opendir(&dirs, path) == FR_OK) {
+        i = strlen(path);
+        while ((f_readdir(&dirs, &finfo) == FR_OK) && finfo.f_name[0]) {
+            if (finfo.f_attrib & AM_DIR) {
+                sprintf(path+i, "/%s", &finfo.f_name[0]);
+                scan_files(path);
+                *(path+i) = '\0';
+            } else {
+                printf("%s/%s\n", path, &finfo.f_name[0]);
+            }
+        }
+    }
+}
+
+
+ + +
+

References

+

f_opendir, f_stat, FILINFO, DIR

+
+ +

Return

+ + diff --git a/doc/en/sdir.html b/doc/en/sdir.html new file mode 100644 index 0000000..ee15cab --- /dev/null +++ b/doc/en/sdir.html @@ -0,0 +1,37 @@ + + + + + + +FatFs - DIR + + + + +
+

DIR

+

The DIR structure is used for the work area to read a directory.

+

FatFs

+
+typedef struct _DIR {
+    DWORD   sclust;       // Directory start cluster
+    DWORD   clust;        // Current reading cluster
+    DWORD   sect;         // Current reading sector
+    WORD    index;        // Current index
+} DIR;
+
+

Tiny-FatFs

+
+typedef struct _DIR {
+    WORD    sclust;       // Directory start cluster
+    WORD    clust;        // Current reading cluster
+    DWORD   sect;         // Current reading sector
+    WORD    index;        // Current index
+} DIR;
+
+
+ +

Return

+ + diff --git a/doc/en/sfatfs.html b/doc/en/sfatfs.html new file mode 100644 index 0000000..b4baae7 --- /dev/null +++ b/doc/en/sfatfs.html @@ -0,0 +1,58 @@ + + + + + + +FatFs - FATFS + + + + +
+

FATFS

+

The FATFS structure holds dynamic work area of the FatFs modlue and it is allocated by an application program. There is no members that can be changed from the application program.

+

FatFs

+
+typedef struct _FATFS {
+    BYTE    fs_type;        // FAT type
+    BYTE    files;          // Number of files currently opend
+    BYTE    sects_clust;    // Sectors per cluster
+    BYTE    n_fats;         // Number of FAT copies
+    WORD    n_rootdir;      // Number of root directory entry (always 0 in FAT32)
+    BYTE    winflag;        // win[] dirty flag (1:must be written back)
+    BYTE    pad1;
+    DWORD   sects_fat;      // Sectors per fat
+    DWORD   max_clust;      // Maximum cluster# + 1
+    DWORD   fatbase;        // FAT start sector
+    DWORD   dirbase;        // Root directory start sector (cluster# for FAT32)
+    DWORD   database;       // Data start sector
+    DWORD   winsect;        // Current sector# appearing in the win[]
+    BYTE    win[512];       // Disk access window for directory and FAT
+} FATFS;
+
+ +

Tiny-FatFs

+
+typedef struct _FATFS {
+    BYTE    fs_type;        // FAT type
+    BYTE    files;          // Number of files currently opend
+    BYTE    sects_clust;    // Sectors per cluster
+    BYTE    n_fats;         // Number of FAT copies
+    WORD    n_rootdir;      // Number of root directory entry
+    BYTE    winflag;        // win[] dirty flag (1:must be written back)
+    BYTE    pad1;
+    WORD    sects_fat;      // Sectors per fat
+    WORD    max_clust;      // Maximum cluster# + 1
+    DWORD   fatbase;        // FAT start sector
+    DWORD   dirbase;        // Root directory start sector
+    DWORD   database;       // Data start sector
+    DWORD   winsect;        // Current sector# appearing in the win[]
+    BYTE    win[512];       // Disk access window for directory, FAT and file
+} FATFS;
+
+
+ +

Return

+ + diff --git a/doc/en/sfile.html b/doc/en/sfile.html new file mode 100644 index 0000000..271fc9e --- /dev/null +++ b/doc/en/sfile.html @@ -0,0 +1,50 @@ + + + + + + +FatFs - FIL + + + + +
+

FIL

+

The FIL structure holds state of a file and it is allocated by an application program. Only buffer member can be initialized by the application program.

+ +

FatFs

+
+typedef struct _FIL {
+    DWORD   fptr;           // File R/W pointer
+    DWORD   fsize;          // File size
+    DWORD   org_clust;      // File start cluster
+    DWORD   curr_clust;     // Current cluster
+    DWORD   curr_sect;      // Current sector
+    DWORD   dir_sect;       // Sector# containing the directory entry
+    BYTE*   dir_ptr;        // Ponter to the directory entry in the window
+    BYTE*   buffer;         // Pointer to 512 byte file R/W buffer
+    BYTE    flag;           // File status flags
+    BYTE    sect_clust;     // Left sectors in current cluster
+} FIL;
+
+ +

Tiny-FatFs

+
+typedef struct _FIL {
+    DWORD   fptr;           // File R/W pointer
+    DWORD   fsize;          // File size
+    WORD    org_clust;      // File start cluster
+    WORD    curr_clust;     // Current cluster
+    DWORD   curr_sect;      // Current sector
+    DWORD   dir_sect;       // Sector# containing the directory entry
+    BYTE*   dir_ptr;        // Ponter to the directory entry in the window
+    BYTE    flag;           // File status flags
+    BYTE    sect_clust;     // Left sectors in current cluster
+} FIL;
+
+
+ +

Return

+ + diff --git a/doc/en/sfileinfo.html b/doc/en/sfileinfo.html new file mode 100644 index 0000000..6508fab --- /dev/null +++ b/doc/en/sfileinfo.html @@ -0,0 +1,42 @@ + + + + + + +FatFs - FILINFO + + + + +
+

FILINFO

+

The FILINFO structure holds a file information returned by f_stat() and f_readdir().

+
+typedef struct _FILINFO {
+    DWORD fsize;            // Size
+    WORD fdate;             // Date
+    WORD ftime;             // Time
+    BYTE fattrib;           // Attribute
+    char fname[8+1+3+1];    // Name
+} FILINFO;
+
+
+ +

Members

+
+
fsize
+
Indicates size of the file in unit of byte. This is always zero when it is a directory.
+
fdate
+
Indicates the date that the file was modified or the directory was created.
+
ftime
+
Indicates the time that the file was modified or the directory was created.
+
fattrib
+
Indicates the file/directory attribute in combination of AM_DIR, AM_RDO, AM_HID, AM_SYS and AM_ARC.
+
fname[]
+
Indicates the file/directory name in 8.3 format null-terminated string.
+
+ +

Return

+ + diff --git a/doc/en/stat.html b/doc/en/stat.html new file mode 100644 index 0000000..fcaa85d --- /dev/null +++ b/doc/en/stat.html @@ -0,0 +1,70 @@ + + + + + + +FatFs - f_stat + + + + +
+

f_stat

+

The f_stat gets the file status.

+
+FRESULT f_stat (
+  const char* FileName,  // Pointer to the file or directory name
+  FILINFO* FileInfo      // Pointer to the FILINFO structure
+);
+
+
+ +
+

Parameters

+
+
FileName
+
Pointer to the null-terminated string that specifies the file or directory name to get the information .
+
FileInfo
+
Pointer to the FILINFO structure to store the information.
+
+
+ + +
+

Return Values

+
+
FR_OK
+
The function succeeded.
+
FR_NOPATH
+
Could not find the path.
+
FR_INVALID_NAME
+
The file name is invalid.
+
FR_NOT_READY
+
The disk drive cannot work due to no medium in the drive or any other reason.
+
FR_RW_ERROR
+
Any error occured in low level disk I/O.
+
FR_INCORRECT_DISK_CHANGE
+
Incorrect disk removal, such as a medium change during any file opend, has been occured.
+
FR_NOT_ENABLED
+
FatFs module has not been enabled.
+
FR_NO_FILESYSTEM
+
There is no valid FAT partition on the disk.
+
+
+ + +
+

Description

+

The f_stat gets the information of a file or directory. For details of the infomation, refer to the FILINFO structure.

+
+ + +
+

References

+

f_opendir, f_readdir, FILINFO

+
+ +

Return

+ + diff --git a/doc/en/sync.html b/doc/en/sync.html new file mode 100644 index 0000000..5ea8bad --- /dev/null +++ b/doc/en/sync.html @@ -0,0 +1,59 @@ + + + + + + +FatFs - f_sync + + + + +
+

f_sync

+

The f_sync flushes the cached information of the wriiting file.

+
+FRESULT f_sync (
+  FIL* FileObject     // Pointer to the file object structure
+);
+
+
+ +
+

Parameters

+
+
FileObject
+
Pointer to the file object to be flushed.
+
+
+ + +
+

Return Values

+
+
FR_OK
+
The function succeeded.
+
FR_RW_ERROR
+
Any error occured in low level disk I/O.
+
FR_INCORRECT_DISK_CHANGE
+
Incorrect disk removal, such as a medium change during any file opend, has been occured.
+
FR_NOT_ENABLED
+
FatFs module has not been enabled.
+
+
+ + +
+

Description

+

The f_sync writes back the cached information of a file being written into the disk. This is the same function as f_close but the file is left opened and can continue the file access. This is suitable for an application of data logger that opens a file for long time in writing mode. The f_sync of periodic or immediataly after f_wriete can minimize the lost data due to an unintentional black out or disk removal. This function is not supported in read-only configuration.

+
+ + +
+

References

+

f_close

+
+ +

Return

+ + diff --git a/doc/en/unlink.html b/doc/en/unlink.html new file mode 100644 index 0000000..4e386c3 --- /dev/null +++ b/doc/en/unlink.html @@ -0,0 +1,62 @@ + + + + + + +FatFs - f_unlink + + + + +
+

f_unlink

+

The f_unlink removes file or directory.

+
+FRESULT f_unlink (
+  const char* FileName  // Pointer to the file or directory name
+);
+
+
+ +
+

Parameters

+
+
FileName
+
Pointer to the null-terminated string that specifies the full path name of a file or directory to be removed. In read only configuration, this function is not supported.
+
+
+ + +
+

Return Values

+
+
FR_OK
+
The function succeeded.
+
FR_NOFILE
+
Could not find the file or directory.
+
FR_NOPATH
+
Could not find the path.
+
FR_INVALID_NAME
+
The path name is invalid.
+
FR_DENIED
+
The function was denied due to any of following reasons: the file or directory has read-only attribute, the directory is not empty.
+
FR_NOT_READY
+
The disk drive cannot work due to no medium in the drive or any other reason.
+
FR_WRITE_PROTECTED
+
Write mode open or creation under the medium is write protected.
+
FR_RW_ERROR
+
Any error occured in low level disk I/O.
+
FR_INCORRECT_DISK_CHANGE
+
Incorrect disk removal, such as a medium change during any file opend, has been occured.
+
FR_NOT_ENABLED
+
FatFs module has not been enabled.
+
FR_NO_FILESYSTEM
+
There is no valid FAT partition on the disk.
+
+
+ + +

Return

+ + diff --git a/doc/en/write.html b/doc/en/write.html new file mode 100644 index 0000000..54307a0 --- /dev/null +++ b/doc/en/write.html @@ -0,0 +1,72 @@ + + + + + + +FatFs - f_write + + + + +
+

f_write

+

The f_write writes data to a file.

+
+FRESULT f_write (
+  FIL* FileObject,     // Pointer to the file object structure
+  const BYTE* Buffer,  // Pointer to the data to be written
+  WORD ByteToWrite,    // Number of bytes to write
+  WORD* ByteWritten    // Pointer to the variable to return number of bytes written
+);
+
+
+ +
+

Parameter

+
+
FileObject
+
Pointer to the valid file object structure.
+
Buffer
+
Pointer to the data to be written.
+
ByteToWrite
+
Specifies number of bytes to write.
+
ByteWritten
+
Pointer to the WORD variable to return number of bytes written.
+
+
+ + +
+

Return Values

+
+
FR_OK
+
The function succeeded.
+
FR_DENIED
+
The function denied due to the file has been opened in read only mode.
+
FR_ALIGN_ERROR
+
The file has been opened in unbufferred mode but unaligned access was detected.
+
FR_RW_ERROR
+
Any error occured in low level disk I/O.
+
FR_NOT_READY
+
The disk drive cannot work due to no medium in the drive or any other reason.
+
FR_NOT_ENABLED
+
FatFs module has not been enabled.
+
+
+ + +
+

Description

+

The read/write pointer increases in number of bytes written. The ByteWritten will be smaller than ByteToWrite when disk full or alignment error occured during write function. This function is not supported in read only configuration.

+
+ + +
+

References

+

f_open, f_read, f_close, FIL

+
+ +

Return

+ + diff --git a/doc/ja/chmod.html b/doc/ja/chmod.html new file mode 100644 index 0000000..32a89e7 --- /dev/null +++ b/doc/ja/chmod.html @@ -0,0 +1,88 @@ + + + + + + +FatFs - f_chmod + + + + +
+

f_chmod

+

ファイルまたはディレクトリの属性を変更します。

+
+FRESULT f_chmod (
+  const char* FileName, // ファイルまたはディレクトリの文字列へのポインタ
+  BYTE Attribute,       // 設定値
+  BYTE AttributeMask    // 変更マスク
+);
+
+
+ +
+

パラメータ

+
+
FileName
+
属性変更対象のファイルまたはディレクトリのフルパス名の入った'\0'で終わる文字列を指定します。
+
Attribute
+
設定する属性。指定可能な属性は次の通りで、これらの組み合わせで指定します。指定されなかった属性は解除されます。
+ + + + + + +
意味
AR_RDOリードオンリー
AR_ARCアーカイブ
AR_SYSシステム
AR_HIDヒドゥン
+
+
AttributeMask
+
変更する属性のマスク。指定した属性が設定または解除されます。Attributeと同じ値を使います。
+
+
+ + +
+

戻り値

+
+
FR_OK
+
正常終了。
+
FR_NOFILE
+
ファイルが見つからない。
+
FR_NOPATH
+
パスが見つからない。
+
FR_INVALID_NAME
+
パス名が不正。
+
FR_NOT_READY
+
メディアがセットされていないなど、ディスクドライブが動作不能状態。
+
FR_WRITE_PROTECTED
+
メディアが書き込み禁止状態。
+
FR_RW_ERROR
+
ディスクアクセスでエラーが発生した。
+
FR_INCORRECT_DISK_CHANGE
+
不正なメディアの取り外しがあった。ファイルを開いたままのメディア交換など。
+
FR_NOT_ENABLED
+
FatFsモジュールが停止状態。
+
FR_NO_FILESYSTEM
+
ディスク上に有効なFATパーテーションが見つからない。
+
+
+ + +
+

解説

+

ファイルまたはディレクトリの属性を変更します。リードオンリー構成ではこの関数はサポートされません。

+
+ + +
+

使用例

+
+    // Set read-only flag , clear archive flag and others are not changed.
+    f_chmod("/file.txt", AR_RDO, AR_RDO | AR_ARC);
+
+
+ +

戻る

+ + diff --git a/doc/ja/close.html b/doc/ja/close.html new file mode 100644 index 0000000..90458d9 --- /dev/null +++ b/doc/ja/close.html @@ -0,0 +1,59 @@ + + + + + + +FatFs - f_close + + + + +
+

f_close

+

ファイルを閉じます。

+
+FRESULT f_close (
+  FIL* FileObject     // ファイルオブジェクト構造体へのポインタ
+);
+
+
+ +
+

パラメータ

+
+
FileObject
+
閉じるファイルオブジェクト構造体へのポインタを指定します。
+
+
+ + +
+

戻り値

+
+
FR_OK
+
正常終了。
+
FR_RW_ERROR
+
ディスクアクセスでエラーが発生した。
+
FR_INCORRECT_DISK_CHANGE
+
不正なメディアの取り外しがあった。ファイルを開いたままのメディア交換など。
+
FR_NOT_ENABLED
+
FatFsモジュールが停止状態。
+
+
+ + +
+

解説

+

ファイルを閉じます。関数が正常終了したら、そのファイルオブジェクト構造体を破棄できます。

+
+ + +
+

参照

+f_open, f_read, f_write, f_sync, FIL, FATFS +
+ +

戻る

+ + diff --git a/doc/ja/dinit.html b/doc/ja/dinit.html new file mode 100644 index 0000000..1cf4c80 --- /dev/null +++ b/doc/ja/dinit.html @@ -0,0 +1,32 @@ + + + + + + +FatFs - disk_initialize + + + + +
+

disk_initialize

+

ディスクドライブを初期化します。

+
+DSTATUS disk_initialize ();
+
+
+ +
+

戻り値

+

この関数は戻り値としてディスクステータスを返します。ディスクステータスの詳細に関してはdisk_status()を参照してください。

+
+ +
+

解説

+

ディスクドライブを初期化します。関数が成功すると、戻り値のSTA_NOINITフラグがクリアされます。

+
+ +

戻る

+ + diff --git a/doc/ja/dread.html b/doc/ja/dread.html new file mode 100644 index 0000000..846bb09 --- /dev/null +++ b/doc/ja/dread.html @@ -0,0 +1,54 @@ + + + + + + +FatFs - disk_read + + + + +
+

disk_read

+

ディスクからセクタを読み出します。

+
+DRESULT disk_read (
+  BYTE* Buffer,        // 読み出しバッファへのポインタ
+  DWORD SectorNumber,  // 読み出し開始セクタ番号
+  BYTE SectorCount     // 読み出しセクタ数
+);
+
+
+ +
+

パラメータ

+
+
Buffer
+
ディスクから読み出したセクタデータを格納するバッファ。SectorCount * 512バイトのサイズが必要です。
+
SectorNumber
+
読み出しを開始するセクタ番号。LBAで指定します。
+
SectorCount
+
読み出すセクタ数。 1〜255で設定します
+
+
+ + +
+

戻り値

+
+
RES_OK
+
正常終了。
+
RES_ERROR
+
読み込み中にエラーが発生した。
+
RES_PARERR
+
パラメータが不正。
+
RES_NOTRDY
+
ドライブが動作可能状態ではない(初期化されていない)。
+
+
+ + +

戻る

+ + diff --git a/doc/ja/dstat.html b/doc/ja/dstat.html new file mode 100644 index 0000000..f1c20fe --- /dev/null +++ b/doc/ja/dstat.html @@ -0,0 +1,35 @@ + + + + + + +FatFs - disk_status + + + + +
+

disk_status

+

ディスクの状態を取得します。

+
+DSTATUS disk_status ();
+
+
+ +
+

戻り値

+

ディスクの状態が次のフラグの組み合わせの値で返されます。

+
+
STA_NOINIT
+
ドライブが初期化されていないことを示すフラグ。電源ONまたはメディアの取り外しでセットされ、disk_initialize()の正常終了でクリア、失敗でセットされます。
+
STA_NODISK
+
メディアがセットされていないことを示すフラグ。メディアが取り外されているときセットされ、メディアがセットされているときクリアされます。ハードディスクでは常にクリアされています。
+
STA_PROTECTED
+
メディアがライトプロテクトされていることを示すフラグ。ライトプロテクトノッチのないメディアでは常にクリアされています。
+
+
+ +

戻る

+ + diff --git a/doc/ja/dwrite.html b/doc/ja/dwrite.html new file mode 100644 index 0000000..1792aaa --- /dev/null +++ b/doc/ja/dwrite.html @@ -0,0 +1,62 @@ + + + + + + +FatFs - disk_write + + + + +
+

disk_write

+

ディスクにセクタデータを書き込みます。

+
+DRESULT disk_write (
+  const BYTE* Buffer,  // 書き込むデータへのポインタ
+  DWORD SectorNumber,  // 書き込み開始セクタ番号
+  BYTE SectorCount     // 書き込みセクタ数
+);
+
+
+ +
+

パラメータ

+
+
Buffer
+
ディスクに書き込むセクタデータを指定します。
+
SectorNumber
+
書き込みを開始するセクタ番号。LBAで指定します。
+
SectorCount
+
書き込むセクタ数。 1〜255で設定します。
+
+
+ + +
+

戻り値

+
+
RES_OK
+
正常終了。
+
RES_ERROR
+
書き込み中にエラーが発生した。
+
RES_WRPRT
+
ディスクが書き込み禁止状態。
+
RES_PARERR
+
パラメータが不正。
+
RES_NOTRDY
+
ドライブが動作可能状態ではない(初期化されていない)。
+
+
+ + +
+

解説

+

リードオンリー構成ではこの関数は必要とされません。

+
+ + +

戻る

+ + diff --git a/doc/ja/fattime.html b/doc/ja/fattime.html new file mode 100644 index 0000000..fcd49ac --- /dev/null +++ b/doc/ja/fattime.html @@ -0,0 +1,49 @@ + + + + + + +FatFs - get_fattime + + + + +
+

get_fattime

+

現在時刻を取得します。

+
+DWORD get_fattime ();
+
+
+ + +
+

戻り値

+

現在のローカルタイムがDWORD値にパックされて返されます。ビットフィールドは次に示すようになります。

+
+
bit31:25
+
1980年を起点とした年が 0..127 で入ります。
+
bit24:21
+
月が 1..12 の値で入ります。
+
bit20:16
+
日が 1..31 の値で入ります。
+
bit15:11
+
時が 0..23 の値で入ります。
+
bit10:5
+
分が 0..59 の値で入ります。
+
bit4:0
+
秒/2が 0..29 の値で入ります。
+
+
+ + +
+

解説

+

RTCをサポートしないシステムでも、何らかの有効な値を返さなければなりません。リードオンリー構成ではこの関数は必要とされません。

+
+ + +

戻る

+ + diff --git a/doc/ja/getfree.html b/doc/ja/getfree.html new file mode 100644 index 0000000..f5066c9 --- /dev/null +++ b/doc/ja/getfree.html @@ -0,0 +1,78 @@ + + + + + + +FatFs - f_getfree + + + + +
+

f_getfree

+

ディスクの未使用領域を得ます。

+
+FRESULT f_getfree (
+  DWORD* Clusters     // 空きクラスタ数を格納する変数へのポインタ
+);
+
+
+ +
+

パラメータ

+
+
Clusters
+
空きクラスタ数を格納するDWORD変数へのポインタを指定します。
+
+
+ + +
+

戻り値

+
+
FR_OK
+
正常終了。*Clustersに空きクラスタ数が返されます。
+
FR_NOT_READY
+
メディアがセットされていないなど、ディスクドライブが動作不能状態。
+
FR_RW_ERROR
+
ディスクアクセスでエラーが発生した。
+
FR_INCORRECT_DISK_CHANGE
+
不正なメディアの取り外しがあった。ファイルを開いたままのメディア交換など。
+
FR_NOT_ENABLED
+
FatFsモジュールが停止状態。
+
FR_NO_FILESYSTEM
+
ディスク上に有効なFATパーテーションが見つからない。
+
+
+ + +
+

解説

+

ファイルシステム上の空きクラスタ数を取得します。FatFs.sects_clust がクラスタあたりのセクタ数を示しているので、これを元に実際の空きサイズが計算できます。

+
+ + +
+

使用例

+
+    DWORD clust;
+
+    // Get free clusters
+    res = f_getfree(&clust);
+    if (res) die(res);
+
+    // Get free bytes
+    printf("%lu bytes available on the disk.\n", clust * FatFs->sects_clust * 512);
+
+
+ + +
+

参照

+FATFS +
+ +

戻る

+ + diff --git a/doc/ja/lseek.html b/doc/ja/lseek.html new file mode 100644 index 0000000..b8b3373 --- /dev/null +++ b/doc/ja/lseek.html @@ -0,0 +1,82 @@ + + + + + + +FatFs - f_lseek + + + + +
+

f_lseek

+

ファイルのR/Wポインタを移動します。

+
+FRESULT f_lseek (
+  FIL* FileObject,   // ファイルオブジェクト構造体へのポインタ
+  DWORD Offset       // 移動先オフセット
+);
+
+
+ +
+

パラメータ

+
+
FileObject
+
対象となるファイルオブジェクト構造体へのポインタを指定します。
+
Offset
+
移動先のオフセット(R/Wポインタ)値。ファイル先頭からのオフセットをバイト単位で指定します。
+
+
+ + +
+

戻り値

+
+
FR_OK
+
正常終了。
+
FR_ALIGN_ERROR
+
アンバッファモードで開いたファイルで、512の整数倍でないオフセットを指定した。
+
FR_RW_ERROR
+
ディスクアクセスでエラーが発生した。
+
FR_INCORRECT_DISK_CHANGE
+
不正なメディアの取り外しがあった。ファイルを開いたままのメディア交換など。
+
FR_NOT_ENABLED
+
FatFsモジュールが停止状態。
+
+
+ + +
+

解説

+

ファイルR/Wポインタを移動します。オフセットの指定はファイル先頭からのみです。ファイル末尾以降への移動はできません。ファイルサイズ以上のオフセットを指定した場合、R/Wポインタはファイル末尾に移動します。

+
+ + +
+

使用例

+
+    // Move to offset of 5000 from top of file.
+    res = f_lseek(&file, 5000);
+    if (res) die(res);
+
+    // Move to 3000 bytes front of current offset.
+    res = f_lseek(&file, file.fptr + 3000);
+    if (res) die(res);
+
+    // Move to 2000 bytes back of current offset.
+    res = f_lseek(&file, file.fptr - 2000);
+    if (res) die(res);
+
+
+ + +
+

参照

+

f_open, FIL

+
+ +

戻る

+ + diff --git a/doc/ja/mkdir.html b/doc/ja/mkdir.html new file mode 100644 index 0000000..dd67dff --- /dev/null +++ b/doc/ja/mkdir.html @@ -0,0 +1,80 @@ + + + + + + +FatFs - f_mkdir + + + + +
+

f_mkdir

+

ディレクトリを作成します。

+
+FRESULT f_mkdir (
+  const char* DirName // 作成するディレクトリ名へのポインタ
+);
+
+
+ +
+

パラメータ

+
+
DirName
+
作成するディレクトリのフルパス名が入った'\0'で終わる文字列へのポインタを指定します。ディレクトリセパレータには'/'を使用します。FatFsモジュールにはカレントディレクトリの概念がないので、パスはルートディレクトリから辿る絶対パスとなります。文字列先頭のスペースはスキップされます。パス先頭の'/'はあってもなくてもかまいません。
+
+
+ + +
+

戻り値

+
+
FR_OK
+
正常終了。
+
FR_NOPATH
+
パスが見つからない。
+
FR_INVALID_NAME
+
パス名が不正。
+
FR_DENIED
+
同名のディレクトリやファイルの存在、ディスクやディレクトリエントリが満杯の場合など。
+
FR_NOT_READY
+
メディアがセットされていないなど、ディスクドライブが動作不能状態。
+
FR_WRITE_PROTECTED
+
メディアが書き込み禁止状態。
+
FR_RW_ERROR
+
ディスクアクセスでエラーが発生した。
+
FR_INCORRECT_DISK_CHANGE
+
不正なメディアの取り外しがあった。ファイルを開いたままのメディア交換など。
+
FR_NOT_ENABLED
+
FatFsモジュールが停止状態。
+
FR_NO_FILESYSTEM
+
ディスク上に有効なFATパーテーションが見つからない。
+
+
+ + +
+

解説

+

空のディレクトリを作成します。リードオンリー構成ではこの関数はサポートされません。

+

+

+
+ + +
+

使用例

+
+    res = f_mkdir("/sub1");
+    if (res) die(res);
+    res = f_mkdir("/sub1/sub2");
+    if (res) die(res);
+    res = f_mkdir("/sub1/sub2/sub3");
+    if (res) die(res);
+
+
+ +

戻る

+ + diff --git a/doc/ja/mountdrv.html b/doc/ja/mountdrv.html new file mode 100644 index 0000000..2bd37be --- /dev/null +++ b/doc/ja/mountdrv.html @@ -0,0 +1,57 @@ + + + + + + +FatFs - f_mountdrv + + + + +
+

f_mountdrv

+

ファイルシステムを明示的に初期化します。

+
+FRESULT f_mountdrv ();
+
+
+ +
+

戻り値

+
+
FR_OK
+
正常終了。
+
FR_NOT_READY
+
メディアがセットされていないなど、ディスクドライブが動作不能状態。
+
FR_RW_ERROR
+
ディスクアクセスでエラーが発生した。
+
FR_NOT_ENABLED
+
FatFsモジュールが停止状態。
+
FR_NO_FILESYSTEM
+
ディスク上に有効なFATファイルシステムが見つからない。
+
+
+ + +
+

解説

+

ファイルシステムを初期化(マウント)して他の全てのファイル関数が使える状態にします。FatFsモジュールではマウント動作はファイル関数呼び出し時に必要に応じて行われるため、通常はこの関数を使用する必要はありません。自動マウント動作中に回復不能エラー(たとえばFR_INCORRECT_DISK_CHANGE)が発生した場合、全てのファイル関数が使えなくなるので、そのときはこの関数で強制マウントして回復します。

+

FatFsモジュールの使用を終了するには、全てのファイルを閉じたあとグローバル変数FatFsをクリアします。その後、ワークエリアは解放できます。f_mountdrv関数内では次の処理が行われます。

+
+ +
+ + +
+

参照

+

FATFS

+
+ +

戻る

+ + diff --git a/doc/ja/open.html b/doc/ja/open.html new file mode 100644 index 0000000..cd95f5d --- /dev/null +++ b/doc/ja/open.html @@ -0,0 +1,141 @@ + + + + + + +FatFs - f_open + + + + +
+

f_open

+

ファイルをオープンまたは作成します。

+
+FRESULT f_open (
+  FIL* FileObject,      // 作成するファイルオブジェクト構造体へのポインタ
+  const char* FileName, // ファイルのフルパス名へのポインタ
+  BYTE ModeFlags        // モードフラグ
+);
+
+
+ +
+

パラメータ

+
+
FileObject
+
新しく作成するファイルオブジェクト構造体へのポインタを指定します。以降、このファイルを閉じるまでそのファイルオブジェクトを使用してファイル操作をします。構造体メンバのbufferは、オープン前または直後にそのファイルで使用するR/Wバッファへのアドレスで初期化しておく必要があります(FA_UNBUFFEREDを指定した場合はバッファは不要)。
+
FileName
+
作成する (またはオープンする) ファイルのフルパス名が入った'\0'で終わる文字列へのポインタを指定します。ディレクトリセパレータには'/'を使用します。FatFsモジュールにはカレントディレクトリの概念がないので、パスはルートディレクトリから辿る絶対パスとなります。文字列先頭のスペースはスキップされます。パス先頭の'/'はあってもなくてもかまいません。
+
ModeFlags
+
ファイルのアクセス方法やオープン方法を決めるフラグです。このパラメータには次の組み合わせを指定します。
+ + + + + + + + +
意味
FA_READ読み出しモードでオープンします。読み書きする場合はFA_WRITEと共に指定します。
FA_WRITE書き込みモードでオープンします。読み書きする場合はFA_READと共に指定します。
FA_UNBUFFERED +FatFsでのみ指定可。指定するとファイルR/Wバッファを使用せず、メモリを節約できます。
+read/write/seekは512の整数倍単位でなければなりません。指定しない場合は、ファイル
+R/Wバッファ(ファイルオブジェクト構造体メンバのbufferで指定)を使用。
FA_OPEN_EXISTING既存のファイルを開きます。ファイルが無いときはエラーになります。
FA_CREATE_ALWAYSファイルを作成します。既存のファイルがある場合は、サイズを0にしてから開きます。
FA_OPEN_ALWAYS既存のファイルを開きます。ファイルが無いときはファイルを作成します。
+
+
+
+ + +
+

戻り値

+
+
FR_OK
+
正常終了。以降、FileObject構造体を使ってこのファイルを操作できます。ファイルを閉じるときは、f_close()を使用します。
+
FR_NOFILE
+
ファイルが見つからない。
+
FR_NOPATH
+
パスが見つからない。
+
FR_INVALID_NAME
+
ファイル名が不正。
+
FR_DENIED
+
アクセスが拒否された。リードオンリーファイルの書き込みモードオープン、同名のディレクトリまたはリードオンリファイルがある状態でのファイル作成、ディスクまたはディレクトリテーブルが満杯でファイルを作成できないなど。
+
FR_NOT_READY
+
メディアがセットされていないなど、ディスクドライブが動作不能状態。
+
FR_WRITE_PROTECTED
+
メディアが書き込み禁止状態で書き込みオープンまたはファイル作成をした。
+
FR_RW_ERROR
+
ディスクアクセスでエラーが発生した。
+
FR_INCORRECT_DISK_CHANGE
+
不正なメディアの取り外しがあった。ファイルを開いたままのメディア交換など。
+
FR_NOT_ENABLED
+
FatFsモジュールが停止状態。
+
FR_NO_FILESYSTEM
+
ディスク上に有効なFATパーテーションが見つからない。
+
+
+ + +
+

解説

+

FatFsモジュールの使用を開始するにはまず、FatFsモジュールにワークエリア(構造体FATFS)を割り当てます。確保したワークエリアを0で初期化したあと、FatFsモジュールのグローバル変数FatFsにそのアドレスを代入するだけでモジュールは動作可能状態になり、ファイル関数が使えるようになります。

+

リードオンリー構成では、FA_WRITE, FA_CREATE_ALWAYS, FA_OPEN_ALWAYSの各フラグはサポートされません。

+
+ + +
+

使用例

+
+void main ()
+{
+    FATFS fs;            // FatFs work area
+    FIL fsrc, fdst;      // file structures
+    BYTE fbuff[512*2];   // file r/w buffers (not required for Tiny-FatFs)
+    BYTE buffer[4096];   // file copy buffer
+    FRESULT res;         // FatFs function common result code
+    WORD br, bw;         // File R/W count
+
+
+    // Activate FatFs module
+    memset(&fs, 0, sizeof(FATFS));
+    FatFs = &fs;
+
+    // Open source file
+    fsrc.buffer = fbuff+0;	// (not required for Tiny-FatFs)
+    res = f_open(&fsrc, "/srcfile.dat", FA_OPEN_EXISTING | FA_READ);
+    if (res) die(res);
+
+    // Create destination file
+    fdst.buffer = fbuff+512;	// (not required for Tiny-FatFs)
+    res = f_open(&fdst, "/dstfile.dat", FA_CREATE_ALWAYS | FA_WRITE);
+    if (res) die(res);
+
+    // Copy source to destination
+    for (;;) {
+        res = f_read(&fsrc, buffer, sizeof(buffer), &br);
+        if (res) die(res);
+        if (br == 0) break;
+        res = f_write(&fdst, buffer, br, &bw);
+        if (res) die(res);
+        if (bw < br) break;
+    }
+
+    // Close all files
+    f_close(&fsrc);
+    f_close(&fdst);
+
+    // Deactivate FatFs module
+    FatFs = NULL;
+}
+
+
+ + +
+

参照

+

f_read, f_write, f_close, FIL, FATFS

+
+ +

戻る

+ + diff --git a/doc/ja/opendir.html b/doc/ja/opendir.html new file mode 100644 index 0000000..72610c8 --- /dev/null +++ b/doc/ja/opendir.html @@ -0,0 +1,70 @@ + + + + + + +FatFs - f_opendir + + + + +
+

f_opendir

+

ディレクトリをオープンします。

+
+FRESULT f_opendir (
+  DIR* DirObject,      // ディレクトリブジェクト構造体へのポインタ
+  const char* DirName  // ディレクトリ名へのポインタ
+);
+
+
+ +
+

パラメータ

+
+
DirObject
+
初期化するディレクトリオブジェクト構造体へのポインタを指定します。
+
DirName
+
オープンするディレクトリのフルパス名が入った'\0'で終わる文字列へのポインタを指定します。ディレクトリセパレータには'/'を使用します。FatFsモジュールにはカレントディレクトリの概念がないので、パスはルートディレクトリから辿る絶対パスとなります。先頭のスペースはスキップされます。パス先頭の'/'はあってもなくてもかまいませんが、最後のディレクトリ名の後ろに'/'を付けることはできません。ルートディレクトリを開くときは、""または"/"を指定します。
+
+
+ + +
+

戻り値

+
+
FR_OK
+
正常終了。
+
FR_NOPATH
+
パスが見つからない。
+
FR_INVALID_NAME
+
パス名が不正。
+
FR_NOT_READY
+
メディアがセットされていないなど、ディスクドライブが動作不能状態。
+
FR_RW_ERROR
+
ディスクアクセスでエラーが発生した。
+
FR_INCORRECT_DISK_CHANGE
+
不正なメディアの取り外しがあった。ファイルを開いたままのメディア交換など。
+
FR_NOT_ENABLED
+
FatFsモジュールが停止状態。
+
FR_NO_FILESYSTEM
+
ディスク上に有効なFATパーテーションが見つからない。
+
+
+ + +
+

解説

+

ディレクトリをオープンします。正常終了したら、DirObject構造体を使ってこのディレクトリの項目を順次読み出せます。使用後の処理は必要ありません。DirObject構造体は任意の時点で破棄できます。

+
+ + +
+

参照

+

f_readdir, DIR

+
+ +

戻る

+ + diff --git a/doc/ja/read.html b/doc/ja/read.html new file mode 100644 index 0000000..d745285 --- /dev/null +++ b/doc/ja/read.html @@ -0,0 +1,72 @@ + + + + + + +FatFs - f_read + + + + +
+

f_read

+

ファイルからデータを読み出します。

+
+FRESULT f_read (
+  FIL* FileObject,    // ファイルオブジェクト構造体
+  BYTE* Buffer,       // 読み出したデータを格納するバッファ
+  WORD ByteToRead,    // 読み出すバイト数
+  WORD* ByteRead      // 読み出されたバイト数
+);
+
+
+ +
+

パラメータ

+
+
FileObject
+
ファイルオブジェクト構造体へのポインタを指定します。
+
Buffer
+
読み出したデータを格納するバッファを指すポインタを指定します。
+
ByteToRead
+
読み出すバイト数を指定します。
+
ByteRead
+
実際に読み出されたバイト数を格納する変数を指すポインタを指定します。
+ +
+ + +
+

戻り値

+
+
FR_OK
+
正常終了。
+
FR_DENIED
+
書き込み専用モードで開いたファイルから読み込もうとした。
+
FR_ALIGN_ERROR
+
アンバッファモードで開かれたファイルで、512の整数倍でないアクセスを検出した。
+
FR_RW_ERROR
+
ディスクアクセスでエラーが発生した。
+
FR_NOT_READY
+
メディアがセットされていないなど、ディスクドライブが動作不能状態。
+
FR_NOT_ENABLED
+
FatFsモジュールが停止状態。
+
+
+ + +
+

解説

+

読み込み開始位置は、現在のファイルR/Wポインタからになります。ファイルR/Wポインタは読み込まれたバイト数だけ進みます。読み込み中にファイルの終端に達すると、*ByteReadByteToReadよりも小さくなります。アンバッファモードでは最後の端数バイトを残してFR_ALIGN_ERRORになります。

+
+ + +
+

参照

+

f_open, f_write, f_close, FIL

+
+ +

戻る

+ + diff --git a/doc/ja/readdir.html b/doc/ja/readdir.html new file mode 100644 index 0000000..a9c73cb --- /dev/null +++ b/doc/ja/readdir.html @@ -0,0 +1,88 @@ + + + + + + +FatFs - f_readdir + + + + +
+

f_readdir

+

ディレクトリ項目を読み出します

+
+FRESULT f_readdir (
+  DIR* DirObject,    // ディレクトリブジェクト構造体へのポインタ
+  FILINFO* FileInfo  // ファイル情報構造体へのポインタ
+);
+
+
+ +
+

パラメータ

+
+
DirObject
+
初期化済みのディレクトリオブジェクト構造体へのポインタを指定します。
+
FileInfo
+
読み出したディレクトリ項目を格納するファイル情報構造体へのポインタを指定します。
+
+
+ + +
+

戻り値

+
+
FR_OK
+
正常終了。
+
FR_NOT_READY
+
メディアがセットされていないなど、ディスクドライブが動作不能状態。
+
FR_RW_ERROR
+
ディスクアクセスでエラーが発生した。
+
FR_NOT_ENABLED
+
FatFsモジュールが停止状態。
+
+
+ + +
+

解説

+

ディレクトリ項目を順次読み出します。この関数を繰り返し実行することによりディレクトリの全ての項目を読み出すことができます。全ての項目を読み出し、読み出す項目がもう無いときは、f_name[]メンバにヌル文字列が返されます。得られるファイル情報の詳細については FILINFO構造体を参照してください。

+
+ + +
+

使用例

+
+void scan_files (char* path)
+{
+    FILINFO finfo;
+    DIR dirs;
+    int i;
+
+    if (f_opendir(&dirs, path) == FR_OK) {
+        i = strlen(path);
+        while ((f_readdir(&dirs, &finfo) == FR_OK) && finfo.f_name[0]) {
+            if (finfo.f_attrib & AM_DIR) {
+                sprintf(path+i, "/%s", &finfo.f_name[0]);
+                scan_files(path);
+                *(path+i) = '\0';
+            } else {
+                printf("%s/%s\n", path, &finfo.f_name[0]);
+            }
+        }
+    }
+}
+
+
+ + +
+

参照

+

f_opendir, f_stat, FILINFO, DIR

+
+ +

戻る

+ + diff --git a/doc/ja/sdir.html b/doc/ja/sdir.html new file mode 100644 index 0000000..ee07752 --- /dev/null +++ b/doc/ja/sdir.html @@ -0,0 +1,37 @@ + + + + + + +FatFs - DIR + + + + +
+

DIR

+

DIR構造体は、ディレクトリ情報読み出しのワークエリアとして使用されます。

+

FatFs

+
+typedef struct _DIR {
+    DWORD   sclust;       // Directory start cluster
+    DWORD   clust;        // Current reading cluster
+    DWORD   sect;         // Current reading sector
+    WORD    index;        // Current index
+} DIR;
+
+

Tiny-FatFs

+
+typedef struct _DIR {
+    WORD    sclust;       // Directory start cluster
+    WORD    clust;        // Current reading cluster
+    DWORD   sect;         // Current reading sector
+    WORD    index;        // Current index
+} DIR;
+
+
+ +

戻る

+ + diff --git a/doc/ja/sfatfs.html b/doc/ja/sfatfs.html new file mode 100644 index 0000000..d08b8f0 --- /dev/null +++ b/doc/ja/sfatfs.html @@ -0,0 +1,58 @@ + + + + + + +FatFs - FATFS + + + + +
+

FATFS

+

FATFS構造体は、FatFsモジュールのダイナミックワークエリアを保持し、アプリケーション側で確保・管理されます。アプリケーションから変更可能なメンバはありません。

+

FatFs

+
+typedef struct _FATFS {
+    BYTE    fs_type;        // FAT type
+    BYTE    files;          // Number of files currently opend
+    BYTE    sects_clust;    // Sectors per cluster
+    BYTE    n_fats;         // Number of FAT copies
+    WORD    n_rootdir;      // Number of root directory entry
+    BYTE    winflag;        // win[] dirty flag (1:must be written back)
+    BYTE    pad1;
+    DWORD   sects_fat;      // Sectors per fat
+    DWORD   max_clust;      // Maximum cluster# + 1
+    DWORD   fatbase;        // FAT start sector
+    DWORD   dirbase;        // Root directory start sector (cluster# for FAT32)
+    DWORD   database;       // Data start sector
+    DWORD   winsect;        // Current sector# appearing in the win[]
+    BYTE    win[512];       // Disk access window for Directory/FAT area
+} FATFS;
+
+ +

Tiny-FatFs

+
+typedef struct _FATFS {
+    BYTE    fs_type;        // FAT type
+    BYTE    files;          // Number of files currently opend
+    BYTE    sects_clust;    // Sectors per cluster
+    BYTE    n_fats;         // Number of FAT copies
+    WORD    n_rootdir;      // Number of root directory entry
+    BYTE    winflag;        // win[] dirty flag (1:must be written back)
+    BYTE    pad1;
+    WORD    sects_fat;      // Sectors per fat
+    WORD    max_clust;      // Maximum cluster# + 1
+    DWORD   fatbase;        // FAT start sector
+    DWORD   dirbase;        // Root directory start sector (cluster# for FAT32)
+    DWORD   database;       // Data start sector
+    DWORD   winsect;        // Current sector# appearing in the win[]
+    BYTE    win[512];       // Disk access window
+} FATFS;
+
+
+ +

戻る

+ + diff --git a/doc/ja/sfile.html b/doc/ja/sfile.html new file mode 100644 index 0000000..c2f42a1 --- /dev/null +++ b/doc/ja/sfile.html @@ -0,0 +1,49 @@ + + + + + + +FatFs - FIL + + + + +
+

FIL

+

FIL構造体は、1個で1ファイルの状態を保持し、アプリケーション側で確保・管理されます。アプリケーションから変更可能なメンバはbufferのみです。

+

FatFs

+
+typedef struct _FIL {
+    DWORD   fptr;           // File R/W pointer
+    DWORD   fsize;          // File size
+    DWORD   org_clust;      // File start cluster
+    DWORD   curr_clust;     // Current cluster
+    DWORD   curr_sect;      // Current sector
+    DWORD   dir_sect;       // Sector# containing the directory entry
+    BYTE*   dir_ptr;        // Ponter to the directory entry in the window
+    BYTE*   buffer;         // Pointer to 512 byte file R/W buffer
+    BYTE    flag;           // File status flags
+    BYTE    sect_clust;     // Left sectors in current cluster
+} FIL;
+
+ +

Tiny-FatFs

+
+typedef struct _FIL {
+    DWORD   fptr;           // File R/W pointer
+    DWORD   fsize;          // File size
+    WORD    org_clust;      // File start cluster
+    WORD    curr_clust;     // Current cluster
+    DWORD   curr_sect;      // Current sector
+    DWORD   dir_sect;       // Sector# containing the directory entry
+    BYTE*   dir_ptr;        // Ponter to the directory entry in the window
+    BYTE    flag;           // File status flags
+    BYTE    sect_clust;     // Left sectors in current cluster
+} FIL;
+
+
+ +

戻る

+ + diff --git a/doc/ja/sfileinfo.html b/doc/ja/sfileinfo.html new file mode 100644 index 0000000..bf3261f --- /dev/null +++ b/doc/ja/sfileinfo.html @@ -0,0 +1,42 @@ + + + + + + +FatFs - FILINFO + + + + +
+

FILINFO

+

FILINFO構造体は、f_stat(), f_readdir()で返されるファイル情報を保持します。

+
+typedef struct _FILINFO {
+    DWORD fsize;            // Size
+    WORD fdate;             // Date
+    WORD ftime;             // Time
+    BYTE fattrib;           // Attribute
+    char fname[8+1+3+1];    // Name
+} FILINFO;
+
+
+ +

メンバ

+
+
fsize
+
ファイルのバイト単位のサイズが格納されます。ディレクトリの場合は常に0です。
+
fdate
+
ファイルの変更された日付、またはディレクトリの作成された日付が格納されます。
+
ftime
+
ファイルの変更された時刻、またはディレクトリの作成された時刻が格納されます。
+
fattrib
+
属性フラグが格納されます。フラグはAM_DIR, AM_RDO, AM_HID, AM_SYS, AM_ARCの組み合わせとなります。
+
fname[]
+
8.3形式の名前が'\0'で終わる文字列として格納されます。
+
+ +

戻る

+ + diff --git a/doc/ja/stat.html b/doc/ja/stat.html new file mode 100644 index 0000000..18ee293 --- /dev/null +++ b/doc/ja/stat.html @@ -0,0 +1,70 @@ + + + + + + +FatFs - f_stat + + + + +
+

f_stat

+

+
+FRESULT f_stat (
+  const char* FileName, // ファイルまたはディレクトリ名へのポインタ
+  FILINFO* FileInfo     // ファイル情報構造体へのポインタ
+);
+
+
+ +
+

パラメータ

+
+
FileName
+
情報を得るファイルまたはディレクトリ名の'\0'で終わる文字列を指すポインタを指定します。
+
FileInfo
+
読み出し結果を格納するファイル情報構造体へのポインタを指定します。
+
+
+ + +
+

戻り値

+
+
FR_OK
+
正常終了。
+
FR_NOPATH
+
パスが見つからない。
+
FR_INVALID_NAME
+
パス名が不正。
+
FR_NOT_READY
+
メディアがセットされていないなど、ディスクドライブが動作不能状態。
+
FR_RW_ERROR
+
ディスクアクセスでエラーが発生した。
+
FR_INCORRECT_DISK_CHANGE
+
不正なメディアの取り外しがあった。ファイルを開いたままのメディア交換など。
+
FR_NOT_ENABLED
+
FatFsモジュールが停止状態。
+
FR_NO_FILESYSTEM
+
ディスク上に有効なFATパーテーションが見つからない。
+
+
+ + +
+

解説

+

ファイルまたはディレクトリに関する情報を得ます。得られるファイル情報の詳細については FILINFO構造体を参照してください。

+
+ + +
+

参照

+

f_opendir, f_readdir, FILINFO, DIR

+
+ +

戻る

+ + diff --git a/doc/ja/sync.html b/doc/ja/sync.html new file mode 100644 index 0000000..8c1f423 --- /dev/null +++ b/doc/ja/sync.html @@ -0,0 +1,60 @@ + + + + + + +FatFs - f_sync + + + + +
+

f_sync

+

書き込み中のファイルのキャッシュされた情報をフラッシュします。

+
+FRESULT f_sync (
+  FIL* FileObject     // ファイルオブジェクト構造体へのポインタ
+);
+
+
+ +
+

パラメータ

+
+
FileObject
+
syncするファイルオブジェクト構造体へのポインタを指定します。
+
+
+ + +
+

戻り値

+
+
FR_OK
+
正常終了。
+
FR_RW_ERROR
+
ディスクアクセスでエラーが発生した。
+
FR_INCORRECT_DISK_CHANGE
+
不正なメディアの取り外しがあった。ファイルを開いたままのメディア交換など。
+
FR_NOT_ENABLED
+
FatFsモジュールが停止状態。
+
+
+ + +
+

解説

+

書き込み中のファイルのそこまでの状態(R/Wバッファ上のデータ、変更されたFATやディレクトリ項目)をディスクに書き込みます。この関数はファイルクローズのプロセスを実行しますが、ファイルは引き続き開かれたままになり、読み書きを続行できます。ロギングなど、書き込みモードで長時間ファイルが開かれているアプリケーションにおいて、定期的または区切りの良いところでsyncすることにより、不意の電源断やメディアの取り外しにより失われるデータを最小限にします。

+

リードオンリー構成ではこの関数はサポートされません。

+
+ + +
+

参照

+

f_close

+
+ +

戻る

+ + diff --git a/doc/ja/unlink.html b/doc/ja/unlink.html new file mode 100644 index 0000000..33af9db --- /dev/null +++ b/doc/ja/unlink.html @@ -0,0 +1,67 @@ + + + + + + +FatFs - f_unlink + + + + +
+

f_unlink

+

ファイルまたはディレクトリを削除します。

+
+FRESULT f_unlink (
+  const char* FileName  // ファイルまたはディレクトリ名へのポインタ
+);
+
+
+ +
+

パラメータ

+
+
FileName
+
削除対象のファイルまたはディレクトリ名の入った'\0'で終わる文字列へのポインタを指定します。リードオンリー構成ではこの関数はサポートされません。
+
+
+ + +
+

戻り値

+
+
FR_OK
+
正常終了。
+
FR_NOFILE
+
ファイルが見つからない。
+
FR_NOPATH
+
パスが見つからない。
+
FR_INVALID_NAME
+
パス名が不正。
+
FR_DENIED
+
対象ファイル・ディレクトリがリードオンリー状態、対象ディレクトリが空でない場合など。
+
FR_NOT_READY
+
メディアがセットされていないなど、ディスクドライブが動作不能状態。
+
FR_WRITE_PROTECTED
+
メディアが書き込み禁止状態。
+
FR_RW_ERROR
+
ディスクアクセスでエラーが発生した。
+
FR_INCORRECT_DISK_CHANGE
+
不正なメディアの取り外しがあった。ファイルを開いたままのメディア交換など。
+
FR_NOT_ENABLED
+
FatFsモジュールが停止状態。
+
FR_NO_FILESYSTEM
+
ディスク上に有効なFATパーテーションが見つからない。
+
+
+ + +
+

解説

+

ファイルまたはディレクトリを削除します。

+
+ +

戻る

+ + diff --git a/doc/ja/write.html b/doc/ja/write.html new file mode 100644 index 0000000..d7339c6 --- /dev/null +++ b/doc/ja/write.html @@ -0,0 +1,72 @@ + + + + + + +FatFs - f_write + + + + +
+

f_write

+

ファイルにデータを書き込みます。

+
+FRESULT f_write (
+  FIL* FileObject,     // ファイルオブジェクト構造体
+  const BYTE* Buffer,  // 書き込みデータ
+  WORD ByteToWrite,    // 書き込むバイト数
+  WORD* ByteWritten    // 書き込まれたバイト数
+);
+
+
+ +
+

パラメータ

+
+
FileObject
+
ファイルオブジェクト構造体へのポインタを指定します。
+
Buffer
+
書き込むデータを格納したバッファを指すポインタを指定します。
+
ByteToWrite
+
書き込むバイト数を指定します。
+
ByteWritten
+
実際に書き込まれたバイト数を格納する変数を指すポインタを指定します。
+
+
+ + +
+

戻り値

+
+
FR_OK
+
正常終了。
+
FR_DENIED
+
読み込み専用モードで開いたファイルに書き込もうとした。
+
FR_ALIGN_ERROR
+
アンバッファモードで開かれたファイルで、512の整数倍でないアクセスを検出した。
+
FR_RW_ERROR
+
ディスクアクセスでエラーが発生した。
+
FR_NOT_READY
+
メディアがセットされていないなど、ディスクドライブが動作不能状態。
+
FR_NOT_ENABLED
+
FatFsモジュールが停止状態。
+
+
+ + +
+

解説

+

書き込み開始位置は、ファイルR/Wポインタの現在位置からになります。ファイルR/Wポインタは実際に書き込まれたバイト数だけ進みます。書き込み中にディスクが一杯になった、またはFR_ALIGN_ERRORが帰ったときは、*ByteWrittenByteToWriteよりも小さくなります。リードオンリー構成ではこの関数はサポートされません。

+
+ + +
+

参照

+

f_open, f_read, f_close, FIL

+
+ +

戻る

+ + diff --git a/doc/layers.png b/doc/layers.png new file mode 100644 index 0000000000000000000000000000000000000000..739fadff06bed7da155b1985766a1bc98a0db695 GIT binary patch literal 1837 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

>&+MCz+nP&VF9^t<`VuE%MI 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 literal 0 HcmV?d00001 diff --git a/doc/rw_cfc.jpeg b/doc/rw_cfc.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..c92d5fe882e6ff751a258ac4c1237fc31bcbd88c GIT binary patch literal 32242 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
A!|GbfO;bh8X!(ZB^a%r{mIAIP9|YQ2P5r zk&*1~mD&!$EmfEitzn@yvXaMD8*sNZQo z#Y)pz%4kl>zqwS!@9InS4|@ix$8Ks0FCZR`^AXrlmHRwL3d=#_#d9l12^1>z&Zd7T zL4I8Qc$Q=QlP@=m{lX#v`X`CMy-L&0xrHlbq3{0VcyB+!m?Nqx$pLxsP|P&&6T~b4 z)NT7U4)?M%MYVP;A8#!3BUI*Rk+hM6vP%OKsn^c9*~Ei?YXudhHo-L7_mR;j1a{Y{W3jfCtcr0NYDZWF-t; zXk_mu@G9QMSVtPHv1MTFRT=m8pZLvnw8dd0wB^;;>m)_!?A7G;tGm_Aj&DY z&YNqS7;+im2FuhljGPk|FfJSp@3R1lt+UouQ+2}?h|6~@Jll4i^Oz=yK;j9K?EcK` z%Av-g=3}QbRnwl9d6gYbAs(E@Hyz-DRH!Jn8^cnL{me1R_l=uwRpp6I#Web$E(mu4 z+P_#9zEkx-_s`S={m1GHkHZmJr`z>E)<6I!xnHOPfk%q{Ozge2m=V8d`rciG0xENK zn2+U0V+5ebu-pNFUD(Mi3F*o<-L<-yKpl-lkwS30e&JXF?mxLpwXb*qQT$Hgp0hKE_vU}K4MKOE$El9txXdZU?>SBSDs%4;1{p|3 zRIVo^Hx06}9miJ?uX$RPj*jELoW@kB87DmgFx(jSh7joQ4M&NEL;9StgSk`x*+nUi A0{{R3 literal 0 HcmV?d00001 diff --git a/doc/rw_mmc.jpeg b/doc/rw_mmc.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..5f8115e1d82dc70eda06d6831b344f3a6f6a5a7a GIT binary patch literal 29612 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 PM?`lB&PeVPf`9+nLqPee literal 0 HcmV?d00001 diff --git a/doc/rwtest.png b/doc/rwtest.png new file mode 100644 index 0000000000000000000000000000000000000000..d2e646f20d61bab39f741921a4b450b8ceeea4eb GIT binary patch literal 19261 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%0D(;M_ zFKlj=eqn6^iOde91?3ND>7QA2!H8*CZz}vl+~E?a3#g1@DgXUilLS)zmu@`*EsREY z5MI?h9lpe{+ZL^gqK&!aW4n#|l$B;}yw6zBjf%Mu2p6&DJy7}&kfnXKu)7<-+>N-& zsGDJ(?(oG1RsErBM488^-|yWx&F1I1!1n`L!7|69~K>l-PSd?P#`omZ*(p1 z^Ke}9a`KMr(n4*Wea!M4-VJ$7vAHB1kMqaV3Y z&4USe^^z2nZdQb$sDxFkJ~H)VxrbZwuO3=qehu zrb=sX4Xo_Ss;>X^M-)s= zH`DKI-Nn0hZ*l0JjG%}40kI`xccz{`9@TY{o{JkTUTfaw1Lp@lhZif)j@xDqiG7u3 zuVsI&yZ%zEw1b>g29=U<@vBd+3O~89sKOaYJjU56tlWDBzec9fQGFi29$hhVwJv)>Ev?OMoQTSgMvXKR#C?}-h}1I;*kQ*oJTEZJbUKO*`$Cw5OrXbAT3a12C6 zYo)85gk_Y=5Jj4A-ylWIkzV@RZu9vKT;fT7f zdswdhZ9ZL_Kj{yfZyEsQ8=MWZ`<|qmlo54kg6LmkTlDGMud|8xR2Lh%@zR7&chHU# zCDV8#zBFVOUxHVb3TL6D4!ylup|f2Q)Og?=U&msV%+~ZBMr5{+v3dj=-waF~P{FI0 z<9}O9s<(P+*X;V{WT-GhHtPeYv35=dVN9{Z5ioS)>NcTn*t+S~ZjLK4a8<-hS>K$e zmH767g@&$Qz2vmZ{fX|*xI%rU9dxQHJm*T=jSaV6I^ZMC_C1a9-ZM4!9abUfx$G*9 zEcy8Ub6vJbKjAoL8Gm2^Wc%9Ye(@pCY$^e1`mx3z{i%m8y)TfMhr51pz^%mXfjq|z ziU0Ah1af>46)wI!hkobA zFdg~v$xy*9ER{(yxWOe`dc%!AOaU3dMjs^+GM&w~f@; zj0!X^T1^I?f^p!95u1zeT9+f$AI$9bIjbq8+X~3*+B@xOT`^Oj=|k6c50|~HwkjOQ z)i)%}E7hC7c-eKuPg5q`)aKGmCd{S +#include "ff.h" /* FatFs declarations */ +#include "diskio.h" /* Include file for user provided functions */ + + +FATFS *FatFs; /* Pointer to the file system object */ + + +/*------------------------------------------------------------------------- + Module Private Functions +-------------------------------------------------------------------------*/ + +/*----------------------*/ +/* Change Window Offset */ + +static +BOOL move_window ( + DWORD sector /* Sector number to make apperance in the FatFs->win */ +) /* Move to zero only writes back dirty window */ +{ + DWORD wsect; + FATFS *fs = FatFs; + + + wsect = fs->winsect; + if (wsect != sector) { /* Changed current window */ +#ifndef _FS_READONLY + BYTE n; + if (fs->winflag) { /* Write back dirty window if needed */ + if (disk_write(fs->win, wsect, 1) != RES_OK) return FALSE; + fs->winflag = 0; + if (wsect < (fs->fatbase + fs->sects_fat)) { /* In FAT area */ + for (n = fs->n_fats; n >= 2; n--) { /* Refrect the change to all FAT copies */ + wsect += fs->sects_fat; + if (disk_write(fs->win, wsect, 1) != RES_OK) break; + } + } + } +#endif + if (sector) { + if (disk_read(fs->win, sector, 1) != RES_OK) return FALSE; + fs->winsect = sector; + } + } + return TRUE; +} + + + +/*---------------------*/ +/* Get Cluster State */ + +static +DWORD get_cluster ( + DWORD clust /* Cluster# to get the link information */ +) +{ + FATFS *fs = FatFs; + + + if ((clust >= 2) && (clust < fs->max_clust)) { /* Valid cluster# */ + switch (fs->fs_type) { + case FS_FAT16 : + if (!move_window(clust / 256 + fs->fatbase)) break; + return LD_WORD(&(fs->win[((WORD)clust * 2) & 511])); + case FS_FAT32 : + if (!move_window(clust / 128 + fs->fatbase)) break; + return LD_DWORD(&(fs->win[((WORD)clust * 4) & 511])); + } + } + return 1; /* Return with 1 means failed */ +} + + + +/*--------------------------*/ +/* Change a Cluster State */ + +#ifndef _FS_READONLY +static +BOOL put_cluster ( + DWORD clust, /* Cluster# to change */ + DWORD val /* New value to mark the cluster */ +) +{ + FATFS *fs = FatFs; + + + switch (fs->fs_type) { + case FS_FAT16 : + if (!move_window(clust / 256 + fs->fatbase)) return FALSE; + ST_WORD(&(fs->win[((WORD)clust * 2) & 511]), (WORD)val); + break; + case FS_FAT32 : + if (!move_window(clust / 128 + fs->fatbase)) return FALSE; + ST_DWORD(&(fs->win[((WORD)clust * 4) & 511]), val); + break; + default : + return FALSE; + } + fs->winflag = 1; + return TRUE; +} +#endif + + + +/*------------------------*/ +/* Remove a Cluster Chain */ + +#ifndef _FS_READONLY +static +BOOL remove_chain ( + DWORD clust /* Cluster# to remove chain from */ +) +{ + DWORD nxt; + + + while ((nxt = get_cluster(clust)) >= 2) { + if (!put_cluster(clust, 0)) return FALSE; + clust = nxt; + } + return TRUE; +} +#endif + + + +/*-----------------------------------*/ +/* Stretch or Create a Cluster Chain */ + +#ifndef _FS_READONLY +static +DWORD create_chain ( + DWORD clust /* Cluster# to stretch, 0 means create new */ +) +{ + DWORD ncl, ccl, mcl = FatFs->max_clust; + + + if (clust == 0) { /* Create new chain */ + ncl = 1; + do { + ncl++; /* Check next cluster */ + if (ncl >= mcl) return 0; /* No free custer was found */ + ccl = get_cluster(ncl); /* Get the cluster status */ + if (ccl == 1) return 0; /* Any error occured */ + } while (ccl); /* Repeat until find a free cluster */ + } + else { /* Stretch existing chain */ + ncl = get_cluster(clust); /* Check the cluster status */ + if (ncl < 2) return 0; /* It is an invalid cluster */ + if (ncl < mcl) return ncl; /* It is already followed by next cluster */ + ncl = clust; /* Search free cluster */ + do { + ncl++; /* Check next cluster */ + if (ncl >= mcl) ncl = 2; /* Wrap around */ + if (ncl == clust) return 0; /* No free custer was found */ + ccl = get_cluster(ncl); /* Get the cluster status */ + if (ccl == 1) return 0; /* Any error occured */ + } while (ccl); /* Repeat until find a free cluster */ + } + + if (!put_cluster(ncl, 0xFFFFFFFF)) return 0; /* Mark the new cluster "in use" */ + if (clust && !put_cluster(clust, ncl)) return 0; /* Link it to previous one if needed */ + + return ncl; /* Return new cluster number */ +} +#endif + + + +/*----------------------------*/ +/* Get Sector# from Cluster# */ + +static +DWORD clust2sect ( + DWORD clust /* Cluster# to be converted */ +) +{ + FATFS *fs = FatFs; + + + clust -= 2; + if (clust >= fs->max_clust) return 0; /* Invalid cluster# */ + return clust * fs->sects_clust + fs->database; +} + + + +/*------------------------*/ +/* Check File System Type */ + +static +BYTE check_fs ( + DWORD sect /* Sector# to check if it is a FAT boot record or not */ +) +{ + static const char fatsign[] = "FAT16FAT32"; + FATFS *fs = FatFs; + + + memset(fs->win, 0, 512); + if (disk_read(fs->win, sect, 1) == RES_OK) { /* Load boot record */ + if (LD_WORD(&(fs->win[510])) == 0xAA55) { /* Is it valid? */ + if (!memcmp(&(fs->win[0x36]), &fatsign[0], 5)) + return FS_FAT16; + if (!memcmp(&(fs->win[0x52]), &fatsign[5], 5) && (fs->win[0x28] == 0)) + return FS_FAT32; + } + } + return 0; +} + + + +/*--------------------------------*/ +/* Move Directory Pointer to Next */ + +static +BOOL next_dir_entry ( + DIR *scan /* Pointer to directory object */ +) +{ + DWORD clust; + WORD idx; + FATFS *fs = FatFs; + + + idx = scan->index + 1; + if ((idx & 15) == 0) { /* Table sector changed? */ + scan->sect++; /* Next sector */ + if (!scan->clust) { /* In static table */ + if (idx >= fs->n_rootdir) return FALSE; /* Reached to end of table */ + } else { /* In dynamic table */ + if (((idx / 16) & (fs->sects_clust - 1)) == 0) { /* Cluster changed? */ + clust = get_cluster(scan->clust); /* Get next cluster */ + if ((clust >= fs->max_clust) || (clust < 2)) /* Reached to end of table */ + return FALSE; + scan->clust = clust; /* Initialize for new cluster */ + scan->sect = clust2sect(clust); + } + } + } + scan->index = idx; /* Lower 4 bit of scan->index indicates offset in scan->sect */ + return TRUE; +} + + + +/*--------------------------------------*/ +/* Get File Status from Directory Entry */ + +static +void get_fileinfo ( + FILINFO *finfo, /* Ptr to Store the File Information */ + const BYTE *dir /* Ptr to the Directory Entry */ +) +{ + BYTE n, c, a; + char *p; + + + p = &(finfo->fname[0]); + a = *(dir+12); /* NT flag */ + for (n = 0; n < 8; n++) { /* Convert file name (body) */ + c = *(dir+n); + if (c == ' ') break; + if (c == 0x05) c = 0xE5; + if ((a & 0x08) && (c >= 'A') && (c <= 'Z')) c += 0x20; + *p++ = c; + } + if (*(dir+8) != ' ') { /* Convert file name (extension) */ + *p++ = '.'; + for (n = 8; n < 11; n++) { + c = *(dir+n); + if (c == ' ') break; + if ((a & 0x10) && (c >= 'A') && (c <= 'Z')) c += 0x20; + *p++ = c; + } + } + *p = '\0'; + + finfo->fattrib = *(dir+11); /* Attribute */ + finfo->fsize = LD_DWORD(dir+28); /* Size */ + finfo->fdate = LD_WORD(dir+24); /* Date */ + finfo->ftime = LD_WORD(dir+22); /* Time */ +} + + + +/*-------------------------------------------------------------------*/ +/* Pick a Paragraph and Create the Name in Format of Directory Entry */ + +static +char make_dirfile ( + const char **path, /* Pointer to the file path pointer */ + char *dirname /* Pointer to directory name buffer {Name(8), Ext(3), NT flag(1)} */ +) +{ + BYTE n, t, c, a, b; + + + memset(dirname, ' ', 8+3); /* Fill buffer with spaces */ + a = 0; b = 0x18; /* NT flag */ + n = 0; t = 8; + for (;;) { + c = *(*path)++; + if (c <= ' ') c = 0; + if ((c == 0) || (c == '/')) { /* Reached to end of str or directory separator */ + if (n == 0) break; + dirname[11] = a & b; return c; + } + if (c == '.') { + if(!(a & 1) && (n >= 1) && (n <= 8)) { /* Enter extension part */ + n = 8; t = 11; continue; + } + break; + } +#ifdef _USE_SJIS + if (((c >= 0x81) && (c <= 0x9F)) || /* Accept S-JIS code */ + ((c >= 0xE0) && (c <= 0xFC))) { + if ((n == 0) && (c == 0xE5)) /* Change heading \xE5 to \x05 */ + c = 0x05; + a ^= 1; goto md_l2; + } + if ((c >= 0x7F) && (c <= 0x80)) break; /* Reject \x7F \x80 */ +#else + if (c >= 0x7F) goto md_l1; /* Accept \x7F-0xFF */ +#endif + if (c == '"') break; /* Reject " */ + if (c <= ')') goto md_l1; /* Accept ! # $ % & ' ( ) */ + if (c <= ',') break; /* Reject * + , */ + if (c <= '9') goto md_l1; /* Accept - 0-9 */ + if (c <= '?') break; /* Reject : ; < = > ? */ + if (!(a & 1)) { /* These checks are not applied to S-JIS 2nd byte */ + if (c == '|') break; /* Reject | */ + if ((c >= '[') && (c <= ']')) break;/* Reject [ \ ] */ + if ((c >= 'A') && (c <= 'Z')) + (t == 8) ? (b &= ~0x08) : (b &= ~0x10); + if ((c >= 'a') && (c <= 'z')) { /* Convert to upper case */ + c -= 0x20; + (t == 8) ? (a |= 0x08) : (a |= 0x10); + } + } + md_l1: + a &= ~1; + md_l2: + if (n >= t) break; + dirname[n++] = c; + } + return 1; +} + + + +/*-------------------*/ +/* Trace a File Path */ + +static +FRESULT trace_path ( + DIR *scan, /* Pointer to directory object to return last directory */ + char *fn, /* Pointer to last segment name to return */ + const char *path, /* Full-path string to trace a file or directory */ + BYTE **dir /* Directory pointer in Win[] to retutn */ +) +{ + DWORD clust; + char ds; + BYTE *dptr = NULL; + FATFS *fs = FatFs; + + /* Initialize directory object */ + clust = fs->dirbase; + if (fs->fs_type == FS_FAT32) { + scan->clust = scan->sclust = clust; + scan->sect = clust2sect(clust); + } else { + scan->clust = scan->sclust = 0; + scan->sect = clust; + } + scan->index = 0; + + while ((*path == ' ') || (*path == '/')) path++; /* Skip leading spaces */ + if ((BYTE)*path < ' ') { /* Null path means the root directory */ + *dir = NULL; return FR_OK; + } + + for (;;) { + ds = make_dirfile(&path, fn); /* Get a paragraph into fn[] */ + if (ds == 1) return FR_INVALID_NAME; + for (;;) { + if (!move_window(scan->sect)) return FR_RW_ERROR; + dptr = &(fs->win[(scan->index & 15) * 32]); /* Pointer to the directory entry */ + if (*dptr == 0) /* Has it reached to end of dir? */ + return !ds ? FR_NO_FILE : FR_NO_PATH; + if ( (*dptr != 0xE5) /* Matched? */ + && !(*(dptr+11) & AM_VOL) + && !memcmp(dptr, fn, 8+3) ) break; + if (!next_dir_entry(scan)) /* Next directory pointer */ + return !ds ? FR_NO_FILE : FR_NO_PATH; + } + if (!ds) { *dir = dptr; return FR_OK; } /* Matched with end of path */ + if (!(*(dptr+11) & AM_DIR)) return FR_NO_PATH; /* Cannot trace because it is a file */ + clust = ((DWORD)LD_WORD(dptr+20) << 16) | LD_WORD(dptr+26); /* Get cluster# of the directory */ + scan->clust = scan->sclust = clust; /* Restart scan with the new directory */ + scan->sect = clust2sect(clust); + scan->index = 0; + } +} + + + +/*---------------------------*/ +/* Reserve a Directory Entry */ + +#ifndef _FS_READONLY +static +BYTE* reserve_direntry ( + DIR *scan /* Target directory to create new entry */ +) +{ + DWORD clust, sector; + BYTE c, n, *dptr; + FATFS *fs = FatFs; + + + /* Re-initialize directory object */ + clust = scan->sclust; + if (clust) { /* Dyanmic directory table */ + scan->clust = clust; + scan->sect = clust2sect(clust); + } else { /* Static directory table */ + scan->sect = fs->dirbase; + } + scan->index = 0; + + do { + if (!move_window(scan->sect)) return NULL; + dptr = &(fs->win[(scan->index & 15) * 32]); /* Pointer to the directory entry */ + c = *dptr; + if ((c == 0) || (c == 0xE5)) return dptr; /* Found an empty entry! */ + } while (next_dir_entry(scan)); /* Next directory pointer */ + /* Reached to end of the directory table */ + + /* Abort when static table or could not stretch dynamic table */ + if ((!clust) || !(clust = create_chain(scan->clust))) return NULL; + if (!move_window(0)) return 0; + + fs->winsect = sector = clust2sect(clust); /* Cleanup the expanded table */ + memset(fs->win, 0, 512); + for (n = fs->sects_clust; n; n--) { + if (disk_write(fs->win, sector, 1) != RES_OK) return NULL; + sector++; + } + fs->winflag = 1; + return fs->win; +} +#endif + + + +/*-----------------------------------------*/ +/* Make Sure that the File System is Valid */ + +static +FRESULT check_mounted () +{ + FATFS *fs = FatFs; + + + if (!fs) return FR_NOT_ENABLED; /* Has the FatFs been enabled? */ + + if (disk_status() & STA_NOINIT) { /* The drive has not been initialized */ + if (fs->files) /* Drive was uninitialized with any file left opend */ + return FR_INCORRECT_DISK_CHANGE; + else + return f_mountdrv();; /* Initialize file system and return resulut */ + } else { /* The drive has been initialized */ + if (!fs->fs_type) /* But the file system has not been initialized */ + return f_mountdrv(); /* Initialize file system and return resulut */ + } + return FR_OK; /* File system is valid */ +} + + + + + +/*--------------------------------------------------------------------------*/ +/* Public Funciotns */ +/*--------------------------------------------------------------------------*/ + + +/*----------------------------------------------------------*/ +/* Load File System Information and Initialize FatFs Module */ + +FRESULT f_mountdrv () +{ + BYTE fat; + DWORD sect, fatend; + FATFS *fs = FatFs; + + + if (!fs) return FR_NOT_ENABLED; + + /* Initialize file system object */ + memset(fs, 0, sizeof(FATFS)); + + /* Initialize disk drive */ + if (disk_initialize() & STA_NOINIT) return FR_NOT_READY; + + /* Search FAT partition */ + fat = check_fs(sect = 0); /* Check sector 0 as an SFD format */ + if (!fat) { /* Not a FAT boot record, it will be an FDISK format */ + /* Check a pri-partition listed in top of the partition table */ + if (fs->win[0x1C2]) { /* Is the partition existing? */ + sect = LD_DWORD(&(fs->win[0x1C6])); /* Partition offset in LBA */ + fat = check_fs(sect); /* Check the partition */ + } + } + if (!fat) return FR_NO_FILESYSTEM; /* No FAT patition */ + + /* Initialize file system object */ + fs->fs_type = fat; /* FAT type */ + fs->sects_fat = /* Sectors per FAT */ + (fat == FS_FAT32) ? LD_DWORD(&(fs->win[0x24])) : LD_WORD(&(fs->win[0x16])); + fs->sects_clust = fs->win[0x0D]; /* Sectors per cluster */ + fs->n_fats = fs->win[0x10]; /* Number of FAT copies */ + fs->fatbase = sect + LD_WORD(&(fs->win[0x0E])); /* FAT start sector (physical) */ + fs->n_rootdir = LD_WORD(&(fs->win[0x11])); /* Nmuber of root directory entries */ + + fatend = fs->sects_fat * fs->n_fats + fs->fatbase; + if (fat == FS_FAT32) { + fs->dirbase = LD_DWORD(&(fs->win[0x2C])); /* Directory start cluster */ + fs->database = fatend; /* Data start sector (physical) */ + } else { + fs->dirbase = fatend; /* Directory start sector (physical) */ + fs->database = fs->n_rootdir / 16 + fatend; /* Data start sector (physical) */ + } + fs->max_clust = /* Maximum cluster number */ + (LD_DWORD(&(fs->win[0x20])) - fs->database + sect) / fs->sects_clust + 2; + + return FR_OK; +} + + + +/*-----------------------------*/ +/* Get Number of Free Clusters */ + +FRESULT f_getfree ( + DWORD *nclust /* Pointer to the double word to return number of free clusters */ +) +{ + DWORD n, clust, sect; + BYTE m, *ptr, fat; + FRESULT res; + FATFS *fs = FatFs; + + + if ((res = check_mounted()) != FR_OK) return res; + fat = fs->fs_type; + + /* Count number of free clusters */ + n = m = clust = 0; + ptr = NULL; + sect = fs->fatbase; + do { + if (m == 0) { + if (!move_window(sect++)) return FR_RW_ERROR; + ptr = fs->win; + } + if (fat == FS_FAT32) { + if (LD_DWORD(ptr) == 0) n++; + ptr += 4; m += 2; + } else { + if (LD_WORD(ptr) == 0) n++; + ptr += 2; m++; + } + clust++; + } while (clust < fs->max_clust); + + *nclust = n; + return FR_OK; +} + + + +/*-----------------------*/ +/* Open or Create a File */ + +FRESULT f_open ( + FIL *fp, /* Pointer to the buffer of new file object to create */ + const char *path, /* Pointer to the file path */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + BYTE *dir; + DIR dirscan; + char fn[8+3+1]; + FATFS *fs = FatFs; + + + if ((res = check_mounted()) != FR_OK) return res; +#ifndef _FS_READONLY + if ((mode & (FA_WRITE|FA_CREATE_ALWAYS)) && (disk_status() & STA_PROTECT)) + return FR_WRITE_PROTECTED; +#endif + + res = trace_path(&dirscan, fn, path, &dir); /* Trace the file path */ + +#ifndef _FS_READONLY + /* Create or Open a File */ + if (mode & (FA_CREATE_ALWAYS|FA_OPEN_ALWAYS)) { + DWORD dw; + if (res != FR_OK) { /* No file, create new */ + mode |= FA_CREATE_ALWAYS; + if (res != FR_NO_FILE) return res; + dir = reserve_direntry(&dirscan); /* Reserve a directory entry */ + if (dir == NULL) return FR_DENIED; + memcpy(dir, fn, 8+3); /* Initialize the new entry */ + *(dir+12) = fn[11]; + memset(dir+13, 0, 32-13); + } else { /* File already exists */ + if ((dir == NULL) || (*(dir+11) & (AM_RDO|AM_DIR))) /* Could not overwrite (R/O or DIR) */ + return FR_DENIED; + if (mode & FA_CREATE_ALWAYS) { /* Resize it to zero */ + dw = fs->winsect; /* Remove the cluster chain */ + if (!remove_chain(((DWORD)LD_WORD(dir+20) << 16) | LD_WORD(dir+26)) + || !move_window(dw) ) + return FR_RW_ERROR; + ST_WORD(dir+20, 0); ST_WORD(dir+26, 0); /* cluster = 0 */ + ST_DWORD(dir+28, 0); /* size = 0 */ + } + } + if (mode & FA_CREATE_ALWAYS) { + *(dir+11) = AM_ARC; + dw = get_fattime(); + ST_DWORD(dir+14, dw); /* Created time */ + ST_DWORD(dir+22, dw); /* Updated time */ + fs->winflag = 1; + } + } + /* Open a File */ + else { +#endif + if (res != FR_OK) return res; /* Trace failed */ + if ((dir == NULL) || (*(dir+11) & AM_DIR)) /* It is a directory */ + return FR_NO_FILE; +#ifndef _FS_READONLY + if ((mode & FA_WRITE) && (*(dir+11) & AM_RDO)) /* R/O violation */ + return FR_DENIED; + } +#endif + +#ifdef _FS_READONLY + fp->flag = mode & (FA_UNBUFFERED|FA_READ); +#else + fp->flag = mode & (FA_UNBUFFERED|FA_WRITE|FA_READ); + fp->dir_sect = fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dir; +#endif + fp->org_clust = ((DWORD)LD_WORD(dir+20) << 16) | LD_WORD(dir+26); /* File start cluster */ + fp->fsize = LD_DWORD(dir+28); /* File size */ + fp->fptr = 0; /* File ptr */ + fp->sect_clust = 1; /* Sector counter */ + fs->files++; + return FR_OK; +} + + + +/*-----------*/ +/* Read File */ + +FRESULT f_read ( + FIL *fp, /* Pointer to the file object */ + BYTE *buff, /* Pointer to data buffer */ + WORD btr, /* Number of bytes to read */ + WORD *br /* Pointer to number of bytes read */ +) +{ + DWORD clust, sect, ln; + WORD rcnt; + BYTE cc; + FATFS *fs = FatFs; + + + *br = 0; + if (!fs) return FR_NOT_ENABLED; + if ((disk_status() & STA_NOINIT) || !fs->fs_type) return FR_NOT_READY; /* Check disk ready */ + if (fp->flag & FA__ERROR) return FR_RW_ERROR; /* Check error flag */ + if (!(fp->flag & FA_READ)) return FR_DENIED; /* Check access mode */ + ln = fp->fsize - fp->fptr; + if (btr > ln) btr = ln; /* Truncate read count by number of bytes left */ + + for ( ; btr; /* Repeat until all data transferred */ + buff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { + if ((fp->fptr & 511) == 0) { /* On the sector boundary */ + if (--(fp->sect_clust)) { /* Decrement sector counter */ + sect = fp->curr_sect + 1; /* Next sector */ + } else { /* Next cluster */ + clust = (fp->fptr == 0) ? fp->org_clust : get_cluster(fp->curr_clust); + if ((clust < 2) || (clust >= fs->max_clust)) goto fr_error; + fp->curr_clust = clust; /* Current cluster */ + sect = clust2sect(clust); /* Current sector */ + fp->sect_clust = fs->sects_clust; /* Re-initialize the sector counter */ + } +#ifndef _FS_READONLY + if (fp->flag & FA__DIRTY) { /* Flush file I/O buffer if needed */ + if (disk_write(fp->buffer, fp->curr_sect, 1) != RES_OK) goto fr_error; + fp->flag &= ~FA__DIRTY; + } +#endif + fp->curr_sect = sect; /* Update current sector */ + cc = btr / 512; /* When left bytes >= 512 */ + if (cc) { /* Read maximum contiguous sectors */ + if (cc > fp->sect_clust) cc = fp->sect_clust; + if (disk_read(buff, sect, cc) != RES_OK) goto fr_error; + fp->sect_clust -= cc - 1; + fp->curr_sect += cc - 1; + rcnt = cc * 512; continue; + } + if (fp->flag & FA_UNBUFFERED) /* Reject unaligned access when unbuffered mode */ + return FR_ALIGN_ERROR; + if (disk_read(fp->buffer, sect, 1) != RES_OK) /* Load the sector into file I/O buffer */ + goto fr_error; + } + rcnt = 512 - (fp->fptr & 511); /* Copy fractional bytes from file I/O buffer */ + if (rcnt > btr) rcnt = btr; + memcpy(buff, &fp->buffer[fp->fptr & 511], rcnt); + } + + return FR_OK; + +fr_error: /* Abort this file due to an unrecoverable error */ + fp->flag |= FA__ERROR; + return FR_RW_ERROR; +} + + + +/*------------*/ +/* Write File */ + +#ifndef _FS_READONLY +FRESULT f_write ( + FIL *fp, /* Pointer to the file object */ + const BYTE *buff, /* Pointer to the data to be written */ + WORD btw, /* Number of bytes to write */ + WORD *bw /* Pointer to number of bytes written */ +) +{ + DWORD clust, sect; + WORD wcnt; + BYTE cc; + FATFS *fs = FatFs; + + + *bw = 0; + if (!fs) return FR_NOT_ENABLED; + if ((disk_status() & STA_NOINIT) || !fs->fs_type) return FR_NOT_READY; + if (fp->flag & FA__ERROR) return FR_RW_ERROR; /* Check error flag */ + if (!(fp->flag & FA_WRITE)) return FR_DENIED; /* Check access mode */ + if (fp->fsize + btw < fp->fsize) btw = 0; /* File size cannot reach 4GB */ + + for ( ; btw; /* Repeat until all data transferred */ + buff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) { + if ((fp->fptr & 511) == 0) { /* On the sector boundary */ + if (--(fp->sect_clust)) { /* Decrement sector counter */ + sect = fp->curr_sect + 1; /* Next sector */ + } else { /* Next cluster */ + if (fp->fptr == 0) { /* Top of the file */ + clust = fp->org_clust; + if (clust == 0) /* No cluster is created */ + fp->org_clust = clust = create_chain(0); /* Create a new cluster chain */ + } else { /* Middle or end of file */ + clust = create_chain(fp->curr_clust); /* Trace or streach cluster chain */ + } + if ((clust < 2) || (clust >= fs->max_clust)) break; + fp->curr_clust = clust; /* Current cluster */ + sect = clust2sect(clust); /* Current sector */ + fp->sect_clust = fs->sects_clust; /* Re-initialize the sector counter */ + } + if (fp->flag & FA__DIRTY) { /* Flush file I/O buffer if needed */ + if (disk_write(fp->buffer, fp->curr_sect, 1) != RES_OK) goto fw_error; + fp->flag &= ~FA__DIRTY; + } + fp->curr_sect = sect; /* Update current sector */ + cc = btw / 512; /* When left bytes >= 512 */ + if (cc) { /* Write maximum contiguous sectors */ + if (cc > fp->sect_clust) cc = fp->sect_clust; + if (disk_write(buff, sect, cc) != RES_OK) goto fw_error; + fp->sect_clust -= cc - 1; + fp->curr_sect += cc - 1; + wcnt = cc * 512; continue; + } + if (fp->flag & FA_UNBUFFERED) /* Reject unalighend access when unbuffered mode */ + return FR_ALIGN_ERROR; + if ((fp->fptr < fp->fsize) && /* Fill sector buffer with file data if needed */ + (disk_read(fp->buffer, sect, 1) != RES_OK)) + goto fw_error; + } + wcnt = 512 - (fp->fptr & 511); /* Copy fractional bytes to file I/O buffer */ + if (wcnt > btw) wcnt = btw; + memcpy(&fp->buffer[fp->fptr & 511], buff, wcnt); + fp->flag |= FA__DIRTY; + } + + if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */ + fp->flag |= FA__WRITTEN; /* Set file changed flag */ + return FR_OK; + +fw_error: /* Abort this file due to an unrecoverable error */ + fp->flag |= FA__ERROR; + return FR_RW_ERROR; +} +#endif + + + +/*-------------------*/ +/* Seek File Pointer */ + +FRESULT f_lseek ( + FIL *fp, /* Pointer to the file object */ + DWORD ofs /* File pointer from top of file */ +) +{ + DWORD clust; + BYTE sc; + FATFS *fs = FatFs; + + + if (!fs) return FR_NOT_ENABLED; + if ((disk_status() & STA_NOINIT) || !fs->fs_type) return FR_NOT_READY; + if (fp->flag & FA__ERROR) return FR_RW_ERROR; +#ifndef _FS_READONLY + if (fp->flag & FA__DIRTY) { /* Write-back dirty buffer if needed */ + if (disk_write(fp->buffer, fp->curr_sect, 1) != RES_OK) goto fk_error; + fp->flag &= ~FA__DIRTY; + } +#endif + if (ofs > fp->fsize) ofs = fp->fsize; /* Clip offset by file size */ + if ((ofs & 511) && (fp->flag & FA_UNBUFFERED)) return FR_ALIGN_ERROR; + fp->fptr = ofs; fp->sect_clust = 1; /* Re-initialize file pointer */ + + /* Seek file pinter if needed */ + if (ofs) { + ofs = (ofs - 1) / 512; /* Calcurate current sector */ + sc = fs->sects_clust; /* Number of sectors in a cluster */ + fp->sect_clust = sc - (ofs % sc); /* Calcurate sector counter */ + ofs /= sc; /* Number of clusters to skip */ + clust = fp->org_clust; /* Seek to current cluster */ + while (ofs--) + clust = get_cluster(clust); + if ((clust < 2) || (clust >= fs->max_clust)) goto fk_error; + fp->curr_clust = clust; + fp->curr_sect = clust2sect(clust) + sc - fp->sect_clust; /* Current sector */ + if (fp->fptr & 511) { /* Load currnet sector if needed */ + if (disk_read(fp->buffer, fp->curr_sect, 1) != RES_OK) + goto fk_error; + } + } + + return FR_OK; + +fk_error: /* Abort this file due to an unrecoverable error */ + fp->flag |= FA__ERROR; + return FR_RW_ERROR; +} + + + +/*-------------------------------------------------*/ +/* Synchronize between File and Disk without Close */ + +#ifndef _FS_READONLY +FRESULT f_sync ( + FIL *fp /* Pointer to the file object */ +) +{ + BYTE *ptr; + FATFS *fs = FatFs; + + + if (!fs) return FR_NOT_ENABLED; + if ((disk_status() & STA_NOINIT) || !fs->fs_type) + return FR_INCORRECT_DISK_CHANGE; + + /* Has the file been written? */ + if (fp->flag & FA__WRITTEN) { + /* Write back data buffer if needed */ + if (fp->flag & FA__DIRTY) { + if (disk_write(fp->buffer, fp->curr_sect, 1) != RES_OK) return FR_RW_ERROR; + fp->flag &= ~FA__DIRTY; + } + /* Update the directory entry */ + if (!move_window(fp->dir_sect)) return FR_RW_ERROR; + ptr = fp->dir_ptr; + *(ptr+11) |= AM_ARC; /* Set archive bit */ + ST_DWORD(ptr+28, fp->fsize); /* Update file size */ + ST_WORD(ptr+26, fp->org_clust); /* Update start cluster */ + ST_WORD(ptr+20, fp->org_clust >> 16); + ST_DWORD(ptr+22, get_fattime()); /* Updated time */ + fs->winflag = 1; + fp->flag &= ~FA__WRITTEN; + } + if (!move_window(0)) return FR_RW_ERROR; + + return FR_OK; +} +#endif + + + +/*------------*/ +/* Close File */ + +FRESULT f_close ( + FIL *fp /* Pointer to the file object to be closed */ +) +{ + FRESULT res; + + +#ifndef _FS_READONLY + res = f_sync(fp); +#else + res = FR_OK; +#endif + if (res == FR_OK) { + fp->flag = 0; + FatFs->files--; + } + return res; +} + + + +/*------------------------------*/ +/* Delete a File or a Directory */ + +#ifndef _FS_READONLY +FRESULT f_unlink ( + const char *path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + BYTE *dir, *sdir; + DWORD dclust, dsect; + DIR dirscan; + char fn[8+3+1]; + FATFS *fs = FatFs; + + + if ((res = check_mounted()) != FR_OK) return res; + if (disk_status() & STA_PROTECT) return FR_WRITE_PROTECTED; + + res = trace_path(&dirscan, fn, path, &dir); /* Trace the file path */ + + if (res != FR_OK) return res; /* Trace failed */ + if (dir == NULL) return FR_NO_FILE; /* It is a root directory */ + if (*(dir+11) & AM_RDO) return FR_DENIED; /* It is a R/O item */ + dsect = fs->winsect; + dclust = ((DWORD)LD_WORD(dir+20) << 16) | LD_WORD(dir+26); + + if (*(dir+11) & AM_DIR) { /* It is a sub-directory */ + dirscan.clust = dclust; /* Check if the sub-dir is empty or not */ + dirscan.sect = clust2sect(dclust); + dirscan.index = 0; + do { + if (!move_window(dirscan.sect)) return FR_RW_ERROR; + sdir = &(fs->win[(dirscan.index & 15) * 32]); + if (*sdir == 0) break; + if (!((*sdir == 0xE5) || (*sdir == '.')) && !(*(sdir+11) & AM_VOL)) + return FR_DENIED; /* The directory is not empty */ + } while (next_dir_entry(&dirscan)); + } + + if (!remove_chain(dclust)) return FR_RW_ERROR; /* Remove the cluster chain */ + if (!move_window(dsect)) return FR_RW_ERROR; /* Mark the directory entry deleted */ + *dir = 0xE5; fs->winflag = 1; + if (!move_window(0)) return FR_RW_ERROR; + + return FR_OK; +} +#endif + + + +/*--------------------*/ +/* Create a Directory */ + +#ifndef _FS_READONLY +FRESULT f_mkdir ( + const char *path /* Pointer to the directory path */ +) +{ + FRESULT res; + BYTE *dir, *w, n; + DWORD sect, dsect, dclust, tim; + DIR dirscan; + char fn[8+3+1]; + FATFS *fs = FatFs; + + + if ((res = check_mounted()) != FR_OK) return res; + if (disk_status() & STA_PROTECT) return FR_WRITE_PROTECTED; + + res = trace_path(&dirscan, fn, path, &dir); /* Trace the file path */ + + if (res == FR_OK) return FR_DENIED; /* Any file or directory is already existing */ + if (res != FR_NO_FILE) return res; + + dir = reserve_direntry(&dirscan); /* Reserve a directory entry */ + if (dir == NULL) return FR_DENIED; + sect = fs->winsect; + dsect = clust2sect(dclust = create_chain(0)); /* Get a new cluster for new directory */ + if (!dsect) return FR_DENIED; + if (!move_window(0)) return 0; + + w = fs->win; + memset(w, 0, 512); /* Initialize the directory table */ + for (n = fs->sects_clust - 1; n; n--) { + if (disk_write(w, dsect+n, 1) != RES_OK) return FR_RW_ERROR; + } + + fs->winsect = dsect; /* Create dot directories */ + memset(w, ' ', 8+3); + *w = '.'; + *(w+11) = AM_DIR; + tim = get_fattime(); + ST_DWORD(w+22, tim); + memcpy(w+32, w, 32); *(w+33) = '.'; + ST_WORD(w+26, dclust); + ST_WORD(w+20, dclust >> 16); + ST_WORD(w+32+26, dirscan.sclust); + ST_WORD(w+32+20, dirscan.sclust >> 16); + fs->winflag = 1; + + if (!move_window(sect)) return FR_RW_ERROR; + memcpy(dir, fn, 8+3); /* Initialize the new entry */ + *(dir+11) = AM_DIR; + *(dir+12) = fn[11]; + memset(dir+13, 0, 32-13); + ST_DWORD(dir+22, tim); /* Crated time */ + ST_WORD(dir+26, dclust); /* Table start cluster */ + ST_WORD(dir+20, dclust >> 16); + fs->winflag = 1; + + if (!move_window(0)) return FR_RW_ERROR; + + return FR_OK; +} +#endif + + + +/*---------------------------*/ +/* Initialize directroy scan */ + +FRESULT f_opendir ( + DIR *scan, /* Pointer to directory object to initialize */ + const char *path /* Pointer to the directory path, null str means the root */ +) +{ + FRESULT res; + BYTE *dir; + char fn[8+3+1]; + + + if ((res = check_mounted()) != FR_OK) return res; + + res = trace_path(scan, fn, path, &dir); /* Trace the directory path */ + + if (res == FR_OK) { /* Trace completed */ + if (dir != NULL) { /* It is not a root dir */ + if (*(dir+11) & AM_DIR) { /* The entry is a directory */ + scan->clust = ((DWORD)LD_WORD(dir+20) << 16) | LD_WORD(dir+26); + scan->sect = clust2sect(scan->clust); + scan->index = 0; + } else { /* The entry is a file */ + res = FR_NO_PATH; + } + } + } + return res; +} + + + +/*----------------------------------*/ +/* Read Directory Entry in Sequense */ + +FRESULT f_readdir ( + DIR *scan, /* Pointer to the directory object */ + FILINFO *finfo /* Pointer to file information to return */ +) +{ + BYTE *dir, c; + FATFS *fs = FatFs; + + + if (!fs) return FR_NOT_ENABLED; + finfo->fname[0] = 0; + if ((disk_status() & STA_NOINIT) || !fs->fs_type) return FR_NOT_READY; + + while (scan->sect) { + if (!move_window(scan->sect)) return FR_RW_ERROR; + dir = &(fs->win[(scan->index & 15) * 32]); /* pointer to the directory entry */ + c = *dir; + if (c == 0) break; /* Has it reached to end of dir? */ + if ((c != 0xE5) && (c != '.') && !(*(dir+11) & AM_VOL)) /* Is it a valid entry? */ + get_fileinfo(finfo, dir); + if (!next_dir_entry(scan)) scan->sect = 0; /* Next entry */ + if (finfo->fname[0]) break; /* Found valid entry */ + } + + return FR_OK; +} + + + +/*-----------------*/ +/* Get File Status */ + +FRESULT f_stat ( + const char *path, /* Pointer to the file path */ + FILINFO *finfo /* Pointer to file information to return */ +) +{ + FRESULT res; + BYTE *dir; + DIR dirscan; + char fn[8+3+1]; + + + if ((res = check_mounted()) != FR_OK) return res; + + res = trace_path(&dirscan, fn, path, &dir); /* Trace the file path */ + + if (res == FR_OK) /* Trace completed */ + get_fileinfo(finfo, dir); + + return res; +} + + + +/*-----------------------*/ +/* Change File Attribute */ + +#ifndef _FS_READONLY +FRESULT f_chmod ( + const char *path, /* Pointer to the file path */ + BYTE value, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + BYTE *dir; + DIR dirscan; + char fn[8+3+1]; + FATFS *fs = FatFs; + + + if ((res = check_mounted()) != FR_OK) return res; + if (disk_status() & STA_PROTECT) return FR_WRITE_PROTECTED; + + res = trace_path(&dirscan, fn, path, &dir); /* Trace the file path */ + + if (res == FR_OK) { /* Trace completed */ + if (dir == NULL) { + res = FR_NO_FILE; + } else { + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ + *(dir+11) = (value & mask) | (*(dir+11) & ~mask); /* Apply attribute change */ + fs->winflag = 1; + if(!move_window(0)) res = FR_RW_ERROR; + } + } + return res; +} +#endif + + diff --git a/src/ff.h b/src/ff.h new file mode 100644 index 0000000..663377d --- /dev/null +++ b/src/ff.h @@ -0,0 +1,186 @@ +/*--------------------------------------------------------------------------/ +/ FatFs - FAT file system module include file R0.01 (C)ChaN, 2006 +/---------------------------------------------------------------------------/ +/ FatFs module is an experimenal project to implement FAT file system to +/ cheap microcontrollers. This is opened for education, reserch and +/ development. You can use it for non-profit or profit use without any +/ limitation under your responsibility. +/---------------------------------------------------------------------------/ +/ Feb 26, 2006 R0.00 Prototype +/ Apr 29, 2006 R0.01 First stable version +/---------------------------------------------------------------------------*/ + +#ifndef _FATFS + +//#define _BYTE_ACC +/* This enables byte-by-byte access for multi-byte variables */ +/* It must be defined on the big-endian processor, or to prevent address error */ + +//#define _FS_READONLY +/* This removes writing code for read-only applications */ + +#define _USE_SJIS +/* This enables Shift-JIS code transparency, or only US-ASCII file name can be accepted */ + + +#include "integer.h" + + +/* Result type for fatfs application interface */ +typedef unsigned char FRESULT; + + +/* File system object structure */ +typedef struct _FATFS { + BYTE fs_type; // FAT type + BYTE files; // Number of files currently opend + BYTE sects_clust; // Sectors per cluster + BYTE n_fats; // Number of FAT copies + WORD n_rootdir; // Number of root directory entry + BYTE winflag; // win[] dirty flag (1:must be written back) + BYTE pad1; + DWORD sects_fat; // Sectors per fat + DWORD max_clust; // Maximum cluster# + 1 + DWORD fatbase; // FAT start sector + DWORD dirbase; // Root directory start sector (cluster# for FAT32) + DWORD database; // Data start sector + DWORD winsect; // Current sector appearing in the win[] + BYTE win[512]; // Disk access window for Directory/FAT area +} FATFS; + + +/* Directory object structure */ +typedef struct _DIR { + DWORD sclust; // Start cluster + DWORD clust; // Current cluster + DWORD sect; // Current sector + WORD index; // Current index +} DIR; + + +/* File object structure */ +typedef struct _FIL { + DWORD fptr; // File R/W pointer + DWORD fsize; // File size + DWORD org_clust; // File start cluster + DWORD curr_clust; // Current cluster + DWORD curr_sect; // Current sector +#ifndef _FS_READONLY + DWORD dir_sect; // Sector containing the directory entry + BYTE* dir_ptr; // Ponter to the directory entry in the window +#endif + BYTE* buffer; // Pointer to 512 byte file R/W buffer + BYTE flag; // File status flags + BYTE sect_clust; // Left sectors in cluster +} FIL; + + +/* File status structure */ +typedef struct _FILINFO { + DWORD fsize; // Size + WORD fdate; // Date + WORD ftime; // Time + BYTE fattrib; // Attribute + char fname[8+1+3+1]; // Name (8.3 format) +} FILINFO; + + + +/*-----------------------------------------------------*/ +/* FatFs module application interface */ + +extern FATFS *FatFs; // Pointer to active file system object + +FRESULT f_open (FIL*, const char*, BYTE); // Open or create a file +FRESULT f_read (FIL*, BYTE*, WORD, WORD*); // Read file +FRESULT f_close (FIL*); // Close file +FRESULT f_lseek (FIL*, DWORD); // Seek file pointer +FRESULT f_opendir (DIR*, const char*); // Initialize to read a directory +FRESULT f_readdir (DIR*, FILINFO*); // Read a directory item +FRESULT f_stat (const char*, FILINFO*); // Get file status +FRESULT f_getfree (DWORD*); // Get number of free clusters +FRESULT f_mountdrv (); // Force initialized the file system +#ifndef _FS_READONLY +FRESULT f_write (FIL*, const BYTE*, WORD, WORD*); // Write file +FRESULT f_sync (FIL*); // Flush cached information of a writing file +FRESULT f_unlink (const char*); // Delete a file or directory +FRESULT f_mkdir (const char*); // Create a directory +FRESULT f_chmod (const char*, BYTE, BYTE); // Change file attriburte +#endif + + +/* User defined function to give a current time to fatfs module */ + +#ifndef _FS_READONLY +DWORD get_fattime(); // 31-25: Year(0-127 +1980), 24-21: Month(1-12), 20-16: Day(1-31) + // 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) +#endif + + + +/* File function return code */ + +#define FR_OK 0 +#define FR_NOT_READY 1 +#define FR_NO_FILE 2 +#define FR_NO_PATH 3 +#define FR_INVALID_NAME 4 +#define FR_DENIED 5 +#define FR_DISK_FULL 6 +#define FR_RW_ERROR 7 +#define FR_ALIGN_ERROR 8 +#define FR_INCORRECT_DISK_CHANGE 9 +#define FR_WRITE_PROTECTED 10 +#define FR_NOT_ENABLED 11 +#define FR_NO_FILESYSTEM 12 + + +/* File access control and file status flags */ + +#define FA_READ 0x01 +#define FA_UNBUFFERED 0x04 +#define FA_OPEN_EXISTING 0x00 +#ifndef _FS_READONLY +#define FA_WRITE 0x02 +#define FA_CREATE_ALWAYS 0x08 +#define FA_OPEN_ALWAYS 0x10 +#define FA__WRITTEN 0x20 +#define FA__DIRTY 0x40 +#endif +#define FA__ERROR 0x80 + + +/* FAT type signature (fs_type) */ + +#define FS_FAT16 1 +#define FS_FAT32 2 + + +/* File attribute mask for directory entry */ + +#define AM_RDO 0x01 // Read Only +#define AM_HID 0x02 // Hidden +#define AM_SYS 0x04 // System +#define AM_VOL 0x08 // Volume Label +#define AM_DIR 0x10 // Directory +#define AM_ARC 0x20 // Archive + + + +/* Multi-byte word access macros */ + +#ifdef _BYTE_ACC +#define LD_WORD(ptr) (((WORD)*(BYTE*)(ptr+1)<<8)|*(ptr)) +#define LD_DWORD(ptr) (((DWORD)*(BYTE*)(ptr+3)<<24)|((DWORD)*(BYTE*)(ptr+2)<<16)|((WORD)*(BYTE*)(ptr+1)<<8)|*(BYTE*)(ptr)) +#define ST_WORD(ptr,val) *(BYTE*)(ptr)=val; *(BYTE*)(ptr+1)=val>>8 +#define ST_DWORD(ptr,val) *(BYTE*)(ptr)=val; *(BYTE*)(ptr+1)=val>>8; *(BYTE*)(ptr+2)=val>>16; *(BYTE*)(ptr+3)=val>>24 +#else +#define LD_WORD(ptr) (*(WORD*)(BYTE*)(ptr)) +#define LD_DWORD(ptr) (*(DWORD*)(BYTE*)(ptr)) +#define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(val) +#define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(val) +#endif + + +#define _FATFS +#endif diff --git a/src/integer.h b/src/integer.h new file mode 100644 index 0000000..651f34e --- /dev/null +++ b/src/integer.h @@ -0,0 +1,12 @@ +#ifndef _INTEGER + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned long DWORD; + +typedef unsigned char BOOL; +#define FALSE 0 +#define TRUE 1 + +#define _INTEGER +#endif diff --git a/src/tff.c b/src/tff.c new file mode 100644 index 0000000..e6c4fb9 --- /dev/null +++ b/src/tff.c @@ -0,0 +1,1141 @@ +/*--------------------------------------------------------------------------/ +/ Tiny FatFs - FAT file system module R0.01 (C)ChaN, 2006 +/---------------------------------------------------------------------------/ +/ FatFs module is an experimenal project to implement FAT file system to +/ cheap microcontrollers. This is opened for education, reserch and +/ development. You can use, modify and republish it for non-profit or profit +/ use without any limitation under your responsibility. +/---------------------------------------------------------------------------/ +/ Feb 26, 2006 R0.00 Prototype +/ Apr 29, 2006 R0.01 First stable version +/---------------------------------------------------------------------------*/ + + +#include +#include "tff.h" /* Tiny-FatFs declarations */ +#include "diskio.h" /* Include file for user provided functions */ + + +FATFS *FatFs; /* Pointer to the file system object */ + + +/*------------------------------------------------------------------------- + Module Private Functions +-------------------------------------------------------------------------*/ + +/*----------------------*/ +/* Change Window Offset */ + +static +BOOL move_window ( + DWORD sector /* Sector number to make apperance in the FatFs->win */ +) /* Move to zero only writes back dirty window */ +{ + DWORD wsect; + FATFS *fs = FatFs; + + + wsect = fs->winsect; + if (wsect != sector) { /* Changed current window */ +#ifndef _FS_READONLY + BYTE n; + if (fs->winflag) { /* Write back dirty window if needed */ + if (disk_write(fs->win, wsect, 1) != RES_OK) return FALSE; + fs->winflag = 0; + if (wsect < (fs->fatbase + fs->sects_fat)) { /* In FAT area */ + for (n = fs->n_fats; n >= 2; n--) { /* Refrect the change to all FAT copies */ + wsect += fs->sects_fat; + if (disk_write(fs->win, wsect, 1) != RES_OK) break; + } + } + } +#endif + if (sector) { + if (disk_read(fs->win, sector, 1) != RES_OK) return FALSE; + fs->winsect = sector; + } + } + return TRUE; +} + + + +/*---------------------*/ +/* Get a Cluster State */ + +static +DWORD get_cluster (WORD clust) /* Cluster# to get the link information */ +{ + FATFS *fs = FatFs; + + + if ((clust >= 2) && (clust < fs->max_clust)) { /* Valid cluster# */ + if (fs->fs_type == FS_FAT16) { + if (move_window(clust / 256 + fs->fatbase)) + return LD_WORD(&(fs->win[(clust * 2) & 511])); + } + } + return 1; /* Return with 1 means failed */ +} + + + +/*------------------------*/ +/* Change a Cluster State */ + +#ifndef _FS_READONLY +static +BOOL put_cluster ( + WORD clust, /* Cluster# to change */ + WORD val /* New value to mark the cluster */ +) +{ + FATFS *fs = FatFs; + + + if (fs->fs_type != FS_FAT16) return FALSE; + if (!move_window(clust / 256 + fs->fatbase)) return FALSE; + ST_WORD(&(fs->win[((WORD)clust * 2) & 511]), (WORD)val); + + fs->winflag = 1; + return TRUE; +} +#endif + + + +/*------------------------*/ +/* Remove a Cluster Chain */ + +#ifndef _FS_READONLY +static +BOOL remove_chain ( + WORD clust /* Cluster# to remove chain from */ +) +{ + WORD nxt; + + + while ((nxt = get_cluster(clust)) >= 2) { + if (!put_cluster(clust, 0)) return FALSE; + clust = nxt; + } + return TRUE; +} +#endif + + + +/*-----------------------------------*/ +/* Stretch or Create a Cluster Chain */ + +#ifndef _FS_READONLY +static +DWORD create_chain ( + WORD clust /* Cluster# to stretch, 0 means create new */ +) +{ + WORD ncl, ccl, mcl = FatFs->max_clust; + + + if (clust == 0) { /* Create new chain */ + ncl = 1; + do { + ncl++; /* Check next cluster */ + if (ncl >= mcl) return 0; /* No free custer was found */ + ccl = get_cluster(ncl); /* Get the cluster status */ + if (ccl == 1) return 0; /* Any error occured */ + } while (ccl); /* Repeat until find a free cluster */ + } + else { /* Stretch existing chain */ + ncl = get_cluster(clust); /* Check the cluster status */ + if (ncl < 2) return 0; /* It is an invalid cluster */ + if (ncl < mcl) return ncl; /* It is already followed by next cluster */ + ncl = clust; /* Search free cluster */ + do { + ncl++; /* Check next cluster */ + if (ncl >= mcl) ncl = 2; /* Wrap around */ + if (ncl == clust) return 0; /* No free custer was found */ + ccl = get_cluster(ncl); /* Get the cluster status */ + if (ccl == 1) return 0; /* Any error occured */ + } while (ccl); /* Repeat until find a free cluster */ + } + + if (!put_cluster(ncl, 0xFFFF)) return 0; /* Mark the new cluster "in use" */ + if (clust && !put_cluster(clust, ncl)) return 0; /* Link it to previous one if needed */ + + return ncl; /* Return new cluster number */ +} +#endif + + + +/*----------------------------*/ +/* Get Sector# from Cluster# */ + +static +DWORD clust2sect ( + WORD clust /* Cluster# to be converted */ +) +{ + FATFS *fs = FatFs; + + + clust -= 2; + if (clust >= fs->max_clust) return 0; /* Invalid cluster# */ + return (DWORD)clust * fs->sects_clust + fs->database; +} + + + +/*------------------------*/ +/* Check File System Type */ + +static +BYTE check_fs ( + DWORD sect /* Sector# to check if it is a FAT boot record or not */ +) +{ + static const char fatsign[] = "FAT16"; + FATFS *fs = FatFs; + + + memset(fs->win, 0, 512); + if (disk_read(fs->win, sect, 1) == RES_OK) { /* Load boot record */ + if (LD_WORD(&(fs->win[510])) == 0xAA55) { /* Is it valid? */ + if (!memcmp(&(fs->win[0x36]), &fatsign[0], 5)) + return FS_FAT16; + } + } + return 0; +} + + + +/*--------------------------------*/ +/* Move Directory Pointer to Next */ + +static +BOOL next_dir_entry ( + DIR *scan /* Pointer to directory object */ +) +{ + WORD clust; + WORD idx; + FATFS *fs = FatFs; + + + idx = scan->index + 1; + if ((idx & 15) == 0) { /* Table sector changed? */ + scan->sect++; /* Next sector */ + if (!scan->clust) { /* In static table */ + if (idx >= fs->n_rootdir) return FALSE; /* Reached to end of table */ + } else { /* In dynamic table */ + if (((idx / 16) & (fs->sects_clust - 1)) == 0) { /* Cluster changed? */ + clust = get_cluster(scan->clust); /* Get next cluster */ + if ((clust >= fs->max_clust) || (clust < 2)) /* Reached to end of table */ + return FALSE; + scan->clust = clust; /* Initialize for new cluster */ + scan->sect = clust2sect(clust); + } + } + } + scan->index = idx; /* Lower 4 bit of scan->index indicates offset in scan->sect */ + return TRUE; +} + + + +/*--------------------------------------*/ +/* Get File Status from Directory Entry */ + +static +void get_fileinfo ( + FILINFO *finfo, /* Ptr to Store the File Information */ + const BYTE *dir /* Ptr to the Directory Entry */ +) +{ + BYTE n, c, a; + char *p; + + + p = &(finfo->fname[0]); + a = *(dir+12); /* NT flag */ + for (n = 0; n < 8; n++) { /* Convert file name (body) */ + c = *(dir+n); + if (c == ' ') break; + if (c == 0x05) c = 0xE5; + if ((a & 0x08) && (c >= 'A') && (c <= 'Z')) c += 0x20; + *p++ = c; + } + if (*(dir+8) != ' ') { /* Convert file name (extension) */ + *p++ = '.'; + for (n = 8; n < 11; n++) { + c = *(dir+n); + if (c == ' ') break; + if ((a & 0x10) && (c >= 'A') && (c <= 'Z')) c += 0x20; + *p++ = c; + } + } + *p = '\0'; + + finfo->fattrib = *(dir+11); /* Attribute */ + finfo->fsize = LD_DWORD(dir+28); /* Size */ + finfo->fdate = LD_WORD(dir+24); /* Date */ + finfo->ftime = LD_WORD(dir+22); /* Time */ +} + + + +/*-----------------------------------------------------*/ +/* Pick a Paragraph and Create Name in Directory Entry */ + +static +char make_dirfile ( + const char **path, /* Pointer to the file path pointer */ + char *dirname /* Pointer to directory name buffer {Name(8), Ext(3), NT flag(1)} */ +) +{ + BYTE n, t, c, a, b; + + + memset(dirname, ' ', 8+3); /* Fill buffer with spaces */ + a = 0; b = 0x18; /* NT flag */ + n = 0; t = 8; + for (;;) { + c = *(*path)++; + if (c <= ' ') c = 0; + if ((c == 0) || (c == '/')) { /* Reached to end of str or directory separator */ + if (n == 0) break; + dirname[11] = a & b; return c; + } + if (c == '.') { + if(!(a & 1) && (n >= 1) && (n <= 8)) { /* Enter extension part */ + n = 8; t = 11; continue; + } + break; + } +#ifdef _USE_SJIS + if (((c >= 0x81) && (c <= 0x9F)) || /* Accept S-JIS code */ + ((c >= 0xE0) && (c <= 0xFC))) { + if ((n == 0) && (c == 0xE5)) /* Change heading \xE5 to \x05 */ + c = 0x05; + a ^= 1; goto md_l2; + } + if ((c >= 0x7F) && (c <= 0x80)) break; /* Reject \x7F \x80 */ +#else + if (c >= 0x7F) goto md_l1; /* Accept \x7F-0xFF */ +#endif + if (c == '"') break; /* Reject " */ + if (c <= ')') goto md_l1; /* Accept ! # $ % & ' ( ) */ + if (c <= ',') break; /* Reject * + , */ + if (c <= '9') goto md_l1; /* Accept - 0-9 */ + if (c <= '?') break; /* Reject : ; < = > ? */ + if (!(a & 1)) { /* These checks are not applied to S-JIS 2nd byte */ + if (c == '|') break; /* Reject | */ + if ((c >= '[') && (c <= ']')) break;/* Reject [ \ ] */ + if ((c >= 'A') && (c <= 'Z')) + (t == 8) ? (b &= ~0x08) : (b &= ~0x10); + if ((c >= 'a') && (c <= 'z')) { /* Convert to upper case */ + c -= 0x20; + (t == 8) ? (a |= 0x08) : (a |= 0x10); + } + } + md_l1: + a &= ~1; + md_l2: + if (n >= t) break; + dirname[n++] = c; + } + return 1; +} + + + +/*-------------------*/ +/* Trace a File Path */ + +static +FRESULT trace_path ( + DIR *scan, /* Pointer to directory object to return last directory */ + char *fn, /* Pointer to last segment name to return */ + const char *path, /* Full-path string to trace a file or directory */ + BYTE **dir /* Directory pointer in Win[] to retutn */ +) +{ + WORD clust; + char ds; + BYTE *dptr = NULL; + FATFS *fs = FatFs; + + /* Initialize directory object */ + clust = fs->dirbase; + scan->clust = scan->sclust = 0; + scan->sect = clust; + scan->index = 0; + + while ((*path == ' ') || (*path == '/')) path++; /* Skip leading spaces */ + if ((BYTE)*path < ' ') { /* Null path means the root directory */ + *dir = NULL; return FR_OK; + } + + for (;;) { + ds = make_dirfile(&path, fn); /* Get a paragraph into fn[] */ + if (ds == 1) return FR_INVALID_NAME; + for (;;) { + if (!move_window(scan->sect)) return FR_RW_ERROR; + dptr = &(fs->win[(scan->index & 15) * 32]); /* Pointer to the directory entry */ + if (*dptr == 0) /* Has it reached to end of dir? */ + return !ds ? FR_NO_FILE : FR_NO_PATH; + if ( (*dptr != 0xE5) /* Matched? */ + && !(*(dptr+11) & AM_VOL) + && !memcmp(dptr, fn, 8+3) ) break; + if (!next_dir_entry(scan)) /* Next directory pointer */ + return !ds ? FR_NO_FILE : FR_NO_PATH; + } + if (!ds) { *dir = dptr; return FR_OK; } /* Matched with end of path */ + if (!(*(dptr+11) & AM_DIR)) return FR_NO_PATH; /* Cannot trace because it is a file */ + clust = LD_WORD(dptr+26); /* Get cluster# of the directory */ + scan->clust = scan->sclust = clust; /* Restart scan with the new directory */ + scan->sect = clust2sect(clust); + scan->index = 0; + } +} + + + +/*---------------------------*/ +/* Reserve a Directory Entry */ + +#ifndef _FS_READONLY +static +BYTE* reserve_direntry ( + DIR *scan /* Target directory to create new entry */ +) +{ + WORD clust; + DWORD sector; + BYTE c, n, *dptr; + FATFS *fs = FatFs; + + + /* Re-initialize directory object */ + clust = scan->sclust; + if (clust) { /* Dyanmic directory table */ + scan->clust = clust; + scan->sect = clust2sect(clust); + } else { /* Static directory table */ + scan->sect = fs->dirbase; + } + scan->index = 0; + + do { + if (!move_window(scan->sect)) return NULL; + dptr = &(fs->win[(scan->index & 15) * 32]); /* Pointer to the directory entry */ + c = *dptr; + if ((c == 0) || (c == 0xE5)) return dptr; /* Found an empty entry! */ + } while (next_dir_entry(scan)); /* Next directory pointer */ + /* Reached to end of the directory table */ + + /* Abort when static table or could not stretch dynamic table */ + if ((!clust) || !(clust = create_chain(scan->clust))) return NULL; + if (!move_window(0)) return 0; + + fs->winsect = sector = clust2sect(clust); /* Cleanup the expanded table */ + memset(fs->win, 0, 512); + for (n = fs->sects_clust; n; n--) { + if (disk_write(fs->win, sector, 1) != RES_OK) return NULL; + sector++; + } + fs->winflag = 1; + return fs->win; +} +#endif + + + +/*-----------------------------------------*/ +/* Make Sure that the File System is Valid */ + +static +FRESULT check_mounted () +{ + FATFS *fs = FatFs; + + + if (!fs) return FR_NOT_ENABLED; /* Has the FatFs been enabled? */ + + if (disk_status() & STA_NOINIT) { /* The drive has not been initialized */ + if (fs->files) /* Drive was uninitialized with any file left opend */ + return FR_INCORRECT_DISK_CHANGE; + else + return f_mountdrv(); /* Initialize file system and return resulut */ + } else { /* The drive has been initialized */ + if (!fs->fs_type) /* But the file system has not been initialized */ + return f_mountdrv(); /* Initialize file system and return resulut */ + } + return FR_OK; /* File system is valid */ +} + + + + + +/*--------------------------------------------------------------------------*/ +/* Public Funciotns */ +/*--------------------------------------------------------------------------*/ + + +/*--------------------------------------------------------*/ +/* Load File System Information and Initialize the Module */ + +FRESULT f_mountdrv () +{ + BYTE fat; + DWORD sect, fatend; + FATFS *fs = FatFs; + + + if (!fs) return FR_NOT_ENABLED; + + /* Initialize file system object */ + memset(fs, 0, sizeof(FATFS)); + + /* Initialize disk drive */ + if (disk_initialize() & STA_NOINIT) return FR_NOT_READY; + + /* Search FAT partition */ + fat = check_fs(sect = 0); /* Check sector 0 as an SFD format */ + if (!fat) { /* Not a FAT boot record, it will be an FDISK format */ + /* Check first pri-partition listed in the partition table */ + if (fs->win[0x1C2]) { /* Is a partition existing? */ + sect = LD_DWORD(&(fs->win[0x1C6])); /* Partition offset in LBA */ + fat = check_fs(sect); /* Check the partition */ + } + } + if (!fat) return FR_NO_FILESYSTEM; /* No FAT patition */ + + /* Initialize file system object */ + fs->fs_type = fat; /* FAT type */ + fs->sects_fat = LD_WORD(&(fs->win[0x16])); /* Sectors per FAT */ + fs->sects_clust = fs->win[0x0D]; /* Sectors per cluster */ + fs->n_fats = fs->win[0x10]; /* Number of FAT copies */ + fs->fatbase = sect + LD_WORD(&(fs->win[0x0E])); /* FAT start sector (physical) */ + fs->n_rootdir = LD_WORD(&(fs->win[0x11])); /* Nmuber of root directory entries */ + + fatend = fs->sects_fat * fs->n_fats + fs->fatbase; + fs->dirbase = fatend; /* Directory start sector (physical) */ + fs->database = fs->n_rootdir / 16 + fatend; /* Data start sector (physical) */ + fs->max_clust = /* Maximum cluster number */ + (LD_DWORD(&(fs->win[0x20])) - fs->database + sect) / fs->sects_clust + 2; + + fs->files = 0; + return FR_OK; +} + + + +/*-----------------------------*/ +/* Get Number of Free Clusters */ + +FRESULT f_getfree ( + DWORD *nclust /* Pointer to the double word to return number of free clusters */ +) +{ + WORD clust; + DWORD n, sect; + BYTE m, *ptr, fat; + FRESULT res; + FATFS *fs = FatFs; + + + if ((res = check_mounted()) != FR_OK) return res; + fat = fs->fs_type; + + /* Count number of free clusters */ + n = clust = m = 0; + ptr = NULL; + sect = fs->fatbase; + do { + if (m == 0) { + if (!move_window(sect++)) return FR_RW_ERROR; + ptr = fs->win; + } + if (LD_WORD(ptr) == 0) n++; + ptr += 2; m++; + clust++; + } while (clust < fs->max_clust); + + *nclust = n; + return FR_OK; +} + + + +/*---------------------*/ +/* Open or Create File */ + +FRESULT f_open ( + FIL *fp, /* Pointer to the buffer of new file object to create */ + const char *path, /* Pointer to the file path */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + BYTE *dir; + DIR dirscan; + char fn[8+3+1]; + FATFS *fs = FatFs; + + + if ((res = check_mounted()) != FR_OK) return res; +#ifndef _FS_READONLY + if ((mode & (FA_WRITE|FA_CREATE_ALWAYS|FA_OPEN_ALWAYS)) && (disk_status() & STA_PROTECT)) + return FR_WRITE_PROTECTED; +#endif + + res = trace_path(&dirscan, fn, path, &dir); /* Trace the file path */ + +#ifndef _FS_READONLY + /* Create or Open a File */ + if (mode & (FA_CREATE_ALWAYS|FA_OPEN_ALWAYS)) { + DWORD dw; + if (res != FR_OK) { /* No file, create new */ + mode |= FA_CREATE_ALWAYS; + if (res != FR_NO_FILE) return res; + dir = reserve_direntry(&dirscan); /* Reserve a directory entry */ + if (dir == NULL) return FR_DENIED; + memcpy(dir, fn, 8+3); /* Initialize the new entry */ + *(dir+12) = fn[11]; + memset(dir+13, 0, 32-13); + } else { /* File already exists */ + if ((dir == NULL) || (*(dir+11) & (AM_RDO|AM_DIR))) /* Could not overwrite (R/O or DIR) */ + return FR_DENIED; + if (mode & FA_CREATE_ALWAYS) { /* Resize it to zero */ + dw = fs->winsect; /* Remove the cluster chain */ + if (!remove_chain(LD_WORD(dir+26)) || !move_window(dw) ) + return FR_RW_ERROR; + ST_WORD(dir+26, 0); /* cluster = 0 */ + ST_DWORD(dir+28, 0); /* size = 0 */ + } + } + if (mode & FA_CREATE_ALWAYS) { + *(dir+11) = AM_ARC; + dw = get_fattime(); + ST_DWORD(dir+14, dw); /* Created time */ + ST_DWORD(dir+22, dw); /* Updated time */ + fs->winflag = 1; + } + } + /* Open a File */ + else { +#endif + if (res != FR_OK) return res; /* Trace failed */ + if ((dir == NULL) || (*(dir+11) & AM_DIR)) /* It is a directory */ + return FR_NO_FILE; +#ifndef _FS_READONLY + if ((mode & FA_WRITE) && (*(dir+11) & AM_RDO)) /* R/O violation */ + return FR_DENIED; + } +#endif + +#ifdef _FS_READONLY + fp->flag = mode & FA_READ; +#else + fp->flag = mode & (FA_WRITE|FA_READ); + fp->dir_sect = fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dir; +#endif + fp->org_clust = LD_WORD(dir+26); /* File start cluster */ + fp->fsize = LD_DWORD(dir+28); /* File size */ + fp->fptr = 0; /* File ptr */ + fp->sect_clust = 1; /* Sector counter */ + fs->files++; + return FR_OK; +} + + + +/*-----------*/ +/* Read File */ + +FRESULT f_read ( + FIL *fp, /* Pointer to the file object */ + BYTE *buff, /* Pointer to data buffer */ + WORD btr, /* Number of bytes to read */ + WORD *br /* Pointer to number of bytes read */ +) +{ + DWORD sect, ln; + WORD clust, rcnt; + BYTE cc; + FATFS *fs = FatFs; + + + *br = 0; + if (!fs) return FR_NOT_ENABLED; + if ((disk_status() & STA_NOINIT) || !fs->fs_type) return FR_NOT_READY; /* Check disk ready */ + if (fp->flag & FA__ERROR) return FR_RW_ERROR; /* Check error flag */ + if (!(fp->flag & FA_READ)) return FR_DENIED; /* Check access mode */ + ln = fp->fsize - fp->fptr; + if (btr > ln) btr = ln; /* Truncate read count by number of bytes left */ + + for ( ; btr; /* Repeat until all data transferred */ + buff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { + if ((fp->fptr & 511) == 0) { /* On the sector boundary */ + if (--(fp->sect_clust)) { /* Decrement sector counter */ + sect = fp->curr_sect + 1; /* Next sector */ + } else { /* Next cluster */ + clust = (fp->fptr == 0) ? fp->org_clust : get_cluster(fp->curr_clust); + if ((clust < 2) || (clust >= fs->max_clust)) goto fr_error; + fp->curr_clust = clust; /* Current cluster */ + sect = clust2sect(clust); /* Current sector */ + fp->sect_clust = fs->sects_clust; /* Re-initialize the sector counter */ + } + fp->curr_sect = sect; /* Update current sector */ + cc = btr / 512; /* When left bytes >= 512 */ + if (cc) { /* Read maximum contiguous sectors */ + if (cc > fp->sect_clust) cc = fp->sect_clust; + if (disk_read(buff, sect, cc) != RES_OK) goto fr_error; + fp->sect_clust -= cc - 1; + fp->curr_sect += cc - 1; + rcnt = cc * 512; continue; + } + } + if (!move_window(fp->curr_sect)) goto fr_error; /* Move sector window */ + rcnt = 512 - (fp->fptr & 511); /* Copy fractional bytes from sector window */ + if (rcnt > btr) rcnt = btr; + memcpy(buff, &fs->win[fp->fptr & 511], rcnt); + } + + return FR_OK; + +fr_error: /* Abort this function due to an unrecoverable error */ + fp->flag |= FA__ERROR; + return FR_RW_ERROR; +} + + + +/*------------*/ +/* Write File */ + +#ifndef _FS_READONLY +FRESULT f_write ( + FIL *fp, /* Pointer to the file object */ + const BYTE *buff, /* Pointer to the data to be written */ + WORD btw, /* Number of bytes to write */ + WORD *bw /* Pointer to number of bytes written */ +) +{ + DWORD sect; + WORD clust, wcnt; + BYTE cc; + FATFS *fs = FatFs; + + + *bw = 0; + if (!fs) return FR_NOT_ENABLED; + if ((disk_status() & STA_NOINIT) || !fs->fs_type) return FR_NOT_READY; + if (fp->flag & FA__ERROR) return FR_RW_ERROR; /* Check error flag */ + if (!(fp->flag & FA_WRITE)) return FR_DENIED; /* Check access mode */ + if (fp->fsize + btw < fp->fsize) btw = 0; /* File size cannot reach 4GB */ + + for ( ; btw; /* Repeat until all data transferred */ + buff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) { + if ((fp->fptr & 511) == 0) { /* On the sector boundary */ + if (--(fp->sect_clust)) { /* Decrement sector counter */ + sect = fp->curr_sect + 1; /* Next sector */ + } else { /* Next cluster */ + if (fp->fptr == 0) { /* Top of the file */ + clust = fp->org_clust; + if (clust == 0) /* No cluster is created */ + fp->org_clust = clust = create_chain(0); /* Create a new cluster chain */ + } else { /* Middle or end of file */ + clust = create_chain(fp->curr_clust); /* Trace or streach cluster chain */ + } + if ((clust < 2) || (clust >= fs->max_clust)) break; + fp->curr_clust = clust; /* Current cluster */ + sect = clust2sect(clust); /* Current sector */ + fp->sect_clust = fs->sects_clust; /* Re-initialize the sector counter */ + } + fp->curr_sect = sect; /* Update current sector */ + cc = btw / 512; /* When left bytes >= 512 */ + if (cc) { /* Write maximum contiguous sectors */ + if (cc > fp->sect_clust) cc = fp->sect_clust; + if (disk_write(buff, sect, cc) != RES_OK) goto fw_error; + fp->sect_clust -= cc - 1; + fp->curr_sect += cc - 1; + wcnt = cc * 512; continue; + } + if (fp->fptr >= fp->fsize) { /* Flush R/W window if needed */ + if (!move_window(0)) goto fw_error; + fs->winsect = fp->curr_sect; + } + } + if (!move_window(fp->curr_sect)) goto fw_error; /* Move sector window */ + wcnt = 512 - (fp->fptr & 511); /* Copy fractional bytes bytes to sector window */ + if (wcnt > btw) wcnt = btw; + memcpy(&fs->win[fp->fptr & 511], buff, wcnt); + fs->winflag = 1; + } + + if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */ + fp->flag |= FA__WRITTEN; /* Set file changed flag */ + return FR_OK; + +fw_error: /* Abort this function due to an unrecoverable error */ + fp->flag |= FA__ERROR; + return FR_RW_ERROR; +} +#endif + + + +/*-------------------*/ +/* Seek File Pointer */ + +FRESULT f_lseek ( + FIL *fp, /* Pointer to the file object */ + DWORD ofs /* File pointer from top of file */ +) +{ + WORD clust; + BYTE sc; + FATFS *fs = FatFs; + + + if (!fs) return FR_NOT_ENABLED; + if ((disk_status() & STA_NOINIT) || !fs->fs_type) return FR_NOT_READY; + if (fp->flag & FA__ERROR) return FR_RW_ERROR; + if (ofs > fp->fsize) ofs = fp->fsize; /* Clip offset by file size */ + fp->fptr = ofs; fp->sect_clust = 1; /* Re-initialize file pointer */ + + /* Seek file pinter if needed */ + if (ofs) { + ofs = (ofs - 1) / 512; /* Calcurate current sector */ + sc = fs->sects_clust; /* Number of sectors in a cluster */ + fp->sect_clust = sc - (ofs % sc); /* Calcurate sector counter */ + ofs /= sc; /* Number of clusters to skip */ + clust = fp->org_clust; /* Seek to current cluster */ + while (ofs--) + clust = get_cluster(clust); + if ((clust < 2) || (clust >= fs->max_clust)) goto fk_error; + fp->curr_clust = clust; + fp->curr_sect = clust2sect(clust) + sc - fp->sect_clust; /* Current sector */ + } + + return FR_OK; + +fk_error: /* Abort this function due to an unrecoverable error */ + fp->flag |= FA__ERROR; + return FR_RW_ERROR; +} + + + +/*-------------------------------------------------*/ +/* Synchronize between File and Disk without Close */ + +#ifndef _FS_READONLY +FRESULT f_sync ( + FIL *fp /* Pointer to the file object */ +) +{ + BYTE *ptr; + FATFS *fs = FatFs; + + + if (!fs) return FR_NOT_ENABLED; + if ((disk_status() & STA_NOINIT) || !fs->fs_type) + return FR_INCORRECT_DISK_CHANGE; + + /* Has the file been written? */ + if (fp->flag & FA__WRITTEN) { + if (!move_window(fp->dir_sect)) return FR_RW_ERROR; + ptr = fp->dir_ptr; + *(ptr+11) |= AM_ARC; /* Set archive bit */ + ST_DWORD(ptr+28, fp->fsize); /* Update file size */ + ST_WORD(ptr+26, fp->org_clust); /* Update start cluster */ + ST_DWORD(ptr+22, get_fattime()); /* Updated time */ + fs->winflag = 1; + fp->flag &= ~FA__WRITTEN; + } + if (!move_window(0)) return FR_RW_ERROR; + + return FR_OK; +} +#endif + + + +/*------------*/ +/* Close File */ + +FRESULT f_close ( + FIL *fp /* Pointer to the file object to be closed */ +) +{ + FRESULT res; + + +#ifndef _FS_READONLY + res = f_sync(fp); +#else + res = FR_OK; +#endif + if (res == FR_OK) { + fp->flag = 0; + FatFs->files--; + } + return res; +} + + + +/*----------------------------*/ +/* Delete a File or Directory */ + +#ifndef _FS_READONLY +FRESULT f_unlink ( + const char *path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + BYTE *dir, *sdir; + DWORD dsect; + WORD dclust; + DIR dirscan; + char fn[8+3+1]; + FATFS *fs = FatFs; + + + if ((res = check_mounted()) != FR_OK) return res; + if (disk_status() & STA_PROTECT) return FR_WRITE_PROTECTED; + + res = trace_path(&dirscan, fn, path, &dir); /* Trace the file path */ + + if (res != FR_OK) return res; /* Trace failed */ + if (dir == NULL) return FR_NO_FILE; /* It is a root directory */ + if (*(dir+11) & AM_RDO) return FR_DENIED; /* It is a R/O item */ + dsect = fs->winsect; + dclust = LD_WORD(dir+26); + + if (*(dir+11) & AM_DIR) { /* It is a sub-directory */ + dirscan.clust = dclust; /* Check if the sub-dir is empty or not */ + dirscan.sect = clust2sect(dclust); + dirscan.index = 0; + do { + if (!move_window(dirscan.sect)) return FR_RW_ERROR; + sdir = &(fs->win[(dirscan.index & 15) * 32]); + if (*sdir == 0) break; + if (!((*sdir == 0xE5) || (*sdir == '.')) && !(*(sdir+11) & AM_VOL)) + return FR_DENIED; /* The directory is not empty */ + } while (next_dir_entry(&dirscan)); + } + + if (!remove_chain(dclust)) return FR_RW_ERROR; /* Remove the cluster chain */ + if (!move_window(dsect)) return FR_RW_ERROR; /* Mark the directory entry deleted */ + *dir = 0xE5; fs->winflag = 1; + if (!move_window(0)) return FR_RW_ERROR; + + return FR_OK; +} +#endif + + + +/*--------------------*/ +/* Create a Directory */ + +#ifndef _FS_READONLY +FRESULT f_mkdir ( + const char *path /* Pointer to the directory path */ +) +{ + FRESULT res; + BYTE *dir, *w, n; + DWORD sect, dsect, tim; + WORD dclust; + DIR dirscan; + char fn[8+3+1]; + FATFS *fs = FatFs; + + + if ((res = check_mounted()) != FR_OK) return res; + if (disk_status() & STA_PROTECT) return FR_WRITE_PROTECTED; + + res = trace_path(&dirscan, fn, path, &dir); /* Trace the file path */ + + if (res == FR_OK) return FR_DENIED; /* Any file or directory is already existing */ + if (res != FR_NO_FILE) return res; + + dir = reserve_direntry(&dirscan); /* Reserve a directory entry */ + if (dir == NULL) return FR_DENIED; + sect = fs->winsect; + dsect = clust2sect(dclust = create_chain(0)); /* Get a new cluster for new directory */ + if (!dsect) return FR_DENIED; + if (!move_window(0)) return 0; + + w = fs->win; + memset(w, 0, 512); /* Initialize the directory table */ + for (n = fs->sects_clust - 1; n; n--) { + if (disk_write(w, dsect+n, 1) != RES_OK) return FR_RW_ERROR; + } + + fs->winsect = dsect; /* Create dot directories */ + memset(w, ' ', 8+3); + *w = '.'; + *(w+11) = AM_DIR; + tim = get_fattime(); + ST_DWORD(w+22, tim); + memcpy(w+32, w, 32); *(w+33) = '.'; + ST_WORD(w+26, dclust); + ST_WORD(w+32+26, dirscan.sclust); + fs->winflag = 1; + + if (!move_window(sect)) return FR_RW_ERROR; + memcpy(dir, fn, 8+3); /* Initialize the new entry */ + *(dir+11) = AM_DIR; + *(dir+12) = fn[11]; + memset(dir+13, 0, 32-13); + ST_DWORD(dir+22, tim); /* Crated time */ + ST_WORD(dir+26, dclust); /* Table start cluster */ + fs->winflag = 1; + + if (!move_window(0)) return FR_RW_ERROR; + + return FR_OK; +} +#endif + + + +/*---------------------------*/ +/* Initialize directroy scan */ + +FRESULT f_opendir ( + DIR *scan, /* Pointer to directory object to initialize */ + const char *path /* Pointer to the directory path, null str means the root */ +) +{ + FRESULT res; + BYTE *dir; + char fn[8+3+1]; + + + if ((res = check_mounted()) != FR_OK) return res; + + res = trace_path(scan, fn, path, &dir); /* Trace the directory path */ + + if (res == FR_OK) { /* Trace completed */ + if (dir != NULL) { /* It is not a root dir */ + if (*(dir+11) & AM_DIR) { /* The entry is a directory */ + scan->clust = LD_WORD(dir+26); + scan->sect = clust2sect(scan->clust); + scan->index = 0; + } else { /* The entry is a file */ + res = FR_NO_PATH; + } + } + } + return res; +} + + + +/*----------------------------------*/ +/* Read Directory Entry in Sequense */ + +FRESULT f_readdir ( + DIR *scan, /* Pointer to the directory object */ + FILINFO *finfo /* Pointer to file information to return */ +) +{ + BYTE *dir, c; + FATFS *fs = FatFs; + + + if (!fs) return FR_NOT_ENABLED; + finfo->fname[0] = 0; + if ((disk_status() & STA_NOINIT) || !fs->fs_type) return FR_NOT_READY; + + while (scan->sect) { + if (!move_window(scan->sect)) return FR_RW_ERROR; + dir = &(fs->win[(scan->index & 15) * 32]); /* pointer to the directory entry */ + c = *dir; + if (c == 0) break; /* Has it reached to end of dir? */ + if ((c != 0xE5) && (c != '.') && !(*(dir+11) & AM_VOL)) /* Is it a valid entry? */ + get_fileinfo(finfo, dir); + if (!next_dir_entry(scan)) scan->sect = 0; /* Next entry */ + if (finfo->fname[0]) break; /* Found valid entry */ + } + + return FR_OK; +} + + + +/*-----------------*/ +/* Get File Status */ + +FRESULT f_stat ( + const char *path, /* Pointer to the file path */ + FILINFO *finfo /* Pointer to file information to return */ +) +{ + FRESULT res; + BYTE *dir; + DIR dirscan; + char fn[8+3+1]; + + + if ((res = check_mounted()) != FR_OK) return res; + + res = trace_path(&dirscan, fn, path, &dir); /* Trace the file path */ + + if (res == FR_OK) /* Trace completed */ + get_fileinfo(finfo, dir); + + return res; +} + + + +/*-----------------------*/ +/* Change File Attribute */ + +#ifndef _FS_READONLY +FRESULT f_chmod ( + const char *path, /* Pointer to the file path */ + BYTE value, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + BYTE *dir; + DIR dirscan; + char fn[8+3+1]; + FATFS *fs = FatFs; + + + if ((res = check_mounted()) != FR_OK) return res; + if (disk_status() & STA_PROTECT) return FR_WRITE_PROTECTED; + + res = trace_path(&dirscan, fn, path, &dir); /* Trace the file path */ + + if (res == FR_OK) { /* Trace completed */ + if (dir == NULL) { + res = FR_NO_FILE; + } else { + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ + *(dir+11) = (value & mask) | (*(dir+11) & ~mask); /* Apply attribute change */ + fs->winflag = 1; + if(!move_window(0)) res = FR_RW_ERROR; + } + } + return res; +} +#endif + + diff --git a/src/tff.h b/src/tff.h new file mode 100644 index 0000000..b1d8597 --- /dev/null +++ b/src/tff.h @@ -0,0 +1,181 @@ +/*--------------------------------------------------------------------------/ +/ FatFs - FAT file system module include file R0.01 (C)ChaN, 2006 +/---------------------------------------------------------------------------/ +/ FatFs module is an experimenal project to implement FAT file system to +/ cheap microcontrollers. This is opened for education, reserch and +/ development. You can use it for non-profit or profit use without any +/ limitation under your responsibility. +/---------------------------------------------------------------------------/ +/ Feb 26, 2006 R0.00 Prototype +/ Apr 29, 2006 R0.01 First stable version +/---------------------------------------------------------------------------*/ + +#ifndef _FATFS + +//#define _BYTE_ACC +/* This enables byte-by-byte access for multi-byte variables */ +/* It must be defined on the big-endian processor, or to prevent address error */ + +//#define _FS_READONLY +/* This removes writing code for read-only applications */ + +#define _USE_SJIS +/* This enables Shift-JIS code transparency, or only US-ASCII file name can be accepted */ + + +#include "integer.h" + + +/* Result type for fatfs application interface */ +typedef unsigned char FRESULT; + + +/* File system object */ +typedef struct _FATFS { + BYTE fs_type; // FAT type + BYTE files; // Number of files currently opend + BYTE sects_clust; // Sectors per cluster + BYTE n_fats; // Number of FAT copies + WORD n_rootdir; // Number of root directory entry + BYTE winflag; // win[] dirty flag (1:must be written back) + BYTE pad1; + WORD sects_fat; // Sectors per fat + WORD max_clust; // Maximum cluster# + 1 + DWORD fatbase; // FAT start sector + DWORD dirbase; // Root directory start sector (cluster# for FAT32) + DWORD database; // Data start sector + DWORD winsect; // Current sector appearing in the win[] + BYTE win[512]; // Disk access window +} FATFS; + + +/* Directory scan object */ +typedef struct _DIR { + WORD sclust; // Start cluster + WORD clust; // Current cluster + DWORD sect; // Current sector + WORD index; // Current index +} DIR; + + +/* File object */ +typedef struct _FIL { + DWORD fptr; // File Pointer + DWORD fsize; // File Size + WORD org_clust; // File start cluster + WORD curr_clust; // Current cluster + DWORD curr_sect; // Current sector +#ifndef _FS_READONLY + DWORD dir_sect; // Sector containing the directory entry + BYTE* dir_ptr; // Ponter to the directory entry in the window +#endif + BYTE flag; // File status flags + BYTE sect_clust; // Left sectors in cluster +} FIL; + + +/* File status structure */ +typedef struct _FILINFO { + DWORD fsize; // Size + WORD fdate; // Date + WORD ftime; // Time + BYTE fattrib; // Attribute + char fname[8+1+3+1]; // File/Directory name (8.3 format) +} FILINFO; + + + +/* fatfs module application interface */ + +extern FATFS *FatFs; // Pointer to active file system object + +FRESULT f_open (FIL*, const char*, BYTE); // Open or create a file +FRESULT f_read (FIL*, BYTE*, WORD, WORD*); // Read file +FRESULT f_close (FIL*); // Close file +FRESULT f_lseek (FIL*, DWORD); // Seek file pointer +FRESULT f_opendir (DIR*, const char*); // Initialize to read a directory +FRESULT f_readdir (DIR*, FILINFO*); // Read a directory item +FRESULT f_stat (const char*, FILINFO*); // Get file status +FRESULT f_getfree (DWORD*); // Get number of free clusters +FRESULT f_mountdrv (); // Force initialized the file system +#ifndef _FS_READONLY +FRESULT f_write (FIL*, const BYTE*, WORD, WORD*); // Write file +FRESULT f_sync (FIL*); // Flush cached information of a writing file +FRESULT f_unlink (const char*); // Delete a file or directory +FRESULT f_mkdir (const char*); // Create a directory +FRESULT f_chmod (const char*, BYTE, BYTE); // Change file attriburte +#endif + + +/* User defined function to give a current time to fatfs module */ + +#ifndef _FS_READONLY +DWORD get_fattime(); // 31-25: Year(0-127 +1980), 24-21: Month(1-12), 20-16: Day(1-31) + // 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) +#endif + + + +/* File function return code */ + +#define FR_OK 0 +#define FR_NOT_READY 1 +#define FR_NO_FILE 2 +#define FR_NO_PATH 3 +#define FR_INVALID_NAME 4 +#define FR_DENIED 5 +#define FR_DISK_FULL 6 +#define FR_RW_ERROR 7 +#define FR_ALIGN_ERROR 8 +#define FR_INCORRECT_DISK_CHANGE 9 +#define FR_WRITE_PROTECTED 10 +#define FR_NOT_ENABLED 11 +#define FR_NO_FILESYSTEM 12 + + +/* File access control and file status flags */ + +#define FA_READ 0x01 +#define FA_OPEN_EXISTING 0x00 +#ifndef _FS_READONLY +#define FA_WRITE 0x02 +#define FA_CREATE_ALWAYS 0x08 +#define FA_OPEN_ALWAYS 0x10 +#define FA__WRITTEN 0x20 +#endif +#define FA__ERROR 0x80 + + +/* FAT type signature (fs_type) */ + +#define FS_FAT16 1 + + +/* File attribute mask for directory entry */ + +#define AM_RDO 0x01 // Read Only +#define AM_HID 0x02 // Hidden +#define AM_SYS 0x04 // System +#define AM_VOL 0x08 // Volume Label +#define AM_DIR 0x10 // Directory +#define AM_ARC 0x20 // Archive + + + +/* Multi-byte word access macros */ + +#ifdef _BYTE_ACC +#define LD_WORD(ptr) (((WORD)*(BYTE*)(ptr+1)<<8)|*(ptr)) +#define LD_DWORD(ptr) (((DWORD)*(BYTE*)(ptr+3)<<24)|((DWORD)*(BYTE*)(ptr+2)<<16)|((WORD)*(BYTE*)(ptr+1)<<8)|*(BYTE*)(ptr)) +#define ST_WORD(ptr,val) *(BYTE*)(ptr)=val; *(BYTE*)(ptr+1)=val>>8 +#define ST_DWORD(ptr,val) *(BYTE*)(ptr)=val; *(BYTE*)(ptr+1)=val>>8; *(BYTE*)(ptr+2)=val>>16; *(BYTE*)(ptr+3)=val>>24 +#else +#define LD_WORD(ptr) (*(WORD*)(BYTE*)(ptr)) +#define LD_DWORD(ptr) (*(DWORD*)(BYTE*)(ptr)) +#define ST_WORD(ptr,val) *(WORD*)(BYTE*)(ptr)=(val) +#define ST_DWORD(ptr,val) *(DWORD*)(BYTE*)(ptr)=(val) +#endif + + +#define _FATFS +#endif