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:
Håkon Øye Amundsen 2020-03-05 12:45:53 +00:00 committed by Carles Cufí
parent 61aff640f9
commit a9676831cb
15 changed files with 806 additions and 0 deletions

View file

@ -244,6 +244,11 @@ current :ref:`stability level <api_lifecycle>`.
- 1.11
- 2.1
* - :ref:`stream_flash`
- Experimental
- 2.3
- 2.3
* - :ref:`flash_map_api`
- Stable
- 1.11

View file

@ -11,3 +11,4 @@ Storage
disk/sdhc.rst
flash_map/flash_map.rst
fcb/fcb.rst
stream/stream_flash.rst

View file

@ -0,0 +1,24 @@
.. _stream_flash:
Stream Flash
############
The Stream Flash module takes contiguous fragments of a stream of data (e.g.
from radio packets), aggregates them into a user-provided buffer, then when the
buffer fills (or stream ends) writes it to a raw flash partition. It supports
providing the read-back buffer to the client to use in validating the persisted
stream content.
One typical use of a stream write operation is when receiving a new firmware
image to be used in a DFU operation.
There are several reasons why one might want to use buffered writes instead of
writing the data directly as it is made available. Some devices have hardware
limitations which does not allow flash writes to be performed in parallell with
other operations, such as radio RX and TX. Also, fewer write operations result
in faster response times seen from the application.
API Reference
*************
.. doxygengroup:: stream_flash
:project: Zephyr

View file

@ -0,0 +1,135 @@
/*
* Copyright (c) 2017, 2020 Nordic Semiconductor ASA
* Copyright (c) 2017 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Public API for stream writes to flash
*/
#ifndef ZEPHYR_INCLUDE_STORAGE_STREAM_FLASH_H_
#define ZEPHYR_INCLUDE_STORAGE_STREAM_FLASH_H_
/**
* @brief Abstraction over stream writes to flash
*
* @defgroup stream_flash Stream to flash interface
* @{
*/
#include <stdbool.h>
#include <drivers/flash.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @typedef stream_flash_callback_t
*
* @brief Signature for callback invoked after flash write completes.
*
* @details Functions of this type are invoked with a buffer containing
* data read back from the flash after a flash write has completed.
* This enables verifying that the data has been correctly stored (for
* instance by using a SHA function). The write buffer 'buf' provided in
* stream_flash_init is used as a read buffer for this purpose.
*
* @param buf Pointer to the data read.
* @param len The length of the data read.
* @param offset The offset the data was read from.
*/
typedef int (*stream_flash_callback_t)(u8_t *buf, size_t len, size_t offset);
/**
* @brief Structure for stream flash context
*
* Users should treat these structures as opaque values and only interact
* with them through the below API.
*/
struct stream_flash_ctx {
u8_t *buf; /* Write buffer */
size_t buf_len; /* Length of write buffer */
size_t buf_bytes; /* Number of bytes currently stored in write buf */
struct device *fdev; /* Flash device */
size_t bytes_written; /* Number of bytes written to flash */
size_t offset; /* Offset from base of flash device to write area */
size_t available; /* Available bytes in write area */
stream_flash_callback_t callback; /* Callback invoked after write op */
#ifdef CONFIG_STREAM_FLASH_ERASE
off_t last_erased_page_start_offset; /* Last erased offset */
#endif
};
/**
* @brief Initialize context needed for stream writes to flash.
*
* @param ctx context to be initialized
* @param fdev Flash device to operate on
* @param buf Write buffer
* @param buf_len Length of write buffer. Can not be larger than the page size
* @param offset Offset within flash device to start writing to
* @param size Number of bytes available for performing buffered write.
* If this is '0', the size will be set to the total size
* of the flash device minus the offset.
* @param cb Callback to be invoked on completed flash write operations.
*
* @return non-negative on success, negative errno code on fail
*/
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);
/**
* @brief Read number of bytes written to the flash.
*
* @note api-tags: pre-kernel-ok isr-ok
*
* @param ctx context
*
* @return Number of bytes written to flash.
*/
size_t stream_flash_bytes_written(struct stream_flash_ctx *ctx);
/**
* @brief Process input buffers to be written to flash device in single blocks.
* Will store remainder between calls.
*
* A final call to this function with flush set to true
* will write out the remaining block buffer to flash.
*
* @param ctx context
* @param data data to write
* @param len Number of bytes to write
* @param flush when true this forces any buffered data to be written to flash
*
* @return non-negative on success, negative errno code on fail
*/
int stream_flash_buffered_write(struct stream_flash_ctx *ctx, const u8_t *data,
size_t len, bool flush);
/**
* @brief Erase the flash page to which a given offset belongs.
*
* This function erases a flash page to which an offset belongs if this page
* is not the page previously erased by the provided ctx
* (ctx->last_erased_page_start_offset).
*
* @param ctx context
* @param off offset from the base address of the flash device
*
* @return non-negative on success, negative errno code on fail
*/
int stream_flash_erase_page(struct stream_flash_ctx *ctx, off_t off);
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#endif /* ZEPHYR_INCLUDE_STORAGE_STREAM_FLASH_H_ */

