blob: 2b2c63c32e14fee9568110c93f705d0db0fe12d2 [file] [log] [blame]
/*
* 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 "settings/settings.h"
#include "settings_priv.h"
#include <zephyr/types.h>
#include <logging/log.h>
LOG_MODULE_REGISTER(settings, CONFIG_SETTINGS_LOG_LEVEL);
sys_slist_t settings_handlers;
static u8_t settings_cmd_inited;
static struct settings_handler *settings_handler_lookup(char *name);
void settings_store_init(void);
void settings_init(void)
{
if (!settings_cmd_inited) {
sys_slist_init(&settings_handlers);
settings_store_init();
settings_cmd_inited = 1U;
}
}
int settings_register(struct settings_handler *handler)
{
if (settings_handler_lookup(handler->name)) {
return -EEXIST;
}
sys_slist_prepend(&settings_handlers, &handler->node);
return 0;
}
/*
* Find settings_handler based on name.
*/
static struct settings_handler *settings_handler_lookup(char *name)
{
struct settings_handler *ch;
SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) {
if (!strcmp(name, ch->name)) {
return ch;
}
}
return NULL;
}
/*
* Separate string into argv array.
*/
int settings_parse_name(char *name, int *name_argc, char *name_argv[])
{
int i = 0;
while (name) {
name_argv[i++] = name;
while (1) {
if (*name == '\0') {
name = NULL;
break;
}
if (*name == *SETTINGS_NAME_SEPARATOR) {
*name = '\0';
name++;
break;
}
name++;
}
}
*name_argc = i;
return 0;
}
static struct settings_handler *settings_parse_and_lookup(char *name,
int *name_argc,
char *name_argv[])
{
int rc;
rc = settings_parse_name(name, name_argc, name_argv);
if (rc) {
return NULL;
}
return settings_handler_lookup(name_argv[0]);
}
int settings_set_value_priv(char *name, void *val_read_cb_ctx, off_t off,
u8_t is_runtime)
{
int name_argc;
char *name_argv[SETTINGS_MAX_DIR_DEPTH];
struct settings_handler *ch;
struct read_value_cb_ctx value_ctx;
ch = settings_parse_and_lookup(name, &name_argc, name_argv);
if (!ch) {
return -EINVAL;
}
value_ctx.read_cb_ctx = val_read_cb_ctx;
value_ctx.off = off;
value_ctx.runtime = is_runtime;
return ch->h_set(name_argc - 1, &name_argv[1], (void *)&value_ctx);
}
/* API */
int settings_set_value(char *name, void *val_str, size_t len)
{
struct runtime_value_ctx rt_ctx;
rt_ctx.p_value = val_str;
rt_ctx.size = len;
return settings_set_value_priv(name, &rt_ctx, 0, 1);
}
/* API */
int settings_val_read_cb(void *value_ctx, void *buf, size_t len)
{
struct read_value_cb_ctx *value_context = value_ctx;
size_t len_read;
int rc;
struct runtime_value_ctx *rt_ctx;
if (value_context->runtime) {
rt_ctx = value_context->read_cb_ctx;
len_read = MIN(len, rt_ctx->size);
memcpy(buf, rt_ctx->p_value, len_read);
return len_read;
} else {
rc = settings_line_val_read(value_context->off, 0, buf, len,
&len_read,
value_context->read_cb_ctx);
}
if (rc == 0) {
return len_read;
}
return rc;
}
/* API */
size_t settings_val_get_len_cb(void *value_ctx)
{
struct read_value_cb_ctx *value_context = value_ctx;
struct runtime_value_ctx *rt_ctx;
if (value_context->runtime) {
rt_ctx = value_context->read_cb_ctx;
return rt_ctx->size;
} else {
return settings_line_val_get_len(value_context->off,
value_context->read_cb_ctx);
}
}
/*
* Get value in printable string form. If value is not string, the value
* will be filled in *buf.
* Return value will be pointer to beginning of that buffer,
* except for string it will pointer to beginning of string.
*/
int settings_get_value(char *name, char *buf, int buf_len)
{
int name_argc;
char *name_argv[SETTINGS_MAX_DIR_DEPTH];
struct settings_handler *ch;
ch = settings_parse_and_lookup(name, &name_argc, name_argv);
if (!ch) {
return -EINVAL;
}
if (!ch->h_get) {
return -EINVAL;
}
return ch->h_get(name_argc - 1, &name_argv[1], buf, buf_len);
}
int settings_commit(char *name)
{
int name_argc;
char *name_argv[SETTINGS_MAX_DIR_DEPTH];
struct settings_handler *ch;
int rc;
int rc2;
if (name) {
ch = settings_parse_and_lookup(name, &name_argc, name_argv);
if (!ch) {
return -EINVAL;
}
if (ch->h_commit) {
return ch->h_commit();
} else {
return 0;
}
} else {
rc = 0;
SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) {
if (ch->h_commit) {
rc2 = ch->h_commit();
if (!rc) {
rc = rc2;
}
}
}
return rc;
}
}