1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

pkg/fatfs: add vfs integration

This commit is contained in:
MichelRottleuthner 2017-05-12 17:46:09 +02:00
parent 7c166d9e1a
commit ee17dae5af
15 changed files with 1016 additions and 1 deletions

View File

@ -668,6 +668,20 @@ ifneq (,$(filter ccn-lite,$(USEPKG)))
USEMODULE += ccn-lite-utils
endif
ifneq (,$(filter fatfs_vfs,$(USEMODULE)))
USEPKG += fatfs
USEMODULE += vfs
ifeq ($(BOARD),native)
USEMODULE += fatfs_diskio_native
FATFS_DISKIO_NATIVE_DEFAULT_FILE ?= \"riot_fatfs_disk.img\"
else
USEMODULE += fatfs_diskio_sdcard_spi
USEMODULE += auto_init_storage
endif
endif
# always select gpio (until explicit dependencies are sorted out)
FEATURES_OPTIONAL += periph_gpio

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2014 Freie Universität Berlin
* 2017 HAW-Hamburg
*
* 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
@ -14,6 +15,7 @@
* @brief Common macros and compiler attributes/pragmas configuration
*
* @author René Kijewski <rene.kijewski@fu-berlin.de>
* @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
*/
#ifndef KERNEL_DEFINES_H
@ -114,6 +116,18 @@
*/
#define ALIGN_OF(T) (offsetof(struct { char c; T t; }, t))
/**
* @def BUILD_BUG_ON(condition)
* @brief Forces a compilation error if condition is true.
* This trick is only needed if the condition can't be evaluated
* before compile time (i.e. sizeof(sometype_t) < 42 )
* For more details on this see for example:
* https://git.kernel.org/pub/scm/linux/kernel/git/stable/
* linux-stable.git/tree/include/linux/bug.h
* @param[in] condition A condition that will be evaluated at compile time
*/
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2 * !!(condition)]))
#ifdef __cplusplus
}
#endif

View File

