Settings subsystem with non-volatile storage

The settings subsystem gives modules a way to store persistent per-device configuration and runtime state.

Settings items are stored as key-value pair strings. By convention, the keys can be organized by the package and subtree defining the key, for example the key id/serial would define the serial configuration element for the package id.

Convenience routines are provided for converting a key value to and from a string type.

Handlers

Settings handlers for subtree implement a set of handler functions. These are registered using a call to settings_register().

h_get
This gets called when asking for a settings element value by its name using settings_get_value().
h_set
This gets called when the value is being set using settings_set_value(), and also when setting is loaded from persisted storage with settings_load().
h_commit
This gets called after the settings have been loaded in full. Sometimes you don’t want an individual setting value to take effect right away, for example if there are multiple settings which are interdependent.
h_export
This gets called to write all current settings. This happens when settings_save() tries to save the settings or transfer to any user-implemented back-end.

Persistence

Backend storage for the settings can be either FCB, a file in the filesystem, or both.

You can declare multiple sources for settings; settings from all of these are restored when settings_load() is called.

There can be only one target for writing settings; this is where data is stored when you call settings_save(), or settings_save_one().

FCB read target is registered using settings_fcb_src(), and write target using settings_fcb_dst(). As a side-effect, settings_fcb_src() initializes the FCB area, so it must be called before calling settings_fcb_dst(). File read target is registered using settings_file_src(), and write target by using settings_file_dst().

Example: Device Configuration

This is a simple example, where the settings handler only implements h_set and h_export. h_set is called when the value is restored from storage (or when set initially), and h_export is used to write the value to storage thanks to storage_func(). The user can also implement some other export functionality, for example, writing to the shell console).

static int8 foo_val;

struct settings_handler my_conf = {
    .name = "foo",
    .h_set = foo_settings_set,
    .h_export = foo_settings_export
};

static int foo_settings_set(int argc, char **argv, char *val)
{
    if (argc == 1) {
        if (!strcmp(argv[0], "bar")) {
            return SETTINGS_VALUE_SET(val, SETTINGS_INT8, foo_val);
        }
    }

    return -ENOENT;
}

static int foo_settings_export(void (*storage_func)(char *name, char *val),
                           enum settings_export_tgt tgt)
{
    char buf[4];

    settings_str_from_value(SETTINGS_INT8, &foo_val, buf, sizeof(buf));
    storage_func("foo/bar", buf)

    return 0;
}

Example: Persist Runtime State

This is a simple example showing how to persist runtime state. In this example, only h_set is defined, which is used when restoring value from persisted storage.

In this example, the foo_callout function increments foo_val, and then persists the latest number. When the system restarts, the application calls settings_load() while initializing, and foo_val will continue counting up from where it was before restart.

static int8 foo_val;

struct settings_handler my_conf = {
    .name = "foo",
    .h_set = foo_settings_set
};

static int foo_settings_set(int argc, char **argv, char *val)
{
    if (argc == 1) {
        if (!strcmp(argv[0], "bar")) {
            return SETTINGS_VALUE_SET(val, SETTINGS_INT8, foo_val);
        }
    }

    return -ENOENT;
}

static void foo_callout(struct os_event *ev)
{
    struct os_callout *c = (struct os_callout *)ev;
    char buf[4];

    foo_val++;
    settings_str_from_value(SETTINGS_INT8, &foo_val, buf, sizeof(buf));
    settings_save_one("foo/bar", bar);

    k_sleep(1000);
    sys_reboot(SYS_REBOOT_COLD);
}

API

The Settings subsystem APIs are provided by settings.h:

group settings

Defines

SETTINGS_MAX_DIR_DEPTH
SETTINGS_MAX_NAME_LEN
SETTINGS_MAX_VAL_LEN
SETTINGS_NAME_SEPARATOR
SETTINGS_EXTRA_LEN
SETTINGS_NMGR_OP

Functions

int settings_subsys_init(void)

Initialization of settings and backend

Can be called at application startup. In case the backend is NFFS Remember to call it after FS was mounted. For FCB backend it can be called without such a restriction.

Return
0 on success, non-zero on failure.

int settings_register(struct settings_handler *cf)

Register a handler for settings items.

Return
0 on success, non-zero on failure.
Parameters
  • cf: Structure containing registration info.

int settings_load(void)

Load serialized items from registered persistence sources. Handlers for serialized item subtrees registered earlier will be called for encountered values.

Return
0 on success, non-zero on failure.

int settings_save(void)

Save currently running serialized items. All serialized items which are different from currently persisted values will be saved.

Return
0 on success, non-zero on failure.

int settings_save_one(const char *name, void *value, size_t val_len)

Write a single serialized value to persisted storage (if it has changed value).

Return
0 on success, non-zero on failure.
Parameters
  • name: Name/key of the settings item.
  • value: Pointer to the value of the settings item. This value will be transferred to the settings_handler::h_export handler implementation.
  • val_len: Length of the value.

int settings_delete(const char *name)

Delete a single serialized in persisted storage.

Deleting an existing key-value pair in the settings mean to set its value to NULL.

Return
0 on success, non-zero on failure.
Parameters
  • name: Name/key of the settings item.

int settings_set_value(char *name, void *value, size_t len)

Set settings item identified by name to be value value. This finds the settings handler for this subtree and calls it’s set handler.

Return
0 on success, non-zero on failure.
Parameters
  • name: Name/key of the settings item.
  • value: Pointer to the value of the settings item. This value will be transferred to the settings_handler::h_set handler implementation.
  • len: Length of value string.

int settings_get_value(char *name, char *buf, int buf_len)

Get value of settings item identified by name. This calls the settings handler h_get for the subtree.

Configuration handler should copy the string to buf, the maximum number of bytes it will copy is limited by buf_len.

Return
Positive: Length of copied dat. Negative: -ERCODE
Parameters
  • name: Name/key of the settings item.
  • buf: buffer for value of the settings item. If value is not string, the value will be filled in *buf.
  • buf_len: size of buf.

int settings_commit(char *name)

Call commit for all settings handler. This should apply all settings which has been set, but not applied yet.

Return
0 on success, non-zero on failure.
Parameters
  • name: Name of the settings subtree, or NULL to commit everything.

int settings_val_read_cb(void *value_ctx, void *buf, size_t len)

Persistent data extracting routine.

This function read and decode data from non volatile storage to user buffer This function should be used inside set handler in order to read the settings data from backend storage.

h_set

Parameters
  • value_ctx: Data contex provided by the

handler.

buf

Parameters
  • buf: Buffer for data read.
  • len: Length of

.

buf

Return Value
  • Negative: value on failure. 0 and positive: Length of data loaded to the

.

size_t settings_val_get_len_cb(void *value_ctx)

This function fetch length of decode data. This function should be used inside set handler in order to detect the settings data length.

h_set

Parameters
  • value_ctx: Data contex provided by the

handler.

Return Value
  • length: of data.

struct settings_handler
#include <settings.h>

Config handlers for subtree implement a set of handler functions. These are registered using a call to settings_register.