1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-26 23:11:19 +01:00

Merge pull request #13366 from bergzand/pr/littlefs2/add_pkg

pkg/littlefs2: Add package for LittleFS v2.x.y
This commit is contained in:
benpicco 2020-02-20 16:23:59 +01:00 committed by GitHub
commit 336bfe54a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1191 additions and 0 deletions

12
pkg/littlefs2/Makefile Normal file
View File

@ -0,0 +1,12 @@
PKG_NAME=littlefs2
PKG_URL=https://github.com/ARMmbed/littlefs.git
# v2.1.4
PKG_VERSION=ce2c01f098f4d2b9479de5a796c3bb531f1fe14c
PKG_LICENSE=Apache-2.0
include $(RIOTBASE)/pkg/pkg.mk
.PHONY: all
all:
"$(MAKE)" -C $(PKG_BUILDDIR) -f $(CURDIR)/Makefile.littlefs2

View File

@ -0,0 +1,5 @@
USEMODULE += vfs
USEMODULE += littlefs2_fs
USEMODULE += mtd
FEATURES_BLACKLIST += arch_msp430

View File

@ -0,0 +1,5 @@
INCLUDES += -I$(PKGDIRBASE)/littlefs2
ifneq (,$(filter littlefs2_fs,$(USEMODULE)))
DIRS += $(RIOTBASE)/pkg/littlefs2/fs
endif

View File

@ -0,0 +1,13 @@
MODULE := littlefs2
CFLAGS += -Wno-format
# GCC 4.9 bug (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64480)
# used by MIPS
CFLAGS += -Wno-missing-field-initializers
# Disable debug printing
ifneq ($(DEVELHELP),1)
CFLAGS += -DLFS_NO_DEBUG -DLFS_NO_WARN -DLFS_NO_ERROR
endif
include $(RIOTBASE)/Makefile.base

16
pkg/littlefs2/doc.txt Normal file
View File

@ -0,0 +1,16 @@
/**
* @defgroup pkg_littlefs2 littlefs v2.x.y file system
* @ingroup pkg
* @ingroup sys_fs
* @brief A little fail-safe filesystem designed for embedded systems,
* v2.x.y release
* @see https://github.com/ARMmbed/littlefs/
*
* ### Migrating
*
* Migrating from Littlefs 1.x.y to 2.x.y can be hard, the disk format changed
* enough to be binary incompatible. See also
* https://github.com/ARMmbed/littlefs/releases/tag/v2.0.0 for a change set
* and a possible migration path.
*
*/

View File

@ -0,0 +1,3 @@
MODULE := littlefs2_fs
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,537 @@
/*
* Copyright (C) 2017 OTA keys S.A.
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup sys_littlefs2
* @{
*
* @file
* @brief littlefs v2 integration with vfs
*
* @author Vincent Dupont <vincent@otakeys.com>
*
* @}
*/
#include <fcntl.h>
#include <errno.h>
#include <inttypes.h>
#include <string.h>
#include "fs/littlefs2_fs.h"
#include "kernel_defines.h"
#define ENABLE_DEBUG (0)
#include <debug.h>
static int littlefs_err_to_errno(ssize_t err)
{
switch (err) {
case LFS_ERR_OK:
return 0;
case LFS_ERR_IO:
return -EIO;
case LFS_ERR_CORRUPT:
return -ENODEV;
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_NOTEMPTY:
return -ENOTEMPTY;
case LFS_ERR_BADF:
return -EBADF;
case LFS_ERR_INVAL:
return -EINVAL;
case LFS_ERR_NOSPC:
return -ENOSPC;
case LFS_ERR_NOMEM:
return -ENOMEM;
default:
return err;
}
}
static int _dev_read(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size)
{
littlefs_desc_t *fs = c->context;
mtd_dev_t *mtd = fs->dev;
DEBUG("lfs_read: c=%p, block=%" PRIu32 ", off=%" PRIu32 ", buf=%p, size=%" PRIu32 "\n",
(void *)c, block, off, buffer, size);
int ret = mtd_read(mtd, buffer, ((fs->base_addr + block) * c->block_size) + off, size);
if (ret >= 0) {
return 0;
}
return ret;
}
static int _dev_write(const struct lfs_config *c, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size)
{
littlefs_desc_t *fs = c->context;
mtd_dev_t *mtd = fs->dev;
DEBUG("lfs_write: c=%p, block=%" PRIu32 ", off=%" PRIu32 ", buf=%p, size=%" PRIu32 "\n",
(void *)c, block, off, buffer, size);
const uint8_t *buf = buffer;
uint32_t addr = ((fs->base_addr + block) * c->block_size) + off;
for (const uint8_t *part = buf; part < buf + size; part += c->prog_size,
addr += c->prog_size) {
int ret = mtd_write(mtd, part, addr, c->prog_size);
if (ret < 0) {
return ret;
}
else if ((unsigned)ret != c->prog_size) {
return -EIO;
}
}
return 0;
}
static int _dev_erase(const struct lfs_config *c, lfs_block_t block)
{
littlefs_desc_t *fs = c->context;
mtd_dev_t *mtd = fs->dev;
DEBUG("lfs_erase: c=%p, block=%" PRIu32 "\n", (void *)c, block);
int ret = mtd_erase(mtd, ((fs->base_addr + block) * c->block_size), c->block_size);
if (ret >= 0) {
return 0;
}
return ret;
}
static int _dev_sync(const struct lfs_config *c)
{
(void)c;
return 0;
}
static int prepare(littlefs_desc_t *fs)
{
mutex_init(&fs->lock);
mutex_lock(&fs->lock);
memset(&fs->fs, 0, sizeof(fs->fs));
if (!fs->config.block_count) {
fs->config.block_count = fs->dev->sector_count - fs->base_addr;
}
if (!fs->config.block_size) {
fs->config.block_size = fs->dev->page_size * fs->dev->pages_per_sector;
}
if (!fs->config.prog_size) {
fs->config.prog_size = fs->dev->page_size;
}
if (!fs->config.read_size) {
fs->config.read_size = fs->dev->page_size;
}
if (!fs->config.cache_size) {
fs->config.cache_size = fs->dev->page_size * CONFIG_LITTLEFS2_CACHE_PAGES;
}
if (!fs->config.block_cycles) {
fs->config.block_cycles = CONFIG_LITTLEFS2_BLOCK_CYCLES;
}
fs->config.lookahead_size = CONFIG_LITTLEFS2_LOOKAHEAD_SIZE;
fs->config.lookahead_buffer = fs->lookahead_buf;
fs->config.context = fs;
fs->config.read = _dev_read;
fs->config.prog = _dev_write;
fs->config.erase = _dev_erase;
fs->config.sync = _dev_sync;
#if CONFIG_LITTLEFS2_FILE_BUFFER_SIZE
fs->config.file_buffer = fs->file_buf;
#endif
#if CONFIG_LITTLEFS2_READ_BUFFER_SIZE
fs->config.read_buffer = fs->read_buf;
#endif
#if CONFIG_LITTLEFS2_PROG_BUFFER_SIZE
fs->config.prog_buffer = fs->prog_buf;
#endif
return mtd_init(fs->dev);
}
static int _format(vfs_mount_t *mountp)
{
littlefs_desc_t *fs = mountp->private_data;
DEBUG("littlefs: format: mountp=%p\n", (void *)mountp);
int ret = prepare(fs);
if (ret) {
return -ENODEV;
}
ret = lfs_format(&fs->fs, &fs->config);
mutex_unlock(&fs->lock);
return littlefs_err_to_errno(ret);
}
static int _mount(vfs_mount_t *mountp)
{
/* if one of the lines below fail to compile you probably need to adjust
vfs buffer sizes ;) */
BUILD_BUG_ON(VFS_DIR_BUFFER_SIZE < sizeof(lfs_dir_t));
BUILD_BUG_ON(VFS_FILE_BUFFER_SIZE < sizeof(lfs_file_t));
littlefs_desc_t *fs = mountp->private_data;
DEBUG("littlefs: mount: mountp=%p\n", (void *)mountp);
int ret = prepare(fs);
if (ret) {
return -ENODEV;
}
ret = lfs_mount(&fs->fs, &fs->config);
mutex_unlock(&fs->lock);
return littlefs_err_to_errno(ret);
}
static int _umount(vfs_mount_t *mountp)
{
littlefs_desc_t *fs = mountp->private_data;
mutex_lock(&fs->lock);
DEBUG("littlefs: umount: mountp=%p\n", (void *)mountp);
int ret = lfs_unmount(&fs->fs);
mutex_unlock(&fs->lock);
return littlefs_err_to_errno(ret);
}
static int _unlink(vfs_mount_t *mountp, const char *name)
{
littlefs_desc_t *fs = mountp->private_data;
mutex_lock(&fs->lock);
DEBUG("littlefs: unlink: mountp=%p, name=%s\n",
(void *)mountp, name);
int ret = lfs_remove(&fs->fs, name);
mutex_unlock(&fs->lock);
return littlefs_err_to_errno(ret);
}
static int _rename(vfs_mount_t *mountp, const char *from_path, const char *to_path)
{
littlefs_desc_t *fs = mountp->private_data;
mutex_lock(&fs->lock);
DEBUG("littlefs: rename: mountp=%p, from=%s, to=%s\n",
(void *)mountp, from_path, to_path);
int ret = lfs_rename(&fs->fs, from_path, to_path);
mutex_unlock(&fs->lock);
return littlefs_err_to_errno(ret);
}
static int _mkdir(vfs_mount_t *mountp, const char *name, mode_t mode)
{
(void)mode;
littlefs_desc_t *fs = mountp->private_data;
mutex_lock(&fs->lock);
DEBUG("littlefs: mkdir: mountp=%p, name=%s, mode=%" PRIu32 "\n",
(void *)mountp, name, (uint32_t)mode);
int ret = lfs_mkdir(&fs->fs, name);
mutex_unlock(&fs->lock);
return littlefs_err_to_errno(ret);
}
static int _rmdir(vfs_mount_t *mountp, const char *name)
{
littlefs_desc_t *fs = mountp->private_data;
mutex_lock(&fs->lock);
DEBUG("littlefs: rmdir: mountp=%p, name=%s\n",
(void *)mountp, name);
int ret = lfs_remove(&fs->fs, name);
mutex_unlock(&fs->lock);
return littlefs_err_to_errno(ret);
}
static int _open(vfs_file_t *filp, const char *name, int flags, mode_t mode, const char *abs_path)
{
littlefs_desc_t *fs = filp->mp->private_data;
lfs_file_t *fp = (lfs_file_t *)&filp->private_data.buffer;
(void) abs_path;
(void) mode;
mutex_lock(&fs->lock);
DEBUG("littlefs: open: filp=%p, fp=%p\n", (void *)filp, (void *)fp);
int l_flags = 0;
if ((flags & O_ACCMODE) == O_RDONLY) {
l_flags |= LFS_O_RDONLY;
}
if ((flags & O_APPEND) == O_APPEND) {
l_flags |= LFS_O_APPEND;
}
if ((flags & O_TRUNC) == O_TRUNC) {
l_flags |= LFS_O_TRUNC;
}
if ((flags & O_CREAT) == O_CREAT) {
l_flags |= LFS_O_CREAT;
}
if ((flags & O_ACCMODE) == O_WRONLY) {
l_flags |= LFS_O_WRONLY;
}
if ((flags & O_ACCMODE) == O_RDWR) {
l_flags |= LFS_O_RDWR;
}
if ((flags & O_EXCL) == O_EXCL) {
l_flags |= LFS_O_EXCL;
}
DEBUG("littlefs: open: %s (abs_path: %s), flags: 0x%x\n", name, abs_path, (int) l_flags);
int ret = lfs_file_open(&fs->fs, fp, name, l_flags);
mutex_unlock(&fs->lock);
return littlefs_err_to_errno(ret);
}
static int _close(vfs_file_t *filp)
{
littlefs_desc_t *fs = filp->mp->private_data;
lfs_file_t *fp = (lfs_file_t *)&filp->private_data.buffer;
mutex_lock(&fs->lock);
DEBUG("littlefs: close: filp=%p, fp=%p\n", (void *)filp, (void *)fp);
int ret = lfs_file_close(&fs->fs, fp);
mutex_unlock(&fs->lock);
return littlefs_err_to_errno(ret);
}
static ssize_t _write(vfs_file_t *filp, const void *src, size_t nbytes)
{
littlefs_desc_t *fs = filp->mp->private_data;
lfs_file_t *fp = (lfs_file_t *)&filp->private_data.buffer;
mutex_lock(&fs->lock);
DEBUG("littlefs: write: filp=%p, fp=%p, src=%p, nbytes=%u\n",
(void *)filp, (void *)fp, (void *)src, (unsigned)nbytes);
ssize_t ret = lfs_file_write(&fs->fs, fp, src, nbytes);
mutex_unlock(&fs->lock);
return littlefs_err_to_errno(ret);
}
static ssize_t _read(vfs_file_t *filp, void *dest, size_t nbytes)
{
littlefs_desc_t *fs = filp->mp->private_data;
lfs_file_t *fp = (lfs_file_t *)&filp->private_data.buffer;
mutex_lock(&fs->lock);
DEBUG("littlefs: read: filp=%p, fp=%p, dest=%p, nbytes=%u\n",
(void *)filp, (void *)fp, (void *)dest, (unsigned)nbytes);
ssize_t ret = lfs_file_read(&fs->fs, fp, dest, nbytes);
mutex_unlock(&fs->lock);
return littlefs_err_to_errno(ret);
}
static off_t _lseek(vfs_file_t *filp, off_t off, int whence)
{
littlefs_desc_t *fs = filp->mp->private_data;
lfs_file_t *fp = (lfs_file_t *)&filp->private_data.buffer;
mutex_lock(&fs->lock);
DEBUG("littlefs: seek: filp=%p, fp=%p, off=%ld, whence=%d\n",
(void *)filp, (void *)fp, (long)off, whence);
int ret = lfs_file_seek(&fs->fs, fp, off, whence);
mutex_unlock(&fs->lock);
return littlefs_err_to_errno(ret);
}
static int _stat(vfs_mount_t *mountp, const char *restrict path, struct stat *restrict buf)
{
littlefs_desc_t *fs = mountp->private_data;
mutex_lock(&fs->lock);
DEBUG("littlefs: stat: mountp=%p, path=%s, buf=%p\n",
(void *)mountp, path, (void *)buf);
struct lfs_info info;
int ret = lfs_stat(&fs->fs, path, &info);
mutex_unlock(&fs->lock);
/* info.name */
buf->st_size = info.size;
switch (info.type) {
case LFS_TYPE_REG:
buf->st_mode = S_IFREG;
break;
case LFS_TYPE_DIR:
buf->st_mode = S_IFDIR;
break;
}
return littlefs_err_to_errno(ret);
}
static int _traverse_cb(void *param, lfs_block_t block)
{
(void)block;
unsigned long *nb_blocks = param;
(*nb_blocks)++;
return 0;
}
static int _statvfs(vfs_mount_t *mountp, const char *restrict path, struct statvfs *restrict buf)
{
(void)path;
littlefs_desc_t *fs = mountp->private_data;
mutex_lock(&fs->lock);
DEBUG("littlefs: statvfs: mountp=%p, path=%s, buf=%p\n",
(void *)mountp, path, (void *)buf);
unsigned long nb_blocks = 0;
int ret = lfs_fs_traverse(&fs->fs, _traverse_cb, &nb_blocks);
mutex_unlock(&fs->lock);
buf->f_bsize = fs->fs.cfg->block_size; /* block size */
buf->f_frsize = fs->fs.cfg->block_size; /* fundamental block size */
buf->f_blocks = fs->fs.cfg->block_count; /* Blocks total */
buf->f_bfree = buf->f_blocks - nb_blocks; /* Blocks free */
buf->f_bavail = buf->f_blocks - nb_blocks; /* Blocks available to non-privileged processes */
buf->f_flag = ST_NOSUID;
buf->f_namemax = LFS_NAME_MAX;
return littlefs_err_to_errno(ret);
}
static int _opendir(vfs_DIR *dirp, const char *dirname, const char *abs_path)
{
(void)abs_path;
littlefs_desc_t *fs = dirp->mp->private_data;
lfs_dir_t *dir = (lfs_dir_t *)&dirp->private_data.buffer;
mutex_lock(&fs->lock);
DEBUG("littlefs: opendir: dirp=%p, dirname=%s (abs_path=%s)\n",
(void *)dirp, dirname, abs_path);
int ret = lfs_dir_open(&fs->fs, dir, dirname);
mutex_unlock(&fs->lock);
return littlefs_err_to_errno(ret);
}
static int _readdir(vfs_DIR *dirp, vfs_dirent_t *entry)
{
littlefs_desc_t *fs = dirp->mp->private_data;
lfs_dir_t *dir = (lfs_dir_t *)&dirp->private_data.buffer;
mutex_lock(&fs->lock);
DEBUG("littlefs: readdir: dirp=%p, entry=%p\n",
(void *)dirp, (void *)entry);
struct lfs_info info;
int ret = lfs_dir_read(&fs->fs, dir, &info);
if (ret >= 0) {
entry->d_ino = info.type;
entry->d_name[0] = '/';
strncpy(entry->d_name + 1, info.name, VFS_NAME_MAX - 1);
}
mutex_unlock(&fs->lock);
return littlefs_err_to_errno(ret);
}
static int _closedir(vfs_DIR *dirp)
{
littlefs_desc_t *fs = dirp->mp->private_data;
lfs_dir_t *dir = (lfs_dir_t *)&dirp->private_data.buffer;
mutex_lock(&fs->lock);
DEBUG("littlefs: closedir: dirp=%p\n", (void *)dirp);
int ret = lfs_dir_close(&fs->fs, dir);
mutex_unlock(&fs->lock);
return littlefs_err_to_errno(ret);
}
static const vfs_file_system_ops_t littlefs_fs_ops = {
.format = _format,
.mount = _mount,
.umount = _umount,
.unlink = _unlink,
.mkdir = _mkdir,
.rmdir = _rmdir,
.rename = _rename,
.stat = _stat,
.statvfs = _statvfs,
};
static const vfs_file_ops_t littlefs_file_ops = {
.open = _open,
.close = _close,
.read = _read,
.write = _write,
.lseek = _lseek,
};
static const vfs_dir_ops_t littlefs_dir_ops = {
.opendir = _opendir,
.readdir = _readdir,
.closedir = _closedir,
};
const vfs_file_system_t littlefs2_file_system = {
.fs_op = &littlefs_fs_ops,
.f_op = &littlefs_file_ops,
.d_op = &littlefs_dir_ops,
};