@ -37,7 +37,7 @@ static int _init(mtd_dev_t *dev)
FILE *f = real_fopen(_dev->fname, "r");
if (!f) {
DEBUG("mtd_native: init: creating file %s\n", name);
DEBUG("mtd_native: init: creating file %s\n", _dev->fname);
f = real_fopen(_dev->fname, "w+");
if (!f) {
return -EIO;

View File

@ -12,6 +12,10 @@ ifneq (,$(filter fatfs_diskio_sdcard_spi,$(USEMODULE)))
DIRS += $(RIOTBASE)/pkg/fatfs/fatfs_diskio/sdcard_spi
endif
ifneq (,$(filter fatfs_vfs,$(USEMODULE)))
DIRS += $(RIOTBASE)/pkg/fatfs/fatfs_vfs
endif
ifeq ($(shell uname -s),Darwin)
CFLAGS += -Wno-empty-body
endif

View File

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

View File

@ -0,0 +1,467 @@
/*
* Copyright (C) 2017 HAW-Hamburg
*
* 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 fs
* @{
*
* @file
* @brief FatFs wrapper for vfs
*
* @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
*
* @}
*/
#include <fcntl.h>
#include <errno.h>
#include <inttypes.h>
#include <sys/stat.h> /* for struct stat */
#include <string.h>
#include "fs/fatfs.h"
#include "kernel_defines.h" /* needed for BUILD_BUG_ON */
#include "time.h"
#define ENABLE_DEBUG (0)
#include <debug.h>
static int fatfs_err_to_errno(int32_t err);
static void _fatfs_time_to_timespec(WORD fdate, WORD ftime, time_t *time);
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(DIR));
BUILD_BUG_ON(VFS_FILE_BUFFER_SIZE < sizeof(fatfs_file_desc_t));
fatfs_desc_t *fs_desc = (fatfs_desc_t *)mountp->private_data;
char volume_str[FATFS_MAX_VOL_STR_LEN];
snprintf(volume_str, sizeof(volume_str), "%d:/", fs_desc->vol_idx);
memset(&fs_desc->fat_fs, 0, sizeof(fs_desc->fat_fs));
DEBUG("mounting file system of volume '%s'\n", volume_str);
FRESULT res = f_mount(&fs_desc->fat_fs, volume_str, 1);
if (res == FR_OK) {
DEBUG("[OK]");
}
else {
DEBUG("[ERROR]");
}
return fatfs_err_to_errno(res);
}
static int _umount(vfs_mount_t *mountp)
{
fatfs_desc_t *fs_desc = mountp->private_data;
DEBUG("fatfs_vfs.c: _umount: private_data = %p\n", mountp->private_data);
char volume_str[FATFS_MAX_VOL_STR_LEN];
snprintf(volume_str, sizeof(volume_str), "%d:/", fs_desc->vol_idx);
DEBUG("unmounting file system of volume '%s'\n", volume_str);
FRESULT res = f_mount(NULL, volume_str, 1);
if (res == FR_OK) {
DEBUG("[OK]");
memset(&fs_desc->fat_fs, 0, sizeof(fs_desc->fat_fs));
}
else {
DEBUG("[ERROR]");
}
return fatfs_err_to_errno(res);
}
static int _unlink(vfs_mount_t *mountp, const char *name)
{
char fatfs_abs_path[FATFS_MAX_VOL_STR_LEN + VFS_NAME_MAX + 1];
fatfs_desc_t *fs_desc = (fatfs_desc_t *)mountp->private_data;
snprintf(fatfs_abs_path, sizeof(fatfs_abs_path), "%d:/%s",
fs_desc->vol_idx, name);
return fatfs_err_to_errno(f_unlink(fatfs_abs_path));
}
static int _rename(vfs_mount_t *mountp, const char *from_path,
const char *to_path)
{
char fatfs_abs_path_f[FATFS_MAX_VOL_STR_LEN + VFS_NAME_MAX + 1];
char fatfs_abs_path_t[FATFS_MAX_VOL_STR_LEN + VFS_NAME_MAX + 1];
fatfs_desc_t *fs_desc = (fatfs_desc_t *)mountp->private_data;
snprintf(fatfs_abs_path_f, sizeof(fatfs_abs_path_f), "%d:/%s",
fs_desc->vol_idx, from_path);
snprintf(fatfs_abs_path_t, sizeof(fatfs_abs_path_t), "%d:/%s",
fs_desc->vol_idx, to_path);
return fatfs_err_to_errno(f_rename(fatfs_abs_path_f, fatfs_abs_path_t));
}
static int _open(vfs_file_t *filp, const char *name, int flags, mode_t mode,
const char *abs_path)
{
fatfs_file_desc_t *fd = (fatfs_file_desc_t *)&filp->private_data.buffer[0];
char fatfs_abs_path[FATFS_MAX_VOL_STR_LEN + VFS_NAME_MAX + 1];
fatfs_desc_t *fs_desc = (fatfs_desc_t *)filp->mp->private_data;
snprintf(fatfs_abs_path, sizeof(fatfs_abs_path), "%d:/%s", fs_desc->vol_idx,
name);
(void) abs_path;
(void) mode; /* fatfs can't use mode param with f_open*/
DEBUG("fatfs_vfs.c: _open: private_data = %p, name = %s; flags = 0x%x\n",
filp->mp->private_data, name, flags);
strncpy(fd->fname, fatfs_abs_path, VFS_NAME_MAX);
uint8_t fatfs_flags = 0;
if ((flags & O_ACCMODE) == O_RDONLY) {
fatfs_flags |= FA_READ;
}
if ((flags & O_ACCMODE) == O_WRONLY) {
fatfs_flags |= FA_WRITE;
}
if ((flags & O_ACCMODE) == O_RDWR) {
fatfs_flags |= (FA_READ | FA_WRITE);
}
if ((flags & O_APPEND) == O_APPEND) {
fatfs_flags |= FA_OPEN_APPEND;
}
if ((flags & O_TRUNC) == O_TRUNC) {
fatfs_flags |= FA_CREATE_ALWAYS;
}
if ((flags & O_CREAT) == O_CREAT) {
fatfs_flags |= FA_CREATE_NEW;
}
else {
fatfs_flags |= FA_OPEN_EXISTING;
}
FRESULT open_resu = f_open(&fd->file, fatfs_abs_path, fatfs_flags);
if (open_resu == FR_OK) {
DEBUG("[OK]");
}
else {
DEBUG("[ERROR]");
}
DEBUG("fatfs_vfs.c _open: returning fatfserr=%d; errno=%d\n", open_resu,
fatfs_err_to_errno(open_resu));
return fatfs_err_to_errno(open_resu);
}
static int _close(vfs_file_t *filp)
{
fatfs_file_desc_t *fd = (fatfs_file_desc_t *)filp->private_data.buffer;
DEBUG("fatfs_vfs.c: _close: private_data = %p\n", filp->mp->private_data);
FRESULT res = f_close(&fd->file);
if (res == FR_OK) {
DEBUG("[OK]");
}
else {
DEBUG("[FAILED] (f_close error %d)\n", res);
}
return fatfs_err_to_errno(res);
}
static ssize_t _write(vfs_file_t *filp, const void *src, size_t nbytes)
{
fatfs_file_desc_t *fd = (fatfs_file_desc_t *)filp->private_data.buffer;
UINT bw;
FRESULT res = f_write(&fd->file, src, nbytes, &bw);
if (res != FR_OK) {
return fatfs_err_to_errno(res);
}
return (ssize_t)bw;
}
static ssize_t _read(vfs_file_t *filp, void *dest, size_t nbytes)
{
fatfs_file_desc_t *fd = (fatfs_file_desc_t *)filp->private_data.buffer;
UINT br;
FRESULT res = f_read(&fd->file, dest, nbytes, &br);
if (res != FR_OK) {
return fatfs_err_to_errno(res);
}
return (ssize_t)br;
}
static off_t _lseek(vfs_file_t *filp, off_t off, int whence)
{
fatfs_file_desc_t *fd = (fatfs_file_desc_t *)filp->private_data.buffer;
FRESULT res;
off_t new_pos = 0;
if (whence == SEEK_SET) {
new_pos = off;
}
else if (whence == SEEK_CUR) {
new_pos = f_tell(&fd->file) + off;
}
else if (whence == SEEK_END) {
new_pos = f_size(&fd->file) + off;
}
else {
return fatfs_err_to_errno(FR_INVALID_PARAMETER);
}
res = f_lseek(&fd->file, new_pos);
if (res == FR_OK) {
return new_pos;
}
return fatfs_err_to_errno(res);
}
static int _fstat(vfs_file_t *filp, struct stat *buf)
{
fatfs_file_desc_t *fd = (fatfs_file_desc_t *)filp->private_data.buffer;
char fatfs_abs_path[FATFS_MAX_VOL_STR_LEN + VFS_NAME_MAX + 1];
fatfs_desc_t *fs_desc = (fatfs_desc_t *)filp->mp->private_data;
FILINFO fi;
FRESULT res;
snprintf(fatfs_abs_path, sizeof(fatfs_abs_path), "%d:/%s", fs_desc->vol_idx,
fd->fname);
memset(buf, 0, sizeof(*buf));
res = f_stat(fatfs_abs_path, &fi);
if (res != FR_OK) {
return fatfs_err_to_errno(res);
}
buf->st_size = fi.fsize;
/* set last modification timestamp */
#ifdef SYS_STAT_H_
_fatfs_time_to_timespec(fi.fdate, fi.ftime, &(buf->st_mtim.tv_sec));
#else
_fatfs_time_to_timespec(fi.fdate, fi.ftime, &(buf->st_mtime));
#endif
if (fi.fattrib & AM_DIR) {
buf->st_mode = S_IFDIR; /**< it's a directory */
}
else {
buf->st_mode = S_IFREG; /**< it's a regular file */
}
/** always grant read access */
buf->st_mode |= (S_IRUSR | S_IRGRP | S_IROTH);
if (fi.fattrib & AM_RDO) {
/** grant write access if file isn't RO*/
buf->st_mode ^= (S_IWUSR | S_IWGRP | S_IWOTH);
}
return fatfs_err_to_errno(res);
}
static int _opendir(vfs_DIR *dirp, const char *dirname, const char *abs_path)
{
DIR *dir = (DIR *)&dirp->private_data.buffer;
char fatfs_abs_path[FATFS_MAX_VOL_STR_LEN + VFS_NAME_MAX + 1];
fatfs_desc_t *fs_desc = (fatfs_desc_t *)dirp->mp->private_data;
(void) abs_path;
snprintf(fatfs_abs_path, sizeof(fatfs_abs_path), "%d:/%s", fs_desc->vol_idx,
dirname);
return fatfs_err_to_errno(f_opendir(dir, fatfs_abs_path));
}
static int _readdir(vfs_DIR *dirp, vfs_dirent_t *entry)
{
DIR *dir = (DIR *)&dirp->private_data.buffer[0];
FILINFO fi;
FRESULT res = f_readdir(dir, &fi);
if (res == FR_OK) {
if (fi.fname[0] == 0) {
return 0; /**< end of dir reached */
}
else {
entry->d_ino = 0; //TODO: set this properly
strncpy(entry->d_name, fi.fname, VFS_NAME_MAX);
return 1;
}
}
return fatfs_err_to_errno(res);
}
static int _closedir(vfs_DIR *dirp)
{
DIR *dir = (DIR *)&dirp->private_data.buffer[0];
return fatfs_err_to_errno(f_closedir(dir));
}
static int _mkdir (vfs_mount_t *mountp, const char *name, mode_t mode)
{
char fatfs_abs_path[FATFS_MAX_VOL_STR_LEN + VFS_NAME_MAX + 1];
fatfs_desc_t *fs_desc = (fatfs_desc_t *)mountp->private_data;
(void) mode;
snprintf(fatfs_abs_path, sizeof(fatfs_abs_path), "%d:/%s", fs_desc->vol_idx,
name);
return fatfs_err_to_errno(f_mkdir(fatfs_abs_path));
}
static int _rmdir (vfs_mount_t *mountp, const char *name)
{
char fatfs_abs_path[FATFS_MAX_VOL_STR_LEN + VFS_NAME_MAX + 1];
fatfs_desc_t *fs_desc = (fatfs_desc_t *)mountp->private_data;
snprintf(fatfs_abs_path, sizeof(fatfs_abs_path), "%d:/%s", fs_desc->vol_idx,
name);
return fatfs_err_to_errno(f_unlink(fatfs_abs_path));
}
static void _fatfs_time_to_timespec(WORD fdate, WORD ftime, time_t *time)
{
const uint16_t days_for_month[12] = { 0, 31, 59, 90, 120, 151, 181, 212,
243, 273, 304, 334 };
int year = ((fdate >> 9) & 0x7F) + FATFS_YEAR_OFFSET - EPOCH_YEAR_OFFSET;
int month = ((fdate >> 5) & 0x0F) - 1; /**< 0..11 */
int day = (fdate & 0x1F) - 1; /**< 0..31 */
int hour = (ftime >> 11) & 0x1F; /**< 0..23 */
int min = (ftime >> 5) & 0x3F; /**< 0..59 */
int sec = (ftime & 0x1F) * 2; /**< 0..58 */
/* leap years since 1970 */
int leap_years = ((year - 1 - EPOCH_YEAR_OFFSET - 2) / 4) -
((year - 1 - 1900) / 100) +
((year - 1 - 1600) / 400);
long days_since_epoch = (year - EPOCH_YEAR_OFFSET) * 365 +
days_for_month[month] +
leap_years +
day;
if ((month > 2) &&
((year % 4 == 0) &&
((year % 100 != 0) || (year % 400 == 0)))) {
days_since_epoch++; /* add one day if this is a leap year */
}
*time = ((((days_since_epoch * 24) + hour) * 60) + min) * 60 + sec;
}
static int fatfs_err_to_errno(int32_t err)
{
switch (err) {
case FR_OK:
return 0;
case FR_DISK_ERR:
return -EIO;
case FR_INT_ERR:
return -EIO;
case FR_NOT_READY:
return -ENODEV;
case FR_NO_FILE:
return -ENOENT;
case FR_NO_PATH:
return -ENOENT;
case FR_INVALID_NAME:
return -ENOENT;
case FR_DENIED:
return -EACCES;
case FR_EXIST:
return -EEXIST;
case FR_INVALID_OBJECT:
#ifdef EBADFD
return -EBADFD;
#else
return -EINVAL;
#endif
case FR_WRITE_PROTECTED:
return -EACCES;
case FR_INVALID_DRIVE:
return -ENXIO;
case FR_NOT_ENABLED:
return -ENODEV;
case FR_NO_FILESYSTEM:
return -ENODEV;
case FR_MKFS_ABORTED:
return -EINVAL;
case FR_TIMEOUT:
return -EBUSY;
case FR_LOCKED:
return -EACCES;
case FR_NOT_ENOUGH_CORE:
return -ENOMEM;
case FR_TOO_MANY_OPEN_FILES:
return -ENFILE;
case FR_INVALID_PARAMETER:
return -EINVAL;
}
return (int) err;
}
static const vfs_file_system_ops_t fatfs_fs_ops = {
.mount = _mount,
.umount = _umount,
.rename = _rename,
.unlink = _unlink,
.mkdir = _mkdir,
.rmdir = _rmdir,
};
static const vfs_file_ops_t fatfs_file_ops = {
.open = _open,
.close = _close,
.read = _read,
.write = _write,
.lseek = _lseek,
.fstat = _fstat,
};
static const vfs_dir_ops_t fatfs_dir_ops = {
.opendir = _opendir,
.readdir = _readdir,
.closedir = _closedir,
};
const vfs_file_system_t fatfs_file_system = {
.fs_op = &fatfs_fs_ops,
.f_op = &fatfs_file_ops,
.d_op = &fatfs_dir_ops,
};

