diff --git a/drivers/include/mtd.h b/drivers/include/mtd.h index c24d2856b9..057ec9e5b9 100644 --- a/drivers/include/mtd.h +++ b/drivers/include/mtd.h @@ -60,8 +60,16 @@ typedef struct { uint32_t sector_count; /**< Number of sector in the MTD */ uint32_t pages_per_sector; /**< Number of pages by sector in the MTD */ uint32_t page_size; /**< Size of the pages in the MTD */ +#if defined(MODULE_MTD_WRITE_PAGE) || DOXYGEN + void *work_area; /**< sector-sized buffer */ +#endif } mtd_dev_t; +/** + * @brief MTD driver can write any data to the storage without erasing it first. + */ +#define MTD_DRIVER_FLAG_DIRECT_WRITE (1 << 0) + /** * @brief MTD driver interface * @@ -203,6 +211,11 @@ struct mtd_desc { * @return < 0 value on error */ int (*power)(mtd_dev_t *dev, enum mtd_power_state power); + + /** + * @brief Properties of the MTD driver + */ + uint8_t flags; }; /** @@ -306,6 +319,36 @@ int mtd_write(mtd_dev_t *mtd, const void *src, uint32_t addr, uint32_t count); int mtd_write_page_raw(mtd_dev_t *mtd, const void *src, uint32_t page, uint32_t offset, uint32_t size); +/** + * @brief Write data to a MTD device with pagewise addressing + * + * The MTD layer will take care of splitting up the transaction into multiple + * writes if it is required by the underlying storage media. + * + * If the underlying sector needs to be erased before it can be written, the MTD + * layer will take care of the read-modify-write operation. + * + * @p offset must be smaller than the page size + * + * @note this requires the `mtd_write_page` module + * + * @param mtd the device to write to + * @param[in] src the buffer to write + * @param[in] page Page number to start writing to + * @param[in] offset byte offset from the start of the page + * @param[in] size the number of bytes to write + * + * @return 0 on success + * @return < 0 if an error occurred + * @return -ENODEV if @p mtd is not a valid device + * @return -ENOTSUP if operation is not supported on @p mtd + * @return -EOVERFLOW if @p addr or @p count are not valid, i.e. outside memory, + * @return -EIO if I/O error occurred + * @return -EINVAL if parameters are invalid + */ +int mtd_write_page(mtd_dev_t *mtd, const void *src, uint32_t page, + uint32_t offset, uint32_t size); + /** * @brief Erase sectors of a MTD device * diff --git a/drivers/mtd/mtd.c b/drivers/mtd/mtd.c index f3fc04c979..5fb8c2d8e4 100644 --- a/drivers/mtd/mtd.c +++ b/drivers/mtd/mtd.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include "bitarithm.h" #include "mtd.h" @@ -32,12 +34,22 @@ int mtd_init(mtd_dev_t *mtd) return -ENODEV; } + int res = -ENOTSUP; + if (mtd->driver->init) { - return mtd->driver->init(mtd); + res = mtd->driver->init(mtd); } - else { - return -ENOTSUP; + +#ifdef MODULE_MTD_WRITE_PAGE + if ((mtd->driver->flags & MTD_DRIVER_FLAG_DIRECT_WRITE) == 0) { + mtd->work_area = malloc(mtd->pages_per_sector * mtd->page_size); + if (mtd->work_area == NULL) { + res = -ENOMEM; + } } +#endif + + return res; } int mtd_read(mtd_dev_t *mtd, void *dest, uint32_t addr, uint32_t count) @@ -125,6 +137,44 @@ int mtd_write(mtd_dev_t *mtd, const void *src, uint32_t addr, uint32_t count) return mtd_write_page_raw(mtd, src, addr >> page_shift, addr & page_mask, count); } +#ifdef MODULE_MTD_WRITE_PAGE +int mtd_write_page(mtd_dev_t *mtd, const void *data, uint32_t page, + uint32_t offset, uint32_t len) +{ + if (!mtd || !mtd->driver) { + return -ENODEV; + } + + if (mtd->work_area == NULL) { + return mtd_write_page_raw(mtd, data, page, offset, len); + } + + int res; + uint8_t *work = mtd->work_area; + const uint32_t sector = page / mtd->pages_per_sector; + const uint32_t sector_page = sector * mtd->pages_per_sector; + const uint32_t sector_size = mtd->pages_per_sector * mtd->page_size; + + /* copy sector to RAM */ + res = mtd_read_page(mtd, work, sector_page, 0, sector_size); + if (res < 0) { + return res; + } + + /* erase sector */ + res = mtd_erase_sector(mtd, sector, 1); + if (res < 0) { + return res; + } + + /* modify sector in RAM */ + memcpy(work + (page - sector_page) * mtd->page_size + offset, data, len); + + /* write back modified sector copy */ + return mtd_write_page_raw(mtd, work, sector_page, 0, sector_size); +} +#endif + int mtd_write_page_raw(mtd_dev_t *mtd, const void *src, uint32_t page, uint32_t offset, uint32_t count) { diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 77cd399323..18652804d4 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -81,6 +81,7 @@ PSEUDOMODULES += log_color PSEUDOMODULES += lora PSEUDOMODULES += mpu_stack_guard PSEUDOMODULES += mpu_noexec_ram +PSEUDOMODULES += mtd_write_page PSEUDOMODULES += nanocoap_% PSEUDOMODULES += netdev_default PSEUDOMODULES += netdev_ieee802154_%