Until now iterable sections APIs have been part of the toolchain (common) headers. They are not strictly related to a toolchain, they just rely on linker providing support for sections. Most files relied on indirect includes to access the API, now, it is included as needed. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
273 lines
5.3 KiB
C
273 lines
5.3 KiB
C
/*
|
|
* Copyright (c) 2018 Nordic Semiconductor ASA
|
|
* Copyright (c) 2015 Runtime Inc
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <errno.h>
|
|
#include <zephyr/kernel.h>
|
|
|
|
#include <zephyr/settings/settings.h>
|
|
#include "settings_priv.h"
|
|
#include <zephyr/types.h>
|
|
#include <zephyr/sys/iterable_sections.h>
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(settings, CONFIG_SETTINGS_LOG_LEVEL);
|
|
|
|
#if defined(CONFIG_SETTINGS_DYNAMIC_HANDLERS)
|
|
sys_slist_t settings_handlers;
|
|
#endif /* CONFIG_SETTINGS_DYNAMIC_HANDLERS */
|
|
|
|
K_MUTEX_DEFINE(settings_lock);
|
|
|
|
|
|
void settings_store_init(void);
|
|
|
|
void settings_init(void)
|
|
{
|
|
#if defined(CONFIG_SETTINGS_DYNAMIC_HANDLERS)
|
|
sys_slist_init(&settings_handlers);
|
|
#endif /* CONFIG_SETTINGS_DYNAMIC_HANDLERS */
|
|
settings_store_init();
|
|
}
|
|
|
|
#if defined(CONFIG_SETTINGS_DYNAMIC_HANDLERS)
|
|
int settings_register(struct settings_handler *handler)
|
|
{
|
|
int rc = 0;
|
|
|
|
STRUCT_SECTION_FOREACH(settings_handler_static, ch) {
|
|
if (strcmp(handler->name, ch->name) == 0) {
|
|
return -EEXIST;
|
|
}
|
|
}
|
|
|
|
k_mutex_lock(&settings_lock, K_FOREVER);
|
|
|
|
struct settings_handler *ch;
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) {
|
|
if (strcmp(handler->name, ch->name) == 0) {
|
|
rc = -EEXIST;
|
|
goto end;
|
|
}
|
|
}
|
|
sys_slist_append(&settings_handlers, &handler->node);
|
|
|
|
end:
|
|
k_mutex_unlock(&settings_lock);
|
|
return rc;
|
|
}
|
|
#endif /* CONFIG_SETTINGS_DYNAMIC_HANDLERS */
|
|
|
|
int settings_name_steq(const char *name, const char *key, const char **next)
|
|
{
|
|
if (next) {
|
|
*next = NULL;
|
|
}
|
|
|
|
if ((!name) || (!key)) {
|
|
return 0;
|
|
}
|
|
|
|
/* name might come from flash directly, in flash the name would end
|
|
* with '=' or '\0' depending how storage is done. Flash reading is
|
|
* limited to what can be read
|
|
*/
|
|
|
|
while ((*key != '\0') && (*key == *name) &&
|
|
(*name != '\0') && (*name != SETTINGS_NAME_END)) {
|
|
key++;
|
|
name++;
|
|
}
|
|
|
|
if (*key != '\0') {
|
|
return 0;
|
|
}
|
|
|
|
if (*name == SETTINGS_NAME_SEPARATOR) {
|
|
if (next) {
|
|
*next = name + 1;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
if ((*name == SETTINGS_NAME_END) || (*name == '\0')) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int settings_name_next(const char *name, const char **next)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (next) {
|
|
*next = NULL;
|
|
}
|
|
|
|
if (!name) {
|
|
return 0;
|
|
}
|
|
|
|
/* name might come from flash directly, in flash the name would end
|
|
* with '=' or '\0' depending how storage is done. Flash reading is
|
|
* limited to what can be read
|
|
*/
|
|
while ((*name != '\0') && (*name != SETTINGS_NAME_END) &&
|
|
(*name != SETTINGS_NAME_SEPARATOR)) {
|
|
rc++;
|
|
name++;
|
|
}
|
|
|
|
if (*name == SETTINGS_NAME_SEPARATOR) {
|
|
if (next) {
|
|
*next = name + 1;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
struct settings_handler_static *settings_parse_and_lookup(const char *name,
|
|
const char **next)
|
|
{
|
|
struct settings_handler_static *bestmatch;
|
|
const char *tmpnext;
|
|
|
|
bestmatch = NULL;
|
|
if (next) {
|
|
*next = NULL;
|
|
}
|
|
|
|
STRUCT_SECTION_FOREACH(settings_handler_static, ch) {
|
|
if (!settings_name_steq(name, ch->name, &tmpnext)) {
|
|
continue;
|
|
}
|
|
if (!bestmatch) {
|
|
bestmatch = ch;
|
|
if (next) {
|
|
*next = tmpnext;
|
|
}
|
|
continue;
|
|
}
|
|
if (settings_name_steq(ch->name, bestmatch->name, NULL)) {
|
|
bestmatch = ch;
|
|
if (next) {
|
|
*next = tmpnext;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_SETTINGS_DYNAMIC_HANDLERS)
|
|
struct settings_handler *ch;
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) {
|
|
if (!settings_name_steq(name, ch->name, &tmpnext)) {
|
|
continue;
|
|
}
|
|
if (!bestmatch) {
|
|
bestmatch = (struct settings_handler_static *)ch;
|
|
if (next) {
|
|
*next = tmpnext;
|
|
}
|
|
continue;
|
|
}
|
|
if (settings_name_steq(ch->name, bestmatch->name, NULL)) {
|
|
bestmatch = (struct settings_handler_static *)ch;
|
|
if (next) {
|
|
*next = tmpnext;
|
|
}
|
|
}
|
|
}
|
|
#endif /* CONFIG_SETTINGS_DYNAMIC_HANDLERS */
|
|
return bestmatch;
|
|
}
|
|
|
|
int settings_call_set_handler(const char *name,
|
|
size_t len,
|
|
settings_read_cb read_cb,
|
|
void *read_cb_arg,
|
|
const struct settings_load_arg *load_arg)
|
|
{
|
|
int rc;
|
|
const char *name_key = name;
|
|
|
|
if (load_arg && load_arg->subtree &&
|
|
!settings_name_steq(name, load_arg->subtree, &name_key)) {
|
|
return 0;
|
|
}
|
|
|
|
if (load_arg && load_arg->cb) {
|
|
rc = load_arg->cb(name_key, len, read_cb, read_cb_arg,
|
|
load_arg->param);
|
|
} else {
|
|
struct settings_handler_static *ch;
|
|
|
|
ch = settings_parse_and_lookup(name, &name_key);
|
|
if (!ch) {
|
|
return 0;
|
|
}
|
|
|
|
rc = ch->h_set(name_key, len, read_cb, read_cb_arg);
|
|
|
|
if (rc != 0) {
|
|
LOG_ERR("set-value failure. key: %s error(%d)",
|
|
name, rc);
|
|
/* Ignoring the error */
|
|
rc = 0;
|
|
} else {
|
|
LOG_DBG("set-value OK. key: %s",
|
|
name);
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int settings_commit(void)
|
|
{
|
|
return settings_commit_subtree(NULL);
|
|
}
|
|
|
|
int settings_commit_subtree(const char *subtree)
|
|
{
|
|
int rc;
|
|
int rc2;
|
|
|
|
rc = 0;
|
|
|
|
STRUCT_SECTION_FOREACH(settings_handler_static, ch) {
|
|
if (subtree && !settings_name_steq(ch->name, subtree, NULL)) {
|
|
continue;
|
|
}
|
|
if (ch->h_commit) {
|
|
rc2 = ch->h_commit();
|
|
if (!rc) {
|
|
rc = rc2;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_SETTINGS_DYNAMIC_HANDLERS)
|
|
struct settings_handler *ch;
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) {
|
|
if (subtree && !settings_name_steq(ch->name, subtree, NULL)) {
|
|
continue;
|
|
}
|
|
if (ch->h_commit) {
|
|
rc2 = ch->h_commit();
|
|
if (!rc) {
|
|
rc = rc2;
|
|
}
|
|
}
|
|
}
|
|
#endif /* CONFIG_SETTINGS_DYNAMIC_HANDLERS */
|
|
|
|
return rc;
|
|
}
|