87
sys/include/fs/fatfs.h Normal file
View File

@ -0,0 +1,87 @@
/*
* Copyright (C) 2017 HAW-Hamburg
*
* 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_fatfs FatFs integration
* @ingroup sys_fs
* @{
*
* @file
* @brief FatFs integration for vfs
*
* @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
*/
#ifndef FATFS_H
#define FATFS_H
#ifdef __cplusplus
extern "C" {
#endif
#ifndef VFS_DIR_BUFFER_SIZE
#define VFS_DIR_BUFFER_SIZE (44)
#endif
#ifndef VFS_FILE_BUFFER_SIZE
#define VFS_FILE_BUFFER_SIZE (72)
#endif
#include "fatfs/ff.h"
#include "vfs.h"
#ifndef FATFS_YEAR_OFFSET
#define FATFS_YEAR_OFFSET (1980)
#endif
#define EPOCH_YEAR_OFFSET (1970)
/** Size of the buffer needed for directory -> should be: sizeof(DIR)*/
#define FATFS_DIR_SIZE (44)
/** the problem with the above is: it's not possible to use sizeof(DIR) as this is later usen in #if (see below) */
/** Size of the buffer needed for directory -> should be: sizeof(fatfs_file_desc_t)*/
#define FATFS_FILE_SIZE (72)
#define FATFS_MAX_VOL_STR_LEN (4) /**< size needed for volume strings like "n:/" where n is the volume id */
#define FATFS_MOUNT_OPT (1) /**< 0:mount on first file access, 1 mount in f_mount() call */
#if (VFS_DIR_BUFFER_SIZE < FATFS_DIR_SIZE)
#error "VFS_DIR_BUFFER_SIZE too small"
#endif
#if (VFS_FILE_BUFFER_SIZE < FATFS_FILE_SIZE)
#error "VFS_FILE_BUFFER_SIZE too small"
#endif
/**
* needed info to run a FatFs instance
*/
typedef struct fatfs_desc {
FATFS fat_fs; /**< FatFs work area needed for each volume */
uint8_t vol_idx; /**< low level device that is used by FatFs */
} fatfs_desc_t;
/**
* info of a single opened file
*/
typedef struct fatfs_file_desc {
FIL file; /**< FatFs work area for a single file */
char fname[VFS_NAME_MAX + 1]; /**< name of the file (some FatFs functions e.g. f_stat use filename instead of FIL) */
} fatfs_file_desc_t;
/** The FatFs vfs driver, a pointer to a fatfs_desc_t must be provided as vfs_mountp::private_data */
extern const vfs_file_system_t fatfs_file_system;
#ifdef __cplusplus
}
#endif
#endif /* FATFS_H */
/** @} */

