Nicolas Borla
/
BBR_1Ebene
BBR 1 Ebene
Diff: mbed-os/features/filesystem/littlefs/LittleFileSystem.cpp
- Revision:
- 0:fbdae7e6d805
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed-os/features/filesystem/littlefs/LittleFileSystem.cpp Mon May 14 11:29:06 2018 +0000 @@ -0,0 +1,540 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mbed.h" +#include "LittleFileSystem.h" +#include "errno.h" +extern "C" { +#include "lfs.h" +#include "lfs_util.h" +} + + +////// Conversion functions ////// +static int lfs_toerror(int err) +{ + switch (err) { + case LFS_ERR_OK: return 0; + case LFS_ERR_IO: return -EIO; + case LFS_ERR_NOENT: return -ENOENT; + case LFS_ERR_EXIST: return -EEXIST; + case LFS_ERR_NOTDIR: return -ENOTDIR; + case LFS_ERR_ISDIR: return -EISDIR; + case LFS_ERR_INVAL: return -EINVAL; + case LFS_ERR_NOSPC: return -ENOSPC; + case LFS_ERR_NOMEM: return -ENOMEM; + default: return err; + } +} + +static int lfs_fromflags(int flags) +{ + return ( + (((flags & 3) == O_RDONLY) ? LFS_O_RDONLY : 0) | + (((flags & 3) == O_WRONLY) ? LFS_O_WRONLY : 0) | + (((flags & 3) == O_RDWR) ? LFS_O_RDWR : 0) | + ((flags & O_CREAT) ? LFS_O_CREAT : 0) | + ((flags & O_EXCL) ? LFS_O_EXCL : 0) | + ((flags & O_TRUNC) ? LFS_O_TRUNC : 0) | + ((flags & O_APPEND) ? LFS_O_APPEND : 0)); +} + +static int lfs_fromwhence(int whence) +{ + switch (whence) { + case SEEK_SET: return LFS_SEEK_SET; + case SEEK_CUR: return LFS_SEEK_CUR; + case SEEK_END: return LFS_SEEK_END; + default: return whence; + } +} + +static int lfs_tomode(int type) +{ + int mode = S_IRWXU | S_IRWXG | S_IRWXO; + switch (type) { + case LFS_TYPE_DIR: return mode | S_IFDIR; + case LFS_TYPE_REG: return mode | S_IFREG; + default: return 0; + } +} + +static int lfs_totype(int type) +{ + switch (type) { + case LFS_TYPE_DIR: return DT_DIR; + case LFS_TYPE_REG: return DT_REG; + default: return DT_UNKNOWN; + } +} + + +////// Block device operations ////// +static int lfs_bd_read(const struct lfs_config *c, lfs_block_t block, + lfs_off_t off, void *buffer, lfs_size_t size) { + BlockDevice *bd = (BlockDevice *)c->context; + return bd->read(buffer, (bd_addr_t)block*c->block_size + off, size); +} + +static int lfs_bd_prog(const struct lfs_config *c, lfs_block_t block, + lfs_off_t off, const void *buffer, lfs_size_t size) { + BlockDevice *bd = (BlockDevice *)c->context; + return bd->program(buffer, (bd_addr_t)block*c->block_size + off, size); +} + +static int lfs_bd_erase(const struct lfs_config *c, lfs_block_t block) +{ + BlockDevice *bd = (BlockDevice *)c->context; + return bd->erase((bd_addr_t)block*c->block_size, c->block_size); +} + +static int lfs_bd_sync(const struct lfs_config *c) +{ + BlockDevice *bd = (BlockDevice *)c->context; + return bd->sync(); +} + + +////// Generic filesystem operations ////// + +// Filesystem implementation (See LittleFileSystem.h) +LittleFileSystem::LittleFileSystem(const char *name, BlockDevice *bd, + lfs_size_t read_size, lfs_size_t prog_size, + lfs_size_t block_size, lfs_size_t lookahead) + : FileSystem(name) + , _read_size(read_size) + , _prog_size(prog_size) + , _block_size(block_size) + , _lookahead(lookahead) { + if (bd) { + mount(bd); + } +} + +LittleFileSystem::~LittleFileSystem() { + // nop if unmounted + unmount(); +} + +int LittleFileSystem::mount(BlockDevice *bd) +{ + _mutex.lock(); + LFS_INFO("mount(%p)", bd); + _bd = bd; + int err = _bd->init(); + if (err) { + LFS_INFO("mount -> %d", err); + _mutex.unlock(); + return err; + } + + memset(&_config, 0, sizeof(_config)); + _config.context = bd; + _config.read = lfs_bd_read; + _config.prog = lfs_bd_prog; + _config.erase = lfs_bd_erase; + _config.sync = lfs_bd_sync; + _config.read_size = bd->get_read_size(); + if (_config.read_size < _read_size) { + _config.read_size = _read_size; + } + _config.prog_size = bd->get_program_size(); + if (_config.prog_size < _prog_size) { + _config.prog_size = _prog_size; + } + _config.block_size = bd->get_erase_size(); + if (_config.block_size < _block_size) { + _config.block_size = _block_size; + } + _config.block_count = bd->size() / _config.block_size; + _config.lookahead = 32 * ((_config.block_count+31)/32); + if (_config.lookahead > _lookahead) { + _config.lookahead = _lookahead; + } + + err = lfs_mount(&_lfs, &_config); + LFS_INFO("mount -> %d", lfs_toerror(err)); + _mutex.unlock(); + return lfs_toerror(err); +} + +int LittleFileSystem::unmount() +{ + _mutex.lock(); + LFS_INFO("unmount(%s)", ""); + if (_bd) { + int err = lfs_unmount(&_lfs); + if (err) { + LFS_INFO("unmount -> %d", lfs_toerror(err)); + _mutex.unlock(); + return lfs_toerror(err); + } + + err = _bd->deinit(); + if (err) { + LFS_INFO("unmount -> %d", err); + _mutex.unlock(); + return err; + } + + _bd = NULL; + } + + LFS_INFO("unmount -> %d", 0); + _mutex.unlock(); + return 0; +} + +int LittleFileSystem::format(BlockDevice *bd, + lfs_size_t read_size, lfs_size_t prog_size, + lfs_size_t block_size, lfs_size_t lookahead) { + LFS_INFO("format(%p, %ld, %ld, %ld, %ld)", + bd, read_size, prog_size, block_size, lookahead); + int err = bd->init(); + if (err) { + LFS_INFO("format -> %d", err); + return err; + } + + lfs_t _lfs; + struct lfs_config _config; + + memset(&_config, 0, sizeof(_config)); + _config.context = bd; + _config.read = lfs_bd_read; + _config.prog = lfs_bd_prog; + _config.erase = lfs_bd_erase; + _config.sync = lfs_bd_sync; + _config.read_size = bd->get_read_size(); + if (_config.read_size < read_size) { + _config.read_size = read_size; + } + _config.prog_size = bd->get_program_size(); + if (_config.prog_size < prog_size) { + _config.prog_size = prog_size; + } + _config.block_size = bd->get_erase_size(); + if (_config.block_size < block_size) { + _config.block_size = block_size; + } + _config.block_count = bd->size() / _config.block_size; + _config.lookahead = 32 * ((_config.block_count+31)/32); + if (_config.lookahead > lookahead) { + _config.lookahead = lookahead; + } + + err = lfs_format(&_lfs, &_config); + if (err) { + LFS_INFO("format -> %d", lfs_toerror(err)); + return lfs_toerror(err); + } + + err = bd->deinit(); + if (err) { + LFS_INFO("format -> %d", err); + return err; + } + + LFS_INFO("format -> %d", 0); + return 0; +} + +int LittleFileSystem::reformat(BlockDevice *bd) +{ + _mutex.lock(); + LFS_INFO("reformat(%p)", bd); + if (_bd) { + if (!bd) { + bd = _bd; + } + + int err = unmount(); + if (err) { + LFS_INFO("reformat -> %d", err); + _mutex.unlock(); + return err; + } + } + + if (!bd) { + LFS_INFO("reformat -> %d", -ENODEV); + _mutex.unlock(); + return -ENODEV; + } + + int err = LittleFileSystem::format(bd, + _read_size, _prog_size, _block_size, _lookahead); + if (err) { + LFS_INFO("reformat -> %d", err); + _mutex.unlock(); + return err; + } + + err = mount(bd); + if (err) { + LFS_INFO("reformat -> %d", err); + _mutex.unlock(); + return err; + } + + LFS_INFO("reformat -> %d", 0); + _mutex.unlock(); + return 0; +} + +int LittleFileSystem::remove(const char *filename) +{ + _mutex.lock(); + LFS_INFO("remove(\"%s\")", filename); + int err = lfs_remove(&_lfs, filename); + LFS_INFO("remove -> %d", lfs_toerror(err)); + _mutex.unlock(); + return lfs_toerror(err); +} + +int LittleFileSystem::rename(const char *oldname, const char *newname) +{ + _mutex.lock(); + LFS_INFO("rename(\"%s\", \"%s\")", oldname, newname); + int err = lfs_rename(&_lfs, oldname, newname); + LFS_INFO("rename -> %d", lfs_toerror(err)); + _mutex.unlock(); + return lfs_toerror(err); +} + +int LittleFileSystem::mkdir(const char *name, mode_t mode) +{ + _mutex.lock(); + LFS_INFO("mkdir(\"%s\", 0x%lx)", name, mode); + int err = lfs_mkdir(&_lfs, name); + LFS_INFO("mkdir -> %d", lfs_toerror(err)); + _mutex.unlock(); + return lfs_toerror(err); +} + +int LittleFileSystem::stat(const char *name, struct stat *st) +{ + struct lfs_info info; + _mutex.lock(); + LFS_INFO("stat(\"%s\", %p)", name, st); + int err = lfs_stat(&_lfs, name, &info); + LFS_INFO("stat -> %d", lfs_toerror(err)); + _mutex.unlock(); + st->st_size = info.size; + st->st_mode = lfs_tomode(info.type); + return lfs_toerror(err); +} + +static int lfs_statvfs_count(void *p, lfs_block_t b) +{ + *(lfs_size_t *)p += 1; + return 0; +} + +int LittleFileSystem::statvfs(const char *name, struct statvfs *st) +{ + memset(st, 0, sizeof(struct statvfs)); + + lfs_size_t in_use = 0; + _mutex.lock(); + LFS_INFO("statvfs(\"%s\", %p)", name, st); + int err = lfs_traverse(&_lfs, lfs_statvfs_count, &in_use); + LFS_INFO("statvfs -> %d", lfs_toerror(err)); + _mutex.unlock(); + if (err) { + return err; + } + + st->f_bsize = _config.block_size; + st->f_frsize = _config.block_size; + st->f_blocks = _config.block_count; + st->f_bfree = _config.block_count - in_use; + st->f_bavail = _config.block_count - in_use; + st->f_namemax = LFS_NAME_MAX; + return 0; +} + +////// File operations ////// +int LittleFileSystem::file_open(fs_file_t *file, const char *path, int flags) +{ + lfs_file_t *f = new lfs_file_t; + _mutex.lock(); + LFS_INFO("file_open(%p, \"%s\", 0x%x)", *file, path, flags); + int err = lfs_file_open(&_lfs, f, path, lfs_fromflags(flags)); + LFS_INFO("file_open -> %d", lfs_toerror(err)); + _mutex.unlock(); + if (!err) { + *file = f; + } else { + delete f; + } + return lfs_toerror(err); +} + +int LittleFileSystem::file_close(fs_file_t file) +{ + lfs_file_t *f = (lfs_file_t *)file; + _mutex.lock(); + LFS_INFO("file_close(%p)", file); + int err = lfs_file_close(&_lfs, f); + LFS_INFO("file_close -> %d", lfs_toerror(err)); + _mutex.unlock(); + delete f; + return lfs_toerror(err); +} + +ssize_t LittleFileSystem::file_read(fs_file_t file, void *buffer, size_t len) +{ + lfs_file_t *f = (lfs_file_t *)file; + _mutex.lock(); + LFS_INFO("file_read(%p, %p, %d)", file, buffer, len); + lfs_ssize_t res = lfs_file_read(&_lfs, f, buffer, len); + LFS_INFO("file_read -> %d", lfs_toerror(res)); + _mutex.unlock(); + return lfs_toerror(res); +} + +ssize_t LittleFileSystem::file_write(fs_file_t file, const void *buffer, size_t len) +{ + lfs_file_t *f = (lfs_file_t *)file; + _mutex.lock(); + LFS_INFO("file_write(%p, %p, %d)", file, buffer, len); + lfs_ssize_t res = lfs_file_write(&_lfs, f, buffer, len); + LFS_INFO("file_write -> %d", lfs_toerror(res)); + _mutex.unlock(); + return lfs_toerror(res); +} + +int LittleFileSystem::file_sync(fs_file_t file) +{ + lfs_file_t *f = (lfs_file_t *)file; + _mutex.lock(); + LFS_INFO("file_sync(%p)", file); + int err = lfs_file_sync(&_lfs, f); + LFS_INFO("file_sync -> %d", lfs_toerror(err)); + _mutex.unlock(); + return lfs_toerror(err); +} + +off_t LittleFileSystem::file_seek(fs_file_t file, off_t offset, int whence) +{ + lfs_file_t *f = (lfs_file_t *)file; + _mutex.lock(); + LFS_INFO("file_seek(%p, %ld, %d)", file, offset, whence); + off_t res = lfs_file_seek(&_lfs, f, offset, lfs_fromwhence(whence)); + LFS_INFO("file_seek -> %d", lfs_toerror(res)); + _mutex.unlock(); + return lfs_toerror(res); +} + +off_t LittleFileSystem::file_tell(fs_file_t file) +{ + lfs_file_t *f = (lfs_file_t *)file; + _mutex.lock(); + LFS_INFO("file_tell(%p)", file); + off_t res = lfs_file_tell(&_lfs, f); + LFS_INFO("file_tell -> %d", lfs_toerror(res)); + _mutex.unlock(); + return lfs_toerror(res); +} + +off_t LittleFileSystem::file_size(fs_file_t file) +{ + lfs_file_t *f = (lfs_file_t *)file; + _mutex.lock(); + LFS_INFO("file_size(%p)", file); + off_t res = lfs_file_size(&_lfs, f); + LFS_INFO("file_size -> %d", lfs_toerror(res)); + _mutex.unlock(); + return lfs_toerror(res); +} + + +////// Dir operations ////// +int LittleFileSystem::dir_open(fs_dir_t *dir, const char *path) +{ + lfs_dir_t *d = new lfs_dir_t; + _mutex.lock(); + LFS_INFO("dir_open(%p, \"%s\")", *dir, path); + int err = lfs_dir_open(&_lfs, d, path); + LFS_INFO("dir_open -> %d", lfs_toerror(err)); + _mutex.unlock(); + if (!err) { + *dir = d; + } else { + delete d; + } + return lfs_toerror(err); +} + +int LittleFileSystem::dir_close(fs_dir_t dir) +{ + lfs_dir_t *d = (lfs_dir_t *)dir; + _mutex.lock(); + LFS_INFO("dir_close(%p)", dir); + int err = lfs_dir_close(&_lfs, d); + LFS_INFO("dir_close -> %d", lfs_toerror(err)); + _mutex.unlock(); + delete d; + return lfs_toerror(err); +} + +ssize_t LittleFileSystem::dir_read(fs_dir_t dir, struct dirent *ent) +{ + lfs_dir_t *d = (lfs_dir_t *)dir; + struct lfs_info info; + _mutex.lock(); + LFS_INFO("dir_read(%p, %p)", dir, ent); + int res = lfs_dir_read(&_lfs, d, &info); + LFS_INFO("dir_read -> %d", lfs_toerror(res)); + _mutex.unlock(); + if (res == 1) { + ent->d_type = lfs_totype(info.type); + strcpy(ent->d_name, info.name); + } + return lfs_toerror(res); +} + +void LittleFileSystem::dir_seek(fs_dir_t dir, off_t offset) +{ + lfs_dir_t *d = (lfs_dir_t *)dir; + _mutex.lock(); + LFS_INFO("dir_seek(%p, %ld)", dir, offset); + lfs_dir_seek(&_lfs, d, offset); + LFS_INFO("dir_seek -> %s", "void"); + _mutex.unlock(); +} + +off_t LittleFileSystem::dir_tell(fs_dir_t dir) +{ + lfs_dir_t *d = (lfs_dir_t *)dir; + _mutex.lock(); + LFS_INFO("dir_tell(%p)", dir); + lfs_soff_t res = lfs_dir_tell(&_lfs, d); + LFS_INFO("dir_tell -> %d", lfs_toerror(res)); + _mutex.unlock(); + return lfs_toerror(res); +} + +void LittleFileSystem::dir_rewind(fs_dir_t dir) +{ + lfs_dir_t *d = (lfs_dir_t *)dir; + _mutex.lock(); + LFS_INFO("dir_rewind(%p)", dir); + lfs_dir_rewind(&_lfs, d); + LFS_INFO("dir_rewind -> %s", "void"); + _mutex.unlock(); +} +