storage: add stream flash library
This library supports stream writes to flash with optinal progressive erase. This module is a direct copy of the functionality found in subsys/dfu/img_util/flash_img.c Signed-off-by: Håkon Øye Amundsen <haakon.amundsen@nordicsemi.no>
This commit is contained in:
parent
61aff640f9
commit
a9676831cb
15 changed files with 806 additions and 0 deletions
|
@ -1,3 +1,4 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
add_subdirectory_ifdef(CONFIG_FLASH_MAP flash_map)
|
||||
add_subdirectory_ifdef(CONFIG_STREAM_FLASH stream)
|
||||
|
|
|
@ -6,5 +6,6 @@
|
|||
menu "Storage"
|
||||
|
||||
source "subsys/storage/flash_map/Kconfig"
|
||||
source "subsys/storage/stream/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
|
7
subsys/storage/stream/CMakeLists.txt
Normal file
7
subsys/storage/stream/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
#
|
||||
# Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
zephyr_sources(stream_flash.c)
|
24
subsys/storage/stream/Kconfig
Normal file
24
subsys/storage/stream/Kconfig
Normal file
|
@ -0,0 +1,24 @@
|
|||
#
|
||||
# Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
menuconfig STREAM_FLASH
|
||||
bool "Stream to flash"
|
||||
select FLASH_PAGE_LAYOUT
|
||||
help
|
||||
Enable support of stream to flash API
|
||||
|
||||
if STREAM_FLASH
|
||||
config STREAM_FLASH_ERASE
|
||||
bool "Perform erase operations"
|
||||
help
|
||||
If disabled an external actor must erase the flash area being written
|
||||
to.
|
||||
|
||||
module = STREAM_FLASH
|
||||
module-str = stream flash
|
||||
source "subsys/logging/Kconfig.template.log_config"
|
||||
|
||||
endif # STREAM_FLASH
|
206
subsys/storage/stream/stream_flash.c
Normal file
206
subsys/storage/stream/stream_flash.c
Normal file
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* Copyright (c) 2017, 2020 Nordic Semiconductor ASA
|
||||
* Copyright (c) 2017 Linaro Limited
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define LOG_MODULE_NAME STREAM_FLASH
|
||||
#define LOG_LEVEL CONFIG_STREAM_FLASH_LOG_LEVEL
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_STREAM_FLASH_LOG_LEVEL);
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <string.h>
|
||||
#include <drivers/flash.h>
|
||||
|
||||
#include <storage/stream_flash.h>
|
||||
|
||||
#ifdef CONFIG_STREAM_FLASH_ERASE
|
||||
|
||||
int stream_flash_erase_page(struct stream_flash_ctx *ctx, off_t off)
|
||||
{
|
||||
int rc;
|
||||
struct flash_pages_info page;
|
||||
|
||||
rc = flash_get_page_info_by_offs(ctx->fdev, off, &page);
|
||||
if (rc != 0) {
|
||||
LOG_ERR("Error %d while getting page info", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (ctx->last_erased_page_start_offset == page.start_offset) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx->last_erased_page_start_offset = page.start_offset;
|
||||
LOG_INF("Erasing page at offset 0x%08lx", (long)page.start_offset);
|
||||
|
||||
flash_write_protection_set(ctx->fdev, false);
|
||||
rc = flash_erase(ctx->fdev, page.start_offset, page.size);
|
||||
flash_write_protection_set(ctx->fdev, true);
|
||||
|
||||
if (rc != 0) {
|
||||
LOG_ERR("Error %d while erasing page", rc);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_STREAM_FLASH_ERASE */
|
||||
|
||||
static int flash_sync(struct stream_flash_ctx *ctx)
|
||||
{
|
||||
int rc = 0;
|
||||
size_t write_addr = ctx->offset + ctx->bytes_written;
|
||||
|
||||
|
||||
if (IS_ENABLED(CONFIG_STREAM_FLASH_ERASE)) {
|
||||
if (ctx->buf_bytes == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = stream_flash_erase_page(ctx,
|
||||
write_addr + ctx->buf_bytes - 1);
|
||||
if (rc < 0) {
|
||||
LOG_ERR("stream_flash_erase_page err %d offset=0x%08zx",
|
||||
rc, write_addr);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
flash_write_protection_set(ctx->fdev, false);
|
||||
rc = flash_write(ctx->fdev, write_addr, ctx->buf, ctx->buf_bytes);
|
||||
flash_write_protection_set(ctx->fdev, true);
|
||||
|
||||
if (rc != 0) {
|
||||
LOG_ERR("flash_write error %d offset=0x%08zx", rc,
|
||||
write_addr);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (ctx->callback) {
|
||||
/* Invert to ensure that caller is able to discover a faulty
|
||||
* flash_read() even if no error code is returned.
|
||||
*/
|
||||
for (int i = 0; i < ctx->buf_bytes; i++) {
|
||||
ctx->buf[i] = ~ctx->buf[i];
|
||||
}
|
||||
|
||||
rc = flash_read(ctx->fdev, write_addr, ctx->buf,
|
||||
ctx->buf_bytes);
|
||||
if (rc != 0) {
|
||||
LOG_ERR("flash read failed: %d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = ctx->callback(ctx->buf, ctx->buf_bytes, write_addr);
|
||||
if (rc != 0) {
|
||||
LOG_ERR("callback failed: %d", rc);
|
||||
}
|
||||
}
|
||||
|
||||
ctx->bytes_written += ctx->buf_bytes;
|
||||
ctx->buf_bytes = 0U;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int stream_flash_buffered_write(struct stream_flash_ctx *ctx, const u8_t *data,
|
||||
size_t len, bool flush)
|
||||
{
|
||||
int processed = 0;
|
||||
int rc = 0;
|
||||
int buf_empty_bytes;
|
||||
|
||||
if (!ctx || !data) {
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (ctx->bytes_written + ctx->buf_bytes + len > ctx->available) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
while ((len - processed) >=
|
||||
(buf_empty_bytes = ctx->buf_len - ctx->buf_bytes)) {
|
||||
memcpy(ctx->buf + ctx->buf_bytes, data + processed,
|
||||
buf_empty_bytes);
|
||||
|
||||
ctx->buf_bytes = ctx->buf_len;
|
||||
rc = flash_sync(ctx);
|
||||
|
||||
if (rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
processed += buf_empty_bytes;
|
||||
}
|
||||
|
||||
/* place rest of the data into ctx->buf */
|
||||
if (processed < len) {
|
||||
memcpy(ctx->buf + ctx->buf_bytes,
|
||||
data + processed, len - processed);
|
||||
ctx->buf_bytes += len - processed;
|
||||
}
|
||||
|
||||
if (flush && ctx->buf_bytes > 0) {
|
||||
rc = flash_sync(ctx);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
size_t stream_flash_bytes_written(struct stream_flash_ctx *ctx)
|
||||
{
|
||||
return ctx->bytes_written;
|
||||
}
|
||||
|
||||
int stream_flash_init(struct stream_flash_ctx *ctx, struct device *fdev,
|
||||
u8_t *buf, size_t buf_len, size_t offset, size_t size,
|
||||
stream_flash_callback_t cb)
|
||||
{
|
||||
if (!ctx || !fdev || !buf) {
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
size_t layout_size = 0;
|
||||
size_t total_size = 0;
|
||||
const struct flash_pages_layout *layout;
|
||||
const struct flash_driver_api *api = fdev->driver_api;
|
||||
|
||||
/* Calculate the total size of the flash device */
|
||||
api->page_layout(fdev, &layout, &layout_size);
|
||||
for (int i = 0; i < layout_size; i++) {
|
||||
|
||||
total_size += layout->pages_count * layout->pages_size;
|
||||
|
||||
if (buf_len > layout->pages_size) {
|
||||
LOG_ERR("Buffer size is bigger than page");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
layout++;
|
||||
|
||||
}
|
||||
|
||||
if ((offset + size) > total_size ||
|
||||
offset % api->write_block_size) {
|
||||
LOG_ERR("Incorrect parameter");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
ctx->fdev = fdev;
|
||||
ctx->buf = buf;
|
||||
ctx->buf_len = buf_len;
|
||||
ctx->bytes_written = 0;
|
||||
ctx->buf_bytes = 0U;
|
||||
ctx->offset = offset;
|
||||
ctx->available = (size == 0 ? total_size - offset : size);
|
||||
ctx->callback = cb;
|
||||
|
||||
#ifdef CONFIG_STREAM_FLASH_ERASE
|
||||
ctx->last_erased_page_start_offset = -1;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue