File syn_lfs.c¶
File List > src > syntropic > storage > syn_lfs.c
Go to the documentation of this file
#if __has_include("syn_config.h")
#include "syn_config.h"
#endif
#if !defined(SYN_USE_LFS) || SYN_USE_LFS
#if defined(SYN_USE_VFS) && !SYN_USE_VFS
#error "syn_lfs requires SYN_USE_VFS=1"
#endif
#include "../port/syn_port_flash.h"
#include "../util/syn_assert.h"
#include "syn_lfs.h"
#if __has_include("lfs.h")
#include "lfs.h"
/* Context structs that pair lfs_t* with each file/dir — avoids relying on
* internal lfs_file_t/lfs_dir_t back-pointers which are version-dependent. */
typedef struct {
lfs_t *lfs;
lfs_file_t file;
bool used;
} SYN_LfsFileCtx;
typedef struct {
lfs_t *lfs;
lfs_dir_t dir;
bool used;
} SYN_LfsDirCtx;
static SYN_LfsFileCtx g_lfs_files[SYN_VFS_MAX_OPEN_FILES];
static SYN_LfsDirCtx g_lfs_dirs[SYN_VFS_MAX_OPEN_DIRS];
/* ── LittleFS block device callbacks ────────────────────────────────────── */
static int syn_lfs_bd_read(const struct lfs_config *c, lfs_block_t block, lfs_off_t off,
void *buffer, lfs_size_t size)
{
const SYN_LfsConfig *cfg = (const SYN_LfsConfig *)c->context;
uint32_t addr = cfg->start_addr + (block * cfg->block_size) + off;
if (syn_port_flash_read(addr, buffer, size) != SYN_OK) {
return LFS_ERR_IO;
}
return LFS_ERR_OK;
}
static int syn_lfs_bd_prog(const struct lfs_config *c, lfs_block_t block, lfs_off_t off,
const void *buffer, lfs_size_t size)
{
const SYN_LfsConfig *cfg = (const SYN_LfsConfig *)c->context;
uint32_t addr = cfg->start_addr + (block * cfg->block_size) + off;
if (syn_port_flash_write(addr, buffer, size) != SYN_OK) {
return LFS_ERR_IO;
}
return LFS_ERR_OK;
}
static int syn_lfs_bd_erase(const struct lfs_config *c, lfs_block_t block)
{
const SYN_LfsConfig *cfg = (const SYN_LfsConfig *)c->context;
uint32_t addr = cfg->start_addr + (block * cfg->block_size);
if (syn_port_flash_erase(addr) != SYN_OK) {
return LFS_ERR_IO;
}
return LFS_ERR_OK;
}
static int syn_lfs_bd_sync(const struct lfs_config *c)
{
(void)c;
return LFS_ERR_OK;
}
void syn_lfs_init_config(struct lfs_config *cfg, const SYN_LfsConfig *syn_cfg)
{
SYN_ASSERT(cfg != NULL);
SYN_ASSERT(syn_cfg != NULL);
if (cfg == NULL || syn_cfg == NULL || syn_cfg->block_size == 0) {
return;
}
cfg->context = (void *)syn_cfg;
cfg->read = syn_lfs_bd_read;
cfg->prog = syn_lfs_bd_prog;
cfg->erase = syn_lfs_bd_erase;
cfg->sync = syn_lfs_bd_sync;
/* These values depend on the flash hardware limitations */
cfg->read_size = 16;
cfg->prog_size = 16;
cfg->block_size = syn_cfg->block_size;
cfg->block_count = syn_cfg->size / syn_cfg->block_size;
cfg->cache_size = 64;
cfg->lookahead_size = 16;
cfg->block_cycles = 500;
}
/* ── VFS Mapping ────────────────────────────────────────────────────────── */
static int syn_lfs_vfs_open(SYN_VfsFile *file, const char *path, int flags, void *fs_data)
{
lfs_t *lfs = (lfs_t *)fs_data;
SYN_ASSERT(lfs != NULL);
/* Allocate a context slot from the static pool */
int f_idx = -1;
for (int i = 0; i < SYN_VFS_MAX_OPEN_FILES; i++) {
if (!g_lfs_files[i].used) {
f_idx = i;
break;
}
}
if (f_idx < 0) {
return -1;
}
g_lfs_files[f_idx].used = true;
g_lfs_files[f_idx].lfs = lfs;
file->fs_file = &g_lfs_files[f_idx];
/* Map flags */
int lfs_flags = 0;
int mode = flags & 0x03;
if (mode == SYN_O_RDONLY)
lfs_flags |= LFS_O_RDONLY;
else if (mode == SYN_O_WRONLY)
lfs_flags |= LFS_O_WRONLY;
else if (mode == SYN_O_RDWR)
lfs_flags |= LFS_O_RDWR;
if (flags & SYN_O_CREAT)
lfs_flags |= LFS_O_CREAT;
if (flags & SYN_O_APPEND)
lfs_flags |= LFS_O_APPEND;
if (flags & SYN_O_TRUNC)
lfs_flags |= LFS_O_TRUNC;
int ret = lfs_file_open(lfs, &g_lfs_files[f_idx].file, path, lfs_flags);
if (ret < 0) {
g_lfs_files[f_idx].used = false;
file->fs_file = NULL;
return ret;
}
return 0;
}
static int syn_lfs_vfs_close(SYN_VfsFile *file)
{
SYN_LfsFileCtx *ctx = (SYN_LfsFileCtx *)file->fs_file;
if (!ctx)
return -1;
int ret = lfs_file_close(ctx->lfs, &ctx->file);
ctx->used = false;
file->fs_file = NULL;
return ret;
}
static int syn_lfs_vfs_read(SYN_VfsFile *file, void *buf, size_t len)
{
SYN_LfsFileCtx *ctx = (SYN_LfsFileCtx *)file->fs_file;
if (!ctx)
return -1;
return (int)lfs_file_read(ctx->lfs, &ctx->file, buf, len);
}
static int syn_lfs_vfs_write(SYN_VfsFile *file, const void *buf, size_t len)
{
SYN_LfsFileCtx *ctx = (SYN_LfsFileCtx *)file->fs_file;
if (!ctx)
return -1;
return (int)lfs_file_write(ctx->lfs, &ctx->file, buf, len);
}
static int32_t syn_lfs_vfs_seek(SYN_VfsFile *file, int32_t offset, int whence)
{
SYN_LfsFileCtx *ctx = (SYN_LfsFileCtx *)file->fs_file;
if (!ctx)
return -1;
int lfs_whence = LFS_SEEK_SET;
if (whence == SYN_SEEK_CUR)
lfs_whence = LFS_SEEK_CUR;
else if (whence == SYN_SEEK_END)
lfs_whence = LFS_SEEK_END;
return (int32_t)lfs_file_seek(ctx->lfs, &ctx->file, offset, lfs_whence);
}
static int32_t syn_lfs_vfs_tell(SYN_VfsFile *file)
{
SYN_LfsFileCtx *ctx = (SYN_LfsFileCtx *)file->fs_file;
if (!ctx)
return -1;
return (int32_t)lfs_file_tell(ctx->lfs, &ctx->file);
}
static int syn_lfs_vfs_unlink(const char *path, void *fs_data)
{
lfs_t *lfs = (lfs_t *)fs_data;
return lfs_remove(lfs, path);
}
static int syn_lfs_vfs_mkdir(const char *path, void *fs_data)
{
lfs_t *lfs = (lfs_t *)fs_data;
return lfs_mkdir(lfs, path);
}
static int syn_lfs_vfs_opendir(SYN_VfsDir *dir, const char *path, void *fs_data)
{
lfs_t *lfs = (lfs_t *)fs_data;
SYN_ASSERT(lfs != NULL);
int d_idx = -1;
for (int i = 0; i < SYN_VFS_MAX_OPEN_DIRS; i++) {
if (!g_lfs_dirs[i].used) {
d_idx = i;
break;
}
}
if (d_idx < 0) {
return -1;
}
g_lfs_dirs[d_idx].used = true;
g_lfs_dirs[d_idx].lfs = lfs;
dir->fs_dir = &g_lfs_dirs[d_idx];
int ret = lfs_dir_open(lfs, &g_lfs_dirs[d_idx].dir, path);
if (ret < 0) {
g_lfs_dirs[d_idx].used = false;
dir->fs_dir = NULL;
return ret;
}
return 0;
}
static int syn_lfs_vfs_readdir(SYN_VfsDir *dir, SYN_VfsDirEnt *ent)
{
SYN_LfsDirCtx *ctx = (SYN_LfsDirCtx *)dir->fs_dir;
if (!ctx)
return -1;
struct lfs_info info;
int ret = lfs_dir_read(ctx->lfs, &ctx->dir, &info);
if (ret <= 0) {
return ret; /* 0 for EOF, negative for error */
}
strncpy(ent->name, info.name, sizeof(ent->name) - 1);
ent->name[sizeof(ent->name) - 1] = '\0';
ent->size = info.size;
ent->is_dir = (info.type == LFS_TYPE_DIR);
return 1;
}
static int syn_lfs_vfs_closedir(SYN_VfsDir *dir)
{
SYN_LfsDirCtx *ctx = (SYN_LfsDirCtx *)dir->fs_dir;
if (!ctx)
return -1;
int ret = lfs_dir_close(ctx->lfs, &ctx->dir);
ctx->used = false;
dir->fs_dir = NULL;
return ret;
}
static const SYN_VfsOps g_lfs_vfs_ops = {.open = syn_lfs_vfs_open,
.close = syn_lfs_vfs_close,
.read = syn_lfs_vfs_read,
.write = syn_lfs_vfs_write,
.seek = syn_lfs_vfs_seek,
.tell = syn_lfs_vfs_tell,
.unlink = syn_lfs_vfs_unlink,
.mkdir = syn_lfs_vfs_mkdir,
.opendir = syn_lfs_vfs_opendir,
.readdir = syn_lfs_vfs_readdir,
.closedir = syn_lfs_vfs_closedir};
const SYN_VfsOps *syn_lfs_get_ops(void)
{
return &g_lfs_vfs_ops;
}
#else
/* Mock stubs if littlefs header is not present */
const SYN_VfsOps *syn_lfs_get_ops(void)
{
return NULL;
}
#endif
#endif /* SYN_USE_LFS */