123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849 |
- /*
- * 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/fs.h>
- #include <fs/fs_sys.h>
- #define LFS_LOG_REGISTER
- #include <lfs_util.h>
- #include <lfs.h>
- #include <fs/littlefs.h>
- #include <drivers/flash.h>
- #include <storage/flash_map.h>
- #include "fs_impl.h"
- struct lfs_file_data {
- struct lfs_file file;
- struct lfs_file_config config;
- void *cache_block;
- };
- #define LFS_FILEP(fp) (&((struct lfs_file_data *)(fp->filep))->file)
- /* Global memory pool for open files and dirs */
- static K_MEM_SLAB_DEFINE(file_data_pool, sizeof(struct lfs_file_data),
- CONFIG_FS_LITTLEFS_NUM_FILES, 4);
- static K_MEM_SLAB_DEFINE(lfs_dir_pool, sizeof(struct lfs_dir),
- CONFIG_FS_LITTLEFS_NUM_DIRS, 4);
- /* Inferred overhead, in bytes, for each k_heap_aligned allocation for
- * the filecache heap. This relates to the CHUNK_UNIT parameter in
- * the heap implementation, but that value is not visible outside the
- * kernel.
- */
- #define FC_HEAP_PER_ALLOC_OVERHEAD 24U
- #if (CONFIG_FS_LITTLEFS_FC_HEAP_SIZE - 0) <= 0
- /* Auto-generate heap size from cache size and number of files */
- #undef CONFIG_FS_LITTLEFS_FC_HEAP_SIZE
- #define CONFIG_FS_LITTLEFS_FC_HEAP_SIZE \
- ((CONFIG_FS_LITTLEFS_CACHE_SIZE + FC_HEAP_PER_ALLOC_OVERHEAD) * \
- CONFIG_FS_LITTLEFS_NUM_FILES)
- #endif /* CONFIG_FS_LITTLEFS_FC_HEAP_SIZE */
- static K_HEAP_DEFINE(file_cache_heap, CONFIG_FS_LITTLEFS_FC_HEAP_SIZE);
- static inline void *fc_allocate(size_t size)
- {
- void *ret = NULL;
- ret = k_heap_alloc(&file_cache_heap, size, K_NO_WAIT);
- return ret;
- }
- static inline void fc_release(void *buf)
- {
- k_heap_free(&file_cache_heap, buf);
- }
- 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 void release_file_data(struct fs_file_t *fp)
- {
- struct lfs_file_data *fdp = fp->filep;
- if (fdp->config.buffer) {
- fc_release(fdp->cache_block);
- }
- k_mem_slab_free(&file_data_pool, &fp->filep);
- fp->filep = NULL;
- }
- static int lfs_flags_from_zephyr(unsigned int zflags)
- {
- int flags = (zflags & FS_O_CREATE) ? LFS_O_CREAT : 0;
- /* LFS_O_READONLY and LFS_O_WRONLY can be selected at the same time,
- * this is not a mistake, together they create RDWR access.
- */
- flags |= (zflags & FS_O_READ) ? LFS_O_RDONLY : 0;
- flags |= (zflags & FS_O_WRITE) ? LFS_O_WRONLY : 0;
- flags |= (zflags & FS_O_APPEND) ? LFS_O_APPEND : 0;
- return flags;
- }
- static int littlefs_open(struct fs_file_t *fp, const char *path,
- fs_mode_t zflags)
- {
- struct fs_littlefs *fs = fp->mp->fs_data;
- struct lfs *lfs = &fs->lfs;
- int flags = lfs_flags_from_zephyr(zflags);
- int ret = k_mem_slab_alloc(&file_data_pool, &fp->filep, K_NO_WAIT);
- if (ret != 0) {
- return ret;
- }
- struct lfs_file_data *fdp = fp->filep;
- memset(fdp, 0, sizeof(*fdp));
- fdp->cache_block = fc_allocate(lfs->cfg->cache_size);
- if (fdp->cache_block == NULL) {
- ret = -ENOMEM;
- goto out;
- }
- fdp->config.buffer = fdp->cache_block;
- path = fs_impl_strip_prefix(path, fp->mp);
- fs_lock(fs);
- ret = lfs_file_opencfg(&fs->lfs, &fdp->file,
- path, flags, &fdp->config);
- fs_unlock(fs);
- out:
- if (ret < 0) {
- release_file_data(fp);
- }
- 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));
- fs_unlock(fs);
- release_file_data(fp);
- 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,
- };
- const 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;
- const 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, (uint32_t)fs->area->fa_off,
- (uint32_t)fs->area->fa_size);
- dev = flash_area_get_device(fs->area);
- if (dev == NULL) {
- LOG_ERR("can't get flash device: %s", log_strdup(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);
- struct lfs_config *lcp = &fs->cfg;
- lfs_size_t read_size = lcp->read_size;
- if (read_size == 0) {
- read_size = CONFIG_FS_LITTLEFS_READ_SIZE;
- }
- lfs_size_t prog_size = lcp->prog_size;
- if (prog_size == 0) {
- prog_size = CONFIG_FS_LITTLEFS_PROG_SIZE;
- }
- /* Yes, you can override block size. */
- lfs_size_t block_size = lcp->block_size;
- if (block_size == 0) {
- block_size = get_block_size(fs->area);
- }
- if (block_size == 0) {
- __ASSERT_NO_MSG(block_size != 0);
- ret = -EINVAL;
- goto out;
- }
- int32_t block_cycles = lcp->block_cycles;
- if (block_cycles == 0) {
- block_cycles = CONFIG_FS_LITTLEFS_BLOCK_CYCLES;
- }
- if (block_cycles <= 0) {
- /* Disable leveling (littlefs v2.1+ semantics) */
- block_cycles = -1;
- }
- lfs_size_t cache_size = lcp->cache_size;
- if (cache_size == 0) {
- cache_size = CONFIG_FS_LITTLEFS_CACHE_SIZE;
- }
- lfs_size_t lookahead_size = lcp->lookahead_size;
- if (lookahead_size == 0) {
- lookahead_size = CONFIG_FS_LITTLEFS_LOOKAHEAD_SIZE;
- }
- /* No, you don't get to override this. */
- lfs_size_t block_count = fs->area->fa_size / block_size;
- LOG_INF("FS at %s:0x%x is %u 0x%x-byte blocks with %u cycle",
- log_strdup(dev->name), (uint32_t)fs->area->fa_off,
- block_count, block_size, block_cycles);
- LOG_INF("sizes: rd %u ; pr %u ; ca %u ; la %u",
- read_size, prog_size, cache_size, lookahead_size);
- __ASSERT_NO_MSG(prog_size != 0);
- __ASSERT_NO_MSG(read_size != 0);
- __ASSERT_NO_MSG(cache_size != 0);
- __ASSERT_NO_MSG(block_size != 0);
- __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");
- /* Set the validated/defaulted values. */
- lcp->context = (void *)fs->area;
- lcp->read = lfs_api_read;
- lcp->prog = lfs_api_prog;
- lcp->erase = lfs_api_erase;
- lcp->sync = lfs_api_sync;
- lcp->read_size = read_size;
- lcp->prog_size = prog_size;
- lcp->block_size = block_size;
- lcp->block_count = block_count;
- lcp->block_cycles = block_cycles;
- lcp->cache_size = cache_size;
- lcp->lookahead_size = lookahead_size;
- /* Mount it, formatting if needed. */
- ret = lfs_mount(&fs->lfs, &fs->cfg);
- if (ret < 0 &&
- (mountp->flags & FS_MOUNT_FLAG_NO_FORMAT) == 0) {
- LOG_WRN("can't mount (LFS %d); formatting", ret);
- if ((mountp->flags & FS_MOUNT_FLAG_READ_ONLY) == 0) {
- ret = lfs_format(&fs->lfs, &fs->cfg);
- if (ret < 0) {
- LOG_ERR("format failed (LFS %d)", ret);
- ret = lfs_to_errno(ret);
- goto out;
- }
- } else {
- LOG_ERR("can not format read-only system");
- ret = -EROFS;
- 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 const 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,
- };
- #define DT_DRV_COMPAT zephyr_fstab_littlefs
- #define FS_PARTITION(inst) DT_PHANDLE_BY_IDX(DT_DRV_INST(inst), partition, 0)
- #define DEFINE_FS(inst) \
- static uint8_t __aligned(4) \
- read_buffer_##inst[DT_INST_PROP(inst, cache_size)]; \
- static uint8_t __aligned(4) \
- prog_buffer_##inst[DT_INST_PROP(inst, cache_size)]; \
- static uint32_t lookahead_buffer_##inst[DT_INST_PROP(inst, lookahead_size) \
- / sizeof(uint32_t)]; \
- BUILD_ASSERT(DT_INST_PROP(inst, read_size) > 0); \
- BUILD_ASSERT(DT_INST_PROP(inst, prog_size) > 0); \
- BUILD_ASSERT(DT_INST_PROP(inst, cache_size) > 0); \
- BUILD_ASSERT(DT_INST_PROP(inst, lookahead_size) > 0); \
- BUILD_ASSERT((DT_INST_PROP(inst, lookahead_size) % 8) == 0); \
- BUILD_ASSERT((DT_INST_PROP(inst, cache_size) \
- % DT_INST_PROP(inst, read_size)) == 0); \
- BUILD_ASSERT((DT_INST_PROP(inst, cache_size) \
- % DT_INST_PROP(inst, prog_size)) == 0); \
- static struct fs_littlefs fs_data_##inst = { \
- .cfg = { \
- .read_size = DT_INST_PROP(inst, read_size), \
- .prog_size = DT_INST_PROP(inst, prog_size), \
- .cache_size = DT_INST_PROP(inst, cache_size), \
- .lookahead_size = DT_INST_PROP(inst, lookahead_size), \
- .read_buffer = read_buffer_##inst, \
- .prog_buffer = prog_buffer_##inst, \
- .lookahead_buffer = lookahead_buffer_##inst, \
- }, \
- }; \
- struct fs_mount_t FS_FSTAB_ENTRY(DT_DRV_INST(inst)) = { \
- .type = FS_LITTLEFS, \
- .mnt_point = DT_INST_PROP(inst, mount_point), \
- .fs_data = &fs_data_##inst, \
- .storage_dev = (void *)DT_FIXED_PARTITION_ID(FS_PARTITION(inst)), \
- .flags = FSTAB_ENTRY_DT_MOUNT_FLAGS(DT_DRV_INST(inst)), \
- };
- DT_INST_FOREACH_STATUS_OKAY(DEFINE_FS)
- #define REFERENCE_MOUNT(inst) (&FS_FSTAB_ENTRY(DT_DRV_INST(inst))),
- static void mount_init(struct fs_mount_t *mp)
- {
- LOG_INF("littlefs partition at %s", mp->mnt_point);
- if ((mp->flags & FS_MOUNT_FLAG_AUTOMOUNT) != 0) {
- int rc = fs_mount(mp);
- if (rc < 0) {
- LOG_ERR("Automount %s failed: %d",
- mp->mnt_point, rc);
- } else {
- LOG_INF("Automount %s succeeded",
- mp->mnt_point);
- }
- }
- }
- static int littlefs_init(const struct device *dev)
- {
- ARG_UNUSED(dev);
- static struct fs_mount_t *partitions[] = {
- DT_INST_FOREACH_STATUS_OKAY(REFERENCE_MOUNT)
- };
- int rc = fs_register(FS_LITTLEFS, &littlefs_fs);
- if (rc == 0) {
- struct fs_mount_t **mpi = partitions;
- while (mpi < (partitions + ARRAY_SIZE(partitions))) {
- mount_init(*mpi++);
- }
- }
- return rc;
- }
- SYS_INIT(littlefs_init, POST_KERNEL, 99);
|