123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- /*
- * 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_PROGRESS
- #include <settings/settings.h>
- static int settings_direct_loader(const char *key, size_t len,
- settings_read_cb read_cb, void *cb_arg,
- void *param)
- {
- struct stream_flash_ctx *ctx = (struct stream_flash_ctx *) param;
- /* Handle the subtree if it is an exact key match. */
- if (settings_name_next(key, NULL) == 0) {
- size_t bytes_written = 0;
- ssize_t len = read_cb(cb_arg, &bytes_written,
- sizeof(bytes_written));
- if (len != sizeof(ctx->bytes_written)) {
- LOG_ERR("Unable to read bytes_written from storage");
- return len;
- }
- /* Check that loaded progress is not outdated. */
- if (bytes_written >= ctx->bytes_written) {
- ctx->bytes_written = bytes_written;
- } else {
- LOG_WRN("Loaded outdated bytes_written %zu < %zu",
- bytes_written, ctx->bytes_written);
- return 0;
- }
- #ifdef CONFIG_STREAM_FLASH_ERASE
- int rc;
- struct flash_pages_info page;
- off_t offset = (off_t) (ctx->offset + ctx->bytes_written) - 1;
- /* Update the last erased page to avoid deleting already
- * written data.
- */
- if (ctx->bytes_written > 0) {
- rc = flash_get_page_info_by_offs(ctx->fdev, offset,
- &page);
- if (rc != 0) {
- LOG_ERR("Error %d while getting page info", rc);
- return rc;
- }
- ctx->last_erased_page_start_offset = page.start_offset;
- } else {
- ctx->last_erased_page_start_offset = -1;
- }
- #endif /* CONFIG_STREAM_FLASH_ERASE */
- }
- return 0;
- }
- #endif /* CONFIG_STREAM_FLASH_PROGRESS */
- #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;
- }
- LOG_DBG("Erasing page at offset 0x%08lx", (long)page.start_offset);
- rc = flash_erase(ctx->fdev, page.start_offset, page.size);
- if (rc != 0) {
- LOG_ERR("Error %d while erasing page", rc);
- } else {
- ctx->last_erased_page_start_offset = page.start_offset;
- }
- 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;
- size_t buf_bytes_aligned;
- size_t fill_length;
- uint8_t filler;
- if (ctx->buf_bytes == 0) {
- return 0;
- }
- if (IS_ENABLED(CONFIG_STREAM_FLASH_ERASE)) {
- 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;
- }
- }
- fill_length = flash_get_write_block_size(ctx->fdev);
- if (ctx->buf_bytes % fill_length) {
- fill_length -= ctx->buf_bytes % fill_length;
- filler = flash_get_parameters(ctx->fdev)->erase_value;
- memset(ctx->buf + ctx->buf_bytes, filler, fill_length);
- } else {
- fill_length = 0;
- }
- buf_bytes_aligned = ctx->buf_bytes + fill_length;
- rc = flash_write(ctx->fdev, write_addr, ctx->buf, buf_bytes_aligned);
- 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);
- return rc;
- }
- }
- ctx->bytes_written += ctx->buf_bytes;
- ctx->buf_bytes = 0U;
- return rc;
- }
- int stream_flash_buffered_write(struct stream_flash_ctx *ctx, const uint8_t *data,
- size_t len, bool flush)
- {
- int processed = 0;
- int rc = 0;
- int buf_empty_bytes;
- if (!ctx) {
- 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;
- }
- struct _inspect_flash {
- size_t buf_len;
- size_t total_size;
- };
- static bool find_flash_total_size(const struct flash_pages_info *info,
- void *data)
- {
- struct _inspect_flash *ctx = (struct _inspect_flash *) data;
- if (ctx->buf_len > info->size) {
- LOG_ERR("Buffer size is bigger than page");
- ctx->total_size = 0;
- return false;
- }
- ctx->total_size += info->size;
- return true;
- }
- int stream_flash_init(struct stream_flash_ctx *ctx, const struct device *fdev,
- uint8_t *buf, size_t buf_len, size_t offset, size_t size,
- stream_flash_callback_t cb)
- {
- if (!ctx || !fdev || !buf) {
- return -EFAULT;
- }
- #ifdef CONFIG_STREAM_FLASH_PROGRESS
- int rc = settings_subsys_init();
- if (rc != 0) {
- LOG_ERR("Error %d initializing settings subsystem", rc);
- return rc;
- }
- #endif
- struct _inspect_flash inspect_flash_ctx = {
- .buf_len = buf_len,
- .total_size = 0
- };
- if (buf_len % flash_get_write_block_size(fdev)) {
- LOG_ERR("Buffer size is not aligned to minimal write-block-size");
- return -EFAULT;
- }
- /* Calculate the total size of the flash device */
- flash_page_foreach(fdev, find_flash_total_size, &inspect_flash_ctx);
- /* The flash size counted should never be equal zero */
- if (inspect_flash_ctx.total_size == 0) {
- return -EFAULT;
- }
- if ((offset + size) > inspect_flash_ctx.total_size ||
- offset % flash_get_write_block_size(fdev)) {
- 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 ? inspect_flash_ctx.total_size - offset :
- size);
- ctx->callback = cb;
- #ifdef CONFIG_STREAM_FLASH_ERASE
- ctx->last_erased_page_start_offset = -1;
- #endif
- return 0;
- }
- #ifdef CONFIG_STREAM_FLASH_PROGRESS
- int stream_flash_progress_load(struct stream_flash_ctx *ctx,
- const char *settings_key)
- {
- if (!ctx || !settings_key) {
- return -EFAULT;
- }
- int rc = settings_load_subtree_direct(settings_key,
- settings_direct_loader,
- (void *) ctx);
- if (rc != 0) {
- LOG_ERR("Error %d while loading progress for \"%s\"",
- rc, settings_key);
- }
- return rc;
- }
- int stream_flash_progress_save(struct stream_flash_ctx *ctx,
- const char *settings_key)
- {
- if (!ctx || !settings_key) {
- return -EFAULT;
- }
- int rc = settings_save_one(settings_key,
- &ctx->bytes_written,
- sizeof(ctx->bytes_written));
- if (rc != 0) {
- LOG_ERR("Error %d while storing progress for \"%s\"",
- rc, settings_key);
- }
- return rc;
- }
- int stream_flash_progress_clear(struct stream_flash_ctx *ctx,
- const char *settings_key)
- {
- if (!ctx || !settings_key) {
- return -EFAULT;
- }
- int rc = settings_delete(settings_key);
- if (rc != 0) {
- LOG_ERR("Error %d while deleting progress for \"%s\"",
- rc, settings_key);
- }
- return rc;
- }
- #endif /* CONFIG_STREAM_FLASH_PROGRESS */
|