View file

@ -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)

View file

@ -6,5 +6,6 @@
menu "Storage"
source "subsys/storage/flash_map/Kconfig"
source "subsys/storage/stream/Kconfig"
endmenu

View file

@ -0,0 +1,7 @@
#
# Copyright (c) 2020 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
zephyr_sources(stream_flash.c)

View 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

View 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;
}

View file

@ -0,0 +1,12 @@
#
# Copyright (c) 2020 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
cmake_minimum_required(VERSION 3.13.1)
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
project(stream_flash)
FILE(GLOB app_sources src/*.c)
target_sources(app PRIVATE ${app_sources})

View file

@ -0,0 +1,7 @@
#
# Copyright (c) 2020 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
CONFIG_MPU_ALLOW_FLASH_WRITE=y

View file

@ -0,0 +1,7 @@
#
# Copyright (c) 2020 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
CONFIG_STREAM_FLASH_ERASE=n

View file

@ -0,0 +1,13 @@
#
# Copyright (c) 2020 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
CONFIG_ZTEST=y
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_DEBUG_OPTIMIZATIONS=y
CONFIG_STREAM_FLASH=y
CONFIG_STREAM_FLASH_ERASE=y

View file

@ -0,0 +1,351 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <zephyr/types.h>
#include <stdbool.h>
#include <ztest.h>
#include <drivers/flash.h>
#include <storage/stream_flash.h>
#define BUF_LEN 512
#define MAX_PAGE_SIZE 0x1000 /* Max supported page size to run test on */
#define MAX_NUM_PAGES 4 /* Max number of pages used in these tests */
#define TESTBUF_SIZE (MAX_PAGE_SIZE * MAX_NUM_PAGES)
#define FLASH_SIZE DT_SOC_NV_FLASH_0_SIZE
#define FLASH_NAME DT_CHOSEN_ZEPHYR_FLASH_CONTROLLER_LABEL
/* so that we don't overwrite the application when running on hw */
#define FLASH_BASE (64*1024)
#define FLASH_AVAILABLE (FLASH_SIZE-FLASH_BASE)
static struct device *fdev;
static const struct flash_driver_api *api;
static const struct flash_pages_layout *layout;
static size_t layout_size;
static struct stream_flash_ctx ctx;
static int page_size;
static u8_t *cb_buf;
static size_t cb_len;
static size_t cb_offset;
static int cb_ret;
static u8_t buf[BUF_LEN];
static u8_t read_buf[TESTBUF_SIZE];
const static u8_t write_buf[TESTBUF_SIZE] = {[0 ... TESTBUF_SIZE - 1] = 0xaa};
static u8_t written_pattern[TESTBUF_SIZE] = {[0 ... TESTBUF_SIZE - 1] = 0xaa};
static u8_t erased_pattern[TESTBUF_SIZE] = {[0 ... TESTBUF_SIZE - 1] = 0xff};
#define VERIFY_BUF(start, size, buf) \
do { \
rc = flash_read(fdev, FLASH_BASE + start, read_buf, size); \
zassert_equal(rc, 0, "should succeed"); \
zassert_mem_equal(read_buf, buf, size, "should equal %s", #buf);\
} while (0)
#define VERIFY_WRITTEN(start, size) VERIFY_BUF(start, size, written_pattern)
#define VERIFY_ERASED(start, size) VERIFY_BUF(start, size, erased_pattern)
int stream_flash_callback(u8_t *buf, size_t len, size_t offset)
{
if (cb_buf) {
zassert_equal(cb_buf, buf, "incorrect buf");
zassert_equal(cb_len, len, "incorrect length");
zassert_equal(cb_offset, offset, "incorrect offset");
}
return cb_ret;
}
static void erase_flash(void)
{
int rc;
rc = flash_write_protection_set(fdev, false);
zassert_equal(rc, 0, "should succeed");
for (int i = 0; i < MAX_NUM_PAGES; i++) {
rc = flash_erase(fdev,
FLASH_BASE + (i * layout->pages_size),
layout->pages_size);
zassert_equal(rc, 0, "should succeed");
}
rc = flash_write_protection_set(fdev, true);
zassert_equal(rc, 0, "should succeed");
}
static void init_target(void)
{
int rc;
/* Ensure that target is clean */
memset(&ctx, 0, sizeof(ctx));
memset(buf, 0, BUF_LEN);
/* Disable callback tests */
cb_len = 0;
cb_offset = 0;
cb_buf = NULL;
cb_ret = 0;
erase_flash();
rc = stream_flash_init(&ctx, fdev, buf, BUF_LEN, FLASH_BASE, 0,
stream_flash_callback);
zassert_equal(rc, 0, "expected success");
}
static void test_stream_flash_init(void)
{
int rc;
init_target();
/* End address out of range */
rc = stream_flash_init(&ctx, fdev, buf, BUF_LEN, FLASH_BASE,
FLASH_AVAILABLE + 4, NULL);
zassert_true(rc < 0, "should fail as size is more than available");
rc = stream_flash_init(NULL, fdev, buf, BUF_LEN, FLASH_BASE, 0, NULL);
zassert_true(rc < 0, "should fail as ctx is NULL");
rc = stream_flash_init(&ctx, NULL, buf, BUF_LEN, FLASH_BASE, 0, NULL);
zassert_true(rc < 0, "should fail as fdev is NULL");
rc = stream_flash_init(&ctx, fdev, NULL, BUF_LEN, FLASH_BASE, 0, NULL);
zassert_true(rc < 0, "should fail as buffer is NULL");
/* Entering '0' as flash size uses rest of flash. */
rc = stream_flash_init(&ctx, fdev, buf, BUF_LEN, FLASH_BASE, 0, NULL);
zassert_equal(rc, 0, "should succeed");
zassert_equal(FLASH_AVAILABLE, ctx.available, "Wrong size");
}
static void test_stream_flash_buffered_write(void)
{
int rc;
init_target();
/* Don't fill up the buffer */
rc = stream_flash_buffered_write(&ctx, write_buf, BUF_LEN - 1, false);
zassert_equal(rc, 0, "expected success");
/* Verify that no data has been written */
VERIFY_ERASED(0, BUF_LEN);
/* Now, write the missing byte, which should trigger a dump to flash */
rc = stream_flash_buffered_write(&ctx, write_buf, 1, false);
zassert_equal(rc, 0, "expected success");
VERIFY_WRITTEN(0, BUF_LEN);
}
static void test_stream_flash_buffered_write_cross_buf_border(void)
{
int rc;
init_target();
/* Test when write crosses border of the buffer */
rc = stream_flash_buffered_write(&ctx, write_buf, BUF_LEN + 128, false);
zassert_equal(rc, 0, "expected success");
/* 1xBuffer should be dumped to flash */
VERIFY_WRITTEN(0, BUF_LEN);
/* Fill rest of the buffer */
rc = stream_flash_buffered_write(&ctx, write_buf, BUF_LEN - 128, false);
zassert_equal(rc, 0, "expected success");
VERIFY_WRITTEN(BUF_LEN, BUF_LEN);
/* Fill half of the buffer */
rc = stream_flash_buffered_write(&ctx, write_buf, BUF_LEN/2, false);
zassert_equal(rc, 0, "expected success");
/* Flush the buffer */
rc = stream_flash_buffered_write(&ctx, write_buf, 0, true);
zassert_equal(rc, 0, "expected success");
/* Two and a half buffers should be written */
VERIFY_WRITTEN(0, BUF_LEN * 2 + BUF_LEN / 2);
}
static void test_stream_flash_buffered_write_multi_page(void)
{
int rc;
int num_pages = MAX_NUM_PAGES - 1;
init_target();
/* Test when write spans multiple pages crosses border of page */
rc = stream_flash_buffered_write(&ctx, write_buf,
(page_size * num_pages) + 128, false);
zassert_equal(rc, 0, "expected success");
/* First three pages should be written */
VERIFY_WRITTEN(0, page_size * num_pages);
/* Fill rest of the page */
rc = stream_flash_buffered_write(&ctx, write_buf,
page_size - 128, false);
zassert_equal(rc, 0, "expected success");
/* First four pages should be written */
VERIFY_WRITTEN(0, BUF_LEN * (num_pages + 1));
}
static void test_stream_flash_bytes_written(void)
{
int rc;
size_t offset;
init_target();
/* Verify that the offset is retained across failed downolads */
rc = stream_flash_buffered_write(&ctx, write_buf, BUF_LEN + 128, false);
zassert_equal(rc, 0, "expected success");
/* First page should be written */
VERIFY_WRITTEN(0, BUF_LEN);
/* Fill rest of the page */
offset = stream_flash_bytes_written(&ctx);
zassert_equal(offset, BUF_LEN, "offset should match buf size");
/* Fill up the buffer MINUS 128 to verify that write_buf_pos is kept */
rc = stream_flash_buffered_write(&ctx, write_buf, BUF_LEN - 128, false);
zassert_equal(rc, 0, "expected success");
/* Second page should be written */
VERIFY_WRITTEN(BUF_LEN, BUF_LEN);
}
static void test_stream_flash_buf_size_greater_than_page_size(void)
{
int rc;
/* To illustrate that other params does not trigger error */
rc = stream_flash_init(&ctx, fdev, buf, 0x10, 0, 0, NULL);
zassert_equal(rc, 0, "expected success");
/* Only change buf_len param */
rc = stream_flash_init(&ctx, fdev, buf, 0x10000, 0, 0, NULL);
zassert_true(rc < 0, "expected failure");
}
static void test_stream_flash_buffered_write_callback(void)
{
int rc;
init_target();
/* Trigger verification in callback */
cb_buf = buf;
cb_len = BUF_LEN;
cb_offset = FLASH_BASE;
rc = stream_flash_buffered_write(&ctx, write_buf, BUF_LEN + 128, false);
zassert_equal(rc, 0, "expected success");
cb_len = BUF_LEN;
cb_offset = FLASH_BASE + BUF_LEN;
/* Fill rest of the buffer */
rc = stream_flash_buffered_write(&ctx, write_buf, BUF_LEN - 128, false);
zassert_equal(rc, 0, "expected success");
VERIFY_WRITTEN(BUF_LEN, BUF_LEN);
/* Fill half of the buffer and flush it to flash */
cb_len = BUF_LEN/2;
cb_offset = FLASH_BASE + (2 * BUF_LEN);
rc = stream_flash_buffered_write(&ctx, write_buf, BUF_LEN/2, true);
zassert_equal(rc, 0, "expected success");
/* Ensure that failing callback trickles up to caller */
cb_ret = -EFAULT;
cb_buf = NULL; /* Don't verify other parameters of the callback */
rc = stream_flash_buffered_write(&ctx, write_buf, BUF_LEN, true);
zassert_equal(rc, -EFAULT, "expected failure from callback");
}
#ifdef CONFIG_STREAM_FLASH_ERASE
static void test_stream_flash_buffered_write_whole_page(void)
{
int rc;
init_target();
/* Write all bytes of a page, verify that next page is not erased */
/* First fill two pages with data */
rc = stream_flash_buffered_write(&ctx, write_buf, page_size * 2, true);
zassert_equal(rc, 0, "expected success");
VERIFY_WRITTEN(0, page_size);
VERIFY_WRITTEN(page_size, page_size);
/* Reset stream_flash context */
memset(&ctx, 0, sizeof(ctx));
memset(buf, 0, BUF_LEN);
rc = stream_flash_init(&ctx, fdev, buf, BUF_LEN, FLASH_BASE, 0,
stream_flash_callback);
zassert_equal(rc, 0, "expected success");
/* Write all bytes of a page, verify that next page is not erased */
rc = stream_flash_buffered_write(&ctx, write_buf, page_size, true);
zassert_equal(rc, 0, "expected success");
/* Second page should not be erased */
VERIFY_WRITTEN(page_size, page_size);
}
static void test_stream_flash_erase_page(void)
{
int rc;
init_target();
/* Write out one buf */
rc = stream_flash_buffered_write(&ctx, write_buf, BUF_LEN, false);
zassert_equal(rc, 0, "expected success");
rc = stream_flash_erase_page(&ctx, FLASH_BASE);
zassert_equal(rc, 0, "expected success");
VERIFY_ERASED(FLASH_BASE, page_size);
}
#endif
void test_main(void)
{
fdev = device_get_binding(FLASH_NAME);
api = fdev->driver_api;
api->page_layout(fdev, &layout, &layout_size);
page_size = layout->pages_size;
__ASSERT_NO_MSG(page_size > BUF_LEN);
ztest_test_suite(lib_stream_flash_test,
ztest_unit_test(test_stream_flash_init),
ztest_unit_test(test_stream_flash_buffered_write),
ztest_unit_test(test_stream_flash_buffered_write_cross_buf_border),
ztest_unit_test(test_stream_flash_buffered_write_multi_page),
ztest_unit_test(test_stream_flash_buf_size_greater_than_page_size),
ztest_unit_test(test_stream_flash_buffered_write_callback),
#ifdef CONFIG_STREAM_FLASH_ERASE
ztest_unit_test(test_stream_flash_buffered_write_whole_page),
ztest_unit_test(test_stream_flash_erase_page),
#endif
ztest_unit_test(test_stream_flash_bytes_written)
);
ztest_run_test_suite(lib_stream_flash_test);
}

View file

@ -0,0 +1,12 @@
tests:
storage.stream_flash:
platform_whitelist: native_posix native_posix_64
tags: stream_flash
storage.stream_flash.no_erase:
extra_args: OVERLAY_CONFIG=no_erase.overlay
platform_whitelist: native_posix native_posix_64
tags: stream_flash
storage.stream_flash.mpu_allow_flash_write:
extra_args: OVERLAY_CONFIG=mpu_allow_flash_write.overlay
platform_whitelist: nrf52840_pca10056
tags: stream_flash