539 lines
9.9 KiB
C
539 lines
9.9 KiB
C
/*
|
|
* Copyright (c) 2017 Codecoup
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <zephyr/types.h>
|
|
#include <errno.h>
|
|
#include <init.h>
|
|
#include <flash.h>
|
|
#include <fs.h>
|
|
#include <crc16.h>
|
|
#include <misc/__assert.h>
|
|
#include <misc/printk.h>
|
|
#include <nffs/nffs.h>
|
|
#include <nffs/os.h>
|
|
|
|
/*
|
|
* 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);
|