diff --git a/samples/subsys/settings/CMakeLists.txt b/samples/subsys/settings/CMakeLists.txt new file mode 100644 index 00000000000..fd908af1234 --- /dev/null +++ b/samples/subsys/settings/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) +#set(QEMU_EXTRA_FLAGS -s) + +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(settings_sample) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/subsys/settings/boards/qemu_x86.conf b/samples/subsys/settings/boards/qemu_x86.conf new file mode 100644 index 00000000000..3746c13c741 --- /dev/null +++ b/samples/subsys/settings/boards/qemu_x86.conf @@ -0,0 +1,2 @@ +CONFIG_NVS=y +CONFIG_SETTINGS_NVS=y diff --git a/samples/subsys/settings/prj.conf b/samples/subsys/settings/prj.conf new file mode 100644 index 00000000000..a37cf73ad72 --- /dev/null +++ b/samples/subsys/settings/prj.conf @@ -0,0 +1,7 @@ +CONFIG_STDOUT_CONSOLE=y +CONFIG_FLASH=y +CONFIG_FLASH_PAGE_LAYOUT=y +CONFIG_FLASH_MAP=y + +CONFIG_SETTINGS=y +CONFIG_SETTINGS_RUNTIME=y diff --git a/samples/subsys/settings/src/main.c b/samples/subsys/settings/src/main.c new file mode 100644 index 00000000000..abc0d195ee0 --- /dev/null +++ b/samples/subsys/settings/src/main.c @@ -0,0 +1,546 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "settings/settings.h" + +#include +#include + +#define GAMMA_DEFAULT_VAl 0 +#define FAIL_MSG "fail (err %d)\n" +#define SECTION_BEGIN_LINE \ + "\n=================================================\n" +/* Default valuse are assigned to settings valuses consuments + * All of them will be overwritten if storage contain proper key-values + */ +u8_t angle_val; +u64_t length_val = 100; +u16_t length_1_val = 40; +u32_t length_2_val = 60; +s32_t voltage_val = -3000; +char source_name_val[6] = ""; + +int alpha_handle_set(const char *name, size_t len, settings_read_cb read_cb, + void *cb_arg); +int alpha_handle_commit(void); +int alpha_handle_export(int (*cb)(const char *name, + const void *value, size_t val_len)); + +int beta_handle_set(const char *name, size_t len, settings_read_cb read_cb, + void *cb_arg); +int beta_handle_commit(void); +int beta_handle_export(int (*cb)(const char *name, + const void *value, size_t val_len)); +int beta_handle_get(const char *name, char *val, int val_len_max); + +/* dynamic main tree handler */ +struct settings_handler alph_handler = { + .name = "alpha", + .h_get = NULL, + .h_set = alpha_handle_set, + .h_commit = alpha_handle_commit, + .h_export = alpha_handle_export +}; + +/* static subtree handler */ +SETTINGS_STATIC_HANDLER_DEFINE(beta, "alpha/beta", beta_handle_get, + beta_handle_set, beta_handle_commit, + beta_handle_export); + +int alpha_handle_set(const char *name, size_t len, settings_read_cb read_cb, + void *cb_arg) +{ + const char *next; + size_t next_len; + int rc; + + if (settings_name_steq(name, "angle/1", &next) && !next) { + if (len != sizeof(angle_val)) { + return -EINVAL; + } + rc = read_cb(cb_arg, &angle_val, sizeof(angle_val)); + printk(" = %d\n", angle_val); + return 0; + } + + next_len = settings_name_next(name, &next); + + if (!next) { + return -ENOENT; + } + + if (!strncmp(name, "length", next_len)) { + next_len = settings_name_next(name, &next); + + if (!next) { + rc = read_cb(cb_arg, &length_val, sizeof(length_val)); + printk(" = %lld\n", length_val); + return 0; + } + + if (!strncmp(next, "1", next_len)) { + rc = read_cb(cb_arg, &length_1_val, + sizeof(length_1_val)); + printk(" = %d\n", length_1_val); + return 0; + } + + if (!strncmp(next, "2", next_len)) { + rc = read_cb(cb_arg, &length_2_val, + sizeof(length_2_val)); + printk(" = %d\n", length_2_val); + return 0; + } + + return -ENOENT; + } + + return -ENOENT; +} + +int beta_handle_set(const char *name, size_t len, settings_read_cb read_cb, + void *cb_arg) +{ + const char *next; + size_t name_len; + int rc; + + name_len = settings_name_next(name, &next); + + if (!next) { + if (!strncmp(name, "voltage", name_len)) { + rc = read_cb(cb_arg, &voltage_val, sizeof(voltage_val)); + printk(" = %d\n", voltage_val); + return 0; + } + + if (!strncmp(name, "source", name_len)) { + if (len > sizeof(source_name_val) - 1) { + printk(" is not compatible " + "with the application\n"); + return -EINVAL; + } + rc = read_cb(cb_arg, source_name_val, + sizeof(source_name_val)); + if (rc < 0) { + return rc; + } else if (rc > 0) { + printk(" = %s\n", + source_name_val); + } + return 0; + } + } + + return -ENOENT; +} + +int alpha_handle_commit(void) +{ + printk("loading all settings under handler is done\n"); + return 0; +} + +int alpha_handle_export(int (*cb)(const char *name, + const void *value, size_t val_len)) +{ + printk("export keys under handler\n"); + (void)cb("alpha/angle/1", &angle_val, sizeof(angle_val)); + (void)cb("alpha/length", &length_val, sizeof(length_val)); + (void)cb("alpha/length/1", &length_1_val, sizeof(length_1_val)); + (void)cb("alpha/length/2", &length_2_val, sizeof(length_2_val)); + + return 0; +} + +int beta_handle_export(int (*cb)(const char *name, + const void *value, size_t val_len)) +{ + printk("export keys under handler\n"); + (void)cb("alpha/beta/voltage", &voltage_val, sizeof(voltage_val)); + (void)cb("alpha/beta/source", source_name_val, strlen(source_name_val) + + 1); + + return 0; +} + +int beta_handle_commit(void) +{ + printk("loading all settings under handler is done\n"); + return 0; +} + +int beta_handle_get(const char *name, char *val, int val_len_max) +{ + const char *next; + + if (settings_name_steq(name, "source", &next) && !next) { + val_len_max = MIN(val_len_max, strlen(source_name_val)); + memcpy(val, source_name_val, val_len_max); + return val_len_max; + } + + return -ENOENT; +} + +static void example_save_and_load_basic(void) +{ + int i, rc; + s32_t val_s32; + + printk(SECTION_BEGIN_LINE); + printk("basic load and save using registered handlers\n"); + /* load all key-values at once + * In case a key-value doesn't exist in the storage + * default valuse should be assigned to settings consuments variable + * before any settings load call + */ + printk("\nload all key-value pairs using registered handlers\n"); + settings_load(); + + val_s32 = voltage_val - 25; + /* save certain key-value directly*/ + printk("\nsave key directly: "); + rc = settings_save_one("alpha/beta/voltage", (const void *)&val_s32, + sizeof(val_s32)); + if (rc) { + printk(FAIL_MSG, rc); + } + + printk("OK.\n"); + + printk("\nload key-value pairs using registered " + "handlers\n"); + settings_load_subtree("alpha/beta"); + + /* save only modified values + * or those that were not saved + * before + */ + i = strlen(source_name_val); + if (i < sizeof(source_name_val) - 1) { + source_name_val[i] = 'a' + i; + source_name_val[i + 1] = 0; + } else { + source_name_val[0] = 0; + } + + angle_val += 1; + + printk("\nsave all key-value pairs using registered handlers\n"); + settings_save(); + + if (++length_1_val > 100) { + length_1_val = 0; + } + + if (--length_2_val > 100) { + length_2_val = 100; + } + + /*--------------------------- + * save only modified values + * or those that were deleted + * before + */ + printk("\nload all key-value pairs using registered handlers\n"); + settings_save(); +} + +struct direct_length_data { + u64_t length; + u16_t length_1; + u32_t length_2; +}; + +static int direct_loader(const char *name, size_t len, settings_read_cb read_cb, + void *cb_arg, void *param) +{ + const char *next; + size_t name_len; + int rc; + struct direct_length_data *dest = (struct direct_length_data *)param; + + printk("direct load: "); + + name_len = settings_name_next(name, &next); + + if (name_len == 0) { + rc = read_cb(cb_arg, &(dest->length), sizeof(dest->length)); + printk("\n"); + return 0; + } + + name_len = settings_name_next(name, &next); + if (next) { + printk("nothing\n"); + return -ENOENT; + } + + if (!strncmp(name, "1", name_len)) { + rc = read_cb(cb_arg, &(dest->length_1), sizeof(dest->length_1)); + printk("\n"); + return 0; + } + + if (!strncmp(name, "2", name_len)) { + rc = read_cb(cb_arg, &(dest->length_2), sizeof(dest->length_2)); + printk("\n"); + return 0; + } + + printk("nothing\n"); + return -ENOENT; +} + +static void example_direct_load_subtree(void) +{ + struct direct_length_data dld; + int rc; + + /* load subtree directly using call-specific handler `direct_loader' + * This handder loads subtree values to call-speciffic structure of type + * 'direct_length_data`. + */ + printk(SECTION_BEGIN_LINE); + printk("loading subtree to destination provided by the caller\n\n"); + rc = settings_load_subtree_direct("alpha/length", direct_loader, + (void *)&dld); + if (rc == 0) { + printk(" direct.length = %lld\n", dld.length); + printk(" direct.length_1 = %d\n", dld.length_1); + printk(" direct.length_2 = %d\n", dld.length_2); + } else { + printk(" direct load fails unexpectedly\n"); + } +} + +struct direct_immediate_value { + size_t len; + void *dest; + u8_t fetched; +}; + +static int direct_loader_immediate_value(const char *name, size_t len, + settings_read_cb read_cb, void *cb_arg, + void *param) +{ + const char *next; + size_t name_len; + int rc; + struct direct_immediate_value *one_value = + (struct direct_immediate_value *)param; + + name_len = settings_name_next(name, &next); + + if (name_len == 0) { + if (len == one_value->len) { + rc = read_cb(cb_arg, one_value->dest, len); + if (rc >= 0) { + one_value->fetched = 1; + printk("immediate load: OK.\n"); + return 0; + } + + printk(FAIL_MSG, rc); + return rc; + } + return -EINVAL; + } + + /* other keys aren't served by the calback + * Return success in order to skip them + * and keep storage processing. + */ + return 0; +} + +int load_immediate_value(const char *name, void *dest, size_t len) +{ + int rc; + struct direct_immediate_value dov; + + dov.fetched = 0; + dov.len = len; + dov.dest = dest; + + rc = settings_load_subtree_direct(name, direct_loader_immediate_value, + (void *)&dov); + if (rc == 0) { + if (!dov.fetched) { + rc = -ENOENT; + } + } + + return rc; +} + +static void example_without_handler(void) +{ + u8_t val_u8; + int rc; + + printk(SECTION_BEGIN_LINE); + printk("Service a key-value pair without dedicated handlers\n\n"); + rc = load_immediate_value("gamma", &val_u8, sizeof(val_u8)); + if (rc == -ENOENT) { + val_u8 = GAMMA_DEFAULT_VAl; + printk(" = %d (default)\n", val_u8); + } else if (rc == 0) { + printk(" = %d\n", val_u8); + } else { + printk("unexpected"FAIL_MSG, rc); + } + + val_u8++; + + printk("save key directly: "); + rc = settings_save_one("gamma", (const void *)&val_u8, + sizeof(val_u8)); + if (rc) { + printk(FAIL_MSG, rc); + } else { + printk("OK.\n"); + } +} + +static void example_initialization(void) +{ + int rc; + + rc = settings_subsys_init(); + if (rc) { + printk("settings subsys initialization: fail (err %d)\n", rc); + return; + } + + printk("settings subsys initialization: OK.\n"); + + rc = settings_register(&alph_handler); + if (rc) { + printk("subtree <%s> handler registered: fail (err %d)\n", + alph_handler.name, rc); + } + + printk("subtree <%s> handler registered: OK\n", alph_handler.name); + printk("subtree has static handler\n"); +} + +static void example_delete(void) +{ + u64_t val_u64; + int rc; + + printk(SECTION_BEGIN_LINE); + printk("Delete a key-value pair\n\n"); + + rc = load_immediate_value("alpha/length", &val_u64, sizeof(val_u64)); + if (rc == 0) { + printk(" value exist in the storage\n"); + } + + printk("delete : "); + rc = settings_delete("alpha/length"); + if (rc) { + printk(FAIL_MSG, rc); + } else { + printk("OK.\n"); + } + + rc = load_immediate_value("alpha/length", &val_u64, sizeof(val_u64)); + if (rc == -ENOENT) { + printk(" Can't to load the value as " + "expected\n"); + } +} + +void example_runtime_usage(void) +{ + int rc; + u8_t injected_str[sizeof(source_name_val)] = "RT"; + + printk(SECTION_BEGIN_LINE); + printk("Inject the value to the setting destination in runtime\n\n"); + + rc = settings_runtime_set("alpha/beta/source", (void *) injected_str, + strlen(injected_str) + 1); + + printk("injected : "); + if (rc) { + printk(FAIL_MSG, rc); + } else { + printk("OK.\n"); + } + + printk(" The settings destination off the key has " + "got value: \"%s\"\n\n", source_name_val); + + /* set settins destination value "by hand" for next example */ + (void) strcpy(source_name_val, "rtos"); + + printk(SECTION_BEGIN_LINE); + printk("Read a value from the setting destination in runtime\n\n"); + + rc = settings_runtime_get("alpha/beta/source", (void *) injected_str, + strlen(injected_str) + 1); + printk("fetched : "); + if (rc < 0) { + printk(FAIL_MSG, rc); + } else { + printk("OK.\n"); + } + + printk(" String value \"%s\" was retrieved from the settings " + "destination off the key \n", + source_name_val); +} + +void main(void) +{ + + int i; + + printk("\n*** Settings usage example ***\n\n"); + + /* settings initialization */ + example_initialization(); + + for (i = 0; i < 6; i++) { + printk("\n##############\n"); + printk("# iteration %d", i); + printk("\n##############\n"); + + /*--------------------------------------------- + * basic save and load using registered handler + */ + example_save_and_load_basic(); + + /*------------------------------------------------- + *load subtree directly using call-specific handler + */ + example_direct_load_subtree(); + + /*------------------------- + * delete certain kay-value + */ + example_delete(); + + /*--------------------------------------- + * a key-value without dedicated handler + */ + example_without_handler(); + } + + /*------------------------------------------------------ + * write and read settings destination using runtime API + */ + example_runtime_usage(); + + printk("\n*** THE END ***\n"); +}