From 19975af460ee33dfd5ebc5f585c76cf74ba281d7 Mon Sep 17 00:00:00 2001 From: Andrei Gansari Date: Thu, 2 Jul 2020 19:35:06 +0300 Subject: [PATCH] samples: flash_shell: unaligned and pattern write Adds unaligned and pattern write functionality. The reason this was added is chips with large pages, e.g. 256 can't be normaly writen via shell, thus the need to write a pattern with any length to a memory area. The unaligned write command allows writing a data frament in any place in memory, the data backup, protection, erasing and writing is automaticaly handled. This makes it easier to test all flash controller functionality in one command, also write any persistent data to any flash address. Signed-off-by: Andrei Gansari --- samples/drivers/flash_shell/prj.conf | 1 + samples/drivers/flash_shell/src/main.c | 205 ++++++++++++++++++++++++- 2 files changed, 204 insertions(+), 2 deletions(-) diff --git a/samples/drivers/flash_shell/prj.conf b/samples/drivers/flash_shell/prj.conf index dca9f52a07..1c1ed0814d 100644 --- a/samples/drivers/flash_shell/prj.conf +++ b/samples/drivers/flash_shell/prj.conf @@ -7,3 +7,4 @@ CONFIG_FLASH=y # If that's the case and you're interested in the flash layout, enable # it here. # CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_HEAP_MEM_POOL_SIZE=16384 diff --git a/samples/drivers/flash_shell/src/main.c b/samples/drivers/flash_shell/src/main.c index 7f02703b86..1dfdb2f294 100644 --- a/samples/drivers/flash_shell/src/main.c +++ b/samples/drivers/flash_shell/src/main.c @@ -49,6 +49,18 @@ LOG_MODULE_REGISTER(app); (" [... byteN]\n\n" \ "Write given bytes, starting at device offset .\n" \ "Pages must be erased before they can be written.") +#define WRITE_UNALIGNED_HELP \ + (" [... byteN]\n\n" \ + "Write given bytes, starting at device offset .\n" \ + "Being unaligned, affected memory areas are backed up, erased, protected" \ + " and then overwritten.\n" \ + "This command is designed to test writing to large flash pages.") +#define WRITE_PATTERN_HELP \ + (" \n\n" \ + "Writes a pattern of (0x00 0x01 .. 0xFF 0x00 ..) of length" \ + " at the offset .\n" \ + "Unaligned writing is used, i.e. protection and erasing are automated." \ + "This command is designed to test writing to large flash pages.") #ifdef CONFIG_FLASH_PAGE_LAYOUT #define PAGE_COUNT_HELP \ "\n\nPrint the number of pages on the flash device." @@ -227,6 +239,141 @@ static int do_write(const struct shell *shell, off_t offset, uint8_t *buf, return ret; } +static int do_write_unaligned(const struct shell *shell, off_t offset, uint8_t *buf, + size_t len, bool read_back) +{ + int ret; + size_t page_size = flash_get_write_block_size(flash_device); + size_t size_before = offset % page_size; + size_t size_after = page_size - ((size_before + len) % page_size); + size_t aligned_size = size_before + len + size_after; + off_t start_page = offset - size_before; + off_t last_page = start_page + aligned_size - page_size; + bool single_page_write = (size_before + len < page_size); + + char *before_data; + char *after_data; + + ret = flash_write_protection_set(flash_device, false); + if (ret) { + PR_ERROR(shell, "Failed to disable flash protection (err: %d)." + "\n", ret); + return ret; + } + + if (0 == size_before && 0 == size_after) { + /* Aligned write */ + flash_erase(flash_device, offset, len); + flash_write(flash_device, offset, buf, len); + ret = flash_write_protection_set(flash_device, true); + if (ret) { + PR_ERROR(shell, "Failed to enable flash protection (err: %d)." + "\n", ret); + } + return ret; + } + + before_data = k_malloc(page_size); + after_data = k_malloc(page_size); + + if (!before_data || !after_data) { + PR_ERROR(shell, "No heap memory for flash manipulation\n"); + ret = -ENOMEM; + goto free_buffers; + } + + /* Stash useful data from the pages that will be affected. */ + if (single_page_write) { + /* Read old data before new data is written. */ + if (size_before) { + flash_read(flash_device, start_page, before_data, size_before); + } + /* Fill the with new data. */ + memcpy(before_data + size_before, buf, len); + /* Fill the last part of old data. */ + if (size_after) { + flash_read(flash_device, offset + len, + before_data + size_before + len, + size_after); + } + } else { + /* Multipage write, different start and end pages. */ + if (size_before) { + flash_read(flash_device, start_page, before_data, + size_before); + /* Fill the rest with new data. */ + memcpy(before_data + size_before, buf, + page_size - size_before); + } + if (size_after) { + /* Copy ending part of new data. */ + memcpy((void *)after_data, + (void *)(buf + len - + ((len + size_before) % page_size)), + page_size - size_after); + /* Copy ending part of flash page. */ + flash_read(flash_device, offset + len, + after_data + (page_size - size_after), + size_after); + } + } + + /* Erase all the pages that overlap with new data. */ + flash_erase(flash_device, start_page, aligned_size); + + /* Write stashed and new data. */ + if (single_page_write || size_before > 0) { + /* Write first page if available. */ + flash_write(flash_device, start_page, before_data, + page_size); + } + if (!single_page_write) { + size_t middle_data_len = aligned_size; + off_t middle_page_start = start_page; + off_t data_offset = (off_t)buf; + + /* Write the middle bit if available */ + if (size_before > 0) { + middle_page_start += page_size; + middle_data_len -= page_size; + data_offset += (page_size - size_before); + } + if (size_after > 0) { + middle_data_len -= page_size; + } + if (middle_data_len > 0) { + flash_write(flash_device, middle_page_start, + (const void *)data_offset, + middle_data_len); + } + + /* Write the last page if needed. */ + if (size_after > 0) { + flash_write(flash_device, last_page, after_data, + page_size); + } + } + + if (read_back) { + PR_SHELL(shell, "Reading back written bytes:\n"); + ret = do_read(shell, offset, len); + } + +free_buffers: + k_free(before_data); + k_free(after_data); + + ret = flash_write_protection_set(flash_device, true); + if (ret) { + PR_ERROR(shell, "Failed to enable flash protection (err: %d)." + "\n", ret); + return ret; + } + + return ret; +} + + static int cmd_flash(const struct shell *shell, size_t argc, char **argv) { ARG_UNUSED(argc); @@ -295,7 +442,7 @@ exit: return err; } -static int cmd_write(const struct shell *shell, size_t argc, char **argv) +static int cmd_write_template(const struct shell *shell, size_t argc, char **argv, bool unaligned) { unsigned long int i, offset; uint8_t buf[ARGC_MAX]; @@ -336,7 +483,57 @@ static int cmd_write(const struct shell *shell, size_t argc, char **argv) } } - err = do_write(shell, offset, buf, i, true); + if (!unaligned) { + err = do_write(shell, offset, buf, i, true); + } else { + err = do_write_unaligned(shell, offset, buf, i, true); + } + +exit: + return err; +} + +static int cmd_write(const struct shell *shell, size_t argc, char **argv) +{ + return cmd_write_template(shell, argc, argv, false); +} + +static int cmd_write_unaligned(const struct shell *shell, size_t argc, char **argv) +{ + return cmd_write_template(shell, argc, argv, true); +} + +static int cmd_write_pattern(const struct shell *shell, size_t argc, char **argv) +{ + int err = check_flash_device(shell); + unsigned long int offset, len, i; + static uint8_t *buf; + + if (err) { + goto exit; + } + + if (parse_ul(argv[1], &offset) || parse_ul(argv[2], &len)) { + PR_ERROR(shell, "Invalid arguments.\n"); + err = -EINVAL; + goto exit; + } + + buf = k_malloc(len); + + if (!buf) { + PR_ERROR(shell, "No heap memory for data pattern\n"); + err = -ENOMEM; + goto exit; + } + + for (i = 0; i < len; i++) { + buf[i] = i & 0xFF; + } + + err = do_write_unaligned(shell, offset, buf, i, true); + + k_free(buf); exit: return err; @@ -610,6 +807,10 @@ SHELL_STATIC_SUBCMD_SET_CREATE(sub_flash, SHELL_CMD_ARG(write, NULL, WRITE_HELP, cmd_write, 3, 255), SHELL_CMD_ARG(write_block_size, NULL, WRITE_BLOCK_SIZE_HELP, cmd_write_block_size, 1, 0), + SHELL_CMD_ARG(write_unaligned, NULL, WRITE_UNALIGNED_HELP, + cmd_write_unaligned, 3, 255), + SHELL_CMD_ARG(write_pattern, NULL, WRITE_PATTERN_HELP, + cmd_write_pattern, 3, 255), SHELL_SUBCMD_SET_END /* Array terminated. */ );