From e2e4dbec399d67337d36b8db5fbdc73737274576 Mon Sep 17 00:00:00 2001 From: Jue Date: Fri, 11 Mar 2022 11:38:28 +0100 Subject: [PATCH] pkg/tinyvcdiff: add MTD driver --- pkg/tinyvcdiff/Kconfig | 17 ++ pkg/tinyvcdiff/Makefile.dep | 3 + pkg/tinyvcdiff/Makefile.include | 5 + .../contrib/tinyvcdiff_mtd/Makefile | 3 + pkg/tinyvcdiff/contrib/tinyvcdiff_mtd/mtd.c | 179 ++++++++++++++++++ pkg/tinyvcdiff/include/vcdiff_mtd.h | 75 ++++++++ 6 files changed, 282 insertions(+) create mode 100644 pkg/tinyvcdiff/Makefile.dep create mode 100644 pkg/tinyvcdiff/contrib/tinyvcdiff_mtd/Makefile create mode 100644 pkg/tinyvcdiff/contrib/tinyvcdiff_mtd/mtd.c create mode 100644 pkg/tinyvcdiff/include/vcdiff_mtd.h diff --git a/pkg/tinyvcdiff/Kconfig b/pkg/tinyvcdiff/Kconfig index 41222ba111..a31b3d46b2 100644 --- a/pkg/tinyvcdiff/Kconfig +++ b/pkg/tinyvcdiff/Kconfig @@ -19,4 +19,21 @@ config TINYVCDIFF_BUFFER_SIZE the underlying MTD or VFS backend. But a size of just 1 byte would work, too. +menuconfig MODULE_TINYVCDIFF_MTD + bool "MTD Backend" + depends on MODULE_MTD + default y + help + Use a MTD device as VCDIFF target or source. + +if MODULE_TINYVCDIFF_MTD + +config TINYVCDIFF_MTD_WRITE_SIZE + int "Write size" + default 4 + help + Alignment and minimum size for MTD write access. + +endif # MODULE_TINYVCDIFF_MTD + endif # PACKAGE_TINYVCDIFF diff --git a/pkg/tinyvcdiff/Makefile.dep b/pkg/tinyvcdiff/Makefile.dep new file mode 100644 index 0000000000..7ce9d749d9 --- /dev/null +++ b/pkg/tinyvcdiff/Makefile.dep @@ -0,0 +1,3 @@ +ifneq (,$(filter mtd,$(USEMODULE))) + USEMODULE += tinyvcdiff_mtd +endif diff --git a/pkg/tinyvcdiff/Makefile.include b/pkg/tinyvcdiff/Makefile.include index 58ccc70e9e..6c0d32f480 100644 --- a/pkg/tinyvcdiff/Makefile.include +++ b/pkg/tinyvcdiff/Makefile.include @@ -8,3 +8,8 @@ ifneq ($(DEVELHELP),1) endif INCLUDES += -I$(PKGDIRBASE)/tinyvcdiff/include +INCLUDES += -I$(RIOTPKG)/tinyvcdiff/include + +ifneq (,$(filter tinyvcdiff_mtd,$(USEMODULE))) + DIRS += $(RIOTPKG)/tinyvcdiff/contrib/tinyvcdiff_mtd +endif diff --git a/pkg/tinyvcdiff/contrib/tinyvcdiff_mtd/Makefile b/pkg/tinyvcdiff/contrib/tinyvcdiff_mtd/Makefile new file mode 100644 index 0000000000..302d3c85ec --- /dev/null +++ b/pkg/tinyvcdiff/contrib/tinyvcdiff_mtd/Makefile @@ -0,0 +1,3 @@ +MODULE = tinyvcdiff_mtd + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/tinyvcdiff/contrib/tinyvcdiff_mtd/mtd.c b/pkg/tinyvcdiff/contrib/tinyvcdiff_mtd/mtd.c new file mode 100644 index 0000000000..5ad45cdfdb --- /dev/null +++ b/pkg/tinyvcdiff/contrib/tinyvcdiff_mtd/mtd.c @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2022 Juergen Fitschen + * + * 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 "vcdiff_mtd.h" +#include "assert.h" +#include + +#define ENABLE_DEBUG 0 +#include "debug.h" + +static int _erase (void *dev, size_t offset, size_t len) +{ + int rc; + vcdiff_mtd_t *mtd = dev; + size_t addr = offset + len; + size_t page = addr / mtd->dev->page_size; + size_t sector = page / mtd->dev->pages_per_sector; + size_t cnt = sector - mtd->next_erase_sector + 1; + + /* early exit if all sectors are already in the erased state */ + if (cnt == 0) { + return 0; + } + + /* erase sectors */ + rc = mtd_erase_sector(mtd->dev, mtd->next_erase_sector, cnt); + if (rc != 0) { + return rc; + } + + mtd->next_erase_sector += cnt; + + return 0; +} + +static int _read (void *dev, uint8_t *dest, size_t offset, size_t len) +{ + vcdiff_mtd_t *mtd = dev; + + /* mtd_read_page will take care of calculation the right page */ + int rc = mtd_read_page(mtd->dev, dest, 0, offset, len); + + return rc; +} + +static int _align_write (vcdiff_mtd_t *mtd, uint8_t **src, size_t *len) +{ + size_t alignment_offset; + size_t copy_len; + + alignment_offset = mtd->offset % CONFIG_TINYVCDIFF_MTD_WRITE_SIZE; + if (alignment_offset == 0) { + /* we are already aligned */ + return 0; + } + + /* check how many bytes have to be copied to be aligned */ + copy_len = CONFIG_TINYVCDIFF_MTD_WRITE_SIZE - alignment_offset; + if (copy_len > *len) { + copy_len = *len; + } + + /* copy unaligned bytes to the write buffer */ + memcpy(&mtd->write_buffer[alignment_offset], *src, copy_len); + alignment_offset += copy_len; + mtd->offset += copy_len; + *len -= copy_len; + *src += copy_len; + DEBUG("_align_write: buffered %zuB for alignment\n", copy_len); + + if (alignment_offset < CONFIG_TINYVCDIFF_MTD_WRITE_SIZE) { + /* we haven't collected enough bytes, yet */ + return 0; + } + + assert(alignment_offset == CONFIG_TINYVCDIFF_MTD_WRITE_SIZE); + + DEBUG("_align_write: write buffered data\n"); + + /* write buffer to mtd + * mtd_read_page will take care of calculation the right page + * it's safe to call mtd_read_page_raw: the erase driver already + * prepared the sector for writing */ + return mtd_write_page_raw(mtd->dev, mtd->write_buffer, 0, + mtd->offset - alignment_offset, + CONFIG_TINYVCDIFF_MTD_WRITE_SIZE); +} + +static int _write (void *dev, uint8_t *src, size_t offset, size_t len) +{ + int rc; + size_t to_copy; + vcdiff_mtd_t *mtd = dev; + + assert(offset == mtd->offset); + + DEBUG("_write: 0x%zx + %zuB\n", mtd->offset, len); + + /* align writes */ + rc = _align_write(dev, &src, &len); + if (rc < 0) { + /* an error occurred */ + return rc; + } else if (len == 0) { + /* no bytes are left */ + return 0; + } + + assert((mtd->offset % CONFIG_TINYVCDIFF_MTD_WRITE_SIZE) == 0); + + /* copy all aligned data */ + to_copy = (len / CONFIG_TINYVCDIFF_MTD_WRITE_SIZE) * CONFIG_TINYVCDIFF_MTD_WRITE_SIZE; + rc = mtd_write_page_raw(mtd->dev, src, 0, mtd->offset, to_copy); + if (rc < 0) { + return rc; + } + mtd->offset += to_copy; + len -= to_copy; + src += to_copy; + if (len == 0) { + /* no bytes are left */ + return 0; + } + + assert(len < CONFIG_TINYVCDIFF_MTD_WRITE_SIZE); + + /* copy remaining bytes into write_buffer */ + memcpy(mtd->write_buffer, src, len); + mtd->offset += len; + DEBUG("_write: buffered %zuB for alignment\n", len); + + return rc; +} + +static int _flush (void *dev) +{ + vcdiff_mtd_t *mtd = dev; + int rc; + uint8_t buf[CONFIG_TINYVCDIFF_MTD_WRITE_SIZE]; + size_t alignment_offset; + + alignment_offset = mtd->offset % CONFIG_TINYVCDIFF_MTD_WRITE_SIZE; + if (alignment_offset == 0) { + /* we are already aligned -> no bytes are left in the buffer */ + return 0; + } + + DEBUG("_flush: write last %zuB\n", alignment_offset); + + /* get present bytes from MTD to pad alignment */ + rc = mtd_read_page(mtd->dev, buf, 0, mtd->offset - alignment_offset, + CONFIG_TINYVCDIFF_MTD_WRITE_SIZE); + if (rc < 0) { + return rc; + } + + /* pad write buffer */ + memcpy(&mtd->write_buffer[alignment_offset], &buf[alignment_offset], + CONFIG_TINYVCDIFF_MTD_WRITE_SIZE - alignment_offset); + + /* write last buffer */ + rc = mtd_write_page_raw(mtd->dev, mtd->write_buffer, 0, + mtd->offset - alignment_offset, + CONFIG_TINYVCDIFF_MTD_WRITE_SIZE); + + return rc; +} + +const vcdiff_driver_t vcdiff_mtd_driver = { + .erase = _erase, + .read = _read, + .write = _write, + .flush = _flush +}; diff --git a/pkg/tinyvcdiff/include/vcdiff_mtd.h b/pkg/tinyvcdiff/include/vcdiff_mtd.h new file mode 100644 index 0000000000..c550264803 --- /dev/null +++ b/pkg/tinyvcdiff/include/vcdiff_mtd.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2022 Juergen Fitschen + * + * 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 + * @ingroup pkg_tinyvcdiff + * + * @author Juergen Fitschen + */ + +#ifndef VCDIFF_MTD_H +#define VCDIFF_MTD_H + +#include "vcdiff.h" +#include "mtd.h" + +#ifndef CONFIG_TINYVCDIFF_MTD_WRITE_SIZE +/** + * @brief Alignment and minimum size for MTD write access + */ +#define CONFIG_TINYVCDIFF_MTD_WRITE_SIZE 4 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Driver for accessing MTD devices + */ +extern const vcdiff_driver_t vcdiff_mtd_driver; + +/** + * @brief Context for the underlying MTD device + */ +typedef struct { + /** + * @brief Instance of the backing MTD device + */ + mtd_dev_t *dev; + + /** + * @brief Number of the next sector that must be erased + */ + size_t next_erase_sector; + + /** + * @brief Buffer for aligned writes + */ + uint8_t write_buffer[CONFIG_TINYVCDIFF_MTD_WRITE_SIZE]; + + /** + * @brief Current offset on the MTD device + */ + size_t offset; +} vcdiff_mtd_t; + +/** + * @brief Initializes vcdiff_mtd_t + */ +#define VCDIFF_MTD_INIT(DEV) { .dev = DEV } + +#ifdef __cplusplus +} +#endif + +#endif /* VCDIFF_MTD_H */ +/** @} */