subsys/fs: add support for littlefs

littlefs is a fail-safe filesystem from ARM Mbed that has wear-leveling
capabilities.

Signed-off-by: Peter A. Bigot <pab@pabigot.com>
Signed-off-by: Jim Paris <jim@bolt.io>
This commit is contained in:
Peter A. Bigot 2019-07-21 11:28:29 -05:00 committed by Carles Cufí
commit fb73fcd4ba
8 changed files with 781 additions and 6 deletions

View file

@ -337,6 +337,7 @@
/subsys/fs/ @nashif /subsys/fs/ @nashif
/subsys/fs/fcb/ @nvlsianpu /subsys/fs/fcb/ @nvlsianpu
/subsys/fs/fuse_fs_access.c @vanwinkeljan /subsys/fs/fuse_fs_access.c @vanwinkeljan
/subsys/fs/littlefs_fs.c @pabigot
/subsys/fs/nvs/ @Laczen /subsys/fs/nvs/ @Laczen
/subsys/logging/ @nordic-krch /subsys/logging/ @nordic-krch
/subsys/mgmt/ @carlescufi @nvlsianpu /subsys/mgmt/ @carlescufi @nvlsianpu

View file

@ -48,6 +48,7 @@ enum fs_dir_entry_type {
enum fs_type { enum fs_type {
FS_FATFS = 0, FS_FATFS = 0,
FS_NFFS, FS_NFFS,
FS_LITTLEFS,
FS_TYPE_END, FS_TYPE_END,
}; };

View file

@ -11,7 +11,7 @@
extern "C" { extern "C" {
#endif #endif
#ifdef CONFIG_FILE_SYSTEM_NFFS #if defined(CONFIG_FILE_SYSTEM_LITTLEFS) || defined(CONFIG_FILE_SYSTEM_NFFS)
#define MAX_FILE_NAME 256 #define MAX_FILE_NAME 256
#else /* FAT_FS */ #else /* FAT_FS */
#define MAX_FILE_NAME 12 /* Uses 8.3 SFN */ #define MAX_FILE_NAME 12 /* Uses 8.3 SFN */

39
include/fs/littlefs.h Normal file
View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2019 Bolt Innovation Management, LLC
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_FS_LITTLEFS_H_
#define ZEPHYR_INCLUDE_FS_LITTLEFS_H_
#include <zephyr/types.h>
#include <kernel.h>
#include <storage/flash_map.h>
#include <lfs.h>
#ifdef __cplusplus
extern "C" {
#endif
/** @brief Filesystem info structure for LittleFS mount */
struct fs_littlefs {
/* These structures are filled automatically at mount. */
struct lfs lfs;
struct lfs_config cfg;
const struct flash_area *area;
struct k_mutex mutex;
/* Static buffers */
u8_t read_buffer[CONFIG_FS_LITTLEFS_CACHE_SIZE];
u8_t prog_buffer[CONFIG_FS_LITTLEFS_CACHE_SIZE];
/* Multiple of 8 bytes, but 4-byte aligned (littlefs #239) */
u32_t lookahead_buffer[CONFIG_FS_LITTLEFS_LOOKAHEAD_SIZE / sizeof(u32_t)];
};
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_FS_LITTLEFS_H_ */

View file

@ -7,12 +7,14 @@ if(CONFIG_FILE_SYSTEM)
zephyr_library_include_directories(${CMAKE_CURRENT_SOURCE_DIR}) zephyr_library_include_directories(${CMAKE_CURRENT_SOURCE_DIR})
zephyr_library_sources(fs.c fs_impl.c) zephyr_library_sources(fs.c fs_impl.c)
zephyr_library_sources_ifdef(CONFIG_FAT_FILESYSTEM_ELM fat_fs.c) zephyr_library_sources_ifdef(CONFIG_FAT_FILESYSTEM_ELM fat_fs.c)
zephyr_library_sources_ifdef(CONFIG_FILE_SYSTEM_LITTLEFS littlefs_fs.c)
zephyr_library_sources_ifdef(CONFIG_FILE_SYSTEM_NFFS nffs_fs.c) zephyr_library_sources_ifdef(CONFIG_FILE_SYSTEM_NFFS nffs_fs.c)
zephyr_library_sources_ifdef(CONFIG_FILE_SYSTEM_SHELL shell.c) zephyr_library_sources_ifdef(CONFIG_FILE_SYSTEM_SHELL shell.c)
zephyr_library_link_libraries(FS) zephyr_library_link_libraries(FS)
target_link_libraries_ifdef(CONFIG_FAT_FILESYSTEM_ELM FS INTERFACE ELMFAT) target_link_libraries_ifdef(CONFIG_FAT_FILESYSTEM_ELM FS INTERFACE ELMFAT)
target_link_libraries_ifdef(CONFIG_FILE_SYSTEM_LITTLEFS FS INTERFACE LITTLEFS)
target_link_libraries_ifdef(CONFIG_FILE_SYSTEM_NFFS FS INTERFACE NFFS) target_link_libraries_ifdef(CONFIG_FILE_SYSTEM_NFFS FS INTERFACE NFFS)
endif() endif()

View file

@ -39,6 +39,13 @@ config FILE_SYSTEM_NFFS
Note: NFFS requires 1-byte unaligned access to flash thus it Note: NFFS requires 1-byte unaligned access to flash thus it
will not work on devices that support only aligned flash access. will not work on devices that support only aligned flash access.
config FILE_SYSTEM_LITTLEFS
bool "LittleFS file system support"
depends on FLASH_MAP
depends on FLASH_PAGE_LAYOUT
help
Enables LittleFS file system support.
config FILE_SYSTEM_SHELL config FILE_SYSTEM_SHELL
bool "Enable file system shell" bool "Enable file system shell"
depends on SHELL depends on SHELL
@ -121,6 +128,63 @@ config NFFS_FILESYSTEM_MAX_BLOCK_SIZE
endmenu endmenu
menu "LittleFS Settings"
visible if FILE_SYSTEM_LITTLEFS
config FS_LITTLEFS_NUM_FILES
int "Maximum number of opened files"
default 4
help
This is a global maximum across all mounted littlefs filesystems.
config FS_LITTLEFS_NUM_DIRS
int "Maximum number of opened directories"
default 4
help
This is a global maximum across all mounted littlefs filesystems.
config FS_LITTLEFS_READ_SIZE
int "Minimum size of a block read"
default 16
help
All read operations will be a multiple of this value.
config FS_LITTLEFS_PROG_SIZE
int "Minimum size of a block program"
default 16
help
All program operations will be a multiple of this value.
config FS_LITTLEFS_CACHE_SIZE
int "Size of block caches in bytes"
default 64
help
Each cache buffers a portion of a block in RAM. The littlefs
needs a read cache, a program cache, and one additional cache
per file. Larger caches can improve performance by storing
more data and reducing the number of disk accesses. Must be a
multiple of the read and program sizes of the underlying flash
device, and a factor of the block size.
config FS_LITTLEFS_LOOKAHEAD_SIZE
int "Size of lookahead buffer in bytes"
default 32
help
A larger lookahead buffer increases the number of blocks found
during an allocation pass. The lookahead buffer is stored as a
compact bitmap, so each byte of RAM can track 8 blocks. Must
be a multiple of 8.
config FS_LITTLEFS_BLOCK_CYCLES
int "Number of erase cycles before moving data to another block"
default 512
help
For dynamic wear leveling, the number of erase cycles before data
is moved to another block. Set to a non-positive value to
disable leveling.
endmenu
endif # FILE_SYSTEM endif # FILE_SYSTEM
source "subsys/fs/fcb/Kconfig" source "subsys/fs/fcb/Kconfig"

665
subsys/fs/littlefs_fs.c Normal file
View file

@ -0,0 +1,665 @@
/*
* Copyright (c) 2019 Bolt Innovation Management, LLC
* Copyright (c) 2019 Peter Bigot Consulting, LLC
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include <string.h>
#include <kernel.h>
#include <errno.h>
#include <init.h>
#include <fs.h>
#define LFS_LOG_REGISTER
#include <lfs_util.h>
#include <lfs.h>
#include <fs/littlefs.h>
#include <flash.h>
#include <flash_map.h>
#include "fs_impl.h"
struct lfs_file_cache {
struct lfs_file file;
struct lfs_file_config config;
u8_t cache[CONFIG_FS_LITTLEFS_CACHE_SIZE];
};
#define LFS_FILEP(fp) (&((struct lfs_file_cache *)(fp->filep))->file)
/* Global memory pool for open files and dirs */
K_MEM_SLAB_DEFINE(lfs_file_pool, sizeof(struct lfs_file_cache),
CONFIG_FS_LITTLEFS_NUM_FILES, 4);
K_MEM_SLAB_DEFINE(lfs_dir_pool, sizeof(struct lfs_dir),
CONFIG_FS_LITTLEFS_NUM_DIRS, 4);
static inline void fs_lock(struct fs_littlefs *fs)
{
k_mutex_lock(&fs->mutex, K_FOREVER);
}
static inline void fs_unlock(struct fs_littlefs *fs)
{
k_mutex_unlock(&fs->mutex);
}
static int lfs_to_errno(int error)
{
if (error >= 0) {
return error;
}
switch (error) {
default:
case LFS_ERR_IO: /* Error during device operation */
return -EIO;
case LFS_ERR_CORRUPT: /* Corrupted */
return -EFAULT;
case LFS_ERR_NOENT: /* No directory entry */
return -ENOENT;
case LFS_ERR_EXIST: /* Entry already exists */
return -EEXIST;
case LFS_ERR_NOTDIR: /* Entry is not a dir */
return -ENOTDIR;
case LFS_ERR_ISDIR: /* Entry is a dir */
return -EISDIR;
case LFS_ERR_NOTEMPTY: /* Dir is not empty */
return -ENOTEMPTY;
case LFS_ERR_BADF: /* Bad file number */
return -EBADF;
case LFS_ERR_FBIG: /* File too large */
return -EFBIG;
case LFS_ERR_INVAL: /* Invalid parameter */
return -EINVAL;
case LFS_ERR_NOSPC: /* No space left on device */
return -ENOSPC;
case LFS_ERR_NOMEM: /* No more memory available */
return -ENOMEM;
}
}
static int errno_to_lfs(int error)
{
if (error >= 0) {
return LFS_ERR_OK;
}
switch (error) {
default:
case -EIO: /* Error during device operation */
return LFS_ERR_IO;
case -EFAULT: /* Corrupted */
return LFS_ERR_CORRUPT;
case -ENOENT: /* No directory entry */
return LFS_ERR_NOENT;
case -EEXIST: /* Entry already exists */
return LFS_ERR_EXIST;
case -ENOTDIR: /* Entry is not a dir */
return LFS_ERR_NOTDIR;
case -EISDIR: /* Entry is a dir */
return LFS_ERR_ISDIR;
case -ENOTEMPTY: /* Dir is not empty */
return LFS_ERR_NOTEMPTY;
case -EBADF: /* Bad file number */
return LFS_ERR_BADF;
case -EFBIG: /* File too large */
return LFS_ERR_FBIG;
case -EINVAL: /* Invalid parameter */
return LFS_ERR_INVAL;
case -ENOSPC: /* No space left on device */
return LFS_ERR_NOSPC;
case -ENOMEM: /* No more memory available */
return LFS_ERR_NOMEM;
}
}
static int lfs_api_read(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size)
{
const struct flash_area *fa = c->context;
size_t offset = block * c->block_size + off;
int rc = flash_area_read(fa, offset, buffer, size);
return errno_to_lfs(rc);
}
static int lfs_api_prog(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size)
{
const struct flash_area *fa = c->context;
size_t offset = block * c->block_size + off;
int rc = flash_area_write(fa, offset, buffer, size);
return errno_to_lfs(rc);
}
static int lfs_api_erase(const struct lfs_config *c, lfs_block_t block)
{
const struct flash_area *fa = c->context;
size_t offset = block * c->block_size;
int rc = flash_area_erase(fa, offset, c->block_size);
return errno_to_lfs(rc);
}
static int lfs_api_sync(const struct lfs_config *c)
{
return LFS_ERR_OK;
}
static int littlefs_open(struct fs_file_t *fp, const char *path)
{
struct fs_littlefs *fs = fp->mp->fs_data;
int flags = LFS_O_CREAT | LFS_O_RDWR;
if (k_mem_slab_alloc(&lfs_file_pool, &fp->filep, K_NO_WAIT) != 0) {
return -ENOMEM;
}
fs_lock(fs);
/* Use cache inside the slab allocation, instead of letting
* littlefs allocate it from the heap.
*/
struct lfs_file_cache *fc = fp->filep;
fc->config = (struct lfs_file_config) {
.buffer = fc->cache,
};
memset(&fc->file, 0, sizeof(struct lfs_file));
path = fs_impl_strip_prefix(path, fp->mp);
int ret = lfs_file_opencfg(&fs->lfs, &fc->file,
path, flags, &fc->config);
fs_unlock(fs);
if (ret < 0) {
k_mem_slab_free(&lfs_file_pool, &fp->filep);
}
return lfs_to_errno(ret);
}
static int littlefs_close(struct fs_file_t *fp)
{
struct fs_littlefs *fs = fp->mp->fs_data;
fs_lock(fs);
int ret = lfs_file_close(&fs->lfs, LFS_FILEP(fp));
k_mem_slab_free(&lfs_file_pool, &fp->filep);
fs_unlock(fs);
return lfs_to_errno(ret);
}
static int littlefs_unlink(struct fs_mount_t *mountp, const char *path)
{
struct fs_littlefs *fs = mountp->fs_data;
path = fs_impl_strip_prefix(path, mountp);
fs_lock(fs);
int ret = lfs_remove(&fs->lfs, path);
fs_unlock(fs);
return lfs_to_errno(ret);
}
static int littlefs_rename(struct fs_mount_t *mountp, const char *from,
const char *to)
{
struct fs_littlefs *fs = mountp->fs_data;
from = fs_impl_strip_prefix(from, mountp);
to = fs_impl_strip_prefix(to, mountp);
fs_lock(fs);
int ret = lfs_rename(&fs->lfs, from, to);
fs_unlock(fs);
return lfs_to_errno(ret);
}
static ssize_t littlefs_read(struct fs_file_t *fp, void *ptr, size_t len)
{
struct fs_littlefs *fs = fp->mp->fs_data;
fs_lock(fs);
ssize_t ret = lfs_file_read(&fs->lfs, LFS_FILEP(fp), ptr, len);
fs_unlock(fs);
return lfs_to_errno(ret);
}
static ssize_t littlefs_write(struct fs_file_t *fp, const void *ptr, size_t len)
{
struct fs_littlefs *fs = fp->mp->fs_data;
fs_lock(fs);
ssize_t ret = lfs_file_write(&fs->lfs, LFS_FILEP(fp), ptr, len);
fs_unlock(fs);
return lfs_to_errno(ret);
}
BUILD_ASSERT((FS_SEEK_SET == LFS_SEEK_SET)
&& (FS_SEEK_CUR == LFS_SEEK_CUR)
&& (FS_SEEK_END == LFS_SEEK_END));
static int littlefs_seek(struct fs_file_t *fp, off_t off, int whence)
{
struct fs_littlefs *fs = fp->mp->fs_data;
fs_lock(fs);
off_t ret = lfs_file_seek(&fs->lfs, LFS_FILEP(fp), off, whence);
fs_unlock(fs);
if (ret >= 0) {
ret = 0;
}
return lfs_to_errno(ret);
}
static off_t littlefs_tell(struct fs_file_t *fp)
{
struct fs_littlefs *fs = fp->mp->fs_data;
fs_lock(fs);
off_t ret = lfs_file_tell(&fs->lfs, LFS_FILEP(fp));
fs_unlock(fs);
return ret;
}
static int littlefs_truncate(struct fs_file_t *fp, off_t length)
{
struct fs_littlefs *fs = fp->mp->fs_data;
fs_lock(fs);
int ret = lfs_file_truncate(&fs->lfs, LFS_FILEP(fp), length);
fs_unlock(fs);
return lfs_to_errno(ret);
}
static int littlefs_sync(struct fs_file_t *fp)
{
struct fs_littlefs *fs = fp->mp->fs_data;
fs_lock(fs);
int ret = lfs_file_sync(&fs->lfs, LFS_FILEP(fp));
fs_unlock(fs);
return lfs_to_errno(ret);
}
static int littlefs_mkdir(struct fs_mount_t *mountp, const char *path)
{
struct fs_littlefs *fs = mountp->fs_data;
path = fs_impl_strip_prefix(path, mountp);
fs_lock(fs);
int ret = lfs_mkdir(&fs->lfs, path);
fs_unlock(fs);
return lfs_to_errno(ret);
}
static int littlefs_opendir(struct fs_dir_t *dp, const char *path)
{
struct fs_littlefs *fs = dp->mp->fs_data;
if (k_mem_slab_alloc(&lfs_dir_pool, &dp->dirp, K_NO_WAIT) != 0) {
return -ENOMEM;
}
memset(dp->dirp, 0, sizeof(struct lfs_dir));
path = fs_impl_strip_prefix(path, dp->mp);
fs_lock(fs);
int ret = lfs_dir_open(&fs->lfs, dp->dirp, path);
fs_unlock(fs);
if (ret < 0) {
k_mem_slab_free(&lfs_dir_pool, &dp->dirp);
}
return lfs_to_errno(ret);
}
static void info_to_dirent(const struct lfs_info *info, struct fs_dirent *entry)
{
entry->type = ((info->type == LFS_TYPE_DIR) ?
FS_DIR_ENTRY_DIR : FS_DIR_ENTRY_FILE);
entry->size = info->size;
strncpy(entry->name, info->name, sizeof(entry->name));
entry->name[sizeof(entry->name) - 1] = '\0';
}
static int littlefs_readdir(struct fs_dir_t *dp, struct fs_dirent *entry)
{
struct fs_littlefs *fs = dp->mp->fs_data;
fs_lock(fs);
struct lfs_info info;
int ret = lfs_dir_read(&fs->lfs, dp->dirp, &info);
fs_unlock(fs);
if (ret > 0) {
info_to_dirent(&info, entry);
ret = 0;
} else if (ret == 0) {
entry->name[0] = 0;
}
return lfs_to_errno(ret);
}
static int littlefs_closedir(struct fs_dir_t *dp)
{
struct fs_littlefs *fs = dp->mp->fs_data;
fs_lock(fs);
int ret = lfs_dir_close(&fs->lfs, dp->dirp);
fs_unlock(fs);
k_mem_slab_free(&lfs_dir_pool, &dp->dirp);
return lfs_to_errno(ret);
}
static int littlefs_stat(struct fs_mount_t *mountp,
const char *path, struct fs_dirent *entry)
{
struct fs_littlefs *fs = mountp->fs_data;
path = fs_impl_strip_prefix(path, mountp);
fs_lock(fs);
struct lfs_info info;
int ret = lfs_stat(&fs->lfs, path, &info);
fs_unlock(fs);
if (ret >= 0) {
info_to_dirent(&info, entry);
ret = 0;
}
return lfs_to_errno(ret);
}
static int littlefs_statvfs(struct fs_mount_t *mountp,
const char *path, struct fs_statvfs *stat)
{
struct fs_littlefs *fs = mountp->fs_data;
struct lfs *lfs = &fs->lfs;
stat->f_bsize = lfs->cfg->prog_size;
stat->f_frsize = lfs->cfg->block_size;
stat->f_blocks = lfs->cfg->block_count;
path = fs_impl_strip_prefix(path, mountp);
fs_lock(fs);
ssize_t ret = lfs_fs_size(lfs);
fs_unlock(fs);
if (ret >= 0) {
stat->f_bfree = stat->f_blocks - ret;
ret = 0;
}
return lfs_to_errno(ret);
}
/* Return maximum page size in a flash area. There's no flash_area
* API to implement this, so we have to make one here.
*/
struct get_page_ctx {
const struct flash_area *area;
lfs_size_t max_size;
};
static bool get_page_cb(const struct flash_pages_info *info, void *ctxp)
{
struct get_page_ctx *ctx = ctxp;
size_t info_start = info->start_offset;
size_t info_end = info_start + info->size - 1U;
size_t area_start = ctx->area->fa_off;
size_t area_end = area_start + ctx->area->fa_size - 1U;
/* Ignore pages outside the area */
if (info_end < area_start) {
return true;
}
if (info_start > area_end) {
return false;
}
if (info->size > ctx->max_size) {
ctx->max_size = info->size;
}
return true;
}
/* Iterate over all page groups in the flash area and return the
* largest page size we see. This works as long as the partition is
* aligned so that erasing with this size is supported throughout the
* partition.
*/
static lfs_size_t get_block_size(const struct flash_area *fa)
{
struct get_page_ctx ctx = {
.area = fa,
.max_size = 0,
};
struct device *dev = flash_area_get_device(fa);
flash_page_foreach(dev, get_page_cb, &ctx);
return ctx.max_size;
}
static int littlefs_mount(struct fs_mount_t *mountp)
{
int ret;
struct fs_littlefs *fs = mountp->fs_data;
unsigned int area_id = (uintptr_t)mountp->storage_dev;
struct device *dev;
LOG_INF("LittleFS version %u.%u, disk version %u.%u",
LFS_VERSION_MAJOR, LFS_VERSION_MINOR,
LFS_DISK_VERSION_MAJOR, LFS_DISK_VERSION_MINOR);
if (fs->area) {
return -EBUSY;
}
/* Create and take mutex. */
k_mutex_init(&fs->mutex);
fs_lock(fs);
/* Open flash area */
ret = flash_area_open(area_id, &fs->area);
if ((ret < 0) || (fs->area == NULL)) {
LOG_ERR("can't open flash area %d", area_id);
ret = -ENODEV;
goto out;
}
LOG_DBG("FS area %u at 0x%x for %u bytes",
area_id, (u32_t)fs->area->fa_off,
(u32_t)fs->area->fa_size);
dev = flash_area_get_device(fs->area);
if (dev == NULL) {
LOG_ERR("can't get flash device: %s", fs->area->fa_dev_name);
ret = -ENODEV;
goto out;
}
BUILD_ASSERT(CONFIG_FS_LITTLEFS_READ_SIZE > 0);
BUILD_ASSERT(CONFIG_FS_LITTLEFS_PROG_SIZE > 0);
BUILD_ASSERT(CONFIG_FS_LITTLEFS_CACHE_SIZE > 0);
BUILD_ASSERT(CONFIG_FS_LITTLEFS_LOOKAHEAD_SIZE > 0);
BUILD_ASSERT((CONFIG_FS_LITTLEFS_LOOKAHEAD_SIZE % 8) == 0);
BUILD_ASSERT((CONFIG_FS_LITTLEFS_CACHE_SIZE
% CONFIG_FS_LITTLEFS_READ_SIZE) == 0);
BUILD_ASSERT((CONFIG_FS_LITTLEFS_CACHE_SIZE
% CONFIG_FS_LITTLEFS_PROG_SIZE) == 0);
lfs_size_t read_size = CONFIG_FS_LITTLEFS_READ_SIZE;
lfs_size_t prog_size = CONFIG_FS_LITTLEFS_PROG_SIZE;
lfs_size_t block_size = get_block_size(fs->area);
s32_t block_cycles = CONFIG_FS_LITTLEFS_BLOCK_CYCLES;
lfs_size_t cache_size = CONFIG_FS_LITTLEFS_CACHE_SIZE;
lfs_size_t lookahead_size = CONFIG_FS_LITTLEFS_LOOKAHEAD_SIZE;
if (block_cycles <= 0) {
/* Disable leveling (littlefs v2.1+ semantics) */
block_cycles = -1;
}
lfs_size_t block_count = fs->area->fa_size / block_size;
LOG_DBG("FS at %s is %u 0x%x-byte blocks with %u cycle", dev->config->name,
block_count, block_size, block_cycles);
LOG_DBG("sizes: rd %u ; pr %u ; ca %u ; la %u",
read_size, prog_size, cache_size, lookahead_size);
__ASSERT((fs->area->fa_size % block_size) == 0,
"partition size must be multiple of block size");
__ASSERT((block_size % prog_size) == 0,
"erase size must be multiple of write size");
__ASSERT((block_size % cache_size) == 0,
"cache size incompatible with block size");
/* Build littlefs config */
fs->cfg = (struct lfs_config) {
.context = (void *)fs->area,
.read = lfs_api_read,
.prog = lfs_api_prog,
.erase = lfs_api_erase,
.sync = lfs_api_sync,
.read_size = read_size,
.prog_size = prog_size,
.block_size = block_size,
.block_count = block_count,
.block_cycles = block_cycles,
.cache_size = cache_size,
.lookahead_size = lookahead_size,
.read_buffer = fs->read_buffer,
.prog_buffer = fs->prog_buffer,
.lookahead_buffer = fs->lookahead_buffer
};
/* Mount it, formatting if needed. */
ret = lfs_mount(&fs->lfs, &fs->cfg);
if (ret < 0) {
LOG_WRN("can't mount (LFS %d); formatting", ret);
ret = lfs_format(&fs->lfs, &fs->cfg);
if (ret < 0) {
LOG_ERR("format failed (LFS %d)", ret);
ret = lfs_to_errno(ret);
goto out;
}
ret = lfs_mount(&fs->lfs, &fs->cfg);
if (ret < 0) {
LOG_ERR("remount after format failed (LFS %d)", ret);
ret = lfs_to_errno(ret);
goto out;
}
}
LOG_INF("%s mounted", log_strdup(mountp->mnt_point));
out:
if (ret < 0) {
fs->area = NULL;
}
fs_unlock(fs);
return ret;
}
static int littlefs_unmount(struct fs_mount_t *mountp)
{
struct fs_littlefs *fs = mountp->fs_data;
fs_lock(fs);
lfs_unmount(&fs->lfs);
flash_area_close(fs->area);
fs->area = NULL;
fs_unlock(fs);
LOG_INF("%s unmounted", log_strdup(mountp->mnt_point));
return 0;
}
/* File system interface */
static struct fs_file_system_t littlefs_fs = {
.open = littlefs_open,
.close = littlefs_close,
.read = littlefs_read,
.write = littlefs_write,
.lseek = littlefs_seek,
.tell = littlefs_tell,
.truncate = littlefs_truncate,
.sync = littlefs_sync,
.opendir = littlefs_opendir,
.readdir = littlefs_readdir,
.closedir = littlefs_closedir,
.mount = littlefs_mount,
.unmount = littlefs_unmount,
.unlink = littlefs_unlink,
.rename = littlefs_rename,
.mkdir = littlefs_mkdir,
.stat = littlefs_stat,
.statvfs = littlefs_statvfs,
};
static int littlefs_init(struct device *dev)
{
ARG_UNUSED(dev);
return fs_register(FS_LITTLEFS, &littlefs_fs);
}
SYS_INIT(littlefs_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);

View file

@ -85,6 +85,9 @@ manifest:
- name: tinycbor - name: tinycbor
path: modules/lib/tinycbor path: modules/lib/tinycbor
revision: 31ae89e4b768612722620cb6cb173a0de4a19cc9 revision: 31ae89e4b768612722620cb6cb173a0de4a19cc9
- name: littlefs
path: modules/fs/littlefs
revision: 52b038794b1ba34bd9aaf4df3e37e65558046342
self: self:
path: zephyr path: zephyr