View File

@ -119,6 +119,29 @@ extern "C" {
#define VFS_DIR_BUFFER_SIZE (12)
#endif
#ifndef VFS_FILE_BUFFER_SIZE
/**
* @brief Size of buffer space in vfs_file_t
*
* Same as with VFS_DIR_BUFFER_SIZE some file systems (e.g. FatFs) require more space
* to store data about their files.
*
*
* Guidelines are same as with VFS_DIR_BUFFER_SIZE, so add the following snippet
* to your fs header:
*
* @attention @code
* #if VFS_FILE_BUFFER_SIZE < 123
* #error VFS_FILE_BUFFER_SIZE is too small, at least 123 bytes is required
* #endif
* @endcode
*
* @attention Put the check in the public header file (.h), do not put the check in the
* implementation (.c) file.
*/
#define VFS_FILE_BUFFER_SIZE (1)
#endif
#ifndef VFS_NAME_MAX
/**
* @brief Maximum length of the name in a @c vfs_dirent_t (not including terminating null)
@ -192,6 +215,7 @@ typedef struct {
union {
void *ptr; /**< pointer to private data */
int value; /**< alternatively, you can use private_data as an int */
uint8_t buffer[VFS_FILE_BUFFER_SIZE]; /**< Buffer space, in case a single pointer is not enough */
} private_data; /**< File system driver private data, implementation defined */
} vfs_file_t;

