Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,13 @@ static int fat_disk_write_sd(const uint8_t* buff, int64_t sector, int count)
_Static_assert(FF_MIN_SS == 512, "this function assumes sector size == 512");
_Static_assert(FF_MAX_SS == 512, "this function assumes sector size == 512");
assertf((uint32_t)sector == sector, "unsupported access to SD card > 2 TiB");
return cart_card_wr_dram(buff, sector, count) ? RES_ERROR : RES_OK;

if (PhysicalAddr(buff) < 0x00800000)
return cart_card_wr_dram(buff, sector, count) ? RES_ERROR : RES_OK;
if (io_accessible(PhysicalAddr(buff)))
return cart_card_wr_cart(PhysicalAddr(buff), sector, count) ? RES_ERROR : RES_OK;

return RES_PARERR;
}

static fat_disk_t fat_disk_sd =
Expand Down
148 changes: 120 additions & 28 deletions src/fat.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* @brief FAT filesystem interface and newlib wrappers.
*/
#include <string.h>
#include <stdbool.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/errno.h>
Expand All @@ -22,6 +23,76 @@ static fat_disk_t fat_disks[FF_VOLUMES] = {0};
static FATFS *fat_volumes[FF_VOLUMES] = {0};
static filesystem_t *fat_filesystems[FF_VOLUMES] = {0};

/*********************************************************************
* Single-sector LRU cache (FAT/dir metadata)
*
* FatFs (TINY) uses disk_read(..., 1) for metadata via move_window, and
* multi-sector I/O for aligned file data. Caching only count==1 accesses
* keeps hot FAT/dir sectors across bulk file reads/writes.
*********************************************************************/

_Static_assert(FF_MIN_SS == 512 && FF_MAX_SS == 512, "sector cache assumes 512-byte sectors");

#define FAT_SECTOR_CACHE_SIZE 8

typedef struct {
bool valid;
LBA_t sector;
uint32_t tick;
uint8_t data[512];
} fat_sector_cache_slot_t;

typedef struct {
uint32_t tick;
fat_sector_cache_slot_t slots[FAT_SECTOR_CACHE_SIZE];
} fat_sector_cache_t;

static fat_sector_cache_t *fat_sector_caches[FF_VOLUMES] = {0};

static fat_sector_cache_slot_t *__fat_cache_lookup(fat_sector_cache_t *cache, LBA_t sector)
{
for (int i = 0; i < FAT_SECTOR_CACHE_SIZE; i++) {
fat_sector_cache_slot_t *slot = &cache->slots[i];
if (slot->valid && slot->sector == sector)
return slot;
}
return NULL;
}

static fat_sector_cache_slot_t *__fat_cache_evict(fat_sector_cache_t *cache)
{
fat_sector_cache_slot_t *victim = NULL;
for (int i = 0; i < FAT_SECTOR_CACHE_SIZE; i++) {
fat_sector_cache_slot_t *slot = &cache->slots[i];
if (!slot->valid)
return slot;
if (!victim || slot->tick < victim->tick)
victim = slot;
}
return victim;
}

static void __fat_cache_store(fat_sector_cache_t *cache, LBA_t sector, const uint8_t *data)
{
fat_sector_cache_slot_t *slot = __fat_cache_lookup(cache, sector);
if (!slot)
slot = __fat_cache_evict(cache);
slot->valid = true;
slot->sector = sector;
slot->tick = ++cache->tick;
memcpy(slot->data, data, 512);
}

static void __fat_cache_invalidate_range(fat_sector_cache_t *cache, LBA_t sector, UINT count)
{
LBA_t end = sector + count;
for (int i = 0; i < FAT_SECTOR_CACHE_SIZE; i++) {
fat_sector_cache_slot_t *slot = &cache->slots[i];
if (slot->valid && slot->sector >= sector && slot->sector < end)
slot->valid = false;
}
}

