diff --git a/samples/subsys/fs/zms/CMakeLists.txt b/samples/subsys/fs/zms/CMakeLists.txt new file mode 100644 index 00000000000..33644e1d903 --- /dev/null +++ b/samples/subsys/fs/zms/CMakeLists.txt @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(zms) + + +target_sources(app PRIVATE src/main.c) +target_include_directories(app PRIVATE ${ZEPHYR_BASE}/subsys/fs/zms) diff --git a/samples/subsys/fs/zms/README.rst b/samples/subsys/fs/zms/README.rst new file mode 100644 index 00000000000..4f06656b28d --- /dev/null +++ b/samples/subsys/fs/zms/README.rst @@ -0,0 +1,93 @@ +.. zephyr:code-sample:: zms + :name: Zephyr Memory Storage (ZMS) + :relevant-api: zms_high_level_api + + Store and retrieve data from storage using the ZMS API. + +Overview +******** + The sample shows how to use ZMS to store ID/VALUE pairs and reads them back. + Deleting an ID/VALE pair is also shown in this sample. + + The sample stores the following items: + 1- A string representing an IP-address: stored at id=1, data="192.168.1.1" + 2- A binary blob representing a key/value pair: stored at id=0xbeefdead, + data={0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF} + 3- A variable (32bit): stored at id=2, data=cnt + 4- A long set of data (128 bytes) + A loop is executed where we mount the storage system, and then write all set + of data. + Each DELETE_ITERATION period, we delete all set of data and verify that it has been deleted. + We generate as well incremented ID/value pairs, we store them until storage is full, then we + delete them and verify that storage is empty. + +Requirements +************ + +* A board with flash support or native_sim target + +Building and Running +******************** + +This sample can be found under :zephyr_file:`samples/subsys/fs/zms` in the Zephyr tree. + +The sample can be built for several platforms, but for the moment it has been tested only +on native_sim target + +.. zephyr-app-commands:: + :zephyr-app: samples/subsys/fs/zms + :goals: build + :compact: + +After running the generated image on a native_sim target, the output on the console shows the +multiple Iterations of read/write/delete exectuted. + +Sample Output +============= + +.. code-block:: console + + *** Booting Zephyr OS build v3.7.0-2383-g624f75400242 *** + [00:00:00.000,000] fs_zms: 3 Sectors of 4096 bytes + [00:00:00.000,000] fs_zms: alloc wra: 0, fc0 + [00:00:00.000,000] fs_zms: data wra: 0, 0 + ITERATION: 0 + Adding IP_ADDRESS 172.16.254.1 at id 1 + Adding key/value at id beefdead + Adding counter at id 2 + Adding Longarray at id 3 + [00:00:00.000,000] fs_zms: 3 Sectors of 4096 bytes + [00:00:00.000,000] fs_zms: alloc wra: 0, f80 + [00:00:00.000,000] fs_zms: data wra: 0, 8c + ITERATION: 1 + ID: 1, IP Address: 172.16.254.1 + Adding IP_ADDRESS 172.16.254.1 at id 1 + Id: beefdead, Key: de ad be ef de ad be ef + Adding key/value at id beefdead + Id: 2, loop_cnt: 0 + Adding counter at id 2 + Id: 3, Longarray: 0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 5 + 4 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f + Adding Longarray at id 3 + . + . + . + . + . + . + [00:00:00.000,000] fs_zms: 3 Sectors of 4096 bytes + [00:00:00.000,000] fs_zms: alloc wra: 0, f40 + [00:00:00.000,000] fs_zms: data wra: 0, 80 + ITERATION: 299 + ID: 1, IP Address: 172.16.254.1 + Adding IP_ADDRESS 172.16.254.1 at id 1 + Id: beefdead, Key: de ad be ef de ad be ef + Adding key/value at id beefdead + Id: 2, loop_cnt: 298 + Adding counter at id 2 + Id: 3, Longarray: 0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50 51 52 53 5 + 4 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d 7e 7f + Adding Longarray at id 3 + Memory is full let's delete all items + Free space in storage is 8064 bytes + Sample code finished Successfully diff --git a/samples/subsys/fs/zms/prj.conf b/samples/subsys/fs/zms/prj.conf new file mode 100644 index 00000000000..343c5021899 --- /dev/null +++ b/samples/subsys/fs/zms/prj.conf @@ -0,0 +1,5 @@ +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y + +CONFIG_ZMS=y +CONFIG_LOG=y diff --git a/samples/subsys/fs/zms/sample.yaml b/samples/subsys/fs/zms/sample.yaml new file mode 100644 index 00000000000..802dabcf0f1 --- /dev/null +++ b/samples/subsys/fs/zms/sample.yaml @@ -0,0 +1,10 @@ +sample: + name: ZMS Sample + +tests: + sample.zms.basic: + tags: zms + depends_on: zms + platform_allow: + - qemu_x86 + - native_posix diff --git a/samples/subsys/fs/zms/src/main.c b/samples/subsys/fs/zms/src/main.c new file mode 100644 index 00000000000..28bc72061b7 --- /dev/null +++ b/samples/subsys/fs/zms/src/main.c @@ -0,0 +1,282 @@ +/* + * ZMS Sample for Zephyr using high level API. + * The sample shows how to use ZMS to store ID/VALUE pairs and reads them back. + * Deleting an ID/VALE pair is also shown in this sample. + * + * The sample stores the following items: + * 1. A string representing an IP-address: stored at id=1, data="192.168.1.1" + * 2. A binary blob representing a key/value pair: stored at id=0xbeefdead, + * data={0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF} + * 3. A variable (32bit): stored at id=2, data=cnt + * 4. A long set of data (128 bytes) + * + * A loop is executed where we mount the storage system, and then write all set + * of data. + * + * Each DELETE_ITERATION period, we delete all set of data and verify that it has been deleted. + * We generate as well incremented ID/value pairs, we store them until storage is full, then we + * delete them and verify that storage is empty. + * + * Copyright (c) 2024 BayLibre SAS + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct zms_fs fs; + +#define ZMS_PARTITION storage_partition +#define ZMS_PARTITION_DEVICE FIXED_PARTITION_DEVICE(ZMS_PARTITION) +#define ZMS_PARTITION_OFFSET FIXED_PARTITION_OFFSET(ZMS_PARTITION) + +#define IP_ADDRESS_ID 1 +#define KEY_VALUE_ID 0xbeefdead +#define CNT_ID 2 +#define LONG_DATA_ID 3 + +#define MAX_ITERATIONS 300 +#define DELETE_ITERATION 10 + +static int delete_and_verify_items(struct zms_fs *fs, uint32_t id) +{ + int rc = 0; + + rc = zms_delete(fs, id); + if (rc) { + goto error1; + } + rc = zms_get_data_length(fs, id); + if (rc > 0) { + goto error2; + } + + return 0; +error1: + printk("Error while deleting item rc=%d\n", rc); + return rc; +error2: + printk("Error, Delete failed item should not be present\n"); + return -1; +} + +static int delete_basic_items(struct zms_fs *fs) +{ + int rc = 0; + + rc = delete_and_verify_items(fs, IP_ADDRESS_ID); + if (rc) { + printk("Error while deleting item %x rc=%d\n", IP_ADDRESS_ID, rc); + return rc; + } + rc = delete_and_verify_items(fs, KEY_VALUE_ID); + if (rc) { + printk("Error while deleting item %x rc=%d\n", KEY_VALUE_ID, rc); + return rc; + } + rc = delete_and_verify_items(fs, CNT_ID); + if (rc) { + printk("Error while deleting item %x rc=%d\n", CNT_ID, rc); + return rc; + } + rc = delete_and_verify_items(fs, LONG_DATA_ID); + if (rc) { + printk("Error while deleting item %x rc=%d\n", LONG_DATA_ID, rc); + } + + return rc; +} + +int main(void) +{ + int rc = 0; + char buf[16]; + uint8_t key[8] = {0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF}, longarray[128]; + uint32_t i_cnt = 0U, i; + uint32_t id = 0; + uint64_t free_space = 0; + struct flash_pages_info info; + + for (int n = 0; n < sizeof(longarray); n++) { + longarray[n] = n; + } + + /* define the zms file system by settings with: + * sector_size equal to the pagesize, + * 3 sectors + * starting at ZMS_PARTITION_OFFSET + */ + fs.flash_device = ZMS_PARTITION_DEVICE; + if (!device_is_ready(fs.flash_device)) { + printk("Storage device %s is not ready\n", fs.flash_device->name); + return 0; + } + fs.offset = ZMS_PARTITION_OFFSET; + rc = flash_get_page_info_by_offs(fs.flash_device, fs.offset, &info); + if (rc) { + printk("Unable to get page info, rc=%d\n", rc); + return 0; + } + fs.sector_size = info.size; + fs.sector_count = 3U; + + for (i = 0; i < MAX_ITERATIONS; i++) { + rc = zms_mount(&fs); + if (rc) { + printk("Storage Init failed, rc=%d\n", rc); + return 0; + } + + printk("ITERATION: %u\n", i); + /* IP_ADDRESS_ID is used to store an address, lets see if we can + * read it from flash, since we don't know the size read the + * maximum possible + */ + rc = zms_read(&fs, IP_ADDRESS_ID, &buf, sizeof(buf)); + if (rc > 0) { + /* item was found, show it */ + buf[rc] = '\0'; + printk("ID: %u, IP Address: %s\n", IP_ADDRESS_ID, buf); + } + /* Rewriting ADDRESS IP even if we found it */ + strncpy(buf, "172.16.254.1", sizeof(buf) - 1); + printk("Adding IP_ADDRESS %s at id %u\n", buf, IP_ADDRESS_ID); + rc = zms_write(&fs, IP_ADDRESS_ID, &buf, strlen(buf)); + if (rc < 0) { + printk("Error while writing Entry rc=%d\n", rc); + break; + } + + /* KEY_VALUE_ID is used to store a key/value pair , lets see if we can read + * it from storage. + */ + rc = zms_read(&fs, KEY_VALUE_ID, &key, sizeof(key)); + if (rc > 0) { /* item was found, show it */ + printk("Id: %x, Key: ", KEY_VALUE_ID); + for (int n = 0; n < 8; n++) { + printk("%x ", key[n]); + } + printk("\n"); + } + /* Rewriting KEY_VALUE even if we found it */ + printk("Adding key/value at id %x\n", KEY_VALUE_ID); + rc = zms_write(&fs, KEY_VALUE_ID, &key, sizeof(key)); + if (rc < 0) { + printk("Error while writing Entry rc=%d\n", rc); + break; + } + + /* CNT_ID is used to store the loop counter, lets see + * if we can read it from storage + */ + rc = zms_read(&fs, CNT_ID, &i_cnt, sizeof(i_cnt)); + if (rc > 0) { /* item was found, show it */ + printk("Id: %d, loop_cnt: %u\n", CNT_ID, i_cnt); + if (i_cnt != (i - 1)) { + break; + } + } + printk("Adding counter at id %u\n", CNT_ID); + rc = zms_write(&fs, CNT_ID, &i, sizeof(i)); + if (rc < 0) { + printk("Error while writing Entry rc=%d\n", rc); + break; + } + + /* LONG_DATA_ID is used to store a larger dataset ,lets see if we can read + * it from flash + */ + rc = zms_read(&fs, LONG_DATA_ID, &longarray, sizeof(longarray)); + if (rc > 0) { + /* item was found, show it */ + printk("Id: %d, Longarray: ", LONG_DATA_ID); + for (int n = 0; n < sizeof(longarray); n++) { + printk("%x ", longarray[n]); + } + printk("\n"); + } + /* Rewrite the entry even if we found it */ + printk("Adding Longarray at id %d\n", LONG_DATA_ID); + rc = zms_write(&fs, LONG_DATA_ID, &longarray, sizeof(longarray)); + if (rc < 0) { + printk("Error while writing Entry rc=%d\n", rc); + break; + } + + /* Each DELETE_ITERATION delete all basic items */ + if (!(i % DELETE_ITERATION) && (i)) { + rc = delete_basic_items(&fs); + if (rc) { + break; + } + } + } + + if (i != MAX_ITERATIONS) { + printk("Error: Something went wrong at iteration %u rc=%d\n", i - 1, rc); + return 0; + } + + while (1) { + /* fill all storage */ + rc = zms_write(&fs, id, &id, sizeof(uint32_t)); + if (rc < 0) { + break; + } + id++; + } + + if (rc == -ENOSPC) { + /* Calculate free space and verify that it is 0 */ + free_space = zms_calc_free_space(&fs); + if (free_space < 0) { + printk("Error while computing free space, rc=%d\n", free_space); + return 0; + } + if (free_space > 0) { + printk("Error: free_space should be 0, computed %llu\n", free_space); + return 0; + } + printk("Memory is full let's delete all items\n"); + + /* Now delete all previously written items */ + for (uint32_t n = 0; n < id; n++) { + rc = delete_and_verify_items(&fs, n); + if (rc) { + printk("Error deleting at id %u\n", n); + return 0; + } + } + rc = delete_basic_items(&fs); + if (rc) { + printk("Error deleting basic items\n"); + return 0; + } + } + + /* + * Let's compute free space in storage. But before doing that let's Garbage collect + * all sectors where we deleted all entries and then compute the free space + */ + for (uint32_t i = 0; i < fs.sector_count; i++) { + rc = zms_sector_use_next(&fs); + if (rc) { + printk("Error while changing sector rc=%d\n", rc); + } + } + free_space = zms_calc_free_space(&fs); + if (free_space < 0) { + printk("Error while computing free space, rc=%d\n", free_space); + return 0; + } + printk("Free space in storage is %llu bytes\n", free_space); + printk("Sample code finished Successfully\n"); + + return 0; +}