View File

@ -0,0 +1,17 @@
#!/usr/bin/env bash
#
# Copyright (C) 2017 HAW-Hamburg
#
# 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.
#
dd if=/dev/zero of=riot_fatfs_disk.img bs=1M count=$1
mkfs.fat riot_fatfs_disk.img
sudo mkdir -p /media/riot_fatfs_disk
sudo mount -o loop,umask=000 riot_fatfs_disk.img /media/riot_fatfs_disk
touch /media/riot_fatfs_disk/test.txt
echo "the test file content 123 abc" | tr '\n' '\0' >> /media/riot_fatfs_disk/test.txt
sudo umount /media/riot_fatfs_disk
tar -cjf riot_fatfs_disk.tar.gz riot_fatfs_disk.img
rm riot_fatfs_disk.img

Binary file not shown.

View File

@ -0,0 +1,34 @@
APPLICATION = pkg_fatfs_vfs
include ../Makefile.tests_common
USEMODULE += fatfs_vfs
FATFS_DISKIO_NATIVE_DEFAULT_FILE ?= \"./bin/riot_fatfs_disk.img\"
# whitelist can be removed when the problem described in #6063 was resolved
# this list is composed of boards that support spi + native
BOARD_WHITELIST := native airfy-beacon arduino-due arduino-duemilanove arduino-mega2560 \
arduino-uno arduino-zero avsextrem cc2538dk fox frdm-k64f iotlab-a8-m3 \
iotlab-m3 limifrog-v1 maple-mini msb-430 msb-430h msba2 msbiot mulle \
nrf52840dk nrf52dk nrf6310 nucleo-f072 nucleo-f091 nucleo-f103 \
nucleo-f302 nucleo-f303 nucleo-f334 nucleo-f401 nucleo-f410 nucleo-f411 \
nucleo-f446 nucleo-l053 nucleo-l073 nucleo-l1 nucleo-l476 nucleo144-f207 \
nucleo144-f303 nucleo144-f413 nucleo144-f429 nucleo144-f446 nucleo32-f031 \
nucleo32-f042 nucleo32-f303 nucleo32-l031 nucleo32-l432 openmote-cc2538 \
pba-d-01-kw2x pca10005 remote-pa remote-reva remote-revb samd21-xpro \
saml21-xpro samr21-xpro sodaq-autonomo spark-core stm32f0discovery \
stm32f3discovery stm32f4discovery telosb udoo waspmote-pro weio \
wsn430-v1_3b wsn430-v1_4 yunjia-nrf51822 z1
# export is needed to pass this value to the Makefile of the native_diskio module
export FATFS_DISKIO_NATIVE_DEFAULT_FILE
include $(RIOTBASE)/Makefile.include
#this generates a compressed fat image file that can be used by the fat driver on native
fatimage:
@./create_fat_image_file.sh
test:
@tar -xjf riot_fatfs_disk.tar.gz -C ./bin/
./tests/01-run.py