/** @brief FatFS disk API: implementation by forwarding to a disk-specific function. */
DSTATUS disk_initialize(BYTE pdrv)
{
Expand All @@ -41,17 +112,45 @@ DSTATUS disk_status(BYTE pdrv)
/** @brief FatFS disk API: implementation by forwarding to a disk-specific function. */
DRESULT disk_read(BYTE pdrv, BYTE* buff, LBA_t sector, UINT count)
{
if (fat_disks[pdrv].disk_read)
return fat_disks[pdrv].disk_read(buff, sector, count);
return RES_PARERR;
if (!fat_disks[pdrv].disk_read)
return RES_PARERR;

fat_sector_cache_t *cache = fat_sector_caches[pdrv];
if (PhysicalAddr(buff) < 0x00800000 && cache && count == 1) {
fat_sector_cache_slot_t *slot = __fat_cache_lookup(cache, sector);
if (slot) {
slot->tick = ++cache->tick;
memcpy(buff, slot->data, 512);
return RES_OK;
}
DRESULT res = fat_disks[pdrv].disk_read(buff, sector, count);
if (res == RES_OK)
__fat_cache_store(cache, sector, buff);
return res;
}

return fat_disks[pdrv].disk_read(buff, sector, count);
}

/** @brief FatFS disk API: implementation by forwarding to a disk-specific function. */
DRESULT disk_write(BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count)
{
if (fat_disks[pdrv].disk_write)
return fat_disks[pdrv].disk_write(buff, sector, count);
return RES_PARERR;
if (!fat_disks[pdrv].disk_write)
return RES_PARERR;

DRESULT res = fat_disks[pdrv].disk_write(buff, sector, count);
if (res != RES_OK)
return res;

fat_sector_cache_t *cache = fat_sector_caches[pdrv];
if (!cache)
return res;

if (count == 1)
__fat_cache_store(cache, sector, buff);
else
__fat_cache_invalidate_range(cache, sector, count);
return res;
}

/** @brief FatFS disk API: implementation by forwarding to a disk-specific function. */
Expand Down Expand Up @@ -102,10 +201,6 @@ DWORD get_fattime(void)
fat_name; \
})

/** @brief Number of static FAT file objects. */
#define NUM_STATIC_FAT_FILES 4
static FIL static_fat_files[NUM_STATIC_FAT_FILES] = {0};