View File

@ -0,0 +1,118 @@
/*
* Copyright (C) 2017 OTA keys S.A.
* Copyright (C) 2020 Inria
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup sys_littlefs2 littlefs v2 integration
* @ingroup pkg_littlefs2
* @brief RIOT integration of littlefs version 2.x.y
*
* @{
*
* @file
* @brief littlefs v2 integration with vfs
*
* @author Vincent Dupont <vincent@otakeys.com>
* @author Koen Zandberg <koen@bergzand.net>
*/
#ifndef FS_LITTLEFS2_FS_H
#define FS_LITTLEFS2_FS_H
#ifdef __cplusplus
extern "C" {
#endif
#include "vfs.h"
#include "lfs.h"
#include "mtd.h"
#include "mutex.h"
/**
* @name littlefs configuration
* @ingroup config
* @{
*/
#ifndef CONFIG_LITTLEFS2_LOOKAHEAD_SIZE
/** Default lookahead size */
#define CONFIG_LITTLEFS2_LOOKAHEAD_SIZE (16)
#endif
#ifndef CONFIG_LITTLEFS2_FILE_BUFFER_SIZE
/** File buffer size, if 0, dynamic allocation is used.
* If set, only one file can be used at a time, must be program size (mtd page
* size is used internally as program size) */
#define CONFIG_LITTLEFS2_FILE_BUFFER_SIZE (0)
#endif
#ifndef CONFIG_LITTLEFS2_READ_BUFFER_SIZE
/** Read buffer size, if 0, dynamic allocation is used.
* If set, it must be read size (mtd page size is used internally as read
* size) */
#define CONFIG_LITTLEFS2_READ_BUFFER_SIZE (0)
#endif
#ifndef CONFIG_LITTLEFS2_PROG_BUFFER_SIZE
/** Prog buffer size, if 0, dynamic allocation is used.
* If set, it must be program size */
#define CONFIG_LITTLEFS2_PROG_BUFFER_SIZE (0)
#endif
#ifndef CONFIG_LITTLEFS2_CACHE_PAGES
/** Sets the number of pages used as cache. Has to be at least 1.
*/
#define CONFIG_LITTLEFS2_CACHE_PAGES (1)
#endif
#ifndef CONFIG_LITTLEFS2_BLOCK_CYCLES
/** Sets the maximum number of erase cycles before blocks are evicted as a part
* of wear leveling. -1 disables wear-leveling. */
#define CONFIG_LITTLEFS2_BLOCK_CYCLES (512)
#endif
/** @} */
/**
* @brief littlefs descriptor for vfs integration
*/
typedef struct {
lfs_t fs; /**< littlefs descriptor */
struct lfs_config config; /**< littlefs config */
mtd_dev_t *dev; /**< mtd device to use */
mutex_t lock; /**< mutex */
/** first block number to use,
* total number of block is defined in @p config.
* if set to 0, the total number of sectors from the mtd is used */
uint32_t base_addr;
#if CONFIG_LITTLEFS2_FILE_BUFFER_SIZE || DOXYGEN
/** file buffer to use internally if CONFIG_LITTLEFS2_FILE_BUFFER_SIZE
* is set */
uint8_t file_buf[CONFIG_LITTLEFS2_FILE_BUFFER_SIZE];
#endif
#if CONFIG_LITTLEFS2_READ_BUFFER_SIZE || DOXYGEN
/** read buffer to use internally if CONFIG_LITTLEFS2_READ_BUFFER_SIZE
* is set */
uint8_t read_buf[CONFIG_LITTLEFS2_READ_BUFFER_SIZE];
#endif
#if CONFIG_LITTLEFS2_PROG_BUFFER_SIZE || DOXYGEN
/** prog buffer to use internally if CONFIG_LITTLEFS2_PROG_BUFFER_SIZE
* is set */
uint8_t prog_buf[CONFIG_LITTLEFS2_PROG_BUFFER_SIZE];
#endif
/** lookahead buffer to use internally */
uint8_t lookahead_buf[CONFIG_LITTLEFS2_LOOKAHEAD_SIZE];
} littlefs_desc_t;
/** The littlefs vfs driver */
extern const vfs_file_system_t littlefs2_file_system;
#ifdef __cplusplus
}
#endif
#endif /* FS_LITTLEFS2_FS_H */
/** @} */