View File

@ -0,0 +1,17 @@
#!/usr/bin/env bash
#
# Copyright (C) 2017 HAW-Hamburg
#
# 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.
#
dd if=/dev/zero of=riot_fatfs_disk.img bs=1M count=128
mkfs.fat riot_fatfs_disk.img
sudo mkdir -p /media/riot_fatfs_disk
sudo mount -o loop,umask=000 riot_fatfs_disk.img /media/riot_fatfs_disk
touch /media/riot_fatfs_disk/test.txt
echo "the test file content 123 abc" | tr '\n' '\0' >> /media/riot_fatfs_disk/test.txt
sudo umount /media/riot_fatfs_disk
tar -cjf riot_fatfs_disk.tar.gz riot_fatfs_disk.img
rm riot_fatfs_disk.img

298
tests/pkg_fatfs_vfs/main.c Normal file
View File

@ -0,0 +1,298 @@
/*
* Copyright (C) 2017 HAW-Hamburg
*
* 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 tests
* @{
*
* @file
* @brief test application for using fatfs with vfs
*
* @author Michel Rottleuthner <michel.rottleuthner@haw-hamburg.de>
* @}
*/
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include "fs/fatfs.h"
#include "vfs.h"
#if FATFS_FFCONF_OPT_FS_NORTC == 0
#include "periph/rtc.h"
#endif
#define MNT_PATH "/test"
#define FNAME1 "TEST.TXT"
#define FNAME2 "NEWFILE.TXT"
#define FNAME_RNMD "RENAMED.TXT"
#define FNAME_NXIST "NOFILE.TXT"
#define FULL_FNAME1 (MNT_PATH "/" FNAME1)
#define FULL_FNAME2 (MNT_PATH "/" FNAME2)
#define FULL_FNAME_RNMD (MNT_PATH "/" FNAME_RNMD)
#define FULL_FNAME_NXIST (MNT_PATH "/" FNAME_NXIST)
#define DIR_NAME "SOMEDIR"
static const char test_txt[] = "the test file content 123 abc";
static const char test_txt2[] = "another text";
static const char test_txt3[] = "hello world for vfs";
static fatfs_desc_t fatfs = {
.vol_idx = 0
};
static vfs_mount_t _test_vfs_mount = {
.mount_point = MNT_PATH,
.fs = &fatfs_file_system,
.private_data = (void *)&fatfs,
};
static void print_test_result(const char *test_name, int ok)
{
printf("%s:[%s]\n", test_name, ok ? "OK" : "FAILED");
}
static void test_mount(void)
{
print_test_result("test_mount__mount", vfs_mount(&_test_vfs_mount) == 0);
print_test_result("test_mount__umount", vfs_umount(&_test_vfs_mount) == 0);
}
static void test_open(void)
{
int fd;
print_test_result("test_open__mount", vfs_mount(&_test_vfs_mount) == 0);
/* try to open file that doesn't exist */
fd = vfs_open(FULL_FNAME_NXIST, O_RDONLY, 0);
print_test_result("test_open__open", fd == -ENOENT);
/* open file with RO, WO and RW access */
fd = vfs_open(FULL_FNAME1, O_RDONLY, 0);
print_test_result("test_open__open_ro", fd >= 0);
print_test_result("test_open__close_ro", vfs_close(fd) == 0);
fd = vfs_open(FULL_FNAME1, O_WRONLY, 0);
print_test_result("test_open__open_wo", fd >= 0);
print_test_result("test_open__close_wo", vfs_close(fd) == 0);
fd = vfs_open(FULL_FNAME1, O_RDWR, 0);
print_test_result("test_open__open_rw", fd >= 0);
print_test_result("test_open__close_rw", vfs_close(fd) == 0);
print_test_result("test_open__umount", vfs_umount(&_test_vfs_mount) == 0);
}
static void test_rw(void)
{
char buf[sizeof(test_txt) + sizeof(test_txt2)];
int fd;
ssize_t nr, nw;
off_t new_pos;
print_test_result("test_rw__mount", vfs_mount(&_test_vfs_mount) == 0);
fd = vfs_open(FULL_FNAME1, O_RDONLY, 0);
print_test_result("test_rw__open_ro", fd >= 0);
/* compare file content with expected value */
memset(buf, 0, sizeof(buf));
nr = vfs_read(fd, buf, sizeof(test_txt));
print_test_result("test_rw__read_ro", (nr == sizeof(test_txt)) &&
(strncmp(buf, test_txt, sizeof(test_txt)) == 0));
/* try to write to RO file (success if no bytes are actually written) */
nw = vfs_write(fd, test_txt2, sizeof(test_txt2));
print_test_result("test_rw__write_ro", nw <= 0);
print_test_result("test_rw__close_ro", vfs_close(fd) == 0);
fd = vfs_open("/test/test.txt", O_WRONLY, 0);
print_test_result("test_rw__open_wo", fd >= 0);
/* try to read from WO file (success if no bytes are actually read) */
nr = vfs_read(fd, buf, sizeof(test_txt));
print_test_result("test_rw__read_wo", nw <= 0);
print_test_result("test_rw__close_wo", vfs_close(fd) == 0);
memset(buf, 0, sizeof(buf));
fd = vfs_open(FULL_FNAME1, O_RDWR, 0);
print_test_result("test_rw__open_rw", fd >= 0);
/* read file content and compare it to the expected value */
nr = vfs_read(fd, buf, sizeof(test_txt));
print_test_result("test_rw__read_rw", (nr == sizeof(test_txt)) &&
(strncmp(buf, test_txt, sizeof(test_txt)) == 0));
/* write to the file (text should be appended to the end of file) */
nw = vfs_write(fd, test_txt2, sizeof(test_txt2));
print_test_result("test_rw__write_rw", nw == sizeof(test_txt2));
/* seek to start of file */
new_pos = vfs_lseek(fd, 0, SEEK_SET);
print_test_result("test_rw__lseek", new_pos == 0);
/* read file content and compare to expected value */
memset(buf, 0, sizeof(buf));
nr = vfs_read(fd, buf, sizeof(buf));
print_test_result("test_rw__read_rw", (nr == sizeof(buf)) &&
(strncmp(buf, test_txt, sizeof(test_txt)) == 0) &&
(strncmp(&buf[sizeof(test_txt)],
test_txt2,
sizeof(test_txt2)) == 0));
print_test_result("test_rw__close_rw", vfs_close(fd) == 0);
/* create new file */
fd = vfs_open(FULL_FNAME2, O_RDWR | O_CREAT, 0);
print_test_result("test_rw__open_rwc", fd >= 0);
/* write to the new file, read it's content and compare to expected value */
nw = vfs_write(fd, test_txt3, sizeof(test_txt3));
print_test_result("test_rw__write_rwc", nw == sizeof(test_txt3));
new_pos = vfs_lseek(fd, 0, SEEK_SET);
print_test_result("test_rw__lseek_rwc", new_pos == 0);
memset(buf, 0, sizeof(buf));
nr = vfs_read(fd, buf, sizeof(test_txt3));
print_test_result("test_rw__read_rwc", (nr == sizeof(test_txt3)) &&
(strncmp(buf, test_txt3, sizeof(test_txt3)) == 0));
print_test_result("test_rw__close_rwc", vfs_close(fd) == 0);
print_test_result("test_rw__umount", vfs_umount(&_test_vfs_mount) == 0);
}
static void test_dir(void)
{
vfs_DIR dir;
vfs_dirent_t entry;
vfs_dirent_t entry2;
print_test_result("test_dir__mount", vfs_mount(&_test_vfs_mount) == 0);
print_test_result("test_dir__opendir", vfs_opendir(&dir, MNT_PATH) == 0);
print_test_result("test_dir__readdir1", vfs_readdir(&dir, &entry) == 1);
print_test_result("test_dir__readdir2", vfs_readdir(&dir, &entry2) == 1);
print_test_result("test_dir__readdir_name",
((strncmp(FNAME1, entry.d_name, sizeof(FNAME1)) == 0) &&
(strncmp(FNAME2, entry2.d_name, sizeof(FNAME2)) == 0))
||
((strncmp(FNAME1, entry2.d_name, sizeof(FNAME1)) == 0) &&
(strncmp(FNAME2, entry.d_name, sizeof(FNAME2)) == 0)));
print_test_result("test_dir__readdir3", vfs_readdir(&dir, &entry2) == 0);
print_test_result("test_dir__closedir", vfs_closedir(&dir) == 0);
print_test_result("test_dir__umount", vfs_umount(&_test_vfs_mount) == 0);
}
static void test_rename(void)
{
vfs_DIR dir;
vfs_dirent_t entry;
vfs_dirent_t entry2;
print_test_result("test_rename__mount", vfs_mount(&_test_vfs_mount) == 0);
print_test_result("test_rename__rename",
vfs_rename(FULL_FNAME1, FULL_FNAME_RNMD) == 0);
print_test_result("test_rename__opendir", vfs_opendir(&dir, MNT_PATH) == 0);
print_test_result("test_rename__readdir1", vfs_readdir(&dir, &entry) == 1);
print_test_result("test_rename__readdir2", vfs_readdir(&dir, &entry2) == 1);
print_test_result("test_rename__check_name",
((strncmp(FNAME_RNMD, entry.d_name, sizeof(FNAME_RNMD)) == 0) &&
(strncmp(FNAME2, entry2.d_name, sizeof(FNAME2)) == 0))
||
((strncmp(FNAME_RNMD, entry2.d_name, sizeof(FNAME_RNMD)) == 0) &&
(strncmp(FNAME2, entry.d_name, sizeof(FNAME2)) == 0)));
print_test_result("test_rename__readdir3", vfs_readdir(&dir, &entry2) == 0);
print_test_result("test_rename__closedir", vfs_closedir(&dir) == 0);
print_test_result("test_rename__umount", vfs_umount(&_test_vfs_mount) == 0);
}
static void test_unlink(void)
{
vfs_DIR dir;
vfs_dirent_t entry;
print_test_result("test_unlink__mount", vfs_mount(&_test_vfs_mount) == 0);
print_test_result("test_unlink__unlink1", vfs_unlink(FULL_FNAME2) == 0);
print_test_result("test_unlink__unlink2", vfs_unlink(FULL_FNAME_RNMD) == 0);
print_test_result("test_unlink__opendir", vfs_opendir(&dir, MNT_PATH) == 0);
print_test_result("test_unlink__readdir", vfs_readdir(&dir, &entry) == 0);
print_test_result("test_unlink__closedir", vfs_closedir(&dir) == 0);
print_test_result("test_unlink__umount", vfs_umount(&_test_vfs_mount) == 0);
}
static void test_mkrmdir(void)
{
vfs_DIR dir;
print_test_result("test_mkrmdir__mount", vfs_mount(&_test_vfs_mount) == 0);
print_test_result("test_mkrmdir__mkdir",
vfs_mkdir(MNT_PATH"/"DIR_NAME, 0) == 0);
print_test_result("test_mkrmdir__opendir1",
vfs_opendir(&dir, MNT_PATH"/"DIR_NAME) == 0);
print_test_result("test_mkrmdir__closedir", vfs_closedir(&dir) == 0);
print_test_result("test_mkrmdir__rmdir",
vfs_rmdir(MNT_PATH"/"DIR_NAME) == 0);
print_test_result("test_mkrmdir__opendir2",
vfs_opendir(&dir, MNT_PATH"/"DIR_NAME) < 0);
print_test_result("test_mkrmdir__umount",
vfs_umount(&_test_vfs_mount) == 0);
}
static void test_create(void)
{
int fd;
ssize_t nw;
print_test_result("test_create__mount", vfs_mount(&_test_vfs_mount) == 0);
fd = vfs_open(FULL_FNAME1, O_WRONLY | O_CREAT, 0);
print_test_result("test_create__open_wo", fd >= 0);
nw = vfs_write(fd, test_txt, sizeof(test_txt));
print_test_result("test_create__write_wo", nw == sizeof(test_txt));
print_test_result("test_create__close_wo", vfs_close(fd) == 0);
print_test_result("test_create__umount", vfs_umount(&_test_vfs_mount) == 0);
}
int main(void)
{
#if FATFS_FFCONF_OPT_FS_NORTC == 0
rtc_init();
#endif
printf("Tests for FatFs over VFS - test results will be printed "
"in the format test_name:result\n");
test_mount();
test_open();
test_rw();
test_dir();
test_rename();
test_unlink();
test_mkrmdir();
test_create();
printf("Test end.\n");
return 0;
}

Binary file not shown.

View File

@ -0,0 +1,36 @@
#!/usr/bin/env python
# Copyright (C) 2017 HAW-Hamburg.de
#
# 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 os
import sys
sys.path.append(os.path.join(os.environ['RIOTBASE'], 'dist/tools/testrunner'))
import testrunner
from datetime import datetime
class TestFailed(Exception):
pass
def testfunc(child):
child.expect(u"Tests for FatFs over VFS - test results will be printed in "
"the format test_name:result\r\n")
while True:
res = child.expect([u"[^\n]*:\[OK\]\r\n",
u"Test end.\r\n",
u".[^\n]*:\[FAILED\]\r\n" ,
u".*\r\n"])
if res > 1:
raise TestFailed(child.after.split(':',1)[0] + " test failed!")
elif res == 1:
break;
if __name__ == "__main__":
sys.exit(testrunner.run(testfunc))