From 81da3ca2950b14a7b0b19b105304bbf2ce701d98 Mon Sep 17 00:00:00 2001 From: Andrzej Kaczmarek Date: Tue, 8 Aug 2017 15:12:55 +0200 Subject: [PATCH] fs: nffs: Add NFFS filesystem support This patch adds filesystem interface implementation for NFFS. Default configuration for mem slabs sizes are the same as in Mynewt. Origin: Original Signed-off-by: Andrzej Kaczmarek --- include/fs/fs_interface.h | 2 + include/fs/nffs_fs.h | 26 ++ subsys/fs/Kconfig | 54 +++- subsys/fs/Makefile | 1 + subsys/fs/nffs_fs.c | 538 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 620 insertions(+), 1 deletion(-) create mode 100644 include/fs/nffs_fs.h create mode 100644 subsys/fs/nffs_fs.c diff --git a/include/fs/fs_interface.h b/include/fs/fs_interface.h index 69d3a906715..e4594d71f5d 100644 --- a/include/fs/fs_interface.h +++ b/include/fs/fs_interface.h @@ -43,6 +43,8 @@ extern "C" { #ifdef CONFIG_FILE_SYSTEM_FAT #include +#elif CONFIG_FILE_SYSTEM_NFFS +#include #endif #ifdef __cplusplus diff --git a/include/fs/nffs_fs.h b/include/fs/nffs_fs.h new file mode 100644 index 00000000000..b4ecd901e80 --- /dev/null +++ b/include/fs/nffs_fs.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2017 Codecoup + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _NFFS_FS_H_ +#define _NFFS_FS_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +FS_FILE_DEFINE(struct nffs_file *fp); +FS_DIR_DEFINE(struct nffs_dir *dp); + +#define MAX_FILE_NAME 256 + +#ifdef __cplusplus +} +#endif + +#endif /* _NFFS_FS_H_ */ diff --git a/subsys/fs/Kconfig b/subsys/fs/Kconfig index d700281f542..c2c722d7630 100644 --- a/subsys/fs/Kconfig +++ b/subsys/fs/Kconfig @@ -22,12 +22,64 @@ config FILE_SYSTEM_SHELL This shell provides basic browsing of the contents of the file system. +choice + prompt "Supported file system" + default FILE_SYSTEM_FAT + config FILE_SYSTEM_FAT bool "FAT file system support" - default y help Enables FAT file system support. +config FILE_SYSTEM_NFFS + bool "NFFS file system support" + select FLASH_PAGE_LAYOUT + help + Enables NFFS file system support. + Note: NFFS requires 1-byte unaligned access to flash thus it + will not work on devices that support only aligned flash access. + +endchoice + +menu "NFFS Settings" + visible if FILE_SYSTEM_NFFS + +config FS_NFFS_FLASH_DEV_NAME + string + prompt "Flash device name to be used" + +config FS_NFFS_NUM_INODES + int "Maximum number of inodes" + range 1 32768 + default 100 + +config FS_NFFS_NUM_BLOCKS + int "Maximum number of blocks" + range 1 32768 + default 100 + +config FS_NFFS_NUM_FILES + int "Maximum number of opened files" + range 1 256 + default 4 + +config FS_NFFS_NUM_DIRS + int "Maximum number of opened directories" + range 1 256 + default 4 + +config FS_NFFS_NUM_CACHE_INODES + int "Number of cached files' inodes" + range 1 512 + default 4 + +config FS_NFFS_NUM_CACHE_BLOCKS + int "Number of cached blocks" + range 1 512 + default 64 + +endmenu + endif # FILE_SYSTEM endmenu diff --git a/subsys/fs/Makefile b/subsys/fs/Makefile index c01105aff8c..344ac2b9eef 100644 --- a/subsys/fs/Makefile +++ b/subsys/fs/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_FILE_SYSTEM_SHELL) += shell.o obj-$(CONFIG_FILE_SYSTEM_FAT) += fat_fs.o +obj-$(CONFIG_FILE_SYSTEM_NFFS) += nffs_fs.o diff --git a/subsys/fs/nffs_fs.c b/subsys/fs/nffs_fs.c new file mode 100644 index 00000000000..56d2869e17e --- /dev/null +++ b/subsys/fs/nffs_fs.c @@ -0,0 +1,538 @@ +/* + * Copyright (c) 2017 Codecoup + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * NFFS code keeps fs state in RAM but access to these structures is not + * thread-safe - we need global lock for each fs operation to guarantee two + * threads won't modify NFFS at the same time. + */ +static struct k_mutex nffs_lock; + +static struct device *flash_dev; +static struct nffs_flash_desc flash_desc; + +K_MEM_SLAB_DEFINE(nffs_file_pool, sizeof(struct nffs_file), + CONFIG_FS_NFFS_NUM_FILES, 4); +K_MEM_SLAB_DEFINE(nffs_dir_pool, sizeof(struct nffs_dir), + CONFIG_FS_NFFS_NUM_DIRS, 4); +K_MEM_SLAB_DEFINE(nffs_inode_entry_pool, sizeof(struct nffs_inode_entry), + CONFIG_FS_NFFS_NUM_INODES, 4); +K_MEM_SLAB_DEFINE(nffs_block_entry_pool, sizeof(struct nffs_hash_entry), + CONFIG_FS_NFFS_NUM_BLOCKS, 4); +K_MEM_SLAB_DEFINE(nffs_cache_inode_pool, sizeof(struct nffs_cache_inode), + CONFIG_FS_NFFS_NUM_CACHE_INODES, 4); +K_MEM_SLAB_DEFINE(nffs_cache_block_pool, sizeof(struct nffs_cache_block), + CONFIG_FS_NFFS_NUM_CACHE_BLOCKS, 4); + +static int translate_error(int error) +{ + switch (error) { + case FS_EOK: + return 0; + case FS_ECORRUPT: + case FS_EHW: + return -EIO; + case FS_EOFFSET: + case FS_EINVAL: + return -EINVAL; + case FS_ENOMEM: + return -ENOMEM; + case FS_ENOENT: + return -ENOENT; + case FS_EEMPTY: + return -ENODEV; + case FS_EFULL: + return -ENOSPC; + case FS_EUNEXP: + case FS_EOS: + return -EIO; + case FS_EEXIST: + return -EEXIST; + case FS_EACCESS: + return -EACCES; + case FS_EUNINIT: + return -EIO; + } + + return -EIO; +} + +int nffs_os_mempool_init(void) +{ + /* + * Just reinitialize slabs here - this is what original implementation + * does. We assume all references to previously allocated blocks, if + * any, are invalidated in NFFS code already. + */ + + k_mem_slab_init(&nffs_file_pool, _k_mem_slab_buf_nffs_file_pool, + sizeof(struct nffs_file), + CONFIG_FS_NFFS_NUM_FILES); + k_mem_slab_init(&nffs_dir_pool, _k_mem_slab_buf_nffs_dir_pool, + sizeof(struct nffs_dir), + CONFIG_FS_NFFS_NUM_DIRS); + k_mem_slab_init(&nffs_inode_entry_pool, + _k_mem_slab_buf_nffs_inode_entry_pool, + sizeof(struct nffs_inode_entry), + CONFIG_FS_NFFS_NUM_INODES); + k_mem_slab_init(&nffs_block_entry_pool, + _k_mem_slab_buf_nffs_block_entry_pool, + sizeof(struct nffs_hash_entry), + CONFIG_FS_NFFS_NUM_BLOCKS); + k_mem_slab_init(&nffs_cache_inode_pool, + _k_mem_slab_buf_nffs_cache_inode_pool, + sizeof(struct nffs_cache_inode), + CONFIG_FS_NFFS_NUM_CACHE_INODES); + k_mem_slab_init(&nffs_cache_block_pool, + _k_mem_slab_buf_nffs_cache_block_pool, + sizeof(struct nffs_cache_block), + CONFIG_FS_NFFS_NUM_CACHE_BLOCKS); + + return 0; +} + +void *nffs_os_mempool_get(nffs_os_mempool_t *pool) +{ + int rc; + void *ptr; + + rc = k_mem_slab_alloc(pool, &ptr, K_NO_WAIT); + if (rc) { + ptr = NULL; + } + + return ptr; +} + +int nffs_os_mempool_free(nffs_os_mempool_t *pool, void *block) +{ + k_mem_slab_free(pool, &block); + + return 0; +} + +int nffs_os_flash_read(u8_t id, u32_t address, void *dst, u32_t num_bytes) +{ + int rc; + + rc = flash_read(flash_dev, address, dst, num_bytes); + + return rc; +} + +int nffs_os_flash_write(u8_t id, u32_t address, const void *src, + u32_t num_bytes) +{ + int rc; + + rc = flash_write_protection_set(flash_dev, false); + if (rc) { + return rc; + } + + rc = flash_write(flash_dev, address, src, num_bytes); + + /* Ignore errors here - this does not affect write operation */ + (void) flash_write_protection_set(flash_dev, true); + + return rc; +} + +int nffs_os_flash_erase(u8_t id, u32_t address, u32_t num_bytes) +{ + int rc; + + rc = flash_write_protection_set(flash_dev, false); + if (rc) { + return rc; + } + + rc = flash_erase(flash_dev, address, num_bytes); + + /* Ignore errors here - this does not affect erase operation */ + (void) flash_write_protection_set(flash_dev, true); + + return rc; +} + +int nffs_os_flash_info(u8_t id, u32_t sector, u32_t *address, u32_t *size) +{ + struct flash_pages_info pi; + int rc; + + rc = flash_get_page_info_by_idx(flash_dev, sector, &pi); + __ASSERT(rc == 0, "Failed to obtain flash page data"); + + *address = pi.start_offset; + *size = pi.size; + + return 0; +} + +u16_t nffs_os_crc16_ccitt(u16_t initial_crc, const void *buf, int len, + int final) +{ + return crc16(buf, len, 0x1021, initial_crc, final); +} + +static int inode_to_dirent(struct nffs_inode_entry *inode, + struct fs_dirent *entry) +{ + u8_t name_len; + u32_t size; + int rc; + + rc = nffs_inode_read_filename(inode, sizeof(entry->name), entry->name, + &name_len); + if (rc) { + return rc; + } + + if (nffs_hash_id_is_dir(inode->nie_hash_entry.nhe_id)) { + entry->type = FS_DIR_ENTRY_DIR; + entry->size = 0; + } else { + entry->type = FS_DIR_ENTRY_FILE; + nffs_inode_data_len(inode, &size); + entry->size = size; + } + + return rc; +} + +int fs_open(fs_file_t *zfp, const char *file_name) +{ + int rc; + + k_mutex_lock(&nffs_lock, K_FOREVER); + + zfp->fp = NULL; + + if (!nffs_misc_ready()) { + k_mutex_unlock(&nffs_lock); + return -ENODEV; + } + + rc = nffs_file_open(&zfp->fp, file_name, + FS_ACCESS_READ | FS_ACCESS_WRITE); + + k_mutex_unlock(&nffs_lock); + + return translate_error(rc); +} + +int fs_close(fs_file_t *zfp) +{ + int rc; + + k_mutex_lock(&nffs_lock, K_FOREVER); + + rc = nffs_file_close(zfp->fp); + if (!rc) { + zfp->fp = NULL; + } + + k_mutex_unlock(&nffs_lock); + + return translate_error(rc); +} + +int fs_unlink(const char *path) +{ + int rc; + + k_mutex_lock(&nffs_lock, K_FOREVER); + + rc = nffs_path_unlink(path); + + k_mutex_unlock(&nffs_lock); + + return translate_error(rc); +} + +ssize_t fs_read(fs_file_t *zfp, void *ptr, size_t size) +{ + u32_t br; + int rc; + + k_mutex_lock(&nffs_lock, K_FOREVER); + + rc = nffs_file_read(zfp->fp, size, ptr, &br); + + k_mutex_unlock(&nffs_lock); + + if (rc) { + return translate_error(rc); + } + + return br; +} + +ssize_t fs_write(fs_file_t *zfp, const void *ptr, size_t size) +{ + int rc; + + k_mutex_lock(&nffs_lock, K_FOREVER); + + rc = nffs_write_to_file(zfp->fp, ptr, size); + + k_mutex_unlock(&nffs_lock); + + if (rc) { + return translate_error(rc); + } + + /* We need to assume all bytes were written */ + return size; +} + +int fs_seek(fs_file_t *zfp, off_t offset, int whence) +{ + u32_t len; + u32_t pos; + int rc; + + k_mutex_lock(&nffs_lock, K_FOREVER); + + switch (whence) { + case FS_SEEK_SET: + pos = offset; + break; + case FS_SEEK_CUR: + pos = zfp->fp->nf_offset + offset; + break; + case FS_SEEK_END: + rc = nffs_inode_data_len(zfp->fp->nf_inode_entry, &len); + if (rc) { + k_mutex_unlock(&nffs_lock); + return -EINVAL; + } + pos = len + offset; + break; + default: + k_mutex_unlock(&nffs_lock); + return -EINVAL; + } + + rc = nffs_file_seek(zfp->fp, pos); + + k_mutex_unlock(&nffs_lock); + + return translate_error(rc); +} + +off_t fs_tell(fs_file_t *zfp) +{ + u32_t offset; + + k_mutex_lock(&nffs_lock, K_FOREVER); + + if (!zfp->fp) { + return -EIO; + k_mutex_unlock(&nffs_lock); + } + + offset = zfp->fp->nf_offset; + + k_mutex_unlock(&nffs_lock); + + return offset; +} + +int fs_truncate(fs_file_t *zfp, off_t length) +{ + /* + * FIXME: + * There is no API in NFFS to truncate opened file. For now we return + * ENOTSUP, but this should be revisited if truncation is implemented + * in NFFS at some point. + */ + + return -ENOTSUP; +} + +int fs_sync(fs_file_t *zfp) +{ + /* + * Files are written to flash immediately so we do not need to support + * sync call - just return success. + */ + + return 0; +} + +int fs_mkdir(const char *path) +{ + int rc; + + k_mutex_lock(&nffs_lock, K_FOREVER); + + if (!nffs_misc_ready()) { + k_mutex_unlock(&nffs_lock); + return -ENODEV; + } + + rc = nffs_path_new_dir(path, NULL); + + k_mutex_unlock(&nffs_lock); + + return translate_error(rc); +} + +int fs_opendir(fs_dir_t *zdp, const char *path) +{ + int rc; + + k_mutex_lock(&nffs_lock, K_FOREVER); + + zdp->dp = NULL; + + if (!nffs_misc_ready()) { + k_mutex_unlock(&nffs_lock); + return -ENODEV; + } + + rc = nffs_dir_open(path, &zdp->dp); + + k_mutex_unlock(&nffs_lock); + + return translate_error(rc); +} + +int fs_readdir(fs_dir_t *zdp, struct fs_dirent *entry) +{ + struct nffs_dirent *dirent; + int rc; + + k_mutex_lock(&nffs_lock, K_FOREVER); + + rc = nffs_dir_read(zdp->dp, &dirent); + switch (rc) { + case 0: + rc = inode_to_dirent(dirent->nde_inode_entry, entry); + break; + case FS_ENOENT: + entry->name[0] = 0; + rc = 0; + break; + default: + break; + } + + k_mutex_unlock(&nffs_lock); + + return translate_error(rc); +} + +int fs_closedir(fs_dir_t *zdp) +{ + int rc; + + k_mutex_lock(&nffs_lock, K_FOREVER); + + rc = nffs_dir_close(zdp->dp); + if (!rc) { + zdp->dp = NULL; + } + + k_mutex_unlock(&nffs_lock); + + return translate_error(rc); +} + +int fs_stat(const char *path, struct fs_dirent *entry) +{ + struct nffs_path_parser parser; + struct nffs_inode_entry *parent; + struct nffs_inode_entry *inode; + int rc; + + k_mutex_lock(&nffs_lock, K_FOREVER); + + nffs_path_parser_new(&parser, path); + + rc = nffs_path_find(&parser, &inode, &parent); + if (rc == 0) { + rc = inode_to_dirent(inode, entry); + } + + k_mutex_unlock(&nffs_lock); + + return translate_error(rc); +} + +int fs_statvfs(struct fs_statvfs *stat) +{ + /* + * FIXME: + * There is not API to retrieve such data in NFFS. + */ + + return -ENOTSUP; +} + +static int fs_init(struct device *dev) +{ + struct nffs_area_desc descs[CONFIG_NFFS_FILESYSTEM_MAX_AREAS + 1]; + int cnt; + int rc; + + ARG_UNUSED(dev); + + k_mutex_init(&nffs_lock); + + flash_dev = device_get_binding(CONFIG_FS_NFFS_FLASH_DEV_NAME); + if (!flash_dev) { + return -ENODEV; + } + + flash_desc.id = 0; + flash_desc.sector_count = flash_get_page_count(flash_dev); + flash_desc.area_offset = FLASH_AREA_NFFS_OFFSET; + flash_desc.area_size = FLASH_AREA_NFFS_SIZE; + + rc = nffs_misc_reset(); + if (rc) { + return -EIO; + } + + cnt = CONFIG_NFFS_FILESYSTEM_MAX_AREAS; + rc = nffs_misc_desc_from_flash_area(&flash_desc, &cnt, descs); + if (rc) { + return -EIO; + } + + rc = nffs_restore_full(descs); + switch (rc) { + case 0: + break; + case FS_ECORRUPT: + rc = nffs_format_full(descs); + if (rc) { + return -EIO; + } + break; + default: + return -EIO; + } + + return 0; +} + +SYS_INIT(fs_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);