| /* |
| * 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 <kernel.h> |
| |
| #include "settings/settings.h" |
| #include "settings_priv.h" |
| #include <zephyr/types.h> |
| |
| #include <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)", |
| log_strdup(name), rc); |
| /* Ignoring the error */ |
| rc = 0; |
| } else { |
| LOG_DBG("set-value OK. key: %s", |
| log_strdup(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; |
| } |