-
Notifications
You must be signed in to change notification settings - Fork 6.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
samples: fs: zms: add a sample app for ZMS storage system
This adds a user application that shows the usage of ZMS The sample app shows three main functions of ZMS: - read/write/delete key/value pairs - fill all storage and delete it - calculate free remaining space Signed-off-by: Riadh Ghaddab <rghaddab@baylibre.com>
- Loading branch information
1 parent
004d6d6
commit fb7dae7
Showing
5 changed files
with
388 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
.. 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/VALUE pair is also shown in this sample. | ||
|
||
The sample stores the following items: | ||
|
||
#. A string representing an IP address: stored at id=1, data="192.168.1.1" | ||
#. A binary blob representing a key/value pair: stored at id=0xbeefdead, | ||
data={0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF} | ||
#. A variable (32bit): stored at id=2, data=cnt | ||
#. 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] <inf> fs_zms: 3 Sectors of 4096 bytes | ||
[00:00:00.000,000] <inf> fs_zms: alloc wra: 0, fc0 | ||
[00:00:00.000,000] <inf> 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] <inf> fs_zms: 3 Sectors of 4096 bytes | ||
[00:00:00.000,000] <inf> fs_zms: alloc wra: 0, f80 | ||
[00:00:00.000,000] <inf> 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] <inf> fs_zms: 3 Sectors of 4096 bytes | ||
[00:00:00.000,000] <inf> fs_zms: alloc wra: 0, f40 | ||
[00:00:00.000,000] <inf> 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
CONFIG_FLASH=y | ||
CONFIG_FLASH_MAP=y | ||
|
||
CONFIG_ZMS=y | ||
CONFIG_LOG=y |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
sample: | ||
name: ZMS Sample | ||
|
||
tests: | ||
sample.zms.basic: | ||
tags: zms | ||
depends_on: zms | ||
platform_allow: | ||
- qemu_x86 | ||
- native_posix |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,267 @@ | ||
/* | ||
* Copyright (c) 2024 BayLibre SAS | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
* | ||
* ZMS Sample for Zephyr using high level API. | ||
* | ||
*/ | ||
|
||
#include <zephyr/kernel.h> | ||
#include <zephyr/sys/reboot.h> | ||
#include <zephyr/device.h> | ||
#include <string.h> | ||
#include <zephyr/drivers/flash.h> | ||
#include <zephyr/storage/flash_map.h> | ||
#include <zephyr/fs/zms.h> | ||
|
||
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; | ||
ssize_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 %u\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 %u bytes\n", free_space); | ||
printk("Sample code finished Successfully\n"); | ||
|
||
return 0; | ||
} |