mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-18 12:52:44 +01:00
180 lines
4.8 KiB
C
180 lines
4.8 KiB
C
|
/*
|
||
|
* 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 <string.h>
|
||
|
|
||
|
#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
|
||
|
};
|