static void __fresult_set_errno(FRESULT err)
{
assertf(err != FR_INT_ERR, "FatFS assertion error");
Expand Down Expand Up @@ -136,17 +231,10 @@ static void __fresult_set_errno(FRESULT err)
static void *__fat_open(char *name, int flags, int volid)
{
FIL *fp = NULL;
for (int i=0; i<NUM_STATIC_FAT_FILES; i++)
if (static_fat_files[i].obj.fs == NULL) {
fp = &static_fat_files[i];
break;
}
fp = malloc(sizeof(FIL));
if (!fp) {
fp = malloc(sizeof(FIL));
if (!fp) {
errno = ENOMEM;
return NULL;
}
errno = ENOMEM;
return NULL;
}

int fatfs_flags = 0;
Expand All @@ -172,10 +260,7 @@ static void *__fat_open(char *name, int flags, int volid)
if (res != FR_OK)
{
__fresult_set_errno(res);
if (fp >= static_fat_files && fp < static_fat_files+NUM_STATIC_FAT_FILES)
fp->obj.fs = NULL;
else
free(fp);
free(fp);
return NULL;
}
return fp;
Expand Down Expand Up @@ -252,10 +337,7 @@ static int __fat_close(void *file)
FIL *fp = file;
FRESULT res = f_close(fp);

if (fp >= static_fat_files && fp < static_fat_files+NUM_STATIC_FAT_FILES)
fp->obj.fs = NULL;
else
free(fp);
free(fp);

if (res != FR_OK) {
__fresult_set_errno(res);
Expand Down Expand Up @@ -468,6 +550,10 @@ int fat_mount(const char *prefix, const fat_disk_t* disk, int flags)
fat_disks[vol_id] = *disk;
fat_filesystems[vol_id] = NULL;

fat_sector_cache_t *cache = calloc(1, sizeof(fat_sector_cache_t));
assertf(cache, "Out of memory");
fat_sector_caches[vol_id] = cache;

FATFS *fatfs = malloc(sizeof(FATFS));
assertf(fatfs, "Out of memory");
char path[3] = {'0' + vol_id, ':', 0};
Expand All @@ -476,6 +562,8 @@ int fat_mount(const char *prefix, const fat_disk_t* disk, int flags)
if (err != FR_OK) {
__fresult_set_errno(err);
free(fatfs);
free(cache);
fat_sector_caches[vol_id] = NULL;
fat_disks[vol_id] = (fat_disk_t){0};
return -1;
}
Expand All @@ -498,6 +586,8 @@ int fat_mount(const char *prefix, const fat_disk_t* disk, int flags)
free(fatfs);
fat_volumes[vol_id] = NULL;
free(newlib_fs);
free(cache);
fat_sector_caches[vol_id] = NULL;
fat_disks[vol_id] = (fat_disk_t){0};
return -1;
}
Expand Down Expand Up @@ -530,6 +620,8 @@ int fat_unmount(int vol_id)
fat_filesystems[vol_id] = NULL;
free(fat_volumes[vol_id]);
fat_volumes[vol_id] = NULL;
free(fat_sector_caches[vol_id]);
fat_sector_caches[vol_id] = NULL;
fat_disks[vol_id] = (fat_disk_t){0};

if (detach_errno) {
Expand Down
59 changes: 59 additions & 0 deletions src/fatfs/ff.c
Original file line number Diff line number Diff line change
Expand Up @@ -4011,6 +4011,9 @@ FRESULT f_read (
FSIZE_t remain;
UINT rcnt, cc, csect;
BYTE *rbuff = (BYTE*)buff;
#if FF_FAST_CONTIGUOUS_READ
DWORD contig, nextclst, prevclst;
#endif


*br = 0; /* Clear read byte counter */
Expand Down Expand Up @@ -4047,9 +4050,35 @@ FRESULT f_read (
sect += csect;
cc = btr / SS(fs); /* When remaining bytes >= sector size, */
if (cc > 0) { /* Read maximum contiguous sectors directly */
#if FF_FAST_CONTIGUOUS_READ
/* Extend past the current cluster while the FAT chain stays
* physically contiguous, so one disk_read covers many clusters. */
contig = fs->csize;
nextclst = fp->clust;
while ((DWORD)csect + cc > contig) {
prevclst = nextclst;
#if FF_USE_FASTSEEK
if (fp->cltbl) {
nextclst = clmt_clust(fp, fp->fptr + (FSIZE_t)contig * SS(fs));
} else
#endif
{
nextclst = get_fat(&fp->obj, prevclst);
}
if (nextclst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
if (nextclst < 2) ABORT(fs, FR_INT_ERR);
if (nextclst != prevclst + 1) break; /* Fragmented: stop coalescing */
contig += fs->csize;
fp->clust = nextclst;
}
if ((DWORD)csect + cc > contig) { /* Clip at contiguous run boundary */
cc = (UINT)(contig - csect);
}
#else
if (csect + cc > fs->csize) { /* Clip at cluster boundary */
cc = fs->csize - csect;
}
#endif
if (disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR);
#if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */
#if FF_FS_TINY
Expand Down Expand Up @@ -4112,6 +4141,9 @@ FRESULT f_write (
LBA_t sect;
UINT wcnt, cc, csect;
const BYTE *wbuff = (const BYTE*)buff;
#if FF_FAST_CONTIGUOUS_WRITE
DWORD contig, nextclst, prevclst;
#endif


*bw = 0; /* Clear write byte counter */
Expand Down Expand Up @@ -4162,9 +4194,36 @@ FRESULT f_write (
sect += csect;
cc = btw / SS(fs); /* When remaining bytes >= sector size, */
if (cc > 0) { /* Write maximum contiguous sectors directly */
#if FF_FAST_CONTIGUOUS_WRITE
/* Follow/allocate while clusters stay contiguous so one
* disk_write can span multiple clusters. */
contig = fs->csize;
nextclst = fp->clust;
while ((DWORD)csect + cc > contig) {
prevclst = nextclst;
#if FF_USE_FASTSEEK
if (fp->cltbl) {
nextclst = clmt_clust(fp, fp->fptr + (FSIZE_t)contig * SS(fs));
} else
#endif
{
nextclst = create_chain(&fp->obj, prevclst);
}
if (nextclst == 0) break; /* Disk full: write what is contiguous so far */
if (nextclst == 1) ABORT(fs, FR_INT_ERR);
if (nextclst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
if (nextclst != prevclst + 1) break; /* Fragmented: stop coalescing */
contig += fs->csize;
fp->clust = nextclst;
}
if ((DWORD)csect + cc > contig) { /* Clip at contiguous run boundary */
cc = (UINT)(contig - csect);
}
#else
if (csect + cc > fs->csize) { /* Clip at cluster boundary */
cc = fs->csize - csect;
}
#endif
if (disk_write(fs->pdrv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR);
#if FF_FS_MINIMIZE <= 2
#if FF_FS_TINY
Expand Down
12 changes: 12 additions & 0 deletions src/fatfs/ffconf.h
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,18 @@
*/


#define FF_FAST_CONTIGUOUS_READ 1
#define FF_FAST_CONTIGUOUS_WRITE 1
/* FF_FAST_CONTIGUOUS_* coalesce multi-cluster I/O into one disk_read/disk_write
/ when the FAT chain is contiguous on disk. FatFs normally clips each transfer
/ at the cluster boundary; enabling this walks the chain and issues a larger
/ transfer when consecutive clusters are adjacent. Useful when per-transfer
/ setup cost dominates (e.g. SD card).
/
/ 0: Clip at each cluster (stock FatFs behavior).
/ 1: Merge contiguous clusters into one transfer.
*/


/*--- End of configuration options ---*/

Expand Down