diff --git a/drivers/include/mtd.h b/drivers/include/mtd.h index bc32eb280f..2d6c5c8026 100644 --- a/drivers/include/mtd.h +++ b/drivers/include/mtd.h @@ -23,6 +23,9 @@ #define MTD_H #include +#if MODULE_VFS +#include "vfs.h" +#endif #ifdef __cplusplus extern "C" { @@ -225,6 +228,13 @@ int mtd_erase(mtd_dev_t *mtd, uint32_t addr, uint32_t count); */ int mtd_power(mtd_dev_t *mtd, enum mtd_power_state power); +#if defined(MODULE_VFS) || defined(DOXYGEN) +/** + * @brief MTD driver for VFS + */ +extern const vfs_file_ops_t mtd_vfs_ops; +#endif + #ifdef __cplusplus } #endif diff --git a/drivers/mtd/mtd-vfs.c b/drivers/mtd/mtd-vfs.c new file mode 100644 index 0000000000..f0f10903bf --- /dev/null +++ b/drivers/mtd/mtd-vfs.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2016 Eistec AB + * + * 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. + */ + +#if MODULE_VFS + +#include +#include +#include + +#include "mtd.h" +#include "vfs.h" + +/** + * @ingroup mtd + * @{ + * + * @file + * + * @brief MTD generic VFS operations + * + * This allows the MTD driver to register as a node on DevFS + * + * See boards/mulle or tests/unittests/tests-devfs for examples on how to use. + * + * Tested with mtd_spi_nor on Mulle + * + * @author Joakim NohlgÄrd + */ + +static int mtd_vfs_fstat(vfs_file_t *filp, struct stat *buf); +static off_t mtd_vfs_lseek(vfs_file_t *filp, off_t off, int whence); +static ssize_t mtd_vfs_read(vfs_file_t *filp, void *dest, size_t nbytes); +static ssize_t mtd_vfs_write(vfs_file_t *filp, const void *src, size_t nbytes); + +const vfs_file_ops_t mtd_vfs_ops = { + .fstat = mtd_vfs_fstat, + .lseek = mtd_vfs_lseek, + .read = mtd_vfs_read, + .write = mtd_vfs_write, +}; + +static int mtd_vfs_fstat(vfs_file_t *filp, struct stat *buf) +{ + if (buf == NULL) { + return -EFAULT; + } + mtd_dev_t *mtd = filp->private_data.ptr; + if (mtd == NULL) { + return -EFAULT; + } + buf->st_nlink = 1; + buf->st_size = mtd->page_size * mtd->sector_count * mtd->pages_per_sector; + return 0; +} + +static off_t mtd_vfs_lseek(vfs_file_t *filp, off_t off, int whence) +{ + mtd_dev_t *mtd = filp->private_data.ptr; + if (mtd == NULL) { + return -EFAULT; + } + switch (whence) { + case SEEK_SET: + break; + case SEEK_CUR: + off += filp->pos; + break; + case SEEK_END: + off += mtd->page_size * mtd->sector_count * mtd->pages_per_sector; + break; + default: + return -EINVAL; + } + if (off < 0) { + /* the resulting file offset would be negative */ + return -EINVAL; + } + /* POSIX allows seeking past the end of the file */ + filp->pos = off; + return off; +} + +static ssize_t mtd_vfs_read(vfs_file_t *filp, void *dest, size_t nbytes) +{ + mtd_dev_t *mtd = filp->private_data.ptr; + if (mtd == NULL) { + return -EFAULT; + } + uint32_t size = mtd->page_size * mtd->sector_count * mtd->pages_per_sector; + uint32_t src = filp->pos; + if (src >= size) { + return 0; + } + if ((src + nbytes) > size) { + nbytes = size - src; + } + int res = mtd_read(mtd, dest, src, nbytes); + if (res < 0) { + return res; + } + /* Advance file position */ + filp->pos += res; + return res; +} + +static ssize_t mtd_vfs_write(vfs_file_t *filp, const void *src, size_t nbytes) +{ + mtd_dev_t *mtd = filp->private_data.ptr; + if (mtd == NULL) { + return -EFAULT; + } + uint32_t size = mtd->page_size * mtd->sector_count * mtd->pages_per_sector; + uint32_t dest = filp->pos; + if (dest >= size) { + /* attempt to write outside the device memory */ + return -ENOSPC; + } + if ((dest + nbytes) > size) { + nbytes = size - dest; + } + int res = mtd_write(mtd, src, dest, nbytes); + if (res < 0) { + return res; + } + /* Advance file position */ + filp->pos += res; + return res; +} + +/** @} */ + +#else +typedef int dont_be_pedantic; +#endif /* MODULE_VFS */