diff --git a/drivers/include/mtd_emulated.h b/drivers/include/mtd_emulated.h new file mode 100644 index 0000000000..6e0195e3bc --- /dev/null +++ b/drivers/include/mtd_emulated.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2023 Gunar Schorcht + * + * 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 drivers_mtd + * @{ + * @brief MTD device that is emulated in RAM for test purposes + * + * Helpers for using emulated MTDs. + * + * @author Gunar Schorcht + */ +#ifndef MTD_EMULATED_H +#define MTD_EMULATED_H + +#include + +#include "board.h" +#include "macros/utils.h" +#include "mtd.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Macro to define an emulated MTD + * + * This macro creates a MTD device that is emulated in RAM. For example, using + * ``` + * MTD_EMULATED_DEV(0, 16, 4, 64) + * ``` + * creates the emulated MTD device `mtd_emulated_dev0` with 16 sectors, 4 pages + * per sector and a page size of 64 bytes. The write size is always 1 byte. + * + * @param n index of the emulated MTD (results into symbol `mtd_emulated_devn`) + * @param sc sectors of the emulated MTD + * @param pps pages per sector of the emulated MTD + * @param ps page size in bytes + */ +#define MTD_EMULATED_DEV(n, sc, pps, ps) \ + uint8_t _mtd_emulated_memory ## n[sc * pps * ps]; \ + \ + mtd_emulated_t mtd_emulated_dev ## n = { \ + .base = { \ + .driver = &_mtd_emulated_driver, \ + .sector_count = sc, \ + .pages_per_sector = pps, \ + .page_size = ps, \ + .write_size = 1, \ + }, \ + .size = sc * pps * ps, \ + .memory = _mtd_emulated_memory ## n, \ + .init_done = false, \ + } \ + +#if MODULE_VFS_AUTO_MOUNT || DOXYGEN +/** + * @brief Macro to define an automatic VFS mount point for an emulated MTD. + * + * For example, using + * ``` + * MTD_EMULATED_DEV_FS(0, 2, fatfs); + * ``` + * automatically mounts the emulated MTD `mtd_emulated_dev0` with FAT file + * system under mount point `/mtde0` with unique index 2. + * + * @param n index of the emulated MTD (symbol `mtd_emulated_devn`, mount point `/mtde0`) + * @param m unique overall index of VFS mount point + * @param fs filesystem type used + */ +#define MTD_EMULATED_DEV_FS(n, m, fs) \ + VFS_AUTO_MOUNT(fs, VFS_MTD(mtd_emulated_dev ## n), "/mtde" # n, m) + +#endif /* MODULE_VFS || DOXYGEN */ + +/** + * @brief Device descriptor for a MTD device that is emulated in RAM + */ +typedef struct { + mtd_dev_t base; /**< inherit from mtd_dev_t object */ + size_t size; /**< total size of the MTD device in bytes */ + uint8_t *memory; /**< RAM that is used for the emulated MTD device */ + bool init_done; /**< indicates whether initialization is already done */ +} mtd_emulated_t; + +/** + * @brief Emulated MTD device operations table for mtd + */ +extern const mtd_desc_t _mtd_emulated_driver; + +#ifdef __cplusplus +} +#endif + +#endif /* MTD_EMULATED_H */ +/** @} */ diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index fc18a5c5b0..23aa76002a 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -105,6 +105,9 @@ config MODULE_MTD_SDCARD bool "MTD interface for SPI SD-Card" depends on MODULE_SDCARD_SPI +config MODULE_MTD_EMULATED + bool "MTD interface for MTD emulated in RAM" + config MODULE_SAM0_SDHC bool "MTD interface for SAM0 SD Host Controller" depends on CPU_COMMON_SAM0 diff --git a/drivers/mtd_emulated/Makefile b/drivers/mtd_emulated/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/drivers/mtd_emulated/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/mtd_emulated/mtd_emulated.c b/drivers/mtd_emulated/mtd_emulated.c new file mode 100644 index 0000000000..68709bfcf7 --- /dev/null +++ b/drivers/mtd_emulated/mtd_emulated.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2023 Gunar Schorcht + * + * 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. + */ + +#include +#include + +#include "assert.h" +#include "macros/utils.h" +#include "mtd_emulated.h" + +static int _init(mtd_dev_t *dev) +{ + mtd_emulated_t *mtd = (mtd_emulated_t *)dev; + assert(mtd); + + if (!mtd->init_done) { + memset(mtd->memory, 0xff, mtd->size); + mtd->init_done = true; + } + return 0; +} + +static int _read(mtd_dev_t *dev, void *dest, uint32_t addr, uint32_t count) +{ + mtd_emulated_t *mtd = (mtd_emulated_t *)dev; + + assert(mtd); + assert(dest); + + if ((addr + count) > mtd->size) { + /* addr + count must not exceed the size of memory */ + return -EOVERFLOW; + } + memcpy(dest, mtd->memory + addr, count); + + return 0; +} + +static int _read_page(mtd_dev_t *dev, void *dest, + uint32_t page, uint32_t offset, uint32_t size) +{ + mtd_emulated_t *mtd = (mtd_emulated_t *)dev; + + (void)mtd; + + assert(mtd); + assert(dest); + + if (((page * mtd->base.page_size) + offset + size) > mtd->size) { + /* page addr + offset + size must not exceed the size of memory */ + return -EOVERFLOW; + } + memcpy(dest, mtd->memory + (page * mtd->base.page_size) + offset, size); + + return size; +} + +static int _write(mtd_dev_t *dev, const void *src, uint32_t addr, uint32_t count) +{ + mtd_emulated_t *mtd = (mtd_emulated_t *)dev; + + (void)mtd; + + assert(mtd); + assert(src); + + if (/* addr + count must be inside a page boundary. */ + (((addr % mtd->base.page_size) + count) > mtd->base.page_size) || + /* addr + count must not exceed the size of memory */ + ((addr + count) > mtd->size)) { + return -EOVERFLOW; + } + memcpy(mtd->memory + addr, src, count); + + return 0; +} + +int _write_page(mtd_dev_t *dev, const void *src, + uint32_t page, uint32_t offset, uint32_t size) +{ + mtd_emulated_t *mtd = (mtd_emulated_t *)dev; + + (void)mtd; + + assert(mtd); + assert(src); + + if (/* offset must be smaller than the page size */ + (offset >= mtd->base.page_size) || + /* page addr + offset + size must not exceed the size of memory */ + ((page * mtd->base.page_size) + offset + size) > mtd->size) { + return -EOVERFLOW; + } + memcpy(mtd->memory + (page * mtd->base.page_size) + offset, src, size); + + return size; +} + +static int _erase(mtd_dev_t *dev, uint32_t addr, uint32_t count) +{ + mtd_emulated_t *mtd = (mtd_emulated_t *)dev; + + (void)mtd; + assert(mtd); + + if (/* addr must be aligned on a sector boundary */ + (addr % (mtd->base.pages_per_sector * mtd->base.page_size) != 0) || + /* count must be a multiple of a sector size. */ + (count % (mtd->base.pages_per_sector * mtd->base.page_size) != 0) || + /* addr + count must not exceed the size of memory */ + ((addr + count) > mtd->size)) { + return -EOVERFLOW; + } + + memset(mtd->memory + addr, 0xff, count); + + return 0; +} + +static int _erase_sector(mtd_dev_t *dev, uint32_t sector, uint32_t num) +{ + mtd_emulated_t *mtd = (mtd_emulated_t *)dev; + + (void)mtd; + assert(mtd); + + if (/* sector must not exceed the number of sectors */ + (sector >= mtd->base.sector_count) || + /* sector + num must not exceed the number of sectors */ + ((sector + num) > mtd->base.sector_count)) { + return -EOVERFLOW; + } + + memset(mtd->memory + (sector * (mtd->base.pages_per_sector * mtd->base.page_size)), + 0xff, num * (mtd->base.pages_per_sector * mtd->base.page_size)); + + return 0; +} + +static int _power(mtd_dev_t *dev, enum mtd_power_state power) +{ + (void)dev; + (void)power; + return 0; +} + +const mtd_desc_t _mtd_emulated_driver = { + .init = _init, + .read = _read, + .read_page = _read_page, + .write = _write, + .write_page = _write_page, + .erase = _erase, + .erase_sector = _erase_sector, + .power = _power, +};