2 * This file is part of Cleanflight and Betaflight.
4 * Cleanflight and Betaflight are free software. You can redistribute
5 * this software and/or modify this software under the terms of the
6 * GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option)
10 * Cleanflight and Betaflight are distributed in the hope that they
11 * will be useful, but WITHOUT ANY WARRANTY; without even the implied
12 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this software.
18 * If not, see <http://www.gnu.org/licenses/>.
22 * This is a FAT16/FAT32 filesystem for SD cards which uses asynchronous operations: The caller need never wait
23 * for the SD card to be ready.
25 * On top of the regular FAT32 concepts, we add the idea of a "super cluster". Given one FAT sector, a super cluster is
26 * the series of clusters which corresponds to all of the cluster entries in that FAT sector. If files are allocated
27 * on super-cluster boundaries, they will have FAT sectors which are dedicated to them and independent of all other
30 * We can pre-allocate a "freefile" which is a file on disk made up of contiguous superclusters. Then when we want
31 * to allocate a file on disk, we can carve it out of the freefile, and know that the clusters will be contiguous
32 * without needing to read the FAT at all (the freefile's FAT is completely determined from its start cluster and file
33 * size, which we get from the directory entry). This allows for extremely fast append-only logging.
47 #include "common/maths.h"
48 #include "common/time.h"
49 #include "common/utils.h"
51 #include "drivers/sdcard.h"
53 #include "fat_standard.h"
55 #include "asyncfatfs.h"
58 #define ONLY_EXPOSE_FOR_TESTING
60 #define ONLY_EXPOSE_FOR_TESTING static
63 #define AFATFS_NUM_CACHE_SECTORS 11
65 // FAT filesystems are allowed to differ from these parameters, but we choose not to support those weird filesystems:
66 #define AFATFS_SECTOR_SIZE 512
67 #define AFATFS_NUM_FATS 2
69 #define AFATFS_MAX_OPEN_FILES 3
71 #define AFATFS_DEFAULT_FILE_DATE FAT_MAKE_DATE(2015, 12, 01)
72 #define AFATFS_DEFAULT_FILE_TIME FAT_MAKE_TIME(00, 00, 00)
75 * How many blocks will we write in a row before we bother using the SDcard's multiple block write method?
76 * If this define is omitted, this disables multi-block write.
78 #define AFATFS_MIN_MULTIPLE_BLOCK_WRITE_COUNT 4
80 #define AFATFS_FILES_PER_DIRECTORY_SECTOR (AFATFS_SECTOR_SIZE / sizeof(fatDirectoryEntry_t))
82 #define AFATFS_FAT32_FAT_ENTRIES_PER_SECTOR (AFATFS_SECTOR_SIZE / sizeof(uint32_t))
83 #define AFATFS_FAT16_FAT_ENTRIES_PER_SECTOR (AFATFS_SECTOR_SIZE / sizeof(uint16_t))
85 // We will read from the file
86 #define AFATFS_FILE_MODE_READ 1
87 // We will write to the file
88 #define AFATFS_FILE_MODE_WRITE 2
89 // We will append to the file, may not be combined with the write flag
90 #define AFATFS_FILE_MODE_APPEND 4
91 // File will occupy a series of superclusters (only valid for creating new files):
92 #define AFATFS_FILE_MODE_CONTIGUOUS 8
93 // File should be created if it doesn't exist:
94 #define AFATFS_FILE_MODE_CREATE 16
95 // The file's directory entry should be locked in cache so we can read it with no latency:
96 #define AFATFS_FILE_MODE_RETAIN_DIRECTORY 32
98 // Open the cache sector for read access (it will be read from disk)
99 #define AFATFS_CACHE_READ 1
100 // Open the cache sector for write access (it will be marked dirty)
101 #define AFATFS_CACHE_WRITE 2
102 // Lock this sector to prevent its state from transitioning (prevent flushes to disk)
103 #define AFATFS_CACHE_LOCK 4
104 // Discard this sector in preference to other sectors when it is in the in-sync state
105 #define AFATFS_CACHE_DISCARDABLE 8
106 // Increase the retain counter of the cache sector to prevent it from being discarded when in the in-sync state
107 #define AFATFS_CACHE_RETAIN 16
109 // Turn the largest free block on the disk into one contiguous file for efficient fragment-free allocation
110 #define AFATFS_USE_FREEFILE
112 // When allocating a freefile, leave this many clusters un-allocated for regular files to use
113 #define AFATFS_FREEFILE_LEAVE_CLUSTERS 100
115 // Filename in 8.3 format:
116 #define AFATFS_FREESPACE_FILENAME "FREESPAC.E"
118 #define AFATFS_INTROSPEC_LOG_FILENAME "ASYNCFAT.LOG"
121 AFATFS_SAVE_DIRECTORY_NORMAL
,
122 AFATFS_SAVE_DIRECTORY_FOR_CLOSE
,
123 AFATFS_SAVE_DIRECTORY_DELETED
124 } afatfsSaveDirectoryEntryMode_e
;
127 AFATFS_CACHE_STATE_EMPTY
,
128 AFATFS_CACHE_STATE_IN_SYNC
,
129 AFATFS_CACHE_STATE_READING
,
130 AFATFS_CACHE_STATE_WRITING
,
131 AFATFS_CACHE_STATE_DIRTY
132 } afatfsCacheBlockState_e
;
135 AFATFS_FILE_TYPE_NONE
,
136 AFATFS_FILE_TYPE_NORMAL
,
137 AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY
,
138 AFATFS_FILE_TYPE_DIRECTORY
142 CLUSTER_SEARCH_FREE_AT_BEGINNING_OF_FAT_SECTOR
,
144 CLUSTER_SEARCH_OCCUPIED
145 } afatfsClusterSearchCondition_e
;
148 AFATFS_CREATEFILE_PHASE_INITIAL
= 0,
149 AFATFS_CREATEFILE_PHASE_FIND_FILE
,
150 AFATFS_CREATEFILE_PHASE_CREATE_NEW_FILE
,
151 AFATFS_CREATEFILE_PHASE_SUCCESS
,
152 AFATFS_CREATEFILE_PHASE_FAILURE
156 AFATFS_FIND_CLUSTER_IN_PROGRESS
,
157 AFATFS_FIND_CLUSTER_FOUND
,
158 AFATFS_FIND_CLUSTER_FATAL
,
159 AFATFS_FIND_CLUSTER_NOT_FOUND
160 } afatfsFindClusterStatus_e
;
162 struct afatfsFileOperation_t
;
164 typedef union afatfsFATSector_t
{
170 typedef struct afatfsCacheBlockDescriptor_t
{
172 * The physical sector index on disk that this cached block corresponds to
174 uint32_t sectorIndex
;
176 // We use an increasing timestamp to identify cache access times.
178 // This is the timestamp that this sector was first marked dirty at (so we can flush sectors in write-order).
179 uint32_t writeTimestamp
;
181 // This is the last time the sector was accessed
182 uint32_t accessTimestamp
;
184 /* This is set to non-zero when we expect to write a consecutive series of this many blocks (including this block),
185 * so we will tell the SD-card to pre-erase those blocks.
187 * This counter only needs to be set on the first block of a consecutive write (though setting it, appropriately
188 * decreased, on the subsequent blocks won't hurt).
190 uint16_t consecutiveEraseBlockCount
;
192 afatfsCacheBlockState_e state
;
195 * The state of this block must not transition (do not flush to disk, do not discard). This is useful for a sector
196 * which is currently being written to by the application (so flushing it would be a waste of time).
198 * This is a binary state rather than a counter because we assume that only one party will be responsible for and
199 * so consider locking a given sector.
204 * A counter for how many parties want this sector to be retained in memory (not discarded). If this value is
205 * non-zero, the sector may be flushed to disk if dirty but must remain in the cache. This is useful if we require
206 * a directory sector to be cached in order to meet our response time requirements.
208 unsigned retainCount
:6;
211 * If this block is in the In Sync state, it should be discarded from the cache in preference to other blocks.
212 * This is useful for data that we don't expect to read again, e.g. data written to an append-only file. This hint
213 * is overridden by the locked and retainCount flags.
215 unsigned discardable
:1;
216 } afatfsCacheBlockDescriptor_t
;
219 AFATFS_FAT_PATTERN_UNTERMINATED_CHAIN
,
220 AFATFS_FAT_PATTERN_TERMINATED_CHAIN
,
221 AFATFS_FAT_PATTERN_FREE
222 } afatfsFATPattern_e
;
225 AFATFS_FREE_SPACE_SEARCH_PHASE_FIND_HOLE
,
226 AFATFS_FREE_SPACE_SEARCH_PHASE_GROW_HOLE
227 } afatfsFreeSpaceSearchPhase_e
;
229 typedef struct afatfsFreeSpaceSearch_t
{
230 uint32_t candidateStart
;
231 uint32_t candidateEnd
;
232 uint32_t bestGapStart
;
233 uint32_t bestGapLength
;
234 afatfsFreeSpaceSearchPhase_e phase
;
235 } afatfsFreeSpaceSearch_t
;
237 typedef struct afatfsFreeSpaceFAT_t
{
238 uint32_t startCluster
;
240 } afatfsFreeSpaceFAT_t
;
242 typedef struct afatfsCreateFile_t
{
243 afatfsFileCallback_t callback
;
246 uint8_t filename
[FAT_FILENAME_LENGTH
];
247 } afatfsCreateFile_t
;
249 typedef struct afatfsSeek_t
{
250 afatfsFileCallback_t callback
;
256 AFATFS_APPEND_SUPERCLUSTER_PHASE_INIT
= 0,
257 AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FREEFILE_DIRECTORY
,
258 AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FAT
,
259 AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FILE_DIRECTORY
260 } afatfsAppendSuperclusterPhase_e
;
262 typedef struct afatfsAppendSupercluster_t
{
263 uint32_t previousCluster
;
264 uint32_t fatRewriteStartCluster
;
265 uint32_t fatRewriteEndCluster
;
266 afatfsAppendSuperclusterPhase_e phase
;
267 } afatfsAppendSupercluster_t
;
270 AFATFS_APPEND_FREE_CLUSTER_PHASE_INITIAL
= 0,
271 AFATFS_APPEND_FREE_CLUSTER_PHASE_FIND_FREESPACE
= 0,
272 AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FAT1
,
273 AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FAT2
,
274 AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FILE_DIRECTORY
,
275 AFATFS_APPEND_FREE_CLUSTER_PHASE_COMPLETE
,
276 AFATFS_APPEND_FREE_CLUSTER_PHASE_FAILURE
277 } afatfsAppendFreeClusterPhase_e
;
279 typedef struct afatfsAppendFreeCluster_t
{
280 uint32_t previousCluster
;
281 uint32_t searchCluster
;
282 afatfsAppendFreeClusterPhase_e phase
;
283 } afatfsAppendFreeCluster_t
;
286 AFATFS_EXTEND_SUBDIRECTORY_PHASE_INITIAL
= 0,
287 AFATFS_EXTEND_SUBDIRECTORY_PHASE_ADD_FREE_CLUSTER
= 0,
288 AFATFS_EXTEND_SUBDIRECTORY_PHASE_WRITE_SECTORS
,
289 AFATFS_EXTEND_SUBDIRECTORY_PHASE_SUCCESS
,
290 AFATFS_EXTEND_SUBDIRECTORY_PHASE_FAILURE
291 } afatfsExtendSubdirectoryPhase_e
;
293 typedef struct afatfsExtendSubdirectory_t
{
294 // We need to call this as a sub-operation so we have it as our first member to be compatible with its memory layout:
295 afatfsAppendFreeCluster_t appendFreeCluster
;
297 afatfsExtendSubdirectoryPhase_e phase
;
299 uint32_t parentDirectoryCluster
;
300 afatfsFileCallback_t callback
;
301 } afatfsExtendSubdirectory_t
;
304 AFATFS_TRUNCATE_FILE_INITIAL
= 0,
305 AFATFS_TRUNCATE_FILE_UPDATE_DIRECTORY
= 0,
306 AFATFS_TRUNCATE_FILE_ERASE_FAT_CHAIN_NORMAL
,
307 #ifdef AFATFS_USE_FREEFILE
308 AFATFS_TRUNCATE_FILE_ERASE_FAT_CHAIN_CONTIGUOUS
,
309 AFATFS_TRUNCATE_FILE_PREPEND_TO_FREEFILE
,
311 AFATFS_TRUNCATE_FILE_SUCCESS
312 } afatfsTruncateFilePhase_e
;
314 typedef struct afatfsTruncateFile_t
{
315 uint32_t startCluster
; // First cluster to erase
316 uint32_t currentCluster
; // Used to mark progress
317 uint32_t endCluster
; // Optional, for contiguous files set to 1 past the end cluster of the file, otherwise set to 0
318 afatfsFileCallback_t callback
;
319 afatfsTruncateFilePhase_e phase
;
320 } afatfsTruncateFile_t
;
323 AFATFS_DELETE_FILE_DELETE_DIRECTORY_ENTRY
,
324 AFATFS_DELETE_FILE_DEALLOCATE_CLUSTERS
325 } afatfsDeleteFilePhase_e
;
327 typedef struct afatfsDeleteFile_t
{
328 afatfsTruncateFile_t truncateFile
;
329 afatfsCallback_t callback
;
330 } afatfsUnlinkFile_t
;
332 typedef struct afatfsCloseFile_t
{
333 afatfsCallback_t callback
;
337 AFATFS_FILE_OPERATION_NONE
,
338 AFATFS_FILE_OPERATION_CREATE_FILE
,
339 AFATFS_FILE_OPERATION_SEEK
, // Seek the file's cursorCluster forwards by seekOffset bytes
340 AFATFS_FILE_OPERATION_CLOSE
,
341 AFATFS_FILE_OPERATION_TRUNCATE
,
342 AFATFS_FILE_OPERATION_UNLINK
,
343 #ifdef AFATFS_USE_FREEFILE
344 AFATFS_FILE_OPERATION_APPEND_SUPERCLUSTER
,
345 AFATFS_FILE_OPERATION_LOCKED
,
347 AFATFS_FILE_OPERATION_APPEND_FREE_CLUSTER
,
348 AFATFS_FILE_OPERATION_EXTEND_SUBDIRECTORY
349 } afatfsFileOperation_e
;
351 typedef struct afatfsFileOperation_t
{
352 afatfsFileOperation_e operation
;
354 afatfsCreateFile_t createFile
;
356 afatfsAppendSupercluster_t appendSupercluster
;
357 afatfsAppendFreeCluster_t appendFreeCluster
;
358 afatfsExtendSubdirectory_t extendSubdirectory
;
359 afatfsUnlinkFile_t unlinkFile
;
360 afatfsTruncateFile_t truncateFile
;
361 afatfsCloseFile_t closeFile
;
363 } afatfsFileOperation_t
;
365 typedef struct afatfsFile_t
{
366 afatfsFileType_e type
;
368 // The byte offset of the cursor within the file
369 uint32_t cursorOffset
;
371 /* The file size in bytes as seen by users of the filesystem (the exact length of the file they've written).
373 * This is only used by users of the filesystem, not us, so it only needs to be up to date for fseek() (to clip
374 * seeks to the EOF), fread(), feof(), and fclose() (which writes the logicalSize to the directory).
376 * It becomes out of date when we fwrite() to extend the length of the file. In this situation, feof() is properly
377 * true, so we don't have to update the logicalSize for fread() or feof() to get the correct result. We only need
378 * to update it when we seek backwards (so we don't forget the logical EOF position), or fclose().
380 uint32_t logicalSize
;
382 /* The allocated size in bytes based on how many clusters have been assigned to the file. Always a multiple of
385 * This is an underestimate for existing files, because we don't bother to check precisely how long the chain is
386 * at the time the file is opened (it might be longer than needed to contain the logical size), but assuming the
387 * filesystem metadata is correct, it should always be at least as many clusters as needed to contain logicalSize.
389 * Since this is an estimate, we only use it to exaggerate the filesize in the directory entry of a file that is
390 * currently being written (so that the final cluster of the file will be entirely readable if power is lost before
391 * we can could update the directory entry with a new logicalSize).
393 uint32_t physicalSize
;
396 * The cluster that the file pointer is currently within. When seeking to the end of the file, this will be
399 uint32_t cursorCluster
;
402 * The cluster before the one the file pointer is inside. This is set to zero when at the start of the file.
404 uint32_t cursorPreviousCluster
;
406 uint8_t mode
; // A combination of AFATFS_FILE_MODE_* flags
407 uint8_t attrib
; // Combination of FAT_FILE_ATTRIBUTE_* flags for the directory entry of this file
409 /* We hold on to one sector entry in the cache and remember its index here. The cache is invalidated when we
410 * seek across a sector boundary. This allows fwrite() to complete faster because it doesn't need to check the
411 * cache on every call.
413 int8_t writeLockedCacheIndex
;
414 // Ditto for fread():
415 int8_t readRetainCacheIndex
;
417 // The position of our directory entry on the disk (so we can update it without consulting a parent directory file)
418 afatfsDirEntryPointer_t directoryEntryPos
;
420 // The first cluster number of the file, or 0 if this file is empty
421 uint32_t firstCluster
;
423 // State for a queued operation on the file
424 struct afatfsFileOperation_t operation
;
428 AFATFS_INITIALIZATION_READ_MBR
,
429 AFATFS_INITIALIZATION_READ_VOLUME_ID
,
431 #ifdef AFATFS_USE_FREEFILE
432 AFATFS_INITIALIZATION_FREEFILE_CREATE
,
433 AFATFS_INITIALIZATION_FREEFILE_CREATING
,
434 AFATFS_INITIALIZATION_FREEFILE_FAT_SEARCH
,
435 AFATFS_INITIALIZATION_FREEFILE_UPDATE_FAT
,
436 AFATFS_INITIALIZATION_FREEFILE_SAVE_DIR_ENTRY
,
437 AFATFS_INITIALIZATION_FREEFILE_LAST
= AFATFS_INITIALIZATION_FREEFILE_SAVE_DIR_ENTRY
,
440 #ifdef AFATFS_USE_INTROSPECTIVE_LOGGING
441 AFATFS_INITIALIZATION_INTROSPEC_LOG_CREATE
,
442 AFATFS_INITIALIZATION_INTROSPEC_LOG_CREATING
,
445 AFATFS_INITIALIZATION_DONE
446 } afatfsInitializationPhase_e
;
448 typedef struct afatfs_t
{
449 fatFilesystemType_e filesystemType
;
451 afatfsFilesystemState_e filesystemState
;
452 afatfsInitializationPhase_e initPhase
;
454 // State used during FS initialisation where only one member of the union is used at a time
455 #ifdef AFATFS_USE_FREEFILE
457 afatfsFreeSpaceSearch_t freeSpaceSearch
;
458 afatfsFreeSpaceFAT_t freeSpaceFAT
;
465 uint8_t cache
[AFATFS_SECTOR_SIZE
* AFATFS_NUM_CACHE_SECTORS
];
467 afatfsCacheBlockDescriptor_t cacheDescriptor
[AFATFS_NUM_CACHE_SECTORS
];
470 int cacheDirtyEntries
; // The number of cache entries in the AFATFS_CACHE_STATE_DIRTY state
471 bool cacheFlushInProgress
;
473 afatfsFile_t openFiles
[AFATFS_MAX_OPEN_FILES
];
475 #ifdef AFATFS_USE_FREEFILE
476 afatfsFile_t freeFile
;
479 #ifdef AFATFS_USE_INTROSPECTIVE_LOGGING
480 afatfsFile_t introSpecLog
;
483 afatfsError_e lastError
;
487 // The current working directory:
488 afatfsFile_t currentDirectory
;
490 uint32_t partitionStartSector
; // The physical sector that the first partition on the device begins at
492 uint32_t fatStartSector
; // The first sector of the first FAT
493 uint32_t fatSectors
; // The size in sectors of a single FAT
496 * Number of clusters available for storing user data. Note that clusters are numbered starting from 2, so the
497 * index of the last cluster on the volume is numClusters + 1 !!!
499 uint32_t numClusters
;
500 uint32_t clusterStartSector
; // The physical sector that the clusters area begins at
501 uint32_t sectorsPerCluster
;
504 * Number of the cluster we last allocated (i.e. free->occupied). Searches for a free cluster will begin after this
507 uint32_t lastClusterAllocated
;
509 /* Mask to be ANDed with a byte offset within a file to give the offset within the cluster */
510 uint32_t byteInClusterMask
;
512 uint32_t rootDirectoryCluster
; // Present on FAT32 and set to zero for FAT16
513 uint32_t rootDirectorySectors
; // Zero on FAT32, for FAT16 the number of sectors that the root directory occupies
517 static DMA_DATA_ZERO_INIT
uint8_t afatfs_cache
[AFATFS_SECTOR_SIZE
* AFATFS_NUM_CACHE_SECTORS
] __attribute__((aligned(32)));
520 static afatfs_t afatfs
;
522 static void afatfs_fileOperationContinue(afatfsFile_t
*file
);
523 static uint8_t* afatfs_fileLockCursorSectorForWrite(afatfsFilePtr_t file
);
524 static uint8_t* afatfs_fileRetainCursorSectorForRead(afatfsFilePtr_t file
);
526 static uint32_t roundUpTo(uint32_t value
, uint32_t rounding
)
528 uint32_t remainder
= value
% rounding
;
531 value
+= rounding
- remainder
;
537 static bool isPowerOfTwo(unsigned int x
)
539 return ((x
!= 0) && ((x
& (~x
+ 1)) == x
));
543 * Check for conditions that should always be true (and if otherwise mean a bug or a corrupt filesystem).
545 * If the condition is false, the filesystem is marked as being in a fatal state.
547 * Returns the value of the condition.
549 static bool afatfs_assert(bool condition
)
552 if (afatfs
.lastError
== AFATFS_ERROR_NONE
) {
553 afatfs
.lastError
= AFATFS_ERROR_GENERIC
;
555 afatfs
.filesystemState
= AFATFS_FILESYSTEM_STATE_FATAL
;
564 static bool afatfs_fileIsBusy(afatfsFilePtr_t file
)
566 return file
->operation
.operation
!= AFATFS_FILE_OPERATION_NONE
;
570 * The number of FAT table entries that fit within one AFATFS sector size.
572 * Note that this is the same as the number of clusters in an AFATFS supercluster.
574 static uint32_t afatfs_fatEntriesPerSector(void)
576 return afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT32
? AFATFS_FAT32_FAT_ENTRIES_PER_SECTOR
: AFATFS_FAT16_FAT_ENTRIES_PER_SECTOR
;
580 * Size of a FAT cluster in bytes
582 ONLY_EXPOSE_FOR_TESTING
583 uint32_t afatfs_clusterSize(void)
585 return afatfs
.sectorsPerCluster
* AFATFS_SECTOR_SIZE
;
589 * Given a byte offset within a file, return the byte offset of that position within the cluster it belongs to.
591 static uint32_t afatfs_byteIndexInCluster(uint32_t byteOffset
)
593 return afatfs
.byteInClusterMask
& byteOffset
;
597 * Given a byte offset within a file, return the index of the sector within the cluster it belongs to.
599 static uint32_t afatfs_sectorIndexInCluster(uint32_t byteOffset
)
601 return afatfs_byteIndexInCluster(byteOffset
) / AFATFS_SECTOR_SIZE
;
604 // Get the buffer memory for the cache entry of the given index.
605 static uint8_t *afatfs_cacheSectorGetMemory(int cacheEntryIndex
)
607 return afatfs
.cache
+ cacheEntryIndex
* AFATFS_SECTOR_SIZE
;
610 static int afatfs_getCacheDescriptorIndexForBuffer(uint8_t *memory
)
612 int index
= (memory
- afatfs
.cache
) / AFATFS_SECTOR_SIZE
;
614 if (afatfs_assert(index
>= 0 && index
< AFATFS_NUM_CACHE_SECTORS
)) {
621 static afatfsCacheBlockDescriptor_t
* afatfs_getCacheDescriptorForBuffer(uint8_t *memory
)
623 return afatfs
.cacheDescriptor
+ afatfs_getCacheDescriptorIndexForBuffer(memory
);
626 static void afatfs_cacheSectorMarkDirty(afatfsCacheBlockDescriptor_t
*descriptor
)
628 if (descriptor
->state
!= AFATFS_CACHE_STATE_DIRTY
) {
629 descriptor
->writeTimestamp
= ++afatfs
.cacheTimer
;
630 descriptor
->state
= AFATFS_CACHE_STATE_DIRTY
;
631 afatfs
.cacheDirtyEntries
++;
635 static void afatfs_cacheSectorInit(afatfsCacheBlockDescriptor_t
*descriptor
, uint32_t sectorIndex
, bool locked
)
637 descriptor
->sectorIndex
= sectorIndex
;
639 descriptor
->accessTimestamp
= descriptor
->writeTimestamp
= ++afatfs
.cacheTimer
;
641 descriptor
->consecutiveEraseBlockCount
= 0;
643 descriptor
->state
= AFATFS_CACHE_STATE_EMPTY
;
645 descriptor
->locked
= locked
;
646 descriptor
->retainCount
= 0;
647 descriptor
->discardable
= 0;
651 * Called by the SD card driver when one of our read operations completes.
653 static void afatfs_sdcardReadComplete(sdcardBlockOperation_e operation
, uint32_t sectorIndex
, uint8_t *buffer
, uint32_t callbackData
)
658 for (int i
= 0; i
< AFATFS_NUM_CACHE_SECTORS
; i
++) {
659 if (afatfs
.cacheDescriptor
[i
].state
!= AFATFS_CACHE_STATE_EMPTY
660 && afatfs
.cacheDescriptor
[i
].sectorIndex
== sectorIndex
662 if (buffer
== NULL
) {
663 // Read failed, mark the sector as empty and whoever asked for it will ask for it again later to retry
664 afatfs
.cacheDescriptor
[i
].state
= AFATFS_CACHE_STATE_EMPTY
;
666 afatfs_assert(afatfs_cacheSectorGetMemory(i
) == buffer
&& afatfs
.cacheDescriptor
[i
].state
== AFATFS_CACHE_STATE_READING
);
668 afatfs
.cacheDescriptor
[i
].state
= AFATFS_CACHE_STATE_IN_SYNC
;
677 * Called by the SD card driver when one of our write operations completes.
679 static void afatfs_sdcardWriteComplete(sdcardBlockOperation_e operation
, uint32_t sectorIndex
, uint8_t *buffer
, uint32_t callbackData
)
684 afatfs
.cacheFlushInProgress
= false;
686 for (int i
= 0; i
< AFATFS_NUM_CACHE_SECTORS
; i
++) {
687 /* Keep in mind that someone may have marked the sector as dirty after writing had already begun. In this case we must leave
688 * it marked as dirty because those modifications may have been made too late to make it to the disk!
690 if (afatfs
.cacheDescriptor
[i
].sectorIndex
== sectorIndex
691 && afatfs
.cacheDescriptor
[i
].state
== AFATFS_CACHE_STATE_WRITING
693 if (buffer
== NULL
) {
694 // Write failed, remark the sector as dirty
695 afatfs
.cacheDescriptor
[i
].state
= AFATFS_CACHE_STATE_DIRTY
;
696 afatfs
.cacheDirtyEntries
++;
698 afatfs_assert(afatfs_cacheSectorGetMemory(i
) == buffer
);
700 afatfs
.cacheDescriptor
[i
].state
= AFATFS_CACHE_STATE_IN_SYNC
;
708 * Attempt to flush the dirty cache entry with the given index to the SDcard.
710 static void afatfs_cacheFlushSector(int cacheIndex
)
712 afatfsCacheBlockDescriptor_t
*cacheDescriptor
= &afatfs
.cacheDescriptor
[cacheIndex
];
714 #ifdef AFATFS_MIN_MULTIPLE_BLOCK_WRITE_COUNT
715 if (cacheDescriptor
->consecutiveEraseBlockCount
) {
716 sdcard_beginWriteBlocks(cacheDescriptor
->sectorIndex
, cacheDescriptor
->consecutiveEraseBlockCount
);
720 switch (sdcard_writeBlock(cacheDescriptor
->sectorIndex
, afatfs_cacheSectorGetMemory(cacheIndex
), afatfs_sdcardWriteComplete
, 0)) {
721 case SDCARD_OPERATION_IN_PROGRESS
:
722 // The card will call us back later when the buffer transmission finishes
723 afatfs
.cacheDirtyEntries
--;
724 cacheDescriptor
->state
= AFATFS_CACHE_STATE_WRITING
;
725 afatfs
.cacheFlushInProgress
= true;
728 case SDCARD_OPERATION_SUCCESS
:
729 // Buffer is already transmitted
730 afatfs
.cacheDirtyEntries
--;
731 cacheDescriptor
->state
= AFATFS_CACHE_STATE_IN_SYNC
;
734 case SDCARD_OPERATION_BUSY
:
735 case SDCARD_OPERATION_FAILURE
:
741 // Check whether every sector in the cache that can be flushed has been synchronized
742 bool afatfs_sectorCacheInSync(void)
744 for (int i
= 0; i
< AFATFS_NUM_CACHE_SECTORS
; i
++) {
745 if ((afatfs
.cacheDescriptor
[i
].state
== AFATFS_CACHE_STATE_WRITING
) ||
746 ((afatfs
.cacheDescriptor
[i
].state
== AFATFS_CACHE_STATE_DIRTY
) && !afatfs
.cacheDescriptor
[i
].locked
)) {
754 * Find a sector in the cache which corresponds to the given physical sector index, or NULL if the sector isn't
755 * cached. Note that the cached sector could be in any state including completely empty.
757 static afatfsCacheBlockDescriptor_t
* afatfs_findCacheSector(uint32_t sectorIndex
)
759 for (int i
= 0; i
< AFATFS_NUM_CACHE_SECTORS
; i
++) {
760 if (afatfs
.cacheDescriptor
[i
].sectorIndex
== sectorIndex
) {
761 return &afatfs
.cacheDescriptor
[i
];
769 * Find or allocate a cache sector for the given sector index on disk. Returns a block which matches one of these
770 * conditions (in descending order of preference):
772 * - The requested sector that already exists in the cache
773 * - The index of an empty sector
774 * - The index of a synced discardable sector
775 * - The index of the oldest synced sector
777 * Otherwise it returns -1 to signal failure (cache is full!)
779 static int afatfs_allocateCacheSector(uint32_t sectorIndex
)
782 int emptyIndex
= -1, discardableIndex
= -1;
784 uint32_t oldestSyncedSectorLastUse
= 0xFFFFFFFF;
785 int oldestSyncedSectorIndex
= -1;
789 afatfs
.numClusters
== 0 // We're unable to check sector bounds during startup since we haven't read volume label yet
790 || sectorIndex
< afatfs
.clusterStartSector
+ afatfs
.numClusters
* afatfs
.sectorsPerCluster
796 for (int i
= 0; i
< AFATFS_NUM_CACHE_SECTORS
; i
++) {
797 if (afatfs
.cacheDescriptor
[i
].sectorIndex
== sectorIndex
) {
799 * If the sector is actually empty then do a complete re-init of it just like the standard
800 * empty case. (Sectors marked as empty should be treated as if they don't have a block index assigned)
802 if (afatfs
.cacheDescriptor
[i
].state
== AFATFS_CACHE_STATE_EMPTY
) {
807 // Bump the last access time
808 afatfs
.cacheDescriptor
[i
].accessTimestamp
= ++afatfs
.cacheTimer
;
812 switch (afatfs
.cacheDescriptor
[i
].state
) {
813 case AFATFS_CACHE_STATE_EMPTY
:
816 case AFATFS_CACHE_STATE_IN_SYNC
:
817 // Is this a synced sector that we could evict from the cache?
818 if (!afatfs
.cacheDescriptor
[i
].locked
&& afatfs
.cacheDescriptor
[i
].retainCount
== 0) {
819 if (afatfs
.cacheDescriptor
[i
].discardable
) {
820 discardableIndex
= i
;
821 } else if (afatfs
.cacheDescriptor
[i
].accessTimestamp
< oldestSyncedSectorLastUse
) {
822 // This is older than last block we decided to evict, so evict this one in preference
823 oldestSyncedSectorLastUse
= afatfs
.cacheDescriptor
[i
].accessTimestamp
;
824 oldestSyncedSectorIndex
= i
;
833 if (emptyIndex
> -1) {
834 allocateIndex
= emptyIndex
;
835 } else if (discardableIndex
> -1) {
836 allocateIndex
= discardableIndex
;
837 } else if (oldestSyncedSectorIndex
> -1) {
838 allocateIndex
= oldestSyncedSectorIndex
;
843 if (allocateIndex
> -1) {
844 afatfs_cacheSectorInit(&afatfs
.cacheDescriptor
[allocateIndex
], sectorIndex
, false);
847 return allocateIndex
;
851 * Attempt to flush dirty cache pages out to the sdcard, returning true if all flushable data has been flushed.
853 bool afatfs_flush(void)
855 if (afatfs
.cacheDirtyEntries
> 0) {
856 // Flush the oldest flushable sector
857 uint32_t earliestSectorTime
= 0xFFFFFFFF;
858 int earliestSectorIndex
= -1;
860 for (int i
= 0; i
< AFATFS_NUM_CACHE_SECTORS
; i
++) {
861 if (afatfs
.cacheDescriptor
[i
].state
== AFATFS_CACHE_STATE_DIRTY
&& !afatfs
.cacheDescriptor
[i
].locked
862 && (earliestSectorIndex
== -1 || afatfs
.cacheDescriptor
[i
].writeTimestamp
< earliestSectorTime
)
864 earliestSectorIndex
= i
;
865 earliestSectorTime
= afatfs
.cacheDescriptor
[i
].writeTimestamp
;
869 if (earliestSectorIndex
> -1) {
870 afatfs_cacheFlushSector(earliestSectorIndex
);
872 // That flush will take time to complete so we may as well tell caller to come back later
881 * Returns true if either the freefile or the regular cluster pool has been exhausted during a previous write operation.
883 bool afatfs_isFull(void)
885 return afatfs
.filesystemFull
;
889 * Get the physical sector number that corresponds to the FAT sector of the given fatSectorIndex within the given
890 * FAT (fatIndex may be 0 or 1). (0, 0) gives the first sector of the first FAT.
892 static uint32_t afatfs_fatSectorToPhysical(int fatIndex
, uint32_t fatSectorIndex
)
894 return afatfs
.fatStartSector
+ (fatIndex
? afatfs
.fatSectors
: 0) + fatSectorIndex
;
897 static uint32_t afatfs_fileClusterToPhysical(uint32_t clusterNumber
, uint32_t sectorIndex
)
899 return afatfs
.clusterStartSector
+ (clusterNumber
- 2) * afatfs
.sectorsPerCluster
+ sectorIndex
;
902 static uint32_t afatfs_fileGetCursorPhysicalSector(afatfsFilePtr_t file
)
904 if (file
->type
== AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY
) {
905 return afatfs
.fatStartSector
+ AFATFS_NUM_FATS
* afatfs
.fatSectors
+ file
->cursorOffset
/ AFATFS_SECTOR_SIZE
;
907 uint32_t cursorSectorInCluster
= afatfs_sectorIndexInCluster(file
->cursorOffset
);
908 return afatfs_fileClusterToPhysical(file
->cursorCluster
, cursorSectorInCluster
);
913 * Sector here is the sector index within the cluster.
915 static void afatfs_fileGetCursorClusterAndSector(afatfsFilePtr_t file
, uint32_t *cluster
, uint16_t *sector
)
917 *cluster
= file
->cursorCluster
;
919 if (file
->type
== AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY
) {
920 *sector
= file
->cursorOffset
/ AFATFS_SECTOR_SIZE
;
922 *sector
= afatfs_sectorIndexInCluster(file
->cursorOffset
);
927 * Get a cache entry for the given sector and store a pointer to the cached memory in *buffer.
929 * physicalSectorIndex - The index of the sector in the SD card to cache
930 * sectorflags - A union of AFATFS_CACHE_* constants that says which operations the sector will be cached for.
931 * buffer - A pointer to the 512-byte memory buffer for the sector will be stored here upon success
932 * eraseCount - For write operations, set to a non-zero number to hint that we plan to write that many sectors
933 * consecutively (including this sector)
936 * AFATFS_OPERATION_SUCCESS - On success
937 * AFATFS_OPERATION_IN_PROGRESS - Card is busy, call again later
938 * AFATFS_OPERATION_FAILURE - When the filesystem encounters a fatal error
940 static afatfsOperationStatus_e
afatfs_cacheSector(uint32_t physicalSectorIndex
, uint8_t **buffer
, uint8_t sectorFlags
, uint32_t eraseCount
)
942 // We never write to the MBR, so any attempt to write there is an asyncfatfs bug
943 if (!afatfs_assert((sectorFlags
& AFATFS_CACHE_WRITE
) == 0 || physicalSectorIndex
!= 0)) {
944 return AFATFS_OPERATION_FAILURE
;
947 int cacheSectorIndex
= afatfs_allocateCacheSector(physicalSectorIndex
);
949 if (cacheSectorIndex
== -1) {
950 // We don't have enough free cache to service this request right now, try again later
951 return AFATFS_OPERATION_IN_PROGRESS
;
954 switch (afatfs
.cacheDescriptor
[cacheSectorIndex
].state
) {
955 case AFATFS_CACHE_STATE_READING
:
956 return AFATFS_OPERATION_IN_PROGRESS
;
959 case AFATFS_CACHE_STATE_EMPTY
:
960 if ((sectorFlags
& AFATFS_CACHE_READ
) != 0) {
961 if (sdcard_readBlock(physicalSectorIndex
, afatfs_cacheSectorGetMemory(cacheSectorIndex
), afatfs_sdcardReadComplete
, 0)) {
962 afatfs
.cacheDescriptor
[cacheSectorIndex
].state
= AFATFS_CACHE_STATE_READING
;
964 return AFATFS_OPERATION_IN_PROGRESS
;
967 // We only get to decide these fields if we're the first ones to cache the sector:
968 afatfs
.cacheDescriptor
[cacheSectorIndex
].discardable
= (sectorFlags
& AFATFS_CACHE_DISCARDABLE
) != 0 ? 1 : 0;
970 #ifdef AFATFS_MIN_MULTIPLE_BLOCK_WRITE_COUNT
971 // Don't bother pre-erasing for small block sequences
972 if (eraseCount
< AFATFS_MIN_MULTIPLE_BLOCK_WRITE_COUNT
) {
975 eraseCount
= MIN(eraseCount
, (uint32_t)UINT16_MAX
); // If caller asked for a longer chain of sectors we silently truncate that here
978 afatfs
.cacheDescriptor
[cacheSectorIndex
].consecutiveEraseBlockCount
= eraseCount
;
983 case AFATFS_CACHE_STATE_WRITING
:
984 case AFATFS_CACHE_STATE_IN_SYNC
:
985 if ((sectorFlags
& AFATFS_CACHE_WRITE
) != 0) {
986 afatfs_cacheSectorMarkDirty(&afatfs
.cacheDescriptor
[cacheSectorIndex
]);
990 case AFATFS_CACHE_STATE_DIRTY
:
991 if ((sectorFlags
& AFATFS_CACHE_LOCK
) != 0) {
992 afatfs
.cacheDescriptor
[cacheSectorIndex
].locked
= 1;
994 if ((sectorFlags
& AFATFS_CACHE_RETAIN
) != 0) {
995 afatfs
.cacheDescriptor
[cacheSectorIndex
].retainCount
++;
998 *buffer
= afatfs_cacheSectorGetMemory(cacheSectorIndex
);
1000 return AFATFS_OPERATION_SUCCESS
;
1004 // Cache block in unknown state, should never happen
1005 afatfs_assert(false);
1006 return AFATFS_OPERATION_FAILURE
;
1011 * Parse the details out of the given MBR sector (512 bytes long). Return true if a compatible filesystem was found.
1013 static bool afatfs_parseMBR(const uint8_t *sector
)
1015 // Check MBR signature
1016 if (sector
[AFATFS_SECTOR_SIZE
- 2] != 0x55 || sector
[AFATFS_SECTOR_SIZE
- 1] != 0xAA)
1019 mbrPartitionEntry_t
*partition
= (mbrPartitionEntry_t
*) (sector
+ 446);
1021 for (int i
= 0; i
< 4; i
++) {
1023 partition
[i
].lbaBegin
> 0
1025 partition
[i
].type
== MBR_PARTITION_TYPE_FAT32
1026 || partition
[i
].type
== MBR_PARTITION_TYPE_FAT32_LBA
1027 || partition
[i
].type
== MBR_PARTITION_TYPE_FAT16
1028 || partition
[i
].type
== MBR_PARTITION_TYPE_FAT16_LBA
1031 afatfs
.partitionStartSector
= partition
[i
].lbaBegin
;
1040 static bool afatfs_parseVolumeID(const uint8_t *sector
)
1042 fatVolumeID_t
*volume
= (fatVolumeID_t
*) sector
;
1044 afatfs
.filesystemType
= FAT_FILESYSTEM_TYPE_INVALID
;
1046 if (volume
->bytesPerSector
!= AFATFS_SECTOR_SIZE
|| volume
->numFATs
!= AFATFS_NUM_FATS
1047 || sector
[510] != FAT_VOLUME_ID_SIGNATURE_1
|| sector
[511] != FAT_VOLUME_ID_SIGNATURE_2
) {
1051 afatfs
.fatStartSector
= afatfs
.partitionStartSector
+ volume
->reservedSectorCount
;
1053 afatfs
.sectorsPerCluster
= volume
->sectorsPerCluster
;
1054 if (afatfs
.sectorsPerCluster
< 1 || afatfs
.sectorsPerCluster
> 128 || !isPowerOfTwo(afatfs
.sectorsPerCluster
)) {
1058 afatfs
.byteInClusterMask
= AFATFS_SECTOR_SIZE
* afatfs
.sectorsPerCluster
- 1;
1060 afatfs
.fatSectors
= volume
->FATSize16
!= 0 ? volume
->FATSize16
: volume
->fatDescriptor
.fat32
.FATSize32
;
1062 // Always zero on FAT32 since rootEntryCount is always zero (this is non-zero on FAT16)
1063 afatfs
.rootDirectorySectors
= ((volume
->rootEntryCount
* FAT_DIRECTORY_ENTRY_SIZE
) + (volume
->bytesPerSector
- 1)) / volume
->bytesPerSector
;
1064 uint32_t totalSectors
= volume
->totalSectors16
!= 0 ? volume
->totalSectors16
: volume
->totalSectors32
;
1065 uint32_t dataSectors
= totalSectors
- (volume
->reservedSectorCount
+ (AFATFS_NUM_FATS
* afatfs
.fatSectors
) + afatfs
.rootDirectorySectors
);
1067 afatfs
.numClusters
= dataSectors
/ volume
->sectorsPerCluster
;
1069 if (afatfs
.numClusters
<= FAT12_MAX_CLUSTERS
) {
1070 afatfs
.filesystemType
= FAT_FILESYSTEM_TYPE_FAT12
;
1072 return false; // FAT12 is not a supported filesystem
1073 } else if (afatfs
.numClusters
<= FAT16_MAX_CLUSTERS
) {
1074 afatfs
.filesystemType
= FAT_FILESYSTEM_TYPE_FAT16
;
1076 afatfs
.filesystemType
= FAT_FILESYSTEM_TYPE_FAT32
;
1079 if (afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT32
) {
1080 afatfs
.rootDirectoryCluster
= volume
->fatDescriptor
.fat32
.rootCluster
;
1082 // FAT16 doesn't store the root directory in clusters
1083 afatfs
.rootDirectoryCluster
= 0;
1086 uint32_t endOfFATs
= afatfs
.fatStartSector
+ AFATFS_NUM_FATS
* afatfs
.fatSectors
;
1088 afatfs
.clusterStartSector
= endOfFATs
+ afatfs
.rootDirectorySectors
;
1094 * Get the position of the FAT entry for the cluster with the given number.
1096 static void afatfs_getFATPositionForCluster(uint32_t cluster
, uint32_t *fatSectorIndex
, uint32_t *fatSectorEntryIndex
)
1098 if (afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT16
) {
1099 uint32_t entriesPerFATSector
= AFATFS_SECTOR_SIZE
/ sizeof(uint16_t);
1101 *fatSectorIndex
= cluster
/ entriesPerFATSector
;
1102 *fatSectorEntryIndex
= cluster
& (entriesPerFATSector
- 1);
1104 uint32_t entriesPerFATSector
= AFATFS_SECTOR_SIZE
/ sizeof(uint32_t);
1106 *fatSectorIndex
= fat32_decodeClusterNumber(cluster
) / entriesPerFATSector
;
1107 *fatSectorEntryIndex
= cluster
& (entriesPerFATSector
- 1);
1111 static bool afatfs_FATIsEndOfChainMarker(uint32_t clusterNumber
)
1113 if (afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT32
) {
1114 return fat32_isEndOfChainMarker(clusterNumber
);
1116 return fat16_isEndOfChainMarker(clusterNumber
);
1121 * Look up the FAT to find out which cluster follows the one with the given number and store it into *nextCluster.
1123 * Use fat_isFreeSpace() and fat_isEndOfChainMarker() on nextCluster to distinguish those special values from regular
1126 * Note that if you're trying to find the next cluster of a file, you should be calling afatfs_fileGetNextCluster()
1127 * instead, as that one supports contiguous freefile-based files (which needn't consult the FAT).
1130 * AFATFS_OPERATION_IN_PROGRESS - FS is busy right now, call again later
1131 * AFATFS_OPERATION_SUCCESS - *nextCluster is set to the next cluster number
1133 static afatfsOperationStatus_e
afatfs_FATGetNextCluster(int fatIndex
, uint32_t cluster
, uint32_t *nextCluster
)
1135 uint32_t fatSectorIndex
, fatSectorEntryIndex
;
1136 afatfsFATSector_t sector
;
1138 afatfs_getFATPositionForCluster(cluster
, &fatSectorIndex
, &fatSectorEntryIndex
);
1140 afatfsOperationStatus_e result
= afatfs_cacheSector(afatfs_fatSectorToPhysical(fatIndex
, fatSectorIndex
), §or
.bytes
, AFATFS_CACHE_READ
, 0);
1142 if (result
== AFATFS_OPERATION_SUCCESS
) {
1143 if (afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT16
) {
1144 *nextCluster
= sector
.fat16
[fatSectorEntryIndex
];
1146 *nextCluster
= fat32_decodeClusterNumber(sector
.fat32
[fatSectorEntryIndex
]);
1154 * Set the cluster number that follows the given cluster. Pass 0xFFFFFFFF for nextCluster to terminate the FAT chain.
1157 * AFATFS_OPERATION_SUCCESS - On success
1158 * AFATFS_OPERATION_IN_PROGRESS - Card is busy, call again later
1159 * AFATFS_OPERATION_FAILURE - When the filesystem encounters a fatal error
1161 static afatfsOperationStatus_e
afatfs_FATSetNextCluster(uint32_t startCluster
, uint32_t nextCluster
)
1163 afatfsFATSector_t sector
;
1164 uint32_t fatSectorIndex
, fatSectorEntryIndex
, fatPhysicalSector
;
1165 afatfsOperationStatus_e result
;
1168 afatfs_assert(startCluster
>= FAT_SMALLEST_LEGAL_CLUSTER_NUMBER
);
1171 afatfs_getFATPositionForCluster(startCluster
, &fatSectorIndex
, &fatSectorEntryIndex
);
1173 fatPhysicalSector
= afatfs_fatSectorToPhysical(0, fatSectorIndex
);
1175 result
= afatfs_cacheSector(fatPhysicalSector
, §or
.bytes
, AFATFS_CACHE_READ
| AFATFS_CACHE_WRITE
, 0);
1177 if (result
== AFATFS_OPERATION_SUCCESS
) {
1178 if (afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT16
) {
1179 sector
.fat16
[fatSectorEntryIndex
] = nextCluster
;
1181 sector
.fat32
[fatSectorEntryIndex
] = nextCluster
;
1189 * Bring the logical filesize up to date with the current cursor position.
1191 static void afatfs_fileUpdateFilesize(afatfsFile_t
*file
)
1193 file
->logicalSize
= MAX(file
->logicalSize
, file
->cursorOffset
);
1196 static void afatfs_fileUnlockCacheSector(afatfsFilePtr_t file
)
1198 if (file
->writeLockedCacheIndex
!= -1) {
1199 afatfs
.cacheDescriptor
[file
->writeLockedCacheIndex
].locked
= 0;
1200 file
->writeLockedCacheIndex
= -1;
1202 if (file
->readRetainCacheIndex
!= -1) {
1203 afatfs
.cacheDescriptor
[file
->readRetainCacheIndex
].retainCount
= MAX((int) afatfs
.cacheDescriptor
[file
->readRetainCacheIndex
].retainCount
- 1, 0);
1204 file
->readRetainCacheIndex
= -1;
1209 * Starting from and including the given cluster number, find the number of the first cluster which matches the given
1212 * searchLimit - Last cluster to examine (exclusive). To search the entire volume, pass:
1213 * afatfs.numClusters + FAT_SMALLEST_LEGAL_CLUSTER_NUMBER
1216 * CLUSTER_SEARCH_FREE_AT_BEGINNING_OF_FAT_SECTOR - Find a cluster marked as free in the FAT which lies at the
1217 * beginning of its FAT sector. The passed initial search 'cluster' must correspond to the first entry of a FAT sector.
1218 * CLUSTER_SEARCH_FREE - Find a cluster marked as free in the FAT
1219 * CLUSTER_SEARCH_OCCUPIED - Find a cluster marked as occupied in the FAT.
1222 * AFATFS_FIND_CLUSTER_FOUND - A cluster matching the criteria was found and stored in *cluster
1223 * AFATFS_FIND_CLUSTER_IN_PROGRESS - The search is not over, call this routine again later with the updated *cluster value to resume
1224 * AFATFS_FIND_CLUSTER_FATAL - An unexpected read error occurred, the volume should be abandoned
1225 * AFATFS_FIND_CLUSTER_NOT_FOUND - The entire device was searched without finding a suitable cluster (the
1226 * *cluster points to just beyond the final cluster).
1228 static afatfsFindClusterStatus_e
afatfs_findClusterWithCondition(afatfsClusterSearchCondition_e condition
, uint32_t *cluster
, uint32_t searchLimit
)
1230 afatfsFATSector_t sector
;
1231 uint32_t fatSectorIndex
, fatSectorEntryIndex
;
1233 uint32_t fatEntriesPerSector
= afatfs_fatEntriesPerSector();
1234 bool lookingForFree
= condition
== CLUSTER_SEARCH_FREE_AT_BEGINNING_OF_FAT_SECTOR
|| condition
== CLUSTER_SEARCH_FREE
;
1238 // Get the FAT entry which corresponds to this cluster so we can begin our search there
1239 afatfs_getFATPositionForCluster(*cluster
, &fatSectorIndex
, &fatSectorEntryIndex
);
1241 switch (condition
) {
1242 case CLUSTER_SEARCH_FREE_AT_BEGINNING_OF_FAT_SECTOR
:
1243 jump
= fatEntriesPerSector
;
1245 // We're supposed to call this routine with the cluster properly aligned
1246 if (!afatfs_assert(fatSectorEntryIndex
== 0)) {
1247 return AFATFS_FIND_CLUSTER_FATAL
;
1250 case CLUSTER_SEARCH_OCCUPIED
:
1251 case CLUSTER_SEARCH_FREE
:
1255 afatfs_assert(false);
1256 return AFATFS_FIND_CLUSTER_FATAL
;
1259 while (*cluster
< searchLimit
) {
1261 #ifdef AFATFS_USE_FREEFILE
1262 // If we're looking inside the freefile, we won't find any free clusters! Skip it!
1263 if (afatfs
.freeFile
.logicalSize
> 0 && *cluster
== afatfs
.freeFile
.firstCluster
) {
1264 *cluster
+= (afatfs
.freeFile
.logicalSize
+ afatfs_clusterSize() - 1) / afatfs_clusterSize();
1266 // Maintain alignment
1267 *cluster
= roundUpTo(*cluster
, jump
);
1268 continue; // Go back to check that the new cluster number is within the volume
1272 afatfsOperationStatus_e status
= afatfs_cacheSector(afatfs_fatSectorToPhysical(0, fatSectorIndex
), §or
.bytes
, AFATFS_CACHE_READ
| AFATFS_CACHE_DISCARDABLE
, 0);
1275 case AFATFS_OPERATION_SUCCESS
:
1277 uint32_t clusterNumber
;
1279 switch (afatfs
.filesystemType
) {
1280 case FAT_FILESYSTEM_TYPE_FAT16
:
1281 clusterNumber
= sector
.fat16
[fatSectorEntryIndex
];
1283 case FAT_FILESYSTEM_TYPE_FAT32
:
1284 clusterNumber
= fat32_decodeClusterNumber(sector
.fat32
[fatSectorEntryIndex
]);
1287 return AFATFS_FIND_CLUSTER_FATAL
;
1290 if (fat_isFreeSpace(clusterNumber
) == lookingForFree
) {
1292 * The final FAT sector may have fewer than fatEntriesPerSector entries in it, so we need to
1293 * check the cluster number is valid here before we report a bogus success!
1295 if (*cluster
< searchLimit
) {
1296 return AFATFS_FIND_CLUSTER_FOUND
;
1298 *cluster
= searchLimit
;
1299 return AFATFS_FIND_CLUSTER_NOT_FOUND
;
1304 fatSectorEntryIndex
+= jump
;
1305 } while (fatSectorEntryIndex
< fatEntriesPerSector
);
1307 // Move on to the next FAT sector
1309 fatSectorEntryIndex
= 0;
1311 case AFATFS_OPERATION_FAILURE
:
1312 return AFATFS_FIND_CLUSTER_FATAL
;
1314 case AFATFS_OPERATION_IN_PROGRESS
:
1315 return AFATFS_FIND_CLUSTER_IN_PROGRESS
;
1320 // We looked at every available cluster and didn't find one matching the condition
1321 *cluster
= searchLimit
;
1322 return AFATFS_FIND_CLUSTER_NOT_FOUND
;
1326 * Get the cluster that follows the currentCluster in the FAT chain for the given file.
1329 * AFATFS_OPERATION_IN_PROGRESS - FS is busy right now, call again later
1330 * AFATFS_OPERATION_SUCCESS - *nextCluster is set to the next cluster number
1332 static afatfsOperationStatus_e
afatfs_fileGetNextCluster(afatfsFilePtr_t file
, uint32_t currentCluster
, uint32_t *nextCluster
)
1334 #ifndef AFATFS_USE_FREEFILE
1337 if ((file
->mode
& AFATFS_FILE_MODE_CONTIGUOUS
) != 0) {
1338 uint32_t freeFileStart
= afatfs
.freeFile
.firstCluster
;
1340 afatfs_assert(currentCluster
+ 1 <= freeFileStart
);
1342 // Would the next cluster lie outside the allocated file? (i.e. beyond the end of the file into the start of the freefile)
1343 if (currentCluster
+ 1 == freeFileStart
) {
1346 *nextCluster
= currentCluster
+ 1;
1349 return AFATFS_OPERATION_SUCCESS
;
1353 return afatfs_FATGetNextCluster(0, currentCluster
, nextCluster
);
1357 #ifdef AFATFS_USE_FREEFILE
1360 * Update the FAT to fill the contiguous series of clusters with indexes [*startCluster...endCluster) with the
1361 * specified pattern.
1363 * AFATFS_FAT_PATTERN_TERMINATED_CHAIN - Chain the clusters together in linear sequence and terminate the final cluster
1364 * AFATFS_FAT_PATTERN_CHAIN - Chain the clusters together without terminating the final entry
1365 * AFATFS_FAT_PATTERN_FREE - Mark the clusters as free space
1368 * AFATFS_OPERATION_SUCCESS - When the entire chain has been written
1369 * AFATFS_OPERATION_IN_PROGRESS - Call again later with the updated *startCluster value in order to resume writing.
1371 static afatfsOperationStatus_e
afatfs_FATFillWithPattern(afatfsFATPattern_e pattern
, uint32_t *startCluster
, uint32_t endCluster
)
1373 afatfsFATSector_t sector
;
1374 uint32_t fatSectorIndex
, firstEntryIndex
, fatPhysicalSector
;
1375 uint8_t fatEntrySize
;
1376 uint32_t nextCluster
;
1377 afatfsOperationStatus_e result
;
1378 uint32_t eraseSectorCount
;
1380 // Find the position of the initial cluster to begin our fill
1381 afatfs_getFATPositionForCluster(*startCluster
, &fatSectorIndex
, &firstEntryIndex
);
1383 fatPhysicalSector
= afatfs_fatSectorToPhysical(0, fatSectorIndex
);
1385 // How many consecutive FAT sectors will we be overwriting?
1386 eraseSectorCount
= (endCluster
- *startCluster
+ firstEntryIndex
+ afatfs_fatEntriesPerSector() - 1) / afatfs_fatEntriesPerSector();
1388 while (*startCluster
< endCluster
) {
1389 // The last entry we will fill inside this sector (exclusive):
1390 uint32_t lastEntryIndex
= MIN(firstEntryIndex
+ (endCluster
- *startCluster
), afatfs_fatEntriesPerSector());
1392 uint8_t cacheFlags
= AFATFS_CACHE_WRITE
| AFATFS_CACHE_DISCARDABLE
;
1394 if (firstEntryIndex
> 0 || lastEntryIndex
< afatfs_fatEntriesPerSector()) {
1395 // We're not overwriting the entire FAT sector so we must read the existing contents
1396 cacheFlags
|= AFATFS_CACHE_READ
;
1399 result
= afatfs_cacheSector(fatPhysicalSector
, §or
.bytes
, cacheFlags
, eraseSectorCount
);
1401 if (result
!= AFATFS_OPERATION_SUCCESS
) {
1405 #ifdef AFATFS_DEBUG_VERBOSE
1406 if (pattern
== AFATFS_FAT_PATTERN_FREE
) {
1407 fprintf(stderr
, "Marking cluster %u to %u as free in FAT sector %u...\n", *startCluster
, endCluster
, fatPhysicalSector
);
1409 fprintf(stderr
, "Writing FAT chain from cluster %u to %u in FAT sector %u...\n", *startCluster
, endCluster
, fatPhysicalSector
);
1414 case AFATFS_FAT_PATTERN_TERMINATED_CHAIN
:
1415 case AFATFS_FAT_PATTERN_UNTERMINATED_CHAIN
:
1416 nextCluster
= *startCluster
+ 1;
1417 // Write all the "next cluster" pointers
1418 if (afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT16
) {
1419 for (uint32_t i
= firstEntryIndex
; i
< lastEntryIndex
; i
++, nextCluster
++) {
1420 sector
.fat16
[i
] = nextCluster
;
1423 for (uint32_t i
= firstEntryIndex
; i
< lastEntryIndex
; i
++, nextCluster
++) {
1424 sector
.fat32
[i
] = nextCluster
;
1428 *startCluster
+= lastEntryIndex
- firstEntryIndex
;
1430 if (pattern
== AFATFS_FAT_PATTERN_TERMINATED_CHAIN
&& *startCluster
== endCluster
) {
1431 // We completed the chain! Overwrite the last entry we wrote with the terminator for the end of the chain
1432 if (afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT16
) {
1433 sector
.fat16
[lastEntryIndex
- 1] = 0xFFFF;
1435 sector
.fat32
[lastEntryIndex
- 1] = 0xFFFFFFFF;
1440 case AFATFS_FAT_PATTERN_FREE
:
1441 fatEntrySize
= afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT16
? sizeof(uint16_t) : sizeof(uint32_t);
1443 memset(sector
.bytes
+ firstEntryIndex
* fatEntrySize
, 0, (lastEntryIndex
- firstEntryIndex
) * fatEntrySize
);
1445 *startCluster
+= lastEntryIndex
- firstEntryIndex
;
1449 fatPhysicalSector
++;
1451 firstEntryIndex
= 0;
1454 return AFATFS_OPERATION_SUCCESS
;
1460 * Write the directory entry for the file into its `directoryEntryPos` position in its containing directory.
1463 * AFATFS_SAVE_DIRECTORY_NORMAL - Store the file's physical size, not the logical size, in the directory entry
1464 * AFATFS_SAVE_DIRECTORY_FOR_CLOSE - We're done extending the file so we can write the logical size now.
1465 * AFATFS_SAVE_DIRECTORY_DELETED - Mark the directory entry as deleted
1468 * AFATFS_OPERATION_SUCCESS - The directory entry has been stored into the directory sector in cache.
1469 * AFATFS_OPERATION_IN_PROGRESS - Cache is too busy, retry later
1470 * AFATFS_OPERATION_FAILURE - If the filesystem enters the fatal state
1472 static afatfsOperationStatus_e
afatfs_saveDirectoryEntry(afatfsFilePtr_t file
, afatfsSaveDirectoryEntryMode_e mode
)
1475 afatfsOperationStatus_e result
;
1477 if (file
->directoryEntryPos
.sectorNumberPhysical
== 0) {
1478 return AFATFS_OPERATION_SUCCESS
; // Root directories don't have a directory entry
1481 result
= afatfs_cacheSector(file
->directoryEntryPos
.sectorNumberPhysical
, §or
, AFATFS_CACHE_READ
| AFATFS_CACHE_WRITE
, 0);
1483 #ifdef AFATFS_DEBUG_VERBOSE
1484 fprintf(stderr
, "Saving directory entry to sector %u...\n", file
->directoryEntryPos
.sectorNumberPhysical
);
1487 if (result
== AFATFS_OPERATION_SUCCESS
) {
1488 if (afatfs_assert(file
->directoryEntryPos
.entryIndex
>= 0)) {
1489 fatDirectoryEntry_t
*entry
= (fatDirectoryEntry_t
*) sector
+ file
->directoryEntryPos
.entryIndex
;
1492 case AFATFS_SAVE_DIRECTORY_NORMAL
:
1493 /* We exaggerate the length of the written file so that if power is lost, the end of the file will
1494 * still be readable (though the very tail of the file will be uninitialized data).
1496 * This way we can avoid updating the directory entry too many times during fwrites() on the file.
1498 entry
->fileSize
= file
->physicalSize
;
1500 case AFATFS_SAVE_DIRECTORY_DELETED
:
1501 entry
->filename
[0] = FAT_DELETED_FILE_MARKER
;
1504 case AFATFS_SAVE_DIRECTORY_FOR_CLOSE
:
1505 // We write the true length of the file on close.
1506 entry
->fileSize
= file
->logicalSize
;
1509 // (sub)directories don't store a filesize in their directory entry:
1510 if (file
->type
== AFATFS_FILE_TYPE_DIRECTORY
) {
1511 entry
->fileSize
= 0;
1514 entry
->firstClusterHigh
= file
->firstCluster
>> 16;
1515 entry
->firstClusterLow
= file
->firstCluster
& 0xFFFF;
1517 return AFATFS_OPERATION_FAILURE
;
1525 * Attempt to add a free cluster to the end of the given file. If the file was previously empty, the directory entry
1526 * is updated to point to the new cluster.
1529 * AFATFS_OPERATION_SUCCESS - The cluster has been appended
1530 * AFATFS_OPERATION_IN_PROGRESS - Cache was busy, so call again later to continue
1531 * AFATFS_OPERATION_FAILURE - Cluster could not be appended because the filesystem ran out of space
1532 * (afatfs.filesystemFull is set to true)
1534 * If the file's operation was AFATFS_FILE_OPERATION_APPEND_FREE_CLUSTER, the file operation is cleared upon completion,
1535 * otherwise it is left alone so that this operation can be called as a sub-operation of some other operation on the
1538 static afatfsOperationStatus_e
afatfs_appendRegularFreeClusterContinue(afatfsFile_t
*file
)
1540 afatfsAppendFreeCluster_t
*opState
= &file
->operation
.state
.appendFreeCluster
;
1541 afatfsOperationStatus_e status
;
1545 switch (opState
->phase
) {
1546 case AFATFS_APPEND_FREE_CLUSTER_PHASE_FIND_FREESPACE
:
1547 switch (afatfs_findClusterWithCondition(CLUSTER_SEARCH_FREE
, &opState
->searchCluster
, afatfs
.numClusters
+ FAT_SMALLEST_LEGAL_CLUSTER_NUMBER
)) {
1548 case AFATFS_FIND_CLUSTER_FOUND
:
1549 afatfs
.lastClusterAllocated
= opState
->searchCluster
;
1551 // Make the cluster available for us to write in
1552 file
->cursorCluster
= opState
->searchCluster
;
1553 file
->physicalSize
+= afatfs_clusterSize();
1555 if (opState
->previousCluster
== 0) {
1556 // This is the new first cluster in the file
1557 file
->firstCluster
= opState
->searchCluster
;
1560 opState
->phase
= AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FAT1
;
1563 case AFATFS_FIND_CLUSTER_FATAL
:
1564 case AFATFS_FIND_CLUSTER_NOT_FOUND
:
1565 // We couldn't find an empty cluster to append to the file
1566 opState
->phase
= AFATFS_APPEND_FREE_CLUSTER_PHASE_FAILURE
;
1569 case AFATFS_FIND_CLUSTER_IN_PROGRESS
:
1573 case AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FAT1
:
1574 // Terminate the new cluster
1575 status
= afatfs_FATSetNextCluster(opState
->searchCluster
, 0xFFFFFFFF);
1577 if (status
== AFATFS_OPERATION_SUCCESS
) {
1578 if (opState
->previousCluster
) {
1579 opState
->phase
= AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FAT2
;
1581 opState
->phase
= AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FILE_DIRECTORY
;
1587 case AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FAT2
:
1588 // Add the new cluster to the pre-existing chain
1589 status
= afatfs_FATSetNextCluster(opState
->previousCluster
, opState
->searchCluster
);
1591 if (status
== AFATFS_OPERATION_SUCCESS
) {
1592 opState
->phase
= AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FILE_DIRECTORY
;
1596 case AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FILE_DIRECTORY
:
1597 if (afatfs_saveDirectoryEntry(file
, AFATFS_SAVE_DIRECTORY_NORMAL
) == AFATFS_OPERATION_SUCCESS
) {
1598 opState
->phase
= AFATFS_APPEND_FREE_CLUSTER_PHASE_COMPLETE
;
1602 case AFATFS_APPEND_FREE_CLUSTER_PHASE_COMPLETE
:
1603 if (file
->operation
.operation
== AFATFS_FILE_OPERATION_APPEND_FREE_CLUSTER
) {
1604 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
1607 return AFATFS_OPERATION_SUCCESS
;
1609 case AFATFS_APPEND_FREE_CLUSTER_PHASE_FAILURE
:
1610 if (file
->operation
.operation
== AFATFS_FILE_OPERATION_APPEND_FREE_CLUSTER
) {
1611 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
1614 afatfs
.filesystemFull
= true;
1615 return AFATFS_OPERATION_FAILURE
;
1619 return AFATFS_OPERATION_IN_PROGRESS
;
1622 static void afatfs_appendRegularFreeClusterInitOperationState(afatfsAppendFreeCluster_t
*state
, uint32_t previousCluster
)
1624 state
->phase
= AFATFS_APPEND_FREE_CLUSTER_PHASE_INITIAL
;
1625 state
->previousCluster
= previousCluster
;
1626 state
->searchCluster
= afatfs
.lastClusterAllocated
;
1630 * Queue up an operation to append a free cluster to the file and update the file's cursorCluster to point to it.
1632 * You must seek to the end of the file first, so file.cursorCluster will be 0 for the first call, and
1633 * `file.cursorPreviousCluster` will be the cluster to append after.
1635 * Note that the cursorCluster will be updated before this operation is completely finished (i.e. before the FAT is
1636 * updated) but you can go ahead and write to it before the operation succeeds.
1639 * AFATFS_OPERATION_SUCCESS - The append completed successfully
1640 * AFATFS_OPERATION_IN_PROGRESS - The operation was queued on the file and will complete later
1641 * AFATFS_OPERATION_FAILURE - Operation could not be queued or append failed, check afatfs.fileSystemFull
1643 static afatfsOperationStatus_e
afatfs_appendRegularFreeCluster(afatfsFilePtr_t file
)
1645 if (file
->operation
.operation
== AFATFS_FILE_OPERATION_APPEND_FREE_CLUSTER
)
1646 return AFATFS_OPERATION_IN_PROGRESS
;
1648 if (afatfs
.filesystemFull
|| afatfs_fileIsBusy(file
)) {
1649 return AFATFS_OPERATION_FAILURE
;
1652 file
->operation
.operation
= AFATFS_FILE_OPERATION_APPEND_FREE_CLUSTER
;
1654 afatfs_appendRegularFreeClusterInitOperationState(&file
->operation
.state
.appendFreeCluster
, file
->cursorPreviousCluster
);
1656 return afatfs_appendRegularFreeClusterContinue(file
);
1659 #ifdef AFATFS_USE_FREEFILE
1662 * Size of a AFATFS supercluster in bytes
1664 ONLY_EXPOSE_FOR_TESTING
1665 uint32_t afatfs_superClusterSize(void)
1667 return afatfs_fatEntriesPerSector() * afatfs_clusterSize();
1671 * Continue to attempt to add a supercluster to the end of the given file.
1673 * If the file operation was set to AFATFS_FILE_OPERATION_APPEND_SUPERCLUSTER and the operation completes, the file's
1674 * operation is cleared.
1677 * AFATFS_OPERATION_SUCCESS - On completion
1678 * AFATFS_OPERATION_IN_PROGRESS - Operation still in progress
1680 static afatfsOperationStatus_e
afatfs_appendSuperclusterContinue(afatfsFile_t
*file
)
1682 afatfsAppendSupercluster_t
*opState
= &file
->operation
.state
.appendSupercluster
;
1684 afatfsOperationStatus_e status
= AFATFS_OPERATION_FAILURE
;
1687 switch (opState
->phase
) {
1688 case AFATFS_APPEND_SUPERCLUSTER_PHASE_INIT
:
1689 // Our file steals the first cluster of the freefile
1691 // We can go ahead and write to that space before the FAT and directory are updated
1692 file
->cursorCluster
= afatfs
.freeFile
.firstCluster
;
1693 file
->physicalSize
+= afatfs_superClusterSize();
1695 /* Remove the first supercluster from the freefile
1697 * Even if the freefile becomes empty, we still don't set its first cluster to zero. This is so that
1698 * afatfs_fileGetNextCluster() can tell where a contiguous file ends (at the start of the freefile).
1700 * Note that normally the freefile can't become empty because it is allocated as a non-integer number
1701 * of superclusters to avoid precisely this situation.
1703 afatfs
.freeFile
.firstCluster
+= afatfs_fatEntriesPerSector();
1704 afatfs
.freeFile
.logicalSize
-= afatfs_superClusterSize();
1705 afatfs
.freeFile
.physicalSize
-= afatfs_superClusterSize();
1707 // The new supercluster needs to have its clusters chained contiguously and marked with a terminator at the end
1708 opState
->fatRewriteStartCluster
= file
->cursorCluster
;
1709 opState
->fatRewriteEndCluster
= opState
->fatRewriteStartCluster
+ afatfs_fatEntriesPerSector();
1711 if (opState
->previousCluster
== 0) {
1712 // This is the new first cluster in the file so we need to update the directory entry
1713 file
->firstCluster
= file
->cursorCluster
;
1716 * We also need to update the FAT of the supercluster that used to end the file so that it no longer
1719 opState
->fatRewriteStartCluster
-= afatfs_fatEntriesPerSector();
1722 opState
->phase
= AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FREEFILE_DIRECTORY
;
1725 case AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FREEFILE_DIRECTORY
:
1726 // First update the freefile's directory entry to remove the first supercluster so we don't risk cross-linking the file
1727 status
= afatfs_saveDirectoryEntry(&afatfs
.freeFile
, AFATFS_SAVE_DIRECTORY_NORMAL
);
1729 if (status
== AFATFS_OPERATION_SUCCESS
) {
1730 opState
->phase
= AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FAT
;
1734 case AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FAT
:
1735 status
= afatfs_FATFillWithPattern(AFATFS_FAT_PATTERN_TERMINATED_CHAIN
, &opState
->fatRewriteStartCluster
, opState
->fatRewriteEndCluster
);
1737 if (status
== AFATFS_OPERATION_SUCCESS
) {
1738 opState
->phase
= AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FILE_DIRECTORY
;
1742 case AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FILE_DIRECTORY
:
1743 // Update the fileSize/firstCluster in the directory entry for the file
1744 status
= afatfs_saveDirectoryEntry(file
, AFATFS_SAVE_DIRECTORY_NORMAL
);
1748 if ((status
== AFATFS_OPERATION_FAILURE
|| status
== AFATFS_OPERATION_SUCCESS
) && file
->operation
.operation
== AFATFS_FILE_OPERATION_APPEND_SUPERCLUSTER
) {
1749 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
1756 * Attempt to queue up an operation to append the first supercluster of the freefile to the given `file` (file's cursor
1757 * must be at end-of-file).
1759 * The new cluster number will be set into the file's cursorCluster.
1762 * AFATFS_OPERATION_SUCCESS - The append completed successfully and the file's cursorCluster has been updated
1763 * AFATFS_OPERATION_IN_PROGRESS - The operation was queued on the file and will complete later, or there is already an
1764 * append in progress.
1765 * AFATFS_OPERATION_FAILURE - Operation could not be queued (file was busy) or append failed (filesystem is full).
1766 * Check afatfs.fileSystemFull
1768 static afatfsOperationStatus_e
afatfs_appendSupercluster(afatfsFilePtr_t file
)
1770 uint32_t superClusterSize
= afatfs_superClusterSize();
1772 if (file
->operation
.operation
== AFATFS_FILE_OPERATION_APPEND_SUPERCLUSTER
) {
1773 return AFATFS_OPERATION_IN_PROGRESS
;
1776 if (afatfs
.freeFile
.logicalSize
< superClusterSize
) {
1777 afatfs
.filesystemFull
= true;
1780 if (afatfs
.filesystemFull
|| afatfs_fileIsBusy(file
)) {
1781 return AFATFS_OPERATION_FAILURE
;
1784 afatfsAppendSupercluster_t
*opState
= &file
->operation
.state
.appendSupercluster
;
1786 file
->operation
.operation
= AFATFS_FILE_OPERATION_APPEND_SUPERCLUSTER
;
1787 opState
->phase
= AFATFS_APPEND_SUPERCLUSTER_PHASE_INIT
;
1788 opState
->previousCluster
= file
->cursorPreviousCluster
;
1790 return afatfs_appendSuperclusterContinue(file
);
1796 * Queue an operation to add a cluster of free space to the end of the file. Must be called when the file's cursor
1797 * is beyond the last allocated cluster.
1799 static afatfsOperationStatus_e
afatfs_appendFreeCluster(afatfsFilePtr_t file
)
1801 afatfsOperationStatus_e status
;
1803 #ifdef AFATFS_USE_FREEFILE
1804 if ((file
->mode
& AFATFS_FILE_MODE_CONTIGUOUS
) != 0) {
1805 // Steal the first cluster from the beginning of the freefile if we can
1806 status
= afatfs_appendSupercluster(file
);
1810 status
= afatfs_appendRegularFreeCluster(file
);
1817 * Returns true if the file's cursor is sitting beyond the end of the last allocated cluster (i.e. the logical fileSize
1820 static bool afatfs_isEndOfAllocatedFile(afatfsFilePtr_t file
)
1822 if (file
->type
== AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY
) {
1823 return file
->cursorOffset
>= AFATFS_SECTOR_SIZE
* afatfs
.rootDirectorySectors
;
1825 return file
->cursorCluster
== 0 || afatfs_FATIsEndOfChainMarker(file
->cursorCluster
);
1830 * Take a lock on the sector at the current file cursor position.
1832 * Returns a pointer to the sector buffer if successful, or NULL if at the end of file (check afatfs_isEndOfAllocatedFile())
1833 * or the sector has not yet been read in from disk.
1835 static uint8_t* afatfs_fileRetainCursorSectorForRead(afatfsFilePtr_t file
)
1839 uint32_t physicalSector
= afatfs_fileGetCursorPhysicalSector(file
);
1841 /* If we've already got a locked sector then we can assume that was the same one that's at the cursor (because this
1842 * cache is invalidated when crossing a sector boundary)
1844 if (file
->readRetainCacheIndex
!= -1) {
1845 if (!afatfs_assert(physicalSector
== afatfs
.cacheDescriptor
[file
->readRetainCacheIndex
].sectorIndex
)) {
1849 result
= afatfs_cacheSectorGetMemory(file
->readRetainCacheIndex
);
1851 if (afatfs_isEndOfAllocatedFile(file
)) {
1855 afatfs_assert(physicalSector
> 0); // We never read the root sector using files
1857 afatfsOperationStatus_e status
= afatfs_cacheSector(
1860 AFATFS_CACHE_READ
| AFATFS_CACHE_RETAIN
,
1864 if (status
!= AFATFS_OPERATION_SUCCESS
) {
1865 // Sector not ready for read
1869 file
->readRetainCacheIndex
= afatfs_getCacheDescriptorIndexForBuffer(result
);
1876 * Lock the sector at the file's cursor position for write, and return a reference to the memory for that sector.
1878 * Returns NULL if the cache was too busy, try again later.
1880 static uint8_t* afatfs_fileLockCursorSectorForWrite(afatfsFilePtr_t file
)
1882 afatfsOperationStatus_e status
;
1884 uint32_t eraseBlockCount
;
1886 // Do we already have a sector locked in our cache at the cursor position?
1887 if (file
->writeLockedCacheIndex
!= -1) {
1888 uint32_t physicalSector
= afatfs_fileGetCursorPhysicalSector(file
);
1890 if (!afatfs_assert(physicalSector
== afatfs
.cacheDescriptor
[file
->writeLockedCacheIndex
].sectorIndex
)) {
1894 result
= afatfs_cacheSectorGetMemory(file
->writeLockedCacheIndex
);
1896 // Find / allocate a sector and lock it in the cache so we can rely on it sticking around
1898 // Are we at the start of an empty file or the end of a non-empty file? If so we need to add a cluster
1899 if (afatfs_isEndOfAllocatedFile(file
) && afatfs_appendFreeCluster(file
) != AFATFS_OPERATION_SUCCESS
) {
1900 // The extension of the file is in progress so please call us again later to try again
1904 uint32_t physicalSector
= afatfs_fileGetCursorPhysicalSector(file
);
1905 uint8_t cacheFlags
= AFATFS_CACHE_WRITE
| AFATFS_CACHE_LOCK
;
1906 uint32_t cursorOffsetInSector
= file
->cursorOffset
% AFATFS_SECTOR_SIZE
;
1907 uint32_t offsetOfStartOfSector
= file
->cursorOffset
& ~((uint32_t) AFATFS_SECTOR_SIZE
- 1);
1908 uint32_t offsetOfEndOfSector
= offsetOfStartOfSector
+ AFATFS_SECTOR_SIZE
;
1911 * If there is data before the write point in this sector, or there could be data after the write-point
1912 * then we need to have the original contents of the sector in the cache for us to merge into
1915 cursorOffsetInSector
> 0
1916 || offsetOfEndOfSector
< file
->logicalSize
1918 cacheFlags
|= AFATFS_CACHE_READ
;
1921 // In contiguous append mode, we'll pre-erase the whole supercluster
1922 if ((file
->mode
& (AFATFS_FILE_MODE_APPEND
| AFATFS_FILE_MODE_CONTIGUOUS
)) == (AFATFS_FILE_MODE_APPEND
| AFATFS_FILE_MODE_CONTIGUOUS
)) {
1923 uint32_t cursorOffsetInSupercluster
= file
->cursorOffset
& (afatfs_superClusterSize() - 1);
1925 eraseBlockCount
= afatfs_fatEntriesPerSector() * afatfs
.sectorsPerCluster
- cursorOffsetInSupercluster
/ AFATFS_SECTOR_SIZE
;
1927 eraseBlockCount
= 0;
1930 status
= afatfs_cacheSector(
1937 if (status
!= AFATFS_OPERATION_SUCCESS
) {
1938 // Not enough cache available to accept this write / sector not ready for read
1942 file
->writeLockedCacheIndex
= afatfs_getCacheDescriptorIndexForBuffer(result
);
1949 * Attempt to seek the file pointer by the offset, relative to the current position.
1951 * Returns true if the seek was completed, or false if you should try again later by calling this routine again (the
1952 * cursor is not moved and no seek operation is queued on the file for you).
1954 * You can only seek forwards by the size of a cluster or less, or backwards to stay within the same cluster. Otherwise
1955 * false will always be returned (calling this routine again will never make progress on the seek).
1957 * This amount of seek is special because we will have to wait on at most one read operation, so it's easy to make
1960 static bool afatfs_fseekAtomic(afatfsFilePtr_t file
, int32_t offset
)
1962 // Seeks within a sector
1963 uint32_t newSectorOffset
= offset
+ file
->cursorOffset
% AFATFS_SECTOR_SIZE
;
1965 // i.e. newSectorOffset is non-negative and smaller than AFATFS_SECTOR_SIZE, we're staying within the same sector
1966 if (newSectorOffset
< AFATFS_SECTOR_SIZE
) {
1967 file
->cursorOffset
+= offset
;
1971 // We're seeking outside the sector so unlock it if we were holding it
1972 afatfs_fileUnlockCacheSector(file
);
1974 // FAT16 root directories are made up of contiguous sectors rather than clusters
1975 if (file
->type
== AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY
) {
1976 file
->cursorOffset
+= offset
;
1981 uint32_t clusterSizeBytes
= afatfs_clusterSize();
1982 uint32_t offsetInCluster
= afatfs_byteIndexInCluster(file
->cursorOffset
);
1983 uint32_t newOffsetInCluster
= offsetInCluster
+ offset
;
1985 afatfsOperationStatus_e status
;
1987 if (offset
> (int32_t) clusterSizeBytes
|| offset
< -(int32_t) offsetInCluster
) {
1991 // Are we seeking outside the cluster? If so we'll need to find out the next cluster number
1992 if (newOffsetInCluster
>= clusterSizeBytes
) {
1993 uint32_t nextCluster
;
1995 status
= afatfs_fileGetNextCluster(file
, file
->cursorCluster
, &nextCluster
);
1997 if (status
== AFATFS_OPERATION_SUCCESS
) {
1998 // Seek to the beginning of the next cluster
1999 uint32_t bytesToSeek
= clusterSizeBytes
- offsetInCluster
;
2001 file
->cursorPreviousCluster
= file
->cursorCluster
;
2002 file
->cursorCluster
= nextCluster
;
2003 file
->cursorOffset
+= bytesToSeek
;
2005 offset
-= bytesToSeek
;
2012 // If we didn't already hit the end of the file, add any remaining offset needed inside the cluster
2013 if (!afatfs_isEndOfAllocatedFile(file
)) {
2014 file
->cursorOffset
+= offset
;
2021 * Returns true if the seek was completed, or false if it is still in progress.
2023 static bool afatfs_fseekInternalContinue(afatfsFile_t
*file
)
2025 afatfsSeek_t
*opState
= &file
->operation
.state
.seek
;
2026 uint32_t clusterSizeBytes
= afatfs_clusterSize();
2027 uint32_t offsetInCluster
= afatfs_byteIndexInCluster(file
->cursorOffset
);
2029 afatfsOperationStatus_e status
;
2031 // Keep advancing the cursor cluster forwards to consume seekOffset
2032 while (offsetInCluster
+ opState
->seekOffset
>= clusterSizeBytes
&& !afatfs_isEndOfAllocatedFile(file
)) {
2033 uint32_t nextCluster
;
2035 status
= afatfs_fileGetNextCluster(file
, file
->cursorCluster
, &nextCluster
);
2037 if (status
== AFATFS_OPERATION_SUCCESS
) {
2038 // Seek to the beginning of the next cluster
2039 uint32_t bytesToSeek
= clusterSizeBytes
- offsetInCluster
;
2041 file
->cursorPreviousCluster
= file
->cursorCluster
;
2042 file
->cursorCluster
= nextCluster
;
2044 file
->cursorOffset
+= bytesToSeek
;
2045 opState
->seekOffset
-= bytesToSeek
;
2046 offsetInCluster
= 0;
2053 // If we didn't already hit the end of the file, add any remaining offset needed inside the cluster
2054 if (!afatfs_isEndOfAllocatedFile(file
)) {
2055 file
->cursorOffset
+= opState
->seekOffset
;
2058 afatfs_fileUpdateFilesize(file
); // TODO do we need this?
2060 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2062 if (opState
->callback
) {
2063 opState
->callback(file
);
2070 * Seek the file pointer forwards by offset bytes. Calls the callback when the seek is complete.
2072 * Will happily seek beyond the logical end of the file.
2075 * AFATFS_OPERATION_SUCCESS - The seek was completed immediately
2076 * AFATFS_OPERATION_IN_PROGRESS - The seek was queued and will complete later
2077 * AFATFS_OPERATION_FAILURE - The seek could not be queued because the file was busy with another operation,
2080 static afatfsOperationStatus_e
afatfs_fseekInternal(afatfsFilePtr_t file
, uint32_t offset
, afatfsFileCallback_t callback
)
2082 // See if we can seek without queuing an operation
2083 if (afatfs_fseekAtomic(file
, offset
)) {
2088 return AFATFS_OPERATION_SUCCESS
;
2090 // Our operation must queue
2091 if (afatfs_fileIsBusy(file
)) {
2092 return AFATFS_OPERATION_FAILURE
;
2095 afatfsSeek_t
*opState
= &file
->operation
.state
.seek
;
2097 file
->operation
.operation
= AFATFS_FILE_OPERATION_SEEK
;
2098 opState
->callback
= callback
;
2099 opState
->seekOffset
= offset
;
2101 return AFATFS_OPERATION_IN_PROGRESS
;
2106 * Attempt to seek the file cursor from the given point (`whence`) by the given offset, just like C's fseek().
2108 * AFATFS_SEEK_SET with offset 0 will always return AFATFS_OPERATION_SUCCESS.
2111 * AFATFS_OPERATION_SUCCESS - The seek was completed immediately
2112 * AFATFS_OPERATION_IN_PROGRESS - The seek was queued and will complete later. Feel free to attempt read/write
2113 * operations on the file, they'll fail until the seek is complete.
2114 * AFATFS_OPERATION_FAILURE - The seek could not be queued because the file was busy with another operation,
2117 afatfsOperationStatus_e
afatfs_fseek(afatfsFilePtr_t file
, int32_t offset
, afatfsSeek_e whence
)
2119 // We need an up-to-date logical filesize so we can clamp seeks to the EOF
2120 afatfs_fileUpdateFilesize(file
);
2123 case AFATFS_SEEK_CUR
:
2125 // Only forwards seeks are supported by this routine:
2126 return afatfs_fseekInternal(file
, MIN(file
->cursorOffset
+ offset
, file
->logicalSize
), NULL
);
2129 // Convert a backwards relative seek into a SEEK_SET. TODO considerable room for improvement if within the same cluster
2130 offset
+= file
->cursorOffset
;
2133 case AFATFS_SEEK_END
:
2134 // Are we already at the right position?
2135 if (file
->logicalSize
+ offset
== file
->cursorOffset
) {
2136 return AFATFS_OPERATION_SUCCESS
;
2139 // Convert into a SEEK_SET
2140 offset
+= file
->logicalSize
;
2143 case AFATFS_SEEK_SET
:
2147 // Now we have a SEEK_SET with a positive offset. Begin by seeking to the start of the file
2148 afatfs_fileUnlockCacheSector(file
);
2150 file
->cursorPreviousCluster
= 0;
2151 file
->cursorCluster
= file
->firstCluster
;
2152 file
->cursorOffset
= 0;
2154 // Then seek forwards by the offset
2155 return afatfs_fseekInternal(file
, MIN((uint32_t) offset
, file
->logicalSize
), NULL
);
2159 * Get the byte-offset of the file's cursor from the start of the file.
2161 * Returns true on success, or false if the file is busy (try again later).
2163 bool afatfs_ftell(afatfsFilePtr_t file
, uint32_t *position
)
2165 if (afatfs_fileIsBusy(file
)) {
2168 *position
= file
->cursorOffset
;
2174 * Attempt to advance the directory pointer `finder` to the next entry in the directory.
2177 * AFATFS_OPERATION_SUCCESS - A pointer to the next directory entry has been loaded into *dirEntry. If the
2178 * directory was exhausted then *dirEntry will be set to NULL.
2179 * AFATFS_OPERATION_IN_PROGRESS - The disk is busy. The pointer is not advanced, call again later to retry.
2181 afatfsOperationStatus_e
afatfs_findNext(afatfsFilePtr_t directory
, afatfsFinder_t
*finder
, fatDirectoryEntry_t
**dirEntry
)
2185 if (finder
->entryIndex
== AFATFS_FILES_PER_DIRECTORY_SECTOR
- 1) {
2186 if (afatfs_fseekAtomic(directory
, AFATFS_SECTOR_SIZE
)) {
2187 finder
->entryIndex
= -1;
2188 // Fall through to read the first entry of that new sector
2190 return AFATFS_OPERATION_IN_PROGRESS
;
2194 sector
= afatfs_fileRetainCursorSectorForRead(directory
);
2197 finder
->entryIndex
++;
2199 *dirEntry
= (fatDirectoryEntry_t
*) sector
+ finder
->entryIndex
;
2201 finder
->sectorNumberPhysical
= afatfs_fileGetCursorPhysicalSector(directory
);
2203 return AFATFS_OPERATION_SUCCESS
;
2205 if (afatfs_isEndOfAllocatedFile(directory
)) {
2208 return AFATFS_OPERATION_SUCCESS
;
2211 return AFATFS_OPERATION_IN_PROGRESS
;
2216 * Release resources associated with a find operation. Calling this more than once is harmless.
2218 void afatfs_findLast(afatfsFilePtr_t directory
)
2220 afatfs_fileUnlockCacheSector(directory
);
2224 * Initialise the finder so that the first call with the directory to findNext() will return the first file in the
2227 void afatfs_findFirst(afatfsFilePtr_t directory
, afatfsFinder_t
*finder
)
2229 afatfs_fseek(directory
, 0, AFATFS_SEEK_SET
);
2230 finder
->entryIndex
= -1;
2233 static afatfsOperationStatus_e
afatfs_extendSubdirectoryContinue(afatfsFile_t
*directory
)
2235 afatfsExtendSubdirectory_t
*opState
= &directory
->operation
.state
.extendSubdirectory
;
2236 afatfsOperationStatus_e status
;
2237 uint8_t *sectorBuffer
;
2238 uint32_t clusterNumber
, physicalSector
;
2239 uint16_t sectorInCluster
;
2242 switch (opState
->phase
) {
2243 case AFATFS_EXTEND_SUBDIRECTORY_PHASE_ADD_FREE_CLUSTER
:
2244 status
= afatfs_appendRegularFreeClusterContinue(directory
);
2246 if (status
== AFATFS_OPERATION_SUCCESS
) {
2247 opState
->phase
= AFATFS_EXTEND_SUBDIRECTORY_PHASE_WRITE_SECTORS
;
2249 } else if (status
== AFATFS_OPERATION_FAILURE
) {
2250 opState
->phase
= AFATFS_EXTEND_SUBDIRECTORY_PHASE_FAILURE
;
2254 case AFATFS_EXTEND_SUBDIRECTORY_PHASE_WRITE_SECTORS
:
2255 // Now, zero out that cluster
2256 afatfs_fileGetCursorClusterAndSector(directory
, &clusterNumber
, §orInCluster
);
2257 physicalSector
= afatfs_fileGetCursorPhysicalSector(directory
);
2260 status
= afatfs_cacheSector(physicalSector
, §orBuffer
, AFATFS_CACHE_WRITE
, 0);
2262 if (status
!= AFATFS_OPERATION_SUCCESS
) {
2266 memset(sectorBuffer
, 0, AFATFS_SECTOR_SIZE
);
2268 // If this is the first sector of a non-root directory, create the "." and ".." entries
2269 if (directory
->directoryEntryPos
.sectorNumberPhysical
!= 0 && directory
->cursorOffset
== 0) {
2270 fatDirectoryEntry_t
*dirEntries
= (fatDirectoryEntry_t
*) sectorBuffer
;
2272 memset(dirEntries
[0].filename
, ' ', sizeof(dirEntries
[0].filename
));
2273 dirEntries
[0].filename
[0] = '.';
2274 dirEntries
[0].firstClusterHigh
= directory
->firstCluster
>> 16;
2275 dirEntries
[0].firstClusterLow
= directory
->firstCluster
& 0xFFFF;
2276 dirEntries
[0].attrib
= FAT_FILE_ATTRIBUTE_DIRECTORY
;
2278 memset(dirEntries
[1].filename
, ' ', sizeof(dirEntries
[1].filename
));
2279 dirEntries
[1].filename
[0] = '.';
2280 dirEntries
[1].filename
[1] = '.';
2281 dirEntries
[1].firstClusterHigh
= opState
->parentDirectoryCluster
>> 16;
2282 dirEntries
[1].firstClusterLow
= opState
->parentDirectoryCluster
& 0xFFFF;
2283 dirEntries
[1].attrib
= FAT_FILE_ATTRIBUTE_DIRECTORY
;
2286 if (sectorInCluster
< afatfs
.sectorsPerCluster
- 1) {
2287 // Move to next sector
2288 afatfs_assert(afatfs_fseekAtomic(directory
, AFATFS_SECTOR_SIZE
));
2296 // Seek back to the beginning of the cluster
2297 afatfs_assert(afatfs_fseekAtomic(directory
, -AFATFS_SECTOR_SIZE
* ((int32_t)afatfs
.sectorsPerCluster
- 1)));
2298 opState
->phase
= AFATFS_EXTEND_SUBDIRECTORY_PHASE_SUCCESS
;
2301 case AFATFS_EXTEND_SUBDIRECTORY_PHASE_SUCCESS
:
2302 directory
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2304 if (opState
->callback
) {
2305 opState
->callback(directory
);
2308 return AFATFS_OPERATION_SUCCESS
;
2310 case AFATFS_EXTEND_SUBDIRECTORY_PHASE_FAILURE
:
2311 directory
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2313 if (opState
->callback
) {
2314 opState
->callback(NULL
);
2316 return AFATFS_OPERATION_FAILURE
;
2320 return AFATFS_OPERATION_IN_PROGRESS
;
2324 * Queue an operation to add a cluster to a sub-directory.
2326 * Tthe new cluster is zero-filled. "." and ".." entries are added if it is the first cluster of a new subdirectory.
2328 * The directory must not be busy, otherwise AFATFS_OPERATION_FAILURE is returned immediately.
2330 * The directory's cursor must lie at the end of the directory file (i.e. isEndOfAllocatedFile() would return true).
2332 * You must provide parentDirectory if this is the first extension to the subdirectory, otherwise pass NULL for that argument.
2334 static afatfsOperationStatus_e
afatfs_extendSubdirectory(afatfsFile_t
*directory
, afatfsFilePtr_t parentDirectory
, afatfsFileCallback_t callback
)
2336 // FAT16 root directories cannot be extended
2337 if (directory
->type
== AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY
|| afatfs_fileIsBusy(directory
)) {
2338 return AFATFS_OPERATION_FAILURE
;
2342 * We'll assume that we're never asked to append the first cluster of a root directory, since any
2343 * reasonably-formatted volume should have a root!
2345 afatfsExtendSubdirectory_t
*opState
= &directory
->operation
.state
.extendSubdirectory
;
2347 directory
->operation
.operation
= AFATFS_FILE_OPERATION_EXTEND_SUBDIRECTORY
;
2349 opState
->phase
= AFATFS_EXTEND_SUBDIRECTORY_PHASE_INITIAL
;
2350 opState
->parentDirectoryCluster
= parentDirectory
? parentDirectory
->firstCluster
: 0;
2351 opState
->callback
= callback
;
2353 afatfs_appendRegularFreeClusterInitOperationState(&opState
->appendFreeCluster
, directory
->cursorPreviousCluster
);
2355 return afatfs_extendSubdirectoryContinue(directory
);
2359 * Allocate space for a new directory entry to be written, store the position of that entry in the finder, and set
2360 * the *dirEntry pointer to point to the entry within the cached FAT sector. This pointer's lifetime is only as good
2361 * as the life of the cache, so don't dawdle.
2363 * Before the first call to this function, call afatfs_findFirst() on the directory.
2365 * The directory sector in the cache is marked as dirty, so any changes written through to the entry will be flushed out
2366 * in a subsequent poll cycle.
2369 * AFATFS_OPERATION_IN_PROGRESS - Call again later to continue
2370 * AFATFS_OPERATION_SUCCESS - Entry has been inserted and *dirEntry and *finder have been updated
2371 * AFATFS_OPERATION_FAILURE - When the directory is full.
2373 static afatfsOperationStatus_e
afatfs_allocateDirectoryEntry(afatfsFilePtr_t directory
, fatDirectoryEntry_t
**dirEntry
, afatfsFinder_t
*finder
)
2375 afatfsOperationStatus_e result
;
2377 if (afatfs_fileIsBusy(directory
)) {
2378 return AFATFS_OPERATION_IN_PROGRESS
;
2381 while ((result
= afatfs_findNext(directory
, finder
, dirEntry
)) == AFATFS_OPERATION_SUCCESS
) {
2383 if (fat_isDirectoryEntryEmpty(*dirEntry
) || fat_isDirectoryEntryTerminator(*dirEntry
)) {
2384 afatfs_cacheSectorMarkDirty(afatfs_getCacheDescriptorForBuffer((uint8_t*) *dirEntry
));
2386 afatfs_findLast(directory
);
2387 return AFATFS_OPERATION_SUCCESS
;
2390 // Need to extend directory size by adding a cluster
2391 result
= afatfs_extendSubdirectory(directory
, NULL
, NULL
);
2393 if (result
== AFATFS_OPERATION_SUCCESS
) {
2394 // Continue the search in the newly-extended directory
2397 // The status (in progress or failure) of extending the directory becomes our status
2407 * Return a pointer to a free entry in the open files table (a file whose type is "NONE"). You should initialize
2408 * the file afterwards with afatfs_initFileHandle().
2410 static afatfsFilePtr_t
afatfs_allocateFileHandle(void)
2412 for (int i
= 0; i
< AFATFS_MAX_OPEN_FILES
; i
++) {
2413 if (afatfs
.openFiles
[i
].type
== AFATFS_FILE_TYPE_NONE
) {
2414 return &afatfs
.openFiles
[i
];
2422 * Continue the file truncation.
2424 * When truncation finally succeeds or fails, the current operation is cleared on the file (if the current operation
2425 * was a truncate), then the truncate operation's callback is called. This allows the truncation to be called as a
2426 * sub-operation without it clearing the parent file operation.
2428 static afatfsOperationStatus_e
afatfs_ftruncateContinue(afatfsFilePtr_t file
, bool markDeleted
)
2430 afatfsTruncateFile_t
*opState
= &file
->operation
.state
.truncateFile
;
2431 afatfsOperationStatus_e status
= AFATFS_OPERATION_FAILURE
;
2433 #ifdef AFATFS_USE_FREEFILE
2434 uint32_t oldFreeFileStart
, freeFileGrow
;
2439 switch (opState
->phase
) {
2440 case AFATFS_TRUNCATE_FILE_UPDATE_DIRECTORY
:
2441 status
= afatfs_saveDirectoryEntry(file
, markDeleted
? AFATFS_SAVE_DIRECTORY_DELETED
: AFATFS_SAVE_DIRECTORY_NORMAL
);
2443 if (status
== AFATFS_OPERATION_SUCCESS
) {
2444 #ifdef AFATFS_USE_FREEFILE
2445 if (opState
->endCluster
) {
2446 opState
->phase
= AFATFS_TRUNCATE_FILE_ERASE_FAT_CHAIN_CONTIGUOUS
;
2450 opState
->phase
= AFATFS_TRUNCATE_FILE_ERASE_FAT_CHAIN_NORMAL
;
2455 #ifdef AFATFS_USE_FREEFILE
2456 case AFATFS_TRUNCATE_FILE_ERASE_FAT_CHAIN_CONTIGUOUS
:
2457 // Prepare the clusters to be added back on to the beginning of the freefile
2458 status
= afatfs_FATFillWithPattern(AFATFS_FAT_PATTERN_UNTERMINATED_CHAIN
, &opState
->currentCluster
, opState
->endCluster
);
2460 if (status
== AFATFS_OPERATION_SUCCESS
) {
2461 opState
->phase
= AFATFS_TRUNCATE_FILE_PREPEND_TO_FREEFILE
;
2465 case AFATFS_TRUNCATE_FILE_PREPEND_TO_FREEFILE
:
2466 // Note, it's okay to run this code several times:
2467 oldFreeFileStart
= afatfs
.freeFile
.firstCluster
;
2469 afatfs
.freeFile
.firstCluster
= opState
->startCluster
;
2471 freeFileGrow
= (oldFreeFileStart
- opState
->startCluster
) * afatfs_clusterSize();
2473 afatfs
.freeFile
.logicalSize
+= freeFileGrow
;
2474 afatfs
.freeFile
.physicalSize
+= freeFileGrow
;
2476 status
= afatfs_saveDirectoryEntry(&afatfs
.freeFile
, AFATFS_SAVE_DIRECTORY_NORMAL
);
2477 if (status
== AFATFS_OPERATION_SUCCESS
) {
2478 opState
->phase
= AFATFS_TRUNCATE_FILE_SUCCESS
;
2483 case AFATFS_TRUNCATE_FILE_ERASE_FAT_CHAIN_NORMAL
:
2484 while (!afatfs_FATIsEndOfChainMarker(opState
->currentCluster
)) {
2485 uint32_t nextCluster
;
2487 status
= afatfs_FATGetNextCluster(0, opState
->currentCluster
, &nextCluster
);
2489 if (status
!= AFATFS_OPERATION_SUCCESS
) {
2493 status
= afatfs_FATSetNextCluster(opState
->currentCluster
, 0);
2495 if (status
!= AFATFS_OPERATION_SUCCESS
) {
2499 opState
->currentCluster
= nextCluster
;
2501 // Searches for unallocated regular clusters should be told about this free cluster now
2502 afatfs
.lastClusterAllocated
= MIN(afatfs
.lastClusterAllocated
, opState
->currentCluster
- 1);
2505 opState
->phase
= AFATFS_TRUNCATE_FILE_SUCCESS
;
2508 case AFATFS_TRUNCATE_FILE_SUCCESS
:
2509 if (file
->operation
.operation
== AFATFS_FILE_OPERATION_TRUNCATE
) {
2510 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2513 if (opState
->callback
) {
2514 opState
->callback(file
);
2517 return AFATFS_OPERATION_SUCCESS
;
2521 if (status
== AFATFS_OPERATION_FAILURE
&& file
->operation
.operation
== AFATFS_FILE_OPERATION_TRUNCATE
) {
2522 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2529 * Queue an operation to truncate the file to zero bytes in length.
2531 * Returns true if the operation was successfully queued or false if the file is busy (try again later).
2533 * The callback is called once the file has been truncated (some time after this routine returns).
2535 bool afatfs_ftruncate(afatfsFilePtr_t file
, afatfsFileCallback_t callback
)
2537 afatfsTruncateFile_t
*opState
;
2539 if (afatfs_fileIsBusy(file
))
2542 file
->operation
.operation
= AFATFS_FILE_OPERATION_TRUNCATE
;
2544 opState
= &file
->operation
.state
.truncateFile
;
2545 opState
->callback
= callback
;
2546 opState
->phase
= AFATFS_TRUNCATE_FILE_INITIAL
;
2547 opState
->startCluster
= file
->firstCluster
;
2548 opState
->currentCluster
= opState
->startCluster
;
2550 #ifdef AFATFS_USE_FREEFILE
2551 if ((file
->mode
& AFATFS_FILE_MODE_CONTIGUOUS
) != 0) {
2552 // The file is contiguous and ends where the freefile begins
2553 opState
->endCluster
= afatfs
.freeFile
.firstCluster
;
2557 // The range of clusters to delete is not contiguous, so follow it as a linked-list instead
2558 opState
->endCluster
= 0;
2561 // We'll drop the cluster chain from the directory entry immediately
2562 file
->firstCluster
= 0;
2563 file
->logicalSize
= 0;
2564 file
->physicalSize
= 0;
2566 afatfs_fseek(file
, 0, AFATFS_SEEK_SET
);
2572 * Load details from the given FAT directory entry into the file.
2574 static void afatfs_fileLoadDirectoryEntry(afatfsFile_t
*file
, fatDirectoryEntry_t
*entry
)
2576 file
->firstCluster
= (uint32_t) (entry
->firstClusterHigh
<< 16) | entry
->firstClusterLow
;
2577 file
->logicalSize
= entry
->fileSize
;
2578 file
->physicalSize
= roundUpTo(entry
->fileSize
, afatfs_clusterSize());
2579 file
->attrib
= entry
->attrib
;
2582 static void afatfs_createFileContinue(afatfsFile_t
*file
)
2584 afatfsCreateFile_t
*opState
= &file
->operation
.state
.createFile
;
2585 fatDirectoryEntry_t
*entry
;
2586 afatfsOperationStatus_e status
;
2590 switch (opState
->phase
) {
2591 case AFATFS_CREATEFILE_PHASE_INITIAL
:
2592 afatfs_findFirst(&afatfs
.currentDirectory
, &file
->directoryEntryPos
);
2593 opState
->phase
= AFATFS_CREATEFILE_PHASE_FIND_FILE
;
2596 case AFATFS_CREATEFILE_PHASE_FIND_FILE
:
2598 status
= afatfs_findNext(&afatfs
.currentDirectory
, &file
->directoryEntryPos
, &entry
);
2601 case AFATFS_OPERATION_SUCCESS
:
2602 // Is this the last entry in the directory?
2603 if (entry
== NULL
|| fat_isDirectoryEntryTerminator(entry
)) {
2604 afatfs_findLast(&afatfs
.currentDirectory
);
2606 if ((file
->mode
& AFATFS_FILE_MODE_CREATE
) != 0) {
2607 // The file didn't already exist, so we can create it. Allocate a new directory entry
2608 afatfs_findFirst(&afatfs
.currentDirectory
, &file
->directoryEntryPos
);
2610 opState
->phase
= AFATFS_CREATEFILE_PHASE_CREATE_NEW_FILE
;
2615 opState
->phase
= AFATFS_CREATEFILE_PHASE_FAILURE
;
2618 } else if (entry
->attrib
& FAT_FILE_ATTRIBUTE_VOLUME_ID
) {
2620 } else if (strncmp(entry
->filename
, (char*) opState
->filename
, FAT_FILENAME_LENGTH
) == 0) {
2621 // We found a file or directory with this name!
2623 // Do not open file as dir or dir as file
2624 if (((entry
->attrib
^ file
->attrib
) & FAT_FILE_ATTRIBUTE_DIRECTORY
) != 0) {
2625 afatfs_findLast(&afatfs
.currentDirectory
);
2626 opState
->phase
= AFATFS_CREATEFILE_PHASE_FAILURE
;
2630 afatfs_fileLoadDirectoryEntry(file
, entry
);
2632 afatfs_findLast(&afatfs
.currentDirectory
);
2634 opState
->phase
= AFATFS_CREATEFILE_PHASE_SUCCESS
;
2636 } // Else this entry doesn't match, fall through and continue the search
2638 case AFATFS_OPERATION_FAILURE
:
2639 afatfs_findLast(&afatfs
.currentDirectory
);
2640 opState
->phase
= AFATFS_CREATEFILE_PHASE_FAILURE
;
2643 case AFATFS_OPERATION_IN_PROGRESS
:
2646 } while (status
== AFATFS_OPERATION_SUCCESS
);
2648 case AFATFS_CREATEFILE_PHASE_CREATE_NEW_FILE
:
2649 status
= afatfs_allocateDirectoryEntry(&afatfs
.currentDirectory
, &entry
, &file
->directoryEntryPos
);
2651 if (status
== AFATFS_OPERATION_SUCCESS
) {
2652 memset(entry
, 0, sizeof(*entry
));
2654 memcpy(entry
->filename
, opState
->filename
, FAT_FILENAME_LENGTH
);
2655 entry
->attrib
= file
->attrib
;
2657 uint16_t fileDate
= AFATFS_DEFAULT_FILE_DATE
;
2658 uint16_t fileTime
= AFATFS_DEFAULT_FILE_TIME
;
2661 // rtcGetDateTime will fill dt with 0000-01-01T00:00:00
2662 // when time is not known.
2663 dateTime_t dt
, local_dt
;
2664 rtcGetDateTime(&dt
);
2666 // By tradition, FAT filesystem timestamps use local time.
2667 dateTimeUTCToLocal(&dt
, &local_dt
);
2668 fileDate
= FAT_MAKE_DATE(local_dt
.year
, local_dt
.month
, local_dt
.day
);
2669 fileTime
= FAT_MAKE_TIME(local_dt
.hours
, local_dt
.minutes
, local_dt
.seconds
);
2673 entry
->creationDate
= fileDate
;
2674 entry
->creationTime
= fileTime
;
2675 entry
->lastWriteDate
= fileDate
;
2676 entry
->lastWriteTime
= fileTime
;
2678 #ifdef AFATFS_DEBUG_VERBOSE
2679 fprintf(stderr
, "Adding directory entry for %.*s to sector %u\n", FAT_FILENAME_LENGTH
, opState
->filename
, file
->directoryEntryPos
.sectorNumberPhysical
);
2682 opState
->phase
= AFATFS_CREATEFILE_PHASE_SUCCESS
;
2684 } else if (status
== AFATFS_OPERATION_FAILURE
) {
2685 opState
->phase
= AFATFS_CREATEFILE_PHASE_FAILURE
;
2689 case AFATFS_CREATEFILE_PHASE_SUCCESS
:
2690 if ((file
->mode
& AFATFS_FILE_MODE_RETAIN_DIRECTORY
) != 0) {
2692 * For this high performance file type, we require the directory entry for the file to be retained
2693 * in the cache at all times.
2695 uint8_t *directorySector
;
2697 status
= afatfs_cacheSector(
2698 file
->directoryEntryPos
.sectorNumberPhysical
,
2700 AFATFS_CACHE_READ
| AFATFS_CACHE_RETAIN
,
2704 if (status
!= AFATFS_OPERATION_SUCCESS
) {
2710 afatfs_fseek(file
, 0, AFATFS_SEEK_SET
);
2713 if (file
->cursorCluster
== 0) {
2714 #ifdef AFATFS_USE_FREEFILE
2715 if ((file
->mode
& AFATFS_FILE_MODE_CONTIGUOUS
) != 0) {
2716 if (afatfs_fileIsBusy(&afatfs
.freeFile
)) {
2717 // Someone else's using the freefile, come back later.
2720 // Lock the freefile for our exclusive access
2721 afatfs
.freeFile
.operation
.operation
= AFATFS_FILE_OPERATION_LOCKED
;
2726 // We can't guarantee that the existing file contents are contiguous
2727 file
->mode
&= ~AFATFS_FILE_MODE_CONTIGUOUS
;
2729 // Seek to the end of the file if it is in append mode
2730 if ((file
->mode
& AFATFS_FILE_MODE_APPEND
) != 0) {
2731 // This replaces our open file operation
2732 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2733 afatfs_fseekInternal(file
, file
->logicalSize
, opState
->callback
);
2737 // If we're only writing (not reading) the file must be truncated
2738 if (file
->mode
== (AFATFS_FILE_MODE_CREATE
| AFATFS_FILE_MODE_WRITE
)) {
2739 // This replaces our open file operation
2740 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2741 afatfs_ftruncate(file
, opState
->callback
);
2746 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2747 opState
->callback(file
);
2749 case AFATFS_CREATEFILE_PHASE_FAILURE
:
2750 file
->type
= AFATFS_FILE_TYPE_NONE
;
2751 opState
->callback(NULL
);
2757 * Reset the in-memory data for the given handle back to the zeroed initial state
2759 static void afatfs_initFileHandle(afatfsFilePtr_t file
)
2761 memset(file
, 0, sizeof(*file
));
2762 file
->writeLockedCacheIndex
= -1;
2763 file
->readRetainCacheIndex
= -1;
2766 static void afatfs_funlinkContinue(afatfsFilePtr_t file
)
2768 afatfsUnlinkFile_t
*opState
= &file
->operation
.state
.unlinkFile
;
2769 afatfsOperationStatus_e status
;
2771 status
= afatfs_ftruncateContinue(file
, true);
2773 if (status
== AFATFS_OPERATION_SUCCESS
) {
2774 // Once the truncation is completed, we can close the file handle
2775 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2776 afatfs_fclose(file
, opState
->callback
);
2781 * Delete and close the file.
2783 * Returns true if the operation was successfully queued (callback will be called some time after this routine returns)
2784 * or false if the file is busy and you should try again later.
2786 bool afatfs_funlink(afatfsFilePtr_t file
, afatfsCallback_t callback
)
2788 afatfsUnlinkFile_t
*opState
= &file
->operation
.state
.unlinkFile
;
2790 if (!file
|| file
->type
== AFATFS_FILE_TYPE_NONE
) {
2795 * Internally an unlink is implemented by first doing a ftruncate(), marking the directory entry as deleted,
2796 * then doing a fclose() operation.
2799 // Start the sub-operation of truncating the file
2800 if (!afatfs_ftruncate(file
, NULL
))
2804 * The unlink operation has its own private callback field so that the truncate suboperation doesn't end up
2805 * calling back early when it completes:
2807 opState
->callback
= callback
;
2809 file
->operation
.operation
= AFATFS_FILE_OPERATION_UNLINK
;
2815 * Open (or create) a file in the CWD with the given filename.
2817 * file - Memory location to store the newly opened file details
2818 * name - Filename in "name.ext" format. No path.
2819 * attrib - FAT file attributes to give the file (if created)
2820 * fileMode - Bitset of AFATFS_FILE_MODE_* constants. Include AFATFS_FILE_MODE_CREATE to create the file if
2821 * it does not exist.
2822 * callback - Called when the operation is complete
2824 static afatfsFilePtr_t
afatfs_createFile(afatfsFilePtr_t file
, const char *name
, uint8_t attrib
, uint8_t fileMode
,
2825 afatfsFileCallback_t callback
)
2827 afatfsCreateFile_t
*opState
= &file
->operation
.state
.createFile
;
2829 afatfs_initFileHandle(file
);
2831 // Queue the operation to finish the file creation
2832 file
->operation
.operation
= AFATFS_FILE_OPERATION_CREATE_FILE
;
2834 file
->mode
= fileMode
;
2836 if (strcmp(name
, ".") == 0) {
2837 file
->firstCluster
= afatfs
.currentDirectory
.firstCluster
;
2838 file
->physicalSize
= afatfs
.currentDirectory
.physicalSize
;
2839 file
->logicalSize
= afatfs
.currentDirectory
.logicalSize
;
2840 file
->attrib
= afatfs
.currentDirectory
.attrib
;
2841 file
->type
= afatfs
.currentDirectory
.type
;
2843 fat_convertFilenameToFATStyle(name
, opState
->filename
);
2844 file
->attrib
= attrib
;
2846 if ((attrib
& FAT_FILE_ATTRIBUTE_DIRECTORY
) != 0) {
2847 file
->type
= AFATFS_FILE_TYPE_DIRECTORY
;
2849 file
->type
= AFATFS_FILE_TYPE_NORMAL
;
2853 opState
->callback
= callback
;
2855 if (strcmp(name
, ".") == 0) {
2856 // Since we already have the directory entry details, we can skip straight to the final operations requried
2857 opState
->phase
= AFATFS_CREATEFILE_PHASE_SUCCESS
;
2859 opState
->phase
= AFATFS_CREATEFILE_PHASE_INITIAL
;
2862 afatfs_createFileContinue(file
);
2867 static void afatfs_fcloseContinue(afatfsFilePtr_t file
)
2869 afatfsCacheBlockDescriptor_t
*descriptor
;
2870 afatfsCloseFile_t
*opState
= &file
->operation
.state
.closeFile
;
2873 * Directories don't update their parent directory entries over time, because their fileSize field in the directory
2874 * never changes (when we add the first cluster to the directory we save the directory entry at that point and it
2875 * doesn't change afterwards). So don't bother trying to save their directory entries during fclose().
2877 * Also if we only opened the file for read then we didn't change the directory entry either.
2879 if (file
->type
!= AFATFS_FILE_TYPE_DIRECTORY
&& file
->type
!= AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY
2880 && (file
->mode
& (AFATFS_FILE_MODE_APPEND
| AFATFS_FILE_MODE_WRITE
)) != 0) {
2881 if (afatfs_saveDirectoryEntry(file
, AFATFS_SAVE_DIRECTORY_FOR_CLOSE
) != AFATFS_OPERATION_SUCCESS
) {
2886 // Release our reservation on the directory cache if needed
2887 if ((file
->mode
& AFATFS_FILE_MODE_RETAIN_DIRECTORY
) != 0) {
2888 descriptor
= afatfs_findCacheSector(file
->directoryEntryPos
.sectorNumberPhysical
);
2891 descriptor
->retainCount
= MAX((int) descriptor
->retainCount
- 1, 0);
2895 // Release locks on the sector at the file cursor position
2896 afatfs_fileUnlockCacheSector(file
);
2898 #ifdef AFATFS_USE_FREEFILE
2899 // Release our exclusive lock on the freefile if needed
2900 if ((file
->mode
& AFATFS_FILE_MODE_CONTIGUOUS
) != 0) {
2901 afatfs_assert(afatfs
.freeFile
.operation
.operation
== AFATFS_FILE_OPERATION_LOCKED
);
2902 afatfs
.freeFile
.operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2906 file
->type
= AFATFS_FILE_TYPE_NONE
;
2907 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2909 if (opState
->callback
) {
2910 opState
->callback();
2915 * Returns true if an operation was successfully queued to close the file and destroy the file handle. If the file is
2916 * currently busy, false is returned and you should retry later.
2918 * If provided, the callback will be called after the operation completes (pass NULL for no callback).
2920 * If this function returns true, you should not make any further calls to the file (as the handle might be reused for a
2923 bool afatfs_fclose(afatfsFilePtr_t file
, afatfsCallback_t callback
)
2925 if (!file
|| file
->type
== AFATFS_FILE_TYPE_NONE
) {
2927 } else if (afatfs_fileIsBusy(file
)) {
2930 afatfs_fileUpdateFilesize(file
);
2932 file
->operation
.operation
= AFATFS_FILE_OPERATION_CLOSE
;
2933 file
->operation
.state
.closeFile
.callback
= callback
;
2934 afatfs_fcloseContinue(file
);
2940 * Create a new directory with the given name, or open the directory if it already exists.
2942 * The directory will be passed to the callback, or NULL if the creation failed.
2944 * Returns true if the directory creation was begun, or false if there are too many open files.
2946 bool afatfs_mkdir(const char *filename
, afatfsFileCallback_t callback
)
2948 afatfsFilePtr_t file
= afatfs_allocateFileHandle();
2951 afatfs_createFile(file
, filename
, FAT_FILE_ATTRIBUTE_DIRECTORY
, AFATFS_FILE_MODE_CREATE
| AFATFS_FILE_MODE_READ
| AFATFS_FILE_MODE_WRITE
, callback
);
2952 } else if (callback
) {
2956 return file
!= NULL
;
2960 * Change the working directory to the directory with the given handle (use fopen). Pass NULL for `directory` in order to
2961 * change to the root directory.
2963 * Returns true on success, false if you should call again later to retry. After changing into a directory, your handle
2964 * to that directory may be closed by fclose().
2966 bool afatfs_chdir(afatfsFilePtr_t directory
)
2968 if (afatfs_fileIsBusy(&afatfs
.currentDirectory
)) {
2973 if (afatfs_fileIsBusy(directory
)) {
2977 memcpy(&afatfs
.currentDirectory
, directory
, sizeof(*directory
));
2980 afatfs_initFileHandle(&afatfs
.currentDirectory
);
2982 afatfs
.currentDirectory
.mode
= AFATFS_FILE_MODE_READ
| AFATFS_FILE_MODE_WRITE
;
2984 if (afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT16
)
2985 afatfs
.currentDirectory
.type
= AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY
;
2987 afatfs
.currentDirectory
.type
= AFATFS_FILE_TYPE_DIRECTORY
;
2989 afatfs
.currentDirectory
.firstCluster
= afatfs
.rootDirectoryCluster
;
2990 afatfs
.currentDirectory
.attrib
= FAT_FILE_ATTRIBUTE_DIRECTORY
;
2992 // Root directories don't have a directory entry to represent themselves:
2993 afatfs
.currentDirectory
.directoryEntryPos
.sectorNumberPhysical
= 0;
2995 afatfs_fseek(&afatfs
.currentDirectory
, 0, AFATFS_SEEK_SET
);
3002 * Begin the process of opening a file with the given name in the current working directory (paths in the filename are
3003 * not supported) using the given mode.
3005 * To open the current working directory, pass "." for filename.
3007 * The complete() callback is called when finished with either a file handle (file was opened) or NULL upon failure.
3009 * Supported file mode strings:
3011 * r - Read from an existing file
3012 * w - Create a file for write access, if the file already exists then truncate it
3013 * a - Create a file for write access to the end of the file only, if the file already exists then append to it
3015 * r+ - Read and write from an existing file
3016 * w+ - Read and write from an existing file, if the file doesn't already exist it is created
3017 * a+ - Read from or append to an existing file, if the file doesn't already exist it is created TODO
3019 * as - Create a new file which is stored contiguously on disk (high performance mode/freefile) for append or write
3020 * ws If the file is already non-empty or freefile support is not compiled in then it will fall back to non-contiguous
3023 * All other mode strings are illegal. In particular, don't add "b" to the end of the mode string.
3025 * Returns false if the the open failed really early (out of file handles).
3027 bool afatfs_fopen(const char *filename
, const char *mode
, afatfsFileCallback_t complete
)
3029 uint8_t fileMode
= 0;
3030 afatfsFilePtr_t file
;
3034 fileMode
= AFATFS_FILE_MODE_READ
;
3037 fileMode
= AFATFS_FILE_MODE_WRITE
| AFATFS_FILE_MODE_CREATE
;
3040 fileMode
= AFATFS_FILE_MODE_APPEND
| AFATFS_FILE_MODE_CREATE
;
3046 fileMode
|= AFATFS_FILE_MODE_READ
;
3048 if (fileMode
== AFATFS_FILE_MODE_READ
) {
3049 fileMode
|= AFATFS_FILE_MODE_WRITE
;
3053 #ifdef AFATFS_USE_FREEFILE
3054 fileMode
|= AFATFS_FILE_MODE_CONTIGUOUS
| AFATFS_FILE_MODE_RETAIN_DIRECTORY
;
3059 file
= afatfs_allocateFileHandle();
3062 afatfs_createFile(file
, filename
, FAT_FILE_ATTRIBUTE_ARCHIVE
, fileMode
, complete
);
3063 } else if (complete
) {
3067 return file
!= NULL
;
3071 * Write a single character to the file at the current cursor position. If the cache is too busy to accept the write,
3072 * it is silently dropped.
3074 void afatfs_fputc(afatfsFilePtr_t file
, uint8_t c
)
3076 uint32_t cursorOffsetInSector
= file
->cursorOffset
% AFATFS_SECTOR_SIZE
;
3078 int cacheIndex
= file
->writeLockedCacheIndex
;
3080 /* If we've already locked the current sector in the cache, and we won't be completing the sector, we won't
3081 * be caching/uncaching/seeking, so we can just run this simpler fast case.
3083 if (cacheIndex
!= -1 && cursorOffsetInSector
!= AFATFS_SECTOR_SIZE
- 1) {
3084 afatfs_cacheSectorGetMemory(cacheIndex
)[cursorOffsetInSector
] = c
;
3085 file
->cursorOffset
++;
3088 afatfs_fwrite(file
, &c
, sizeof(c
));
3093 * Attempt to write `len` bytes from `buffer` into the `file`.
3095 * Returns the number of bytes actually written.
3097 * 0 will be returned when:
3098 * The filesystem is busy (try again later)
3100 * Fewer bytes will be written than requested when:
3101 * The write spanned a sector boundary and the next sector's contents/location was not yet available in the cache.
3102 * Or you tried to extend the length of the file but the filesystem is full (check afatfs_isFull()).
3104 uint32_t afatfs_fwrite(afatfsFilePtr_t file
, const uint8_t *buffer
, uint32_t len
)
3106 if ((file
->mode
& (AFATFS_FILE_MODE_APPEND
| AFATFS_FILE_MODE_WRITE
)) == 0) {
3110 if (afatfs_fileIsBusy(file
)) {
3111 // There might be a seek pending
3115 uint32_t cursorOffsetInSector
= file
->cursorOffset
% AFATFS_SECTOR_SIZE
;
3116 uint32_t writtenBytes
= 0;
3119 uint32_t bytesToWriteThisSector
= MIN(AFATFS_SECTOR_SIZE
- cursorOffsetInSector
, len
);
3120 uint8_t *sectorBuffer
;
3122 sectorBuffer
= afatfs_fileLockCursorSectorForWrite(file
);
3123 if (!sectorBuffer
) {
3124 // Cache is currently busy
3128 memcpy(sectorBuffer
+ cursorOffsetInSector
, buffer
, bytesToWriteThisSector
);
3130 writtenBytes
+= bytesToWriteThisSector
;
3133 * If the seek doesn't complete immediately then we'll break and wait for that seek to complete by waiting for
3134 * the file to be non-busy on entry again.
3136 * A seek operation should always be able to queue on the file since we have checked that the file wasn't busy
3137 * on entry (fseek will never return AFATFS_OPERATION_FAILURE).
3139 * If the seek has to queue, when the seek completes, it'll update the fileSize for us to contain the cursor.
3141 if (afatfs_fseekInternal(file
, bytesToWriteThisSector
, NULL
) == AFATFS_OPERATION_IN_PROGRESS
) {
3145 #ifdef AFATFS_USE_FREEFILE
3146 if ((file
->mode
& AFATFS_FILE_MODE_CONTIGUOUS
) != 0) {
3147 afatfs_assert(file
->cursorCluster
< afatfs
.freeFile
.firstCluster
);
3151 len
-= bytesToWriteThisSector
;
3152 buffer
+= bytesToWriteThisSector
;
3153 cursorOffsetInSector
= 0;
3156 return writtenBytes
;
3160 * Attempt to read `len` bytes from `file` into the `buffer`.
3162 * Returns the number of bytes actually read.
3164 * 0 will be returned when:
3165 * The filesystem is busy (try again later)
3166 * EOF was reached (check afatfs_isEof())
3168 * Fewer bytes than requested will be read when:
3169 * The read spans a AFATFS_SECTOR_SIZE boundary and the following sector was not available in the cache yet.
3171 uint32_t afatfs_fread(afatfsFilePtr_t file
, uint8_t *buffer
, uint32_t len
)
3173 if ((file
->mode
& AFATFS_FILE_MODE_READ
) == 0) {
3177 if (afatfs_fileIsBusy(file
)) {
3178 // There might be a seek pending
3183 * If we've just previously fwritten() to extend the file, the logical filesize will be out of date and the cursor
3184 * will appear to be beyond the end of the file (but actually it's precisely at the end of the file, because if
3185 * we had seeked backwards to where we could read something with fseek(), we would have updated the filesize).
3187 if (file
->cursorOffset
>= file
->logicalSize
)
3190 len
= MIN(file
->logicalSize
- file
->cursorOffset
, len
);
3192 uint32_t readBytes
= 0;
3193 uint32_t cursorOffsetInSector
= file
->cursorOffset
% AFATFS_SECTOR_SIZE
;
3196 uint32_t bytesToReadThisSector
= MIN(AFATFS_SECTOR_SIZE
- cursorOffsetInSector
, len
);
3197 uint8_t *sectorBuffer
;
3199 sectorBuffer
= afatfs_fileRetainCursorSectorForRead(file
);
3200 if (!sectorBuffer
) {
3201 // Cache is currently busy
3205 memcpy(buffer
, sectorBuffer
+ cursorOffsetInSector
, bytesToReadThisSector
);
3207 readBytes
+= bytesToReadThisSector
;
3210 * If the seek doesn't complete immediately then we'll break and wait for that seek to complete by waiting for
3211 * the file to be non-busy on entry again.
3213 * A seek operation should always be able to queue on the file since we have checked that the file wasn't busy
3214 * on entry (fseek will never return AFATFS_OPERATION_FAILURE).
3216 if (afatfs_fseekInternal(file
, bytesToReadThisSector
, NULL
) == AFATFS_OPERATION_IN_PROGRESS
) {
3220 len
-= bytesToReadThisSector
;
3221 buffer
+= bytesToReadThisSector
;
3222 cursorOffsetInSector
= 0;
3229 * Returns true if the file's pointer position currently lies at the end-of-file point (i.e. one byte beyond the last
3230 * byte in the file).
3232 bool afatfs_feof(afatfsFilePtr_t file
)
3234 return file
->cursorOffset
>= file
->logicalSize
;
3238 * Continue any queued operations on the given file.
3240 static void afatfs_fileOperationContinue(afatfsFile_t
*file
)
3242 if (file
->type
== AFATFS_FILE_TYPE_NONE
)
3245 switch (file
->operation
.operation
) {
3246 case AFATFS_FILE_OPERATION_CREATE_FILE
:
3247 afatfs_createFileContinue(file
);
3249 case AFATFS_FILE_OPERATION_SEEK
:
3250 afatfs_fseekInternalContinue(file
);
3252 case AFATFS_FILE_OPERATION_CLOSE
:
3253 afatfs_fcloseContinue(file
);
3255 case AFATFS_FILE_OPERATION_UNLINK
:
3256 afatfs_funlinkContinue(file
);
3258 case AFATFS_FILE_OPERATION_TRUNCATE
:
3259 afatfs_ftruncateContinue(file
, false);
3261 #ifdef AFATFS_USE_FREEFILE
3262 case AFATFS_FILE_OPERATION_APPEND_SUPERCLUSTER
:
3263 afatfs_appendSuperclusterContinue(file
);
3265 case AFATFS_FILE_OPERATION_LOCKED
:
3269 case AFATFS_FILE_OPERATION_APPEND_FREE_CLUSTER
:
3270 afatfs_appendRegularFreeClusterContinue(file
);
3272 case AFATFS_FILE_OPERATION_EXTEND_SUBDIRECTORY
:
3273 afatfs_extendSubdirectoryContinue(file
);
3275 case AFATFS_FILE_OPERATION_NONE
:
3282 * Check files for pending operations and execute them.
3284 static void afatfs_fileOperationsPoll(void)
3286 afatfs_fileOperationContinue(&afatfs
.currentDirectory
);
3288 #ifdef AFATFS_USE_INTROSPECTIVE_LOGGING
3289 afatfs_fileOperationContinue(&afatfs
.introSpecLog
);
3292 for (int i
= 0; i
< AFATFS_MAX_OPEN_FILES
; i
++) {
3293 afatfs_fileOperationContinue(&afatfs
.openFiles
[i
]);
3297 #ifdef AFATFS_USE_FREEFILE
3300 * Return the available size of the freefile (used for files in contiguous append mode)
3302 uint32_t afatfs_getContiguousFreeSpace(void)
3304 return afatfs
.freeFile
.logicalSize
;
3308 * Call to set up the initial state for finding the largest block of free space on the device whose corresponding FAT
3309 * sectors are themselves entirely free space (so the free space has dedicated FAT sectors of its own).
3311 static void afatfs_findLargestContiguousFreeBlockBegin(void)
3313 // The first FAT sector has two reserved entries, so it isn't eligible for this search. Start at the next FAT sector.
3314 afatfs
.initState
.freeSpaceSearch
.candidateStart
= afatfs_fatEntriesPerSector();
3315 afatfs
.initState
.freeSpaceSearch
.candidateEnd
= afatfs
.initState
.freeSpaceSearch
.candidateStart
;
3316 afatfs
.initState
.freeSpaceSearch
.bestGapStart
= 0;
3317 afatfs
.initState
.freeSpaceSearch
.bestGapLength
= 0;
3318 afatfs
.initState
.freeSpaceSearch
.phase
= AFATFS_FREE_SPACE_SEARCH_PHASE_FIND_HOLE
;
3322 * Call to continue the search for the largest contiguous block of free space on the device.
3325 * AFATFS_OPERATION_IN_PROGRESS - SD card is busy, call again later to resume
3326 * AFATFS_OPERATION_SUCCESS - When the search has finished and afatfs.initState.freeSpaceSearch has been updated with the details of the best gap.
3327 * AFATFS_OPERATION_FAILURE - When a read error occured
3329 static afatfsOperationStatus_e
afatfs_findLargestContiguousFreeBlockContinue(void)
3331 afatfsFreeSpaceSearch_t
*opState
= &afatfs
.initState
.freeSpaceSearch
;
3332 uint32_t fatEntriesPerSector
= afatfs_fatEntriesPerSector();
3333 uint32_t candidateGapLength
, searchLimit
;
3334 afatfsFindClusterStatus_e searchStatus
;
3337 switch (opState
->phase
) {
3338 case AFATFS_FREE_SPACE_SEARCH_PHASE_FIND_HOLE
:
3339 // Find the first free cluster
3340 switch (afatfs_findClusterWithCondition(CLUSTER_SEARCH_FREE_AT_BEGINNING_OF_FAT_SECTOR
, &opState
->candidateStart
, afatfs
.numClusters
+ FAT_SMALLEST_LEGAL_CLUSTER_NUMBER
)) {
3341 case AFATFS_FIND_CLUSTER_FOUND
:
3342 opState
->candidateEnd
= opState
->candidateStart
+ 1;
3343 opState
->phase
= AFATFS_FREE_SPACE_SEARCH_PHASE_GROW_HOLE
;
3346 case AFATFS_FIND_CLUSTER_FATAL
:
3347 // Some sort of read error occured
3348 return AFATFS_OPERATION_FAILURE
;
3350 case AFATFS_FIND_CLUSTER_NOT_FOUND
:
3351 // We finished searching the volume (didn't find any more holes to examine)
3352 return AFATFS_OPERATION_SUCCESS
;
3354 case AFATFS_FIND_CLUSTER_IN_PROGRESS
:
3355 return AFATFS_OPERATION_IN_PROGRESS
;
3358 case AFATFS_FREE_SPACE_SEARCH_PHASE_GROW_HOLE
:
3359 // Find the first used cluster after the beginning of the hole (that signals the end of the hole)
3361 // Don't search beyond the end of the volume, or such that the freefile size would exceed the max filesize
3362 searchLimit
= MIN((uint64_t) opState
->candidateStart
+ FAT_MAXIMUM_FILESIZE
/ afatfs_clusterSize(), afatfs
.numClusters
+ FAT_SMALLEST_LEGAL_CLUSTER_NUMBER
);
3364 searchStatus
= afatfs_findClusterWithCondition(CLUSTER_SEARCH_OCCUPIED
, &opState
->candidateEnd
, searchLimit
);
3366 switch (searchStatus
) {
3367 case AFATFS_FIND_CLUSTER_NOT_FOUND
:
3368 case AFATFS_FIND_CLUSTER_FOUND
:
3369 // Either we found a used sector, or the search reached the end of the volume or exceeded the max filesize
3370 candidateGapLength
= opState
->candidateEnd
- opState
->candidateStart
;
3372 if (candidateGapLength
> opState
->bestGapLength
) {
3373 opState
->bestGapStart
= opState
->candidateStart
;
3374 opState
->bestGapLength
= candidateGapLength
;
3377 if (searchStatus
== AFATFS_FIND_CLUSTER_NOT_FOUND
) {
3378 // This is the best hole there can be
3379 return AFATFS_OPERATION_SUCCESS
;
3381 // Start a new search for a new hole
3382 opState
->candidateStart
= roundUpTo(opState
->candidateEnd
+ 1, fatEntriesPerSector
);
3383 opState
->phase
= AFATFS_FREE_SPACE_SEARCH_PHASE_FIND_HOLE
;
3387 case AFATFS_FIND_CLUSTER_FATAL
:
3388 // Some sort of read error occured
3389 return AFATFS_OPERATION_FAILURE
;
3391 case AFATFS_FIND_CLUSTER_IN_PROGRESS
:
3392 return AFATFS_OPERATION_IN_PROGRESS
;
3399 static void afatfs_freeFileCreated(afatfsFile_t
*file
)
3402 // Did the freefile already have allocated space?
3403 if (file
->logicalSize
> 0) {
3404 // We've completed freefile init, move on to the next init phase
3405 afatfs
.initPhase
= AFATFS_INITIALIZATION_FREEFILE_LAST
+ 1;
3407 // Allocate clusters for the freefile
3408 afatfs_findLargestContiguousFreeBlockBegin();
3409 afatfs
.initPhase
= AFATFS_INITIALIZATION_FREEFILE_FAT_SEARCH
;
3412 // Failed to allocate an entry
3413 afatfs
.lastError
= AFATFS_ERROR_GENERIC
;
3414 afatfs
.filesystemState
= AFATFS_FILESYSTEM_STATE_FATAL
;
3420 #ifdef AFATFS_USE_INTROSPECTIVE_LOGGING
3422 static void afatfs_introspecLogCreated(afatfsFile_t
*file
)
3427 afatfs
.lastError
= AFATFS_ERROR_GENERIC
;
3428 afatfs
.filesystemState
= AFATFS_FILESYSTEM_STATE_FATAL
;
3434 static void afatfs_initContinue(void)
3436 #ifdef AFATFS_USE_FREEFILE
3437 afatfsOperationStatus_e status
;
3444 switch (afatfs
.initPhase
) {
3445 case AFATFS_INITIALIZATION_READ_MBR
:
3446 if (afatfs_cacheSector(0, §or
, AFATFS_CACHE_READ
| AFATFS_CACHE_DISCARDABLE
, 0) == AFATFS_OPERATION_SUCCESS
) {
3447 if (afatfs_parseMBR(sector
)) {
3448 afatfs
.initPhase
= AFATFS_INITIALIZATION_READ_VOLUME_ID
;
3451 afatfs
.lastError
= AFATFS_ERROR_BAD_MBR
;
3452 afatfs
.filesystemState
= AFATFS_FILESYSTEM_STATE_FATAL
;
3456 case AFATFS_INITIALIZATION_READ_VOLUME_ID
:
3457 if (afatfs_cacheSector(afatfs
.partitionStartSector
, §or
, AFATFS_CACHE_READ
| AFATFS_CACHE_DISCARDABLE
, 0) == AFATFS_OPERATION_SUCCESS
) {
3458 if (afatfs_parseVolumeID(sector
)) {
3459 // Open the root directory
3464 afatfs
.lastError
= AFATFS_ERROR_BAD_FILESYSTEM_HEADER
;
3465 afatfs
.filesystemState
= AFATFS_FILESYSTEM_STATE_FATAL
;
3470 #ifdef AFATFS_USE_FREEFILE
3471 case AFATFS_INITIALIZATION_FREEFILE_CREATE
:
3472 afatfs
.initPhase
= AFATFS_INITIALIZATION_FREEFILE_CREATING
;
3474 afatfs_createFile(&afatfs
.freeFile
, AFATFS_FREESPACE_FILENAME
, FAT_FILE_ATTRIBUTE_SYSTEM
| FAT_FILE_ATTRIBUTE_READ_ONLY
,
3475 AFATFS_FILE_MODE_CREATE
| AFATFS_FILE_MODE_RETAIN_DIRECTORY
, afatfs_freeFileCreated
);
3477 case AFATFS_INITIALIZATION_FREEFILE_CREATING
:
3478 afatfs_fileOperationContinue(&afatfs
.freeFile
);
3480 case AFATFS_INITIALIZATION_FREEFILE_FAT_SEARCH
:
3481 if (afatfs_findLargestContiguousFreeBlockContinue() == AFATFS_OPERATION_SUCCESS
) {
3482 // If the freefile ends up being empty then we only have to save its directory entry:
3483 afatfs
.initPhase
= AFATFS_INITIALIZATION_FREEFILE_SAVE_DIR_ENTRY
;
3485 if (afatfs
.initState
.freeSpaceSearch
.bestGapLength
> AFATFS_FREEFILE_LEAVE_CLUSTERS
+ 1) {
3486 afatfs
.initState
.freeSpaceSearch
.bestGapLength
-= AFATFS_FREEFILE_LEAVE_CLUSTERS
;
3488 /* So that the freefile never becomes empty, we want it to occupy a non-integer number of
3489 * superclusters. So its size mod the number of clusters in a supercluster should be 1.
3491 afatfs
.initState
.freeSpaceSearch
.bestGapLength
= ((afatfs
.initState
.freeSpaceSearch
.bestGapLength
- 1) & ~(afatfs_fatEntriesPerSector() - 1)) + 1;
3493 // Anything useful left over?
3494 if (afatfs
.initState
.freeSpaceSearch
.bestGapLength
> afatfs_fatEntriesPerSector()) {
3495 uint32_t startCluster
= afatfs
.initState
.freeSpaceSearch
.bestGapStart
;
3496 // Points 1-beyond the final cluster of the freefile:
3497 uint32_t endCluster
= afatfs
.initState
.freeSpaceSearch
.bestGapStart
+ afatfs
.initState
.freeSpaceSearch
.bestGapLength
;
3499 afatfs_assert(endCluster
< afatfs
.numClusters
+ FAT_SMALLEST_LEGAL_CLUSTER_NUMBER
);
3501 afatfs
.initState
.freeSpaceFAT
.startCluster
= startCluster
;
3502 afatfs
.initState
.freeSpaceFAT
.endCluster
= endCluster
;
3504 afatfs
.freeFile
.firstCluster
= startCluster
;
3506 afatfs
.freeFile
.logicalSize
= afatfs
.initState
.freeSpaceSearch
.bestGapLength
* afatfs_clusterSize();
3507 afatfs
.freeFile
.physicalSize
= afatfs
.freeFile
.logicalSize
;
3509 // We can write the FAT table for the freefile now
3510 afatfs
.initPhase
= AFATFS_INITIALIZATION_FREEFILE_UPDATE_FAT
;
3511 } // Else the freefile's FAT chain and filesize remains the default (empty)
3517 case AFATFS_INITIALIZATION_FREEFILE_UPDATE_FAT
:
3518 status
= afatfs_FATFillWithPattern(AFATFS_FAT_PATTERN_TERMINATED_CHAIN
, &afatfs
.initState
.freeSpaceFAT
.startCluster
, afatfs
.initState
.freeSpaceFAT
.endCluster
);
3520 if (status
== AFATFS_OPERATION_SUCCESS
) {
3521 afatfs
.initPhase
= AFATFS_INITIALIZATION_FREEFILE_SAVE_DIR_ENTRY
;
3524 } else if (status
== AFATFS_OPERATION_FAILURE
) {
3525 afatfs
.lastError
= AFATFS_ERROR_GENERIC
;
3526 afatfs
.filesystemState
= AFATFS_FILESYSTEM_STATE_FATAL
;
3529 case AFATFS_INITIALIZATION_FREEFILE_SAVE_DIR_ENTRY
:
3530 status
= afatfs_saveDirectoryEntry(&afatfs
.freeFile
, AFATFS_SAVE_DIRECTORY_NORMAL
);
3532 if (status
== AFATFS_OPERATION_SUCCESS
) {
3535 } else if (status
== AFATFS_OPERATION_FAILURE
) {
3536 afatfs
.lastError
= AFATFS_ERROR_GENERIC
;
3537 afatfs
.filesystemState
= AFATFS_FILESYSTEM_STATE_FATAL
;
3542 #ifdef AFATFS_USE_INTROSPECTIVE_LOGGING
3543 case AFATFS_INITIALIZATION_INTROSPEC_LOG_CREATE
:
3544 afatfs
.initPhase
= AFATFS_INITIALIZATION_INTROSPEC_LOG_CREATING
;
3546 afatfs_createFile(&afatfs
.introSpecLog
, AFATFS_INTROSPEC_LOG_FILENAME
, FAT_FILE_ATTRIBUTE_ARCHIVE
,
3547 AFATFS_FILE_MODE_CREATE
| AFATFS_FILE_MODE_APPEND
, afatfs_introspecLogCreated
);
3549 case AFATFS_INITIALIZATION_INTROSPEC_LOG_CREATING
:
3550 afatfs_fileOperationContinue(&afatfs
.introSpecLog
);
3554 case AFATFS_INITIALIZATION_DONE
:
3555 afatfs
.filesystemState
= AFATFS_FILESYSTEM_STATE_READY
;
3561 * Check to see if there are any pending operations on the filesystem and perform a little work (without waiting on the
3562 * sdcard). You must call this periodically.
3564 void afatfs_poll(void)
3566 // Only attempt to continue FS operations if the card is present & ready, otherwise we would just be wasting time
3567 if (sdcard_poll()) {
3570 switch (afatfs
.filesystemState
) {
3571 case AFATFS_FILESYSTEM_STATE_INITIALIZATION
:
3572 afatfs_initContinue();
3574 case AFATFS_FILESYSTEM_STATE_READY
:
3575 afatfs_fileOperationsPoll();
3583 #ifdef AFATFS_USE_INTROSPECTIVE_LOGGING
3585 void afatfs_sdcardProfilerCallback(sdcardBlockOperation_e operation
, uint32_t blockIndex
, uint32_t duration
)
3587 // Make sure the log file has actually been opened before we try to log to it:
3588 if (afatfs
.introSpecLog
.type
== AFATFS_FILE_TYPE_NONE
) {
3593 LOG_ENTRY_SIZE
= 16 // Log entry size should be a power of two to avoid partial fwrites()
3596 uint8_t buffer
[LOG_ENTRY_SIZE
];
3598 buffer
[0] = operation
;
3600 // Padding/reserved:
3605 buffer
[4] = blockIndex
& 0xFF;
3606 buffer
[5] = (blockIndex
>> 8) & 0xFF;
3607 buffer
[6] = (blockIndex
>> 16) & 0xFF;
3608 buffer
[7] = (blockIndex
>> 24) & 0xFF;
3610 buffer
[8] = duration
& 0xFF;
3611 buffer
[9] = (duration
>> 8) & 0xFF;
3612 buffer
[10] = (duration
>> 16) & 0xFF;
3613 buffer
[11] = (duration
>> 24) & 0xFF;
3615 // Padding/reserved:
3621 // Ignore write failures
3622 afatfs_fwrite(&afatfs
.introSpecLog
, buffer
, LOG_ENTRY_SIZE
);
3627 afatfsFilesystemState_e
afatfs_getFilesystemState(void)
3629 return afatfs
.filesystemState
;
3632 afatfsError_e
afatfs_getLastError(void)
3634 return afatfs
.lastError
;
3637 void afatfs_init(void)
3640 afatfs
.cache
= afatfs_cache
;
3642 afatfs
.filesystemState
= AFATFS_FILESYSTEM_STATE_INITIALIZATION
;
3643 afatfs
.initPhase
= AFATFS_INITIALIZATION_READ_MBR
;
3644 afatfs
.lastClusterAllocated
= FAT_SMALLEST_LEGAL_CLUSTER_NUMBER
;
3646 #ifdef AFATFS_USE_INTROSPECTIVE_LOGGING
3647 sdcard_setProfilerCallback(afatfs_sdcardProfilerCallback
);
3652 * Shut down the filesystem, flushing all data to the disk. Keep calling until it returns true.
3654 * dirty - Set to true to skip the flush operation and terminate immediately (buffered data will be lost!)
3656 bool afatfs_destroy(bool dirty
)
3658 // Only attempt detailed cleanup if the filesystem is in reasonable looking state
3659 if (!dirty
&& afatfs
.filesystemState
== AFATFS_FILESYSTEM_STATE_READY
) {
3660 int openFileCount
= 0;
3662 for (int i
= 0; i
< AFATFS_MAX_OPEN_FILES
; i
++) {
3663 if (afatfs
.openFiles
[i
].type
!= AFATFS_FILE_TYPE_NONE
) {
3664 afatfs_fclose(&afatfs
.openFiles
[i
], NULL
);
3665 // The close operation might not finish right away, so count this file as still open for now
3670 #ifdef AFATFS_USE_INTROSPECTIVE_LOGGING
3671 if (afatfs
.introSpecLog
.type
!= AFATFS_FILE_TYPE_NONE
) {
3672 afatfs_fclose(&afatfs
.introSpecLog
, NULL
);
3677 #ifdef AFATFS_USE_FREEFILE
3678 if (afatfs
.freeFile
.type
!= AFATFS_FILE_TYPE_NONE
) {
3679 afatfs_fclose(&afatfs
.freeFile
, NULL
);
3684 if (afatfs
.currentDirectory
.type
!= AFATFS_FILE_TYPE_NONE
) {
3685 afatfs_fclose(&afatfs
.currentDirectory
, NULL
);
3691 if (!afatfs_flush()) {
3695 if (afatfs
.cacheFlushInProgress
) {
3699 if (openFileCount
> 0) {
3704 /* All sector locks should have been released by closing the files, so the subsequent flush should have written
3705 * all dirty pages to disk. If not, something's wrong:
3707 for (int i
= 0; i
< AFATFS_NUM_CACHE_SECTORS
; i
++) {
3708 afatfs_assert(afatfs
.cacheDescriptor
[i
].state
!= AFATFS_CACHE_STATE_DIRTY
);
3713 // Clear the afatfs so it's as if we never ran
3714 memset(&afatfs
, 0, sizeof(afatfs
));
3720 * Get a pessimistic estimate of the amount of buffer space that we have available to write to immediately.
3722 uint32_t afatfs_getFreeBufferSpace(void)
3724 uint32_t result
= 0;
3725 for (int i
= 0; i
< AFATFS_NUM_CACHE_SECTORS
; i
++) {
3726 if (!afatfs
.cacheDescriptor
[i
].locked
&& (afatfs
.cacheDescriptor
[i
].state
== AFATFS_CACHE_STATE_EMPTY
|| afatfs
.cacheDescriptor
[i
].state
== AFATFS_CACHE_STATE_IN_SYNC
)) {
3727 result
+= AFATFS_SECTOR_SIZE
;