Don't handle non-MSP characters on VTX MSP port (#14091)
[betaflight.git] / src / main / io / asyncfatfs / asyncfatfs.c
blob97c6f7fe927ca654af89acae672b732667f04e24
1 /*
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)
8 * any later version.
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/>.
21 /**
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
28 * files.
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.
36 #include <stdint.h>
37 #include <stdlib.h>
38 #include <string.h>
40 #include "platform.h"
42 #ifdef AFATFS_DEBUG
43 #include <signal.h>
44 #include <stdio.h>
45 #endif
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"
57 #ifdef AFATFS_DEBUG
58 #define ONLY_EXPOSE_FOR_TESTING
59 #else
60 #define ONLY_EXPOSE_FOR_TESTING static
61 #endif
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"
120 typedef enum {
121 AFATFS_SAVE_DIRECTORY_NORMAL,
122 AFATFS_SAVE_DIRECTORY_FOR_CLOSE,
123 AFATFS_SAVE_DIRECTORY_DELETED
124 } afatfsSaveDirectoryEntryMode_e;
126 typedef enum {
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;
134 typedef enum {
135 AFATFS_FILE_TYPE_NONE,
136 AFATFS_FILE_TYPE_NORMAL,
137 AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY,
138 AFATFS_FILE_TYPE_DIRECTORY
139 } afatfsFileType_e;
141 typedef enum {
142 CLUSTER_SEARCH_FREE_AT_BEGINNING_OF_FAT_SECTOR,
143 CLUSTER_SEARCH_FREE,
144 CLUSTER_SEARCH_OCCUPIED
145 } afatfsClusterSearchCondition_e;
147 enum {
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
155 typedef enum {
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 {
165 uint8_t *bytes;
166 uint16_t *fat16;
167 uint32_t *fat32;
168 } 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.
201 unsigned locked:1;
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;
218 typedef enum {
219 AFATFS_FAT_PATTERN_UNTERMINATED_CHAIN,
220 AFATFS_FAT_PATTERN_TERMINATED_CHAIN,
221 AFATFS_FAT_PATTERN_FREE
222 } afatfsFATPattern_e;
224 typedef enum {
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;
239 uint32_t endCluster;
240 } afatfsFreeSpaceFAT_t;
242 typedef struct afatfsCreateFile_t {
243 afatfsFileCallback_t callback;
245 uint8_t phase;
246 uint8_t filename[FAT_FILENAME_LENGTH];
247 } afatfsCreateFile_t;
249 typedef struct afatfsSeek_t {
250 afatfsFileCallback_t callback;
252 uint32_t seekOffset;
253 } afatfsSeek_t;
255 typedef enum {
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;
269 typedef enum {
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;
285 typedef enum {
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;
303 typedef enum {
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,
310 #endif
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;
322 typedef enum {
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;
334 } afatfsCloseFile_t;
336 typedef enum {
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,
346 #endif
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;
353 union {
354 afatfsCreateFile_t createFile;
355 afatfsSeek_t seek;
356 afatfsAppendSupercluster_t appendSupercluster;
357 afatfsAppendFreeCluster_t appendFreeCluster;
358 afatfsExtendSubdirectory_t extendSubdirectory;
359 afatfsUnlinkFile_t unlinkFile;
360 afatfsTruncateFile_t truncateFile;
361 afatfsCloseFile_t closeFile;
362 } state;
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
383 * the cluster size.
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
397 * set to zero.
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;
425 } afatfsFile_t;
427 typedef enum {
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,
438 #endif
440 #ifdef AFATFS_USE_INTROSPECTIVE_LOGGING
441 AFATFS_INITIALIZATION_INTROSPEC_LOG_CREATE,
442 AFATFS_INITIALIZATION_INTROSPEC_LOG_CREATING,
443 #endif
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
456 union {
457 afatfsFreeSpaceSearch_t freeSpaceSearch;
458 afatfsFreeSpaceFAT_t freeSpaceFAT;
459 } initState;
460 #endif
462 #ifdef STM32H7
463 uint8_t *cache;
464 #else
465 uint8_t cache[AFATFS_SECTOR_SIZE * AFATFS_NUM_CACHE_SECTORS];
466 #endif
467 afatfsCacheBlockDescriptor_t cacheDescriptor[AFATFS_NUM_CACHE_SECTORS];
468 uint32_t cacheTimer;
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;
477 #endif
479 #ifdef AFATFS_USE_INTROSPECTIVE_LOGGING
480 afatfsFile_t introSpecLog;
481 #endif
483 afatfsError_e lastError;
485 bool filesystemFull;
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
505 * cluster.
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
514 } afatfs_t;
516 #ifdef STM32H7
517 static DMA_DATA_ZERO_INIT uint8_t afatfs_cache[AFATFS_SECTOR_SIZE * AFATFS_NUM_CACHE_SECTORS] __attribute__((aligned(32)));
518 #endif
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;
530 if (remainder > 0) {
531 value += rounding - remainder;
534 return value;
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)
551 if (!condition) {
552 if (afatfs.lastError == AFATFS_ERROR_NONE) {
553 afatfs.lastError = AFATFS_ERROR_GENERIC;
555 afatfs.filesystemState = AFATFS_FILESYSTEM_STATE_FATAL;
556 #ifdef AFATFS_DEBUG
557 raise(SIGTRAP);
558 #endif
561 return condition;
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(const uint8_t *memory)
612 int index = (memory - afatfs.cache) / AFATFS_SECTOR_SIZE;
614 if (afatfs_assert(index >= 0 && index < AFATFS_NUM_CACHE_SECTORS)) {
615 return index;
616 } else {
617 return -1;
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)
655 (void) operation;
656 (void) 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;
665 } else {
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;
671 break;
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)
681 (void) operation;
682 (void) 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++;
697 } else {
698 afatfs_assert(afatfs_cacheSectorGetMemory(i) == buffer);
700 afatfs.cacheDescriptor[i].state = AFATFS_CACHE_STATE_IN_SYNC;
702 break;
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);
718 #endif
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;
726 break;
728 case SDCARD_OPERATION_SUCCESS:
729 // Buffer is already transmitted
730 afatfs.cacheDirtyEntries--;
731 cacheDescriptor->state = AFATFS_CACHE_STATE_IN_SYNC;
732 break;
734 case SDCARD_OPERATION_BUSY:
735 case SDCARD_OPERATION_FAILURE:
736 default:
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)) {
747 return false;
750 return true;
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];
765 return NULL;
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)
781 int allocateIndex;
782 int emptyIndex = -1, discardableIndex = -1;
784 uint32_t oldestSyncedSectorLastUse = 0xFFFFFFFF;
785 int oldestSyncedSectorIndex = -1;
787 if (
788 !afatfs_assert(
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
793 return -1;
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) {
803 emptyIndex = i;
804 break;
807 // Bump the last access time
808 afatfs.cacheDescriptor[i].accessTimestamp = ++afatfs.cacheTimer;
809 return i;
812 switch (afatfs.cacheDescriptor[i].state) {
813 case AFATFS_CACHE_STATE_EMPTY:
814 emptyIndex = i;
815 break;
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;
827 break;
828 default:
833 if (emptyIndex > -1) {
834 allocateIndex = emptyIndex;
835 } else if (discardableIndex > -1) {
836 allocateIndex = discardableIndex;
837 } else if (oldestSyncedSectorIndex > -1) {
838 allocateIndex = oldestSyncedSectorIndex;
839 } else {
840 allocateIndex = -1;
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
873 return false;
877 return true;
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;
906 } else {
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;
921 } else {
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)
935 * Returns:
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;
957 break;
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) {
973 eraseCount = 0;
974 } else {
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;
979 #endif
981 FALLTHROUGH;
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]);
988 FALLTHROUGH;
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;
1001 break;
1003 default:
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)
1017 return false;
1019 mbrPartitionEntry_t *partition = (mbrPartitionEntry_t *) (sector + 446);
1021 for (int i = 0; i < 4; i++) {
1022 if (
1023 partition[i].lbaBegin > 0
1024 && (
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;
1033 return true;
1037 return false;
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) {
1048 return false;
1051 afatfs.fatStartSector = afatfs.partitionStartSector + volume->reservedSectorCount;
1053 afatfs.sectorsPerCluster = volume->sectorsPerCluster;
1054 if (afatfs.sectorsPerCluster < 1 || afatfs.sectorsPerCluster > 128 || !isPowerOfTwo(afatfs.sectorsPerCluster)) {
1055 return false;
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;
1075 } else {
1076 afatfs.filesystemType = FAT_FILESYSTEM_TYPE_FAT32;
1079 if (afatfs.filesystemType == FAT_FILESYSTEM_TYPE_FAT32) {
1080 afatfs.rootDirectoryCluster = volume->fatDescriptor.fat32.rootCluster;
1081 } else {
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;
1090 return true;
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);
1103 } else {
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);
1115 } else {
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
1124 * cluster numbers.
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).
1129 * Returns:
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), &sector.bytes, AFATFS_CACHE_READ, 0);
1142 if (result == AFATFS_OPERATION_SUCCESS) {
1143 if (afatfs.filesystemType == FAT_FILESYSTEM_TYPE_FAT16) {
1144 *nextCluster = sector.fat16[fatSectorEntryIndex];
1145 } else {
1146 *nextCluster = fat32_decodeClusterNumber(sector.fat32[fatSectorEntryIndex]);
1150 return result;
1154 * Set the cluster number that follows the given cluster. Pass 0xFFFFFFFF for nextCluster to terminate the FAT chain.
1156 * Returns:
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;
1167 #ifdef AFATFS_DEBUG
1168 afatfs_assert(startCluster >= FAT_SMALLEST_LEGAL_CLUSTER_NUMBER);
1169 #endif
1171 afatfs_getFATPositionForCluster(startCluster, &fatSectorIndex, &fatSectorEntryIndex);
1173 fatPhysicalSector = afatfs_fatSectorToPhysical(0, fatSectorIndex);
1175 result = afatfs_cacheSector(fatPhysicalSector, &sector.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;
1180 } else {
1181 sector.fat32[fatSectorEntryIndex] = nextCluster;
1185 return result;
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
1210 * condition.
1212 * searchLimit - Last cluster to examine (exclusive). To search the entire volume, pass:
1213 * afatfs.numClusters + FAT_SMALLEST_LEGAL_CLUSTER_NUMBER
1215 * Condition:
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.
1221 * Returns:
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;
1236 int jump;
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;
1249 break;
1250 case CLUSTER_SEARCH_OCCUPIED:
1251 case CLUSTER_SEARCH_FREE:
1252 jump = 1;
1253 break;
1254 default:
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
1270 #endif
1272 afatfsOperationStatus_e status = afatfs_cacheSector(afatfs_fatSectorToPhysical(0, fatSectorIndex), &sector.bytes, AFATFS_CACHE_READ | AFATFS_CACHE_DISCARDABLE, 0);
1274 switch (status) {
1275 case AFATFS_OPERATION_SUCCESS:
1276 do {
1277 uint32_t clusterNumber;
1279 switch (afatfs.filesystemType) {
1280 case FAT_FILESYSTEM_TYPE_FAT16:
1281 clusterNumber = sector.fat16[fatSectorEntryIndex];
1282 break;
1283 case FAT_FILESYSTEM_TYPE_FAT32:
1284 clusterNumber = fat32_decodeClusterNumber(sector.fat32[fatSectorEntryIndex]);
1285 break;
1286 default:
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;
1297 } else {
1298 *cluster = searchLimit;
1299 return AFATFS_FIND_CLUSTER_NOT_FOUND;
1303 (*cluster) += jump;
1304 fatSectorEntryIndex += jump;
1305 } while (fatSectorEntryIndex < fatEntriesPerSector);
1307 // Move on to the next FAT sector
1308 fatSectorIndex++;
1309 fatSectorEntryIndex = 0;
1310 break;
1311 case AFATFS_OPERATION_FAILURE:
1312 return AFATFS_FIND_CLUSTER_FATAL;
1313 break;
1314 case AFATFS_OPERATION_IN_PROGRESS:
1315 return AFATFS_FIND_CLUSTER_IN_PROGRESS;
1316 break;
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.
1328 * Returns:
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
1335 (void) file;
1336 #else
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) {
1344 *nextCluster = 0;
1345 } else {
1346 *nextCluster = currentCluster + 1;
1349 return AFATFS_OPERATION_SUCCESS;
1350 } else
1351 #endif
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
1367 * Returns -
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, &sector.bytes, cacheFlags, eraseSectorCount);
1401 if (result != AFATFS_OPERATION_SUCCESS) {
1402 return result;
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);
1408 } else {
1409 fprintf(stderr, "Writing FAT chain from cluster %u to %u in FAT sector %u...\n", *startCluster, endCluster, fatPhysicalSector);
1411 #endif
1413 switch (pattern) {
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;
1422 } else {
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;
1434 } else {
1435 sector.fat32[lastEntryIndex - 1] = 0xFFFFFFFF;
1437 break;
1439 break;
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;
1446 break;
1449 fatPhysicalSector++;
1450 eraseSectorCount--;
1451 firstEntryIndex = 0;
1454 return AFATFS_OPERATION_SUCCESS;
1457 #endif
1460 * Write the directory entry for the file into its `directoryEntryPos` position in its containing directory.
1462 * mode:
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
1467 * Returns:
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)
1474 uint8_t *sector;
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, &sector, 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);
1485 #endif
1487 if (result == AFATFS_OPERATION_SUCCESS) {
1488 if (afatfs_assert(file->directoryEntryPos.entryIndex >= 0)) {
1489 fatDirectoryEntry_t *entry = (fatDirectoryEntry_t *) sector + file->directoryEntryPos.entryIndex;
1491 switch (mode) {
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;
1499 break;
1500 case AFATFS_SAVE_DIRECTORY_DELETED:
1501 entry->filename[0] = FAT_DELETED_FILE_MARKER;
1502 FALLTHROUGH;
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;
1516 } else {
1517 return AFATFS_OPERATION_FAILURE;
1521 return result;
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.
1528 * Returns:
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
1536 * file.
1538 static afatfsOperationStatus_e afatfs_appendRegularFreeClusterContinue(afatfsFile_t *file)
1540 afatfsAppendFreeCluster_t *opState = &file->operation.state.appendFreeCluster;
1541 afatfsOperationStatus_e status;
1543 doMore:
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;
1561 goto doMore;
1562 break;
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;
1567 goto doMore;
1568 break;
1569 case AFATFS_FIND_CLUSTER_IN_PROGRESS:
1570 break;
1572 break;
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;
1580 } else {
1581 opState->phase = AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FILE_DIRECTORY;
1584 goto doMore;
1586 break;
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;
1593 goto doMore;
1595 break;
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;
1599 goto doMore;
1601 break;
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;
1608 break;
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;
1616 break;
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.
1638 * Returns:
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.
1676 * Returns:
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;
1686 doMore:
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;
1714 } else {
1716 * We also need to update the FAT of the supercluster that used to end the file so that it no longer
1717 * terminates there
1719 opState->fatRewriteStartCluster -= afatfs_fatEntriesPerSector();
1722 opState->phase = AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FREEFILE_DIRECTORY;
1723 goto doMore;
1724 break;
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;
1731 goto doMore;
1733 break;
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;
1739 goto doMore;
1741 break;
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);
1745 break;
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;
1752 return status;
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.
1761 * Returns:
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);
1793 #endif
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);
1807 } else
1808 #endif
1810 status = afatfs_appendRegularFreeCluster(file);
1813 return status;
1817 * Returns true if the file's cursor is sitting beyond the end of the last allocated cluster (i.e. the logical fileSize
1818 * is not checked).
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;
1824 } else {
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)
1837 uint8_t *result;
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)) {
1846 return NULL;
1849 result = afatfs_cacheSectorGetMemory(file->readRetainCacheIndex);
1850 } else {
1851 if (afatfs_isEndOfAllocatedFile(file)) {
1852 return NULL;
1855 afatfs_assert(physicalSector > 0); // We never read the root sector using files
1857 afatfsOperationStatus_e status = afatfs_cacheSector(
1858 physicalSector,
1859 &result,
1860 AFATFS_CACHE_READ | AFATFS_CACHE_RETAIN,
1864 if (status != AFATFS_OPERATION_SUCCESS) {
1865 // Sector not ready for read
1866 return NULL;
1869 file->readRetainCacheIndex = afatfs_getCacheDescriptorIndexForBuffer(result);
1872 return 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;
1883 uint8_t *result;
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)) {
1891 return NULL;
1894 result = afatfs_cacheSectorGetMemory(file->writeLockedCacheIndex);
1895 } else {
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
1901 return NULL;
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
1914 if (
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;
1926 } else {
1927 eraseBlockCount = 0;
1930 status = afatfs_cacheSector(
1931 physicalSector,
1932 &result,
1933 cacheFlags,
1934 eraseBlockCount
1937 if (status != AFATFS_OPERATION_SUCCESS) {
1938 // Not enough cache available to accept this write / sector not ready for read
1939 return NULL;
1942 file->writeLockedCacheIndex = afatfs_getCacheDescriptorIndexForBuffer(result);
1945 return 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
1958 * the seek atomic.
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;
1968 return true;
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;
1978 return true;
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) {
1988 return false;
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;
2006 } else {
2007 // Try again later
2008 return false;
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;
2017 return true;
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;
2047 } else {
2048 // Try again later
2049 return false;
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);
2066 return true;
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.
2074 * Returns:
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,
2078 * try again later.
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)) {
2084 if (callback) {
2085 callback(file);
2088 return AFATFS_OPERATION_SUCCESS;
2089 } else {
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.
2110 * Returns:
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,
2115 * try again later.
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);
2122 switch (whence) {
2123 case AFATFS_SEEK_CUR:
2124 if (offset >= 0) {
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;
2131 break;
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;
2141 break;
2143 case AFATFS_SEEK_SET:
2144 break;
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)) {
2166 return false;
2167 } else {
2168 *position = file->cursorOffset;
2169 return true;
2174 * Attempt to advance the directory pointer `finder` to the next entry in the directory.
2176 * Returns:
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)
2183 uint8_t *sector;
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
2189 } else {
2190 return AFATFS_OPERATION_IN_PROGRESS;
2194 sector = afatfs_fileRetainCursorSectorForRead(directory);
2196 if (sector) {
2197 finder->entryIndex++;
2199 *dirEntry = (fatDirectoryEntry_t*) sector + finder->entryIndex;
2201 finder->sectorNumberPhysical = afatfs_fileGetCursorPhysicalSector(directory);
2203 return AFATFS_OPERATION_SUCCESS;
2204 } else {
2205 if (afatfs_isEndOfAllocatedFile(directory)) {
2206 *dirEntry = NULL;
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
2225 * directory.
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;
2241 doMore:
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;
2248 goto doMore;
2249 } else if (status == AFATFS_OPERATION_FAILURE) {
2250 opState->phase = AFATFS_EXTEND_SUBDIRECTORY_PHASE_FAILURE;
2251 goto doMore;
2253 break;
2254 case AFATFS_EXTEND_SUBDIRECTORY_PHASE_WRITE_SECTORS:
2255 // Now, zero out that cluster
2256 afatfs_fileGetCursorClusterAndSector(directory, &clusterNumber, &sectorInCluster);
2257 physicalSector = afatfs_fileGetCursorPhysicalSector(directory);
2259 while (1) {
2260 status = afatfs_cacheSector(physicalSector, &sectorBuffer, AFATFS_CACHE_WRITE, 0);
2262 if (status != AFATFS_OPERATION_SUCCESS) {
2263 return status;
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));
2289 sectorInCluster++;
2290 physicalSector++;
2291 } else {
2292 break;
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;
2299 goto doMore;
2300 break;
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;
2309 break;
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;
2317 break;
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.
2368 * Returns:
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) {
2382 if (*dirEntry) {
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;
2389 } else {
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
2395 continue;
2396 } else {
2397 // The status (in progress or failure) of extending the directory becomes our status
2398 break;
2403 return result;
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];
2418 return NULL;
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;
2435 #endif
2437 doMore:
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;
2447 } else
2448 #endif
2450 opState->phase = AFATFS_TRUNCATE_FILE_ERASE_FAT_CHAIN_NORMAL;
2452 goto doMore;
2454 break;
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;
2462 goto doMore;
2464 break;
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;
2479 goto doMore;
2481 break;
2482 #endif
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) {
2490 return status;
2493 status = afatfs_FATSetNextCluster(opState->currentCluster, 0);
2495 if (status != AFATFS_OPERATION_SUCCESS) {
2496 return status;
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;
2506 goto doMore;
2507 break;
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;
2518 break;
2521 if (status == AFATFS_OPERATION_FAILURE && file->operation.operation == AFATFS_FILE_OPERATION_TRUNCATE) {
2522 file->operation.operation = AFATFS_FILE_OPERATION_NONE;
2525 return status;
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))
2540 return false;
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;
2554 } else
2555 #endif
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);
2568 return true;
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;
2588 doMore:
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;
2594 goto doMore;
2595 break;
2596 case AFATFS_CREATEFILE_PHASE_FIND_FILE:
2597 do {
2598 status = afatfs_findNext(&afatfs.currentDirectory, &file->directoryEntryPos, &entry);
2600 switch (status) {
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;
2611 goto doMore;
2612 } else {
2613 // File not found.
2615 opState->phase = AFATFS_CREATEFILE_PHASE_FAILURE;
2616 goto doMore;
2618 } else if (entry->attrib & FAT_FILE_ATTRIBUTE_VOLUME_ID) {
2619 break;
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;
2627 goto doMore;
2630 afatfs_fileLoadDirectoryEntry(file, entry);
2632 afatfs_findLast(&afatfs.currentDirectory);
2634 opState->phase = AFATFS_CREATEFILE_PHASE_SUCCESS;
2635 goto doMore;
2636 } // Else this entry doesn't match, fall through and continue the search
2637 break;
2638 case AFATFS_OPERATION_FAILURE:
2639 afatfs_findLast(&afatfs.currentDirectory);
2640 opState->phase = AFATFS_CREATEFILE_PHASE_FAILURE;
2641 goto doMore;
2642 break;
2643 case AFATFS_OPERATION_IN_PROGRESS:
2646 } while (status == AFATFS_OPERATION_SUCCESS);
2647 break;
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;
2660 #ifdef USE_RTC_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);
2665 if (dt.year != 0) {
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);
2671 #endif
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);
2680 #endif
2682 opState->phase = AFATFS_CREATEFILE_PHASE_SUCCESS;
2683 goto doMore;
2684 } else if (status == AFATFS_OPERATION_FAILURE) {
2685 opState->phase = AFATFS_CREATEFILE_PHASE_FAILURE;
2686 goto doMore;
2688 break;
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,
2699 &directorySector,
2700 AFATFS_CACHE_READ | AFATFS_CACHE_RETAIN,
2704 if (status != AFATFS_OPERATION_SUCCESS) {
2705 // Retry next time
2706 break;
2710 afatfs_fseek(file, 0, AFATFS_SEEK_SET);
2712 // Is file empty?
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.
2718 break;
2719 } else {
2720 // Lock the freefile for our exclusive access
2721 afatfs.freeFile.operation.operation = AFATFS_FILE_OPERATION_LOCKED;
2724 #endif
2725 } else {
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);
2734 break;
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);
2742 break;
2746 file->operation.operation = AFATFS_FILE_OPERATION_NONE;
2747 opState->callback(file);
2748 break;
2749 case AFATFS_CREATEFILE_PHASE_FAILURE:
2750 file->type = AFATFS_FILE_TYPE_NONE;
2751 opState->callback(NULL);
2752 break;
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) {
2791 return true;
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))
2801 return false;
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;
2811 return true;
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;
2842 } else {
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;
2848 } else {
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;
2858 } else {
2859 opState->phase = AFATFS_CREATEFILE_PHASE_INITIAL;
2862 afatfs_createFileContinue(file);
2864 return 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) {
2882 return;
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);
2890 if (descriptor) {
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;
2904 #endif
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
2921 * new file).
2923 bool afatfs_fclose(afatfsFilePtr_t file, afatfsCallback_t callback)
2925 if (!file || file->type == AFATFS_FILE_TYPE_NONE) {
2926 return true;
2927 } else if (afatfs_fileIsBusy(file)) {
2928 return false;
2929 } else {
2930 afatfs_fileUpdateFilesize(file);
2932 file->operation.operation = AFATFS_FILE_OPERATION_CLOSE;
2933 file->operation.state.closeFile.callback = callback;
2934 afatfs_fcloseContinue(file);
2935 return true;
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();
2950 if (file) {
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) {
2953 callback(NULL);
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)) {
2969 return false;
2972 if (directory) {
2973 if (afatfs_fileIsBusy(directory)) {
2974 return false;
2977 memcpy(&afatfs.currentDirectory, directory, sizeof(*directory));
2978 return true;
2979 } else {
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;
2986 else
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);
2997 return true;
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
3021 * operation.
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;
3032 switch (mode[0]) {
3033 case 'r':
3034 fileMode = AFATFS_FILE_MODE_READ;
3035 break;
3036 case 'w':
3037 fileMode = AFATFS_FILE_MODE_WRITE | AFATFS_FILE_MODE_CREATE;
3038 break;
3039 case 'a':
3040 fileMode = AFATFS_FILE_MODE_APPEND | AFATFS_FILE_MODE_CREATE;
3041 break;
3044 switch (mode[1]) {
3045 case '+':
3046 fileMode |= AFATFS_FILE_MODE_READ;
3048 if (fileMode == AFATFS_FILE_MODE_READ) {
3049 fileMode |= AFATFS_FILE_MODE_WRITE;
3051 break;
3052 case 's':
3053 #ifdef AFATFS_USE_FREEFILE
3054 fileMode |= AFATFS_FILE_MODE_CONTIGUOUS | AFATFS_FILE_MODE_RETAIN_DIRECTORY;
3055 #endif
3056 break;
3059 file = afatfs_allocateFileHandle();
3061 if (file) {
3062 afatfs_createFile(file, filename, FAT_FILE_ATTRIBUTE_ARCHIVE, fileMode, complete);
3063 } else if (complete) {
3064 complete(NULL);
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++;
3086 } else {
3087 // Slow path
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) {
3107 return 0;
3110 if (afatfs_fileIsBusy(file)) {
3111 // There might be a seek pending
3112 return 0;
3115 uint32_t cursorOffsetInSector = file->cursorOffset % AFATFS_SECTOR_SIZE;
3116 uint32_t writtenBytes = 0;
3118 while (len > 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
3125 break;
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) {
3142 break;
3145 #ifdef AFATFS_USE_FREEFILE
3146 if ((file->mode & AFATFS_FILE_MODE_CONTIGUOUS) != 0) {
3147 afatfs_assert(file->cursorCluster < afatfs.freeFile.firstCluster);
3149 #endif
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) {
3174 return 0;
3177 if (afatfs_fileIsBusy(file)) {
3178 // There might be a seek pending
3179 return 0;
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)
3188 return 0;
3190 len = MIN(file->logicalSize - file->cursorOffset, len);
3192 uint32_t readBytes = 0;
3193 uint32_t cursorOffsetInSector = file->cursorOffset % AFATFS_SECTOR_SIZE;
3195 while (len > 0) {
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
3202 return readBytes;
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) {
3217 break;
3220 len -= bytesToReadThisSector;
3221 buffer += bytesToReadThisSector;
3222 cursorOffsetInSector = 0;
3225 return readBytes;
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)
3243 return;
3245 switch (file->operation.operation) {
3246 case AFATFS_FILE_OPERATION_CREATE_FILE:
3247 afatfs_createFileContinue(file);
3248 break;
3249 case AFATFS_FILE_OPERATION_SEEK:
3250 afatfs_fseekInternalContinue(file);
3251 break;
3252 case AFATFS_FILE_OPERATION_CLOSE:
3253 afatfs_fcloseContinue(file);
3254 break;
3255 case AFATFS_FILE_OPERATION_UNLINK:
3256 afatfs_funlinkContinue(file);
3257 break;
3258 case AFATFS_FILE_OPERATION_TRUNCATE:
3259 afatfs_ftruncateContinue(file, false);
3260 break;
3261 #ifdef AFATFS_USE_FREEFILE
3262 case AFATFS_FILE_OPERATION_APPEND_SUPERCLUSTER:
3263 afatfs_appendSuperclusterContinue(file);
3264 break;
3265 case AFATFS_FILE_OPERATION_LOCKED:
3267 break;
3268 #endif
3269 case AFATFS_FILE_OPERATION_APPEND_FREE_CLUSTER:
3270 afatfs_appendRegularFreeClusterContinue(file);
3271 break;
3272 case AFATFS_FILE_OPERATION_EXTEND_SUBDIRECTORY:
3273 afatfs_extendSubdirectoryContinue(file);
3274 break;
3275 case AFATFS_FILE_OPERATION_NONE:
3277 break;
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);
3290 #endif
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.
3324 * Returns:
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;
3336 while (1) {
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;
3344 break;
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;
3357 break;
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;
3380 } else {
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;
3385 break;
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;
3394 break;
3399 static void afatfs_freeFileCreated(afatfsFile_t *file)
3401 if (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;
3406 } else {
3407 // Allocate clusters for the freefile
3408 afatfs_findLargestContiguousFreeBlockBegin();
3409 afatfs.initPhase = AFATFS_INITIALIZATION_FREEFILE_FAT_SEARCH;
3411 } else {
3412 // Failed to allocate an entry
3413 afatfs.lastError = AFATFS_ERROR_GENERIC;
3414 afatfs.filesystemState = AFATFS_FILESYSTEM_STATE_FATAL;
3418 #endif
3420 #ifdef AFATFS_USE_INTROSPECTIVE_LOGGING
3422 static void afatfs_introspecLogCreated(afatfsFile_t *file)
3424 if (file) {
3425 afatfs.initPhase++;
3426 } else {
3427 afatfs.lastError = AFATFS_ERROR_GENERIC;
3428 afatfs.filesystemState = AFATFS_FILESYSTEM_STATE_FATAL;
3432 #endif
3434 static void afatfs_initContinue(void)
3436 #ifdef AFATFS_USE_FREEFILE
3437 afatfsOperationStatus_e status;
3438 #endif
3440 uint8_t *sector;
3442 doMore:
3444 switch (afatfs.initPhase) {
3445 case AFATFS_INITIALIZATION_READ_MBR:
3446 if (afatfs_cacheSector(0, &sector, AFATFS_CACHE_READ | AFATFS_CACHE_DISCARDABLE, 0) == AFATFS_OPERATION_SUCCESS) {
3447 if (afatfs_parseMBR(sector)) {
3448 afatfs.initPhase = AFATFS_INITIALIZATION_READ_VOLUME_ID;
3449 goto doMore;
3450 } else {
3451 afatfs.lastError = AFATFS_ERROR_BAD_MBR;
3452 afatfs.filesystemState = AFATFS_FILESYSTEM_STATE_FATAL;
3455 break;
3456 case AFATFS_INITIALIZATION_READ_VOLUME_ID:
3457 if (afatfs_cacheSector(afatfs.partitionStartSector, &sector, AFATFS_CACHE_READ | AFATFS_CACHE_DISCARDABLE, 0) == AFATFS_OPERATION_SUCCESS) {
3458 if (afatfs_parseVolumeID(sector)) {
3459 // Open the root directory
3460 afatfs_chdir(NULL);
3462 afatfs.initPhase++;
3463 } else {
3464 afatfs.lastError = AFATFS_ERROR_BAD_FILESYSTEM_HEADER;
3465 afatfs.filesystemState = AFATFS_FILESYSTEM_STATE_FATAL;
3468 break;
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);
3476 break;
3477 case AFATFS_INITIALIZATION_FREEFILE_CREATING:
3478 afatfs_fileOperationContinue(&afatfs.freeFile);
3479 break;
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)
3514 goto doMore;
3516 break;
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;
3523 goto doMore;
3524 } else if (status == AFATFS_OPERATION_FAILURE) {
3525 afatfs.lastError = AFATFS_ERROR_GENERIC;
3526 afatfs.filesystemState = AFATFS_FILESYSTEM_STATE_FATAL;
3528 break;
3529 case AFATFS_INITIALIZATION_FREEFILE_SAVE_DIR_ENTRY:
3530 status = afatfs_saveDirectoryEntry(&afatfs.freeFile, AFATFS_SAVE_DIRECTORY_NORMAL);
3532 if (status == AFATFS_OPERATION_SUCCESS) {
3533 afatfs.initPhase++;
3534 goto doMore;
3535 } else if (status == AFATFS_OPERATION_FAILURE) {
3536 afatfs.lastError = AFATFS_ERROR_GENERIC;
3537 afatfs.filesystemState = AFATFS_FILESYSTEM_STATE_FATAL;
3539 break;
3540 #endif
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);
3548 break;
3549 case AFATFS_INITIALIZATION_INTROSPEC_LOG_CREATING:
3550 afatfs_fileOperationContinue(&afatfs.introSpecLog);
3551 break;
3552 #endif
3554 case AFATFS_INITIALIZATION_DONE:
3555 afatfs.filesystemState = AFATFS_FILESYSTEM_STATE_READY;
3556 break;
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()) {
3568 afatfs_flush();
3570 switch (afatfs.filesystemState) {
3571 case AFATFS_FILESYSTEM_STATE_INITIALIZATION:
3572 afatfs_initContinue();
3573 break;
3574 case AFATFS_FILESYSTEM_STATE_READY:
3575 afatfs_fileOperationsPoll();
3576 break;
3577 default:
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) {
3589 return;
3592 enum {
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:
3601 buffer[1] = 0;
3602 buffer[2] = 0;
3603 buffer[3] = 0;
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:
3616 buffer[12] = 0;
3617 buffer[13] = 0;
3618 buffer[14] = 0;
3619 buffer[15] = 0;
3621 // Ignore write failures
3622 afatfs_fwrite(&afatfs.introSpecLog, buffer, LOG_ENTRY_SIZE);
3625 #endif
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)
3639 #ifdef STM32H7
3640 afatfs.cache = afatfs_cache;
3641 #endif
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);
3648 #endif
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
3666 openFileCount++;
3670 #ifdef AFATFS_USE_INTROSPECTIVE_LOGGING
3671 if (afatfs.introSpecLog.type != AFATFS_FILE_TYPE_NONE) {
3672 afatfs_fclose(&afatfs.introSpecLog, NULL);
3673 openFileCount++;
3675 #endif
3677 #ifdef AFATFS_USE_FREEFILE
3678 if (afatfs.freeFile.type != AFATFS_FILE_TYPE_NONE) {
3679 afatfs_fclose(&afatfs.freeFile, NULL);
3680 openFileCount++;
3682 #endif
3684 if (afatfs.currentDirectory.type != AFATFS_FILE_TYPE_NONE) {
3685 afatfs_fclose(&afatfs.currentDirectory, NULL);
3686 openFileCount++;
3689 afatfs_poll();
3691 if (!afatfs_flush()) {
3692 return false;
3695 if (afatfs.cacheFlushInProgress) {
3696 return false;
3699 if (openFileCount > 0) {
3700 return false;
3703 #ifdef AFATFS_DEBUG
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);
3710 #endif
3713 // Clear the afatfs so it's as if we never ran
3714 memset(&afatfs, 0, sizeof(afatfs));
3716 return true;
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;
3730 return result;