View File

@ -0,0 +1,11 @@
include ../Makefile.tests_common
# Set vfs file and dir buffer sizes
CFLAGS += -DVFS_FILE_BUFFER_SIZE=84 -DVFS_DIR_BUFFER_SIZE=52
# Reduce LFS_NAME_MAX to 31 (as VFS_NAME_MAX default)
CFLAGS += -DLFS_NAME_MAX=31
USEPKG += littlefs2
USEMODULE += embunit
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,17 @@
BOARD_INSUFFICIENT_MEMORY := \
arduino-duemilanove \
arduino-leonardo \
arduino-nano \
arduino-uno \
atmega328p \
i-nucleo-lrwan1 \
nucleo-f031k6 \
nucleo-f042k6 \
nucleo-l031k6 \
nucleo-l053r8 \
nucleo-f030r8 \
stm32f030f4-demo\
stm32f0discovery \
stm32l0538-disco \
waspmote-pro \
#

436
tests/pkg_littlefs2/main.c Normal file
View File

@ -0,0 +1,436 @@
/*
* Copyright (C) 2017 OTA keys S.A.
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @{
*
* @file
*/
#include "fs/littlefs2_fs.h"
#include "vfs.h"
#include "mtd.h"
#include "board.h"
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include "embUnit.h"
/* Define MTD_0 in board.h to use the board mtd if any */
#ifdef MTD_0
#define _dev (MTD_0)
#else
/* Test mock object implementing a simple RAM-based mtd */
#ifndef SECTOR_COUNT
#define SECTOR_COUNT 16
#endif
#ifndef PAGE_PER_SECTOR
#define PAGE_PER_SECTOR 4
#endif
#ifndef PAGE_SIZE
#define PAGE_SIZE 64
#endif
static uint8_t dummy_memory[PAGE_PER_SECTOR * PAGE_SIZE * SECTOR_COUNT];
static int _init(mtd_dev_t *dev)
{
(void)dev;
return 0;
}
static int _read(mtd_dev_t *dev, void *buff, uint32_t addr, uint32_t size)
{
(void)dev;
if (addr + size > sizeof(dummy_memory)) {
return -EOVERFLOW;
}
memcpy(buff, dummy_memory + addr, size);
return size;
}
static int _write(mtd_dev_t *dev, const void *buff, uint32_t addr, uint32_t size)
{
(void)dev;
if (addr + size > sizeof(dummy_memory)) {
return -EOVERFLOW;
}
if (size > PAGE_SIZE) {
return -EOVERFLOW;
}
memcpy(dummy_memory + addr, buff, size);
return size;
}
static int _erase(mtd_dev_t *dev, uint32_t addr, uint32_t size)
{
(void)dev;
if (size % (PAGE_PER_SECTOR * PAGE_SIZE) != 0) {
return -EOVERFLOW;
}
if (addr % (PAGE_PER_SECTOR * PAGE_SIZE) != 0) {
return -EOVERFLOW;
}
if (addr + size > sizeof(dummy_memory)) {
return -EOVERFLOW;
}
memset(dummy_memory + addr, 0xff, size);
return 0;
}
static int _power(mtd_dev_t *dev, enum mtd_power_state power)
{
(void)dev;
(void)power;
return 0;
}
static const mtd_desc_t driver = {
.init = _init,
.read = _read,
.write = _write,
.erase = _erase,
.power = _power,
};
static mtd_dev_t dev = {
.driver = &driver,
.sector_count = SECTOR_COUNT,
.pages_per_sector = PAGE_PER_SECTOR,
.page_size = PAGE_SIZE,
};
static mtd_dev_t *_dev = (mtd_dev_t*) &dev;
#endif /* MTD_0 */
static littlefs_desc_t littlefs_desc;
static vfs_mount_t _test_littlefs_mount = {
.fs = &littlefs2_file_system,
.mount_point = "/test-littlefs",
.private_data = &littlefs_desc,
};
static void test_littlefs_setup(void)
{
littlefs_desc.dev = _dev;
vfs_mount(&_test_littlefs_mount);
}
static void test_littlefs_teardown(void)
{
vfs_unlink("/test-littlefs/test.txt");
vfs_unlink("/test-littlefs/test0.txt");
vfs_unlink("/test-littlefs/test1.txt");
vfs_unlink("/test-littlefs/a/test2.txt");
vfs_rmdir("/test-littlefs/a");
vfs_umount(&_test_littlefs_mount);
}
static void tests_littlefs_format(void)
{
int res;
vfs_umount(&_test_littlefs_mount);
res = mtd_erase(_dev, 0, _dev->page_size * _dev->pages_per_sector * _dev->sector_count);
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_mount(&_test_littlefs_mount);
TEST_ASSERT(res < 0);
/* 1. format an invalid file system (failed mount) */
res = vfs_format(&_test_littlefs_mount);
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_mount(&_test_littlefs_mount);
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_umount(&_test_littlefs_mount);
TEST_ASSERT_EQUAL_INT(0, res);
/* 2. format a valid file system */
res = vfs_format(&_test_littlefs_mount);
TEST_ASSERT_EQUAL_INT(0, res);
}
static void tests_littlefs_mount_umount(void)
{
int res;
res = vfs_umount(&_test_littlefs_mount);
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_mount(&_test_littlefs_mount);
TEST_ASSERT_EQUAL_INT(0, res);
}
static void tests_littlefs_open_close(void)
{
int res;
res = vfs_open("/test-littlefs/test.txt", O_CREAT | O_RDWR, 0);
TEST_ASSERT(res >= 0);
res = vfs_close(res);
TEST_ASSERT_EQUAL_INT(0, res);
}
static void tests_littlefs_write(void)
{
const char buf[] = "TESTSTRING";
char r_buf[2 * sizeof(buf)];
int res;
int fd = vfs_open("/test-littlefs/test.txt", O_CREAT | O_RDWR, 0);
TEST_ASSERT(fd >= 0);
res = vfs_write(fd, buf, sizeof(buf));
TEST_ASSERT_EQUAL_INT(sizeof(buf), res);
res = vfs_lseek(fd, 0, SEEK_SET);
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_read(fd, r_buf, sizeof(r_buf));
TEST_ASSERT_EQUAL_INT(sizeof(buf), res);
TEST_ASSERT_EQUAL_STRING(&buf[0], &r_buf[0]);
res = vfs_close(fd);
TEST_ASSERT_EQUAL_INT(0, res);
fd = vfs_open("/test-littlefs/test.txt", O_RDONLY, 0);
TEST_ASSERT(fd >= 0);
res = vfs_read(fd, r_buf, sizeof(r_buf));
TEST_ASSERT_EQUAL_INT(sizeof(buf), res);
TEST_ASSERT_EQUAL_STRING(&buf[0], &r_buf[0]);
res = vfs_close(fd);
TEST_ASSERT_EQUAL_INT(0, res);
}
static void tests_littlefs_unlink(void)
{
const char buf[] = "TESTSTRING";
int res;
int fd = vfs_open("/test-littlefs/test.txt", O_CREAT | O_RDWR, 0);
TEST_ASSERT(fd >= 0);
res = vfs_write(fd, buf, sizeof(buf));
TEST_ASSERT_EQUAL_INT(sizeof(buf), res);
res = vfs_close(fd);
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_unlink("/test-littlefs/test.txt");
TEST_ASSERT_EQUAL_INT(0, res);
}
static void tests_littlefs_readdir(void)
{
const char buf0[] = "TESTSTRING";
const char buf1[] = "TESTTESTSTRING";
const char buf2[] = "TESTSTRINGSTRING";
int res;
int fd0 = vfs_open("/test-littlefs/test0.txt", O_CREAT | O_RDWR, 0);
TEST_ASSERT(fd0 >= 0);
int fd1 = vfs_open("/test-littlefs/test1.txt", O_CREAT | O_RDWR, 0);
TEST_ASSERT(fd1 >= 0);
int fd2 = vfs_open("/test-littlefs/a/test2.txt", O_CREAT | O_RDWR, 0);
TEST_ASSERT(fd2 < 0);
res = vfs_mkdir("/test-littlefs/a", 0);
TEST_ASSERT_EQUAL_INT(0, res);
fd2 = vfs_open("/test-littlefs/a/test2.txt", O_CREAT | O_RDWR, 0);
TEST_ASSERT(fd2 >= 0);
res = vfs_write(fd0, buf0, sizeof(buf0));
TEST_ASSERT_EQUAL_INT(sizeof(buf0), res);
res = vfs_write(fd1, buf1, sizeof(buf1));
TEST_ASSERT_EQUAL_INT(sizeof(buf1), res);
res = vfs_write(fd2, buf2, sizeof(buf2));
TEST_ASSERT_EQUAL_INT(sizeof(buf2), res);
res = vfs_close(fd0);
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_close(fd1);
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_close(fd2);
TEST_ASSERT_EQUAL_INT(0, res);
vfs_DIR dirp;
res = vfs_opendir(&dirp, "/test-littlefs");
TEST_ASSERT_EQUAL_INT(0, res);
vfs_dirent_t entry;
int nb_files = 0;
do {
res = vfs_readdir(&dirp, &entry);
if (res == 1 && (strcmp("/test0.txt", &(entry.d_name[0])) == 0 ||
strcmp("/test1.txt", &(entry.d_name[0])) == 0)) {
nb_files++;
}
} while (res == 1);
TEST_ASSERT_EQUAL_INT(2, nb_files);
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_closedir(&dirp);
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_opendir(&dirp, "/test-littlefs/a");
TEST_ASSERT_EQUAL_INT(0, res);
nb_files = 0;
do {
res = vfs_readdir(&dirp, &entry);
if (res == 1 && strcmp("/test2.txt", &(entry.d_name[0])) == 0) {
nb_files++;
}
} while (res == 1);
TEST_ASSERT_EQUAL_INT(1, nb_files);
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_unlink("/test-littlefs/test0.txt");
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_unlink("/test-littlefs/test1.txt");
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_unlink("/test-littlefs/a/test2.txt");
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_closedir(&dirp);
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_rmdir("/test-littlefs/a");
TEST_ASSERT_EQUAL_INT(0, res);
}
static void tests_littlefs_rename(void)
{
const char buf[] = "TESTSTRING";
char r_buf[2 * sizeof(buf)];
int res;
int fd = vfs_open("/test-littlefs/test.txt", O_CREAT | O_RDWR, 0);
TEST_ASSERT(fd >= 0);
res = vfs_write(fd, buf, sizeof(buf));
TEST_ASSERT(res == sizeof(buf));
res = vfs_lseek(fd, 0, SEEK_SET);
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_read(fd, r_buf, sizeof(r_buf));
TEST_ASSERT(res == sizeof(buf));
TEST_ASSERT_EQUAL_STRING(&buf[0], &r_buf[0]);
res = vfs_close(fd);
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_rename("/test-littlefs/test.txt", "/test-littlefs/test1.txt");
TEST_ASSERT_EQUAL_INT(0, res);
fd = vfs_open("/test-littlefs/test.txt", O_RDONLY, 0);
TEST_ASSERT(fd < 0);
fd = vfs_open("/test-littlefs/test1.txt", O_RDONLY, 0);
TEST_ASSERT(fd >= 0);
res = vfs_lseek(fd, 0, SEEK_SET);
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_read(fd, r_buf, sizeof(r_buf));
TEST_ASSERT(res == sizeof(buf));
TEST_ASSERT_EQUAL_STRING(&buf[0], &r_buf[0]);
res = vfs_close(fd);
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_unlink("/test-littlefs/test1.txt");
TEST_ASSERT_EQUAL_INT(0, res);
}
static void tests_littlefs_statvfs(void)
{
const char buf[] = "TESTSTRING";
struct statvfs stat1;
struct statvfs stat2;
int res = vfs_statvfs("/test-littlefs/", &stat1);
TEST_ASSERT_EQUAL_INT(0, res);
TEST_ASSERT_EQUAL_INT(_dev->page_size * _dev->pages_per_sector, stat1.f_bsize);
TEST_ASSERT_EQUAL_INT(_dev->page_size * _dev->pages_per_sector, stat1.f_frsize);
TEST_ASSERT((_dev->pages_per_sector * _dev->page_size * _dev->sector_count) >=
stat1.f_blocks);
int fd = vfs_open("/test-littlefs/test.txt", O_CREAT | O_RDWR, 0);
TEST_ASSERT(fd >= 0);
for (int i = 0; i < 128; ++i) {
res = vfs_write(fd, buf, sizeof(buf));
TEST_ASSERT(res == sizeof(buf));
}
res = vfs_close(fd);
TEST_ASSERT_EQUAL_INT(0, res);
res = vfs_statvfs("/test-littlefs/", &stat2);
TEST_ASSERT_EQUAL_INT(0, res);
TEST_ASSERT_EQUAL_INT(_dev->page_size * _dev->pages_per_sector, stat2.f_bsize);
TEST_ASSERT_EQUAL_INT(_dev->page_size * _dev->pages_per_sector, stat2.f_frsize);
TEST_ASSERT(stat1.f_bfree > stat2.f_bfree);
TEST_ASSERT(stat1.f_bavail > stat2.f_bavail);
}
Test *tests_littlefs(void)
{
#ifndef MTD_0
memset(dummy_memory, 0xff, sizeof(dummy_memory));
#endif
EMB_UNIT_TESTFIXTURES(fixtures) {
new_TestFixture(tests_littlefs_format),
new_TestFixture(tests_littlefs_mount_umount),
new_TestFixture(tests_littlefs_open_close),
new_TestFixture(tests_littlefs_write),
new_TestFixture(tests_littlefs_unlink),
new_TestFixture(tests_littlefs_readdir),
new_TestFixture(tests_littlefs_rename),
new_TestFixture(tests_littlefs_statvfs),
};
EMB_UNIT_TESTCALLER(littlefs_tests, test_littlefs_setup, test_littlefs_teardown, fixtures);
return (Test *)&littlefs_tests;
}
int main(void)
{
TESTS_START();
TESTS_RUN(tests_littlefs());
TESTS_END();
return 0;
}
/** @} */

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# Copyright (C) 2017 Freie Universität Berlin
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
import sys
from testrunner import run
def testfunc(child):
child.expect(r'OK \(\d+ tests\)')
if __name__ == "__main__":
sys.exit(run(testfunc))