subsys: Add a new settings subsystem

Adapt the MyNewt non-volatile configuration system to become a settings
system in Zephyr.
The original code was modifed in the following ways:

* Renamed from config to settings
* Use the zephyr FCB, FS API, and base64 subsystems
* lltoa like function was added to sources as it was required but not
  included in Zephyr itself.
* The original code was modified to use Zephyr's slist.h as single
  linked list implementation.
* Reworked code which was using strtok_r, added function
  for decoding a string to a s64_t value.
* Thank to the above the settings subsys doesn't require newlibc anymore.

Signed-off-by: Andrzej Puzdrowski <andrzej.puzdrowski@nordicsemi.no>
Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
This commit is contained in:
Andrzej Puzdrowski 2018-02-02 14:30:56 +01:00 committed by Anas Nashif
commit 94ff339cbf
45 changed files with 3526 additions and 0 deletions

268
include/settings/settings.h Normal file
View file

@ -0,0 +1,268 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __SETTINGS_H_
#define __SETTINGS_H_
#include <misc/util.h>
#include <misc/slist.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup settings system
* @{
*/
#define SETTINGS_MAX_DIR_DEPTH 8 /* max depth of settings tree */
#define SETTINGS_MAX_NAME_LEN (8 * SETTINGS_MAX_DIR_DEPTH)
#define SETTINGS_MAX_VAL_LEN 256
#define SETTINGS_NAME_SEPARATOR "/"
/* pleace for settings additions:
* up to 7 separators, '=', '\0'
*/
#define SETTINGS_EXTRA_LEN ((SETTINGS_MAX_DIR_DEPTH - 1) + 2)
#define SETTINGS_NMGR_OP 0
/**
* Type of settings value.
*/
enum settings_type {
SETTINGS_NONE = 0,
SETTINGS_INT8,
SETTINGS_INT16,
SETTINGS_INT32,
SETTINGS_INT64,
SETTINGS_STRING,
SETTINGS_BYTES,
SETTINGS_FLOAT,
SETTINGS_DOUBLE,
SETTINGS_BOOL,
} __attribute__((__packed__));
/**
* Parameter to commit handler describing where data is going to.
*/
enum settings_export_tgt {
SETTINGS_EXPORT_PERSIST, /* Value is to be persisted. */
SETTINGS_EXPORT_SHOW /* Value is to be displayed. */
};
/**
* @struct settings_handler
* Config handlers for subtree implement a set of handler functions.
* These are registered using a call to settings_register().
*
* @param settings_handler::node Linked list node info for module internal usage.
*
* @param settings_handler::name Name of subtree.
*
* @param settings_handler::h_get Get values handler of settings items
* identified by keyword names.Parameters:
* - argc - count of item in argv.
* - argv - array of pointers to keyword names.
* - val - buffer for a value.
* - val_len_max - size of that buffer.
*
* @param settings_handler::h_set Sey value handler of settings items
* identified by keyword names. Parameters:
* - argc - count of item in argv, argv - array of pointers to keyword names.
* - val- pointer to value to be set.
*
* @param settings_handler::h_commit This handler gets called after settings
* has been loaded in full. User might use it to apply setting to
* the application.
*
* @param settings_handler::h_export This gets called to dump all current
* settings items.
* This happens when settings_save() tries to save the settings. Parameters:
* - tgt: indicates where data is going to.
* - Export_function: the pointer to the internal function which appends
* a single key-value pair to persisted settings. Don't store duplicated
* value. The name is subtree/key string, val is the string with
* value.
*
* @remarks The User might limit a implementations of handler to serving only
* one keyword at one call - what will impose limit to get/set values using full
* subtree/key name.
*/
struct settings_handler {
sys_snode_t node;
char *name;
char *(*h_get)(int argc, char **argv, char *val, int val_len_max);
int (*h_set)(int argc, char **argv, char *val);
int (*h_commit)(void);
int (*h_export)(void (*export_func)(char *name, char *val),
enum settings_export_tgt tgt);
};
/**
* 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.
*/
void settings_subsys_init(void);
/**
* Register a handler for settings items.
*
* @param cf Structure containing registration info.
*
* @return 0 on success, non-zero on failure.
*/
int settings_register(struct settings_handler *cf);
/**
* 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_load(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(void);
/**
* Write a single serialized value to persisted storage (if it has
* changed value).
*
* @param name Name/key of the settings item.
* @param var Value of the settings item.
*
* @return 0 on success, non-zero on failure.
*/
int settings_save_one(const char *name, char *var);
/**
* Set settings item identified by @p name to be value @p val_str.
* This finds the settings handler for this subtree and calls it's
* set handler.
*
* @param name Name/key of the settings item.
* @param val_str Value of the settings item.
*
* @return 0 on success, non-zero on failure.
*/
int settings_set_value(char *name, char *val_str);
/**
* Get value of settings item identified by @p name.
* This calls the settings handler h_get for the subtree.
*
* Configuration handler can copy the string to @p buf, the maximum
* number of bytes it will copy is limited by @p buf_len.
*
* @param name Name/key of the settings item.
*
* @param buf buffer for value of the settings item.
* If value is not string, the value will be filled in *buf.
*
* @param buf_len size of buf.
*
* @return value will be pointer to beginning of the buf,
* except for string it will pointer to beginning of string source.
*/
char *settings_get_value(char *name, char *buf, int buf_len);
/**
* Call commit for all settings handler. This should apply all
* settings which has been set, but not applied yet.
*
* @param name Name of the settings subtree, or NULL to commit everything.
*
* @return 0 on success, non-zero on failure.
*/
int settings_commit(char *name);
/**
* Convenience routine for converting value passed as a string to native
* data type.
*
* @param val_str Value of the settings item as string.
* @param type Type of the value to convert to.
* @param vp Pointer to variable to fill with the decoded value.
* @param maxlen the vp buffer size.
*
* @return 0 on success, non-zero on failure.
*/
int settings_value_from_str(char *val_str, enum settings_type type, void *vp,
int maxlen);
/**
* Convenience routine for converting byte array passed as a base64
* encoded string.
*
* @param val_str Value of the settings item as string.
* @param vp Pointer to variable to fill with the decoded value.
* @param len Size of that variable. On return the number of bytes in the array.
*
* @return 0 on success, non-zero on failure.
*/
int settings_bytes_from_str(char *val_str, void *vp, int *len);
/**
* Convenience routine for converting native data type to a string.
*
* @param type Type of the value to convert from.
* @param vp Pointer to variable to convert.
* @param buf Buffer where string value will be stored.
* @param buf_len Size of the buffer.
*
* @return 0 on success, non-zero on failure.
*/
char *settings_str_from_value(enum settings_type type, void *vp, char *buf,
int buf_len);
#define SETTINGS_STR_FROM_BYTES_LEN(len) (((len) * 4 / 3) + 4)
/**
* Convenience routine for converting byte array into a base64
* encoded string.
*
* @param vp Pointer to variable to convert.
* @param vp_len Number of bytes to convert.
* @param buf Buffer where string value will be stored.
* @param buf_len Size of the buffer.
*
* @return 0 on success, non-zero on failure.
*/
char *settings_str_from_bytes(void *vp, int vp_len, char *buf, int buf_len);
#define SETTINGS_VALUE_SET(str, type, val) \
settings_value_from_str((str), (type), &(val), sizeof(val))
/**
* @} settings
*/
/*
* Config storage
*/
struct settings_store_itf;
struct settings_store {
sys_snode_t cs_next;
const struct settings_store_itf *cs_itf;
};
#ifdef __cplusplus
}
#endif
#endif /* __SETTINGS_H_ */

View file

@ -12,3 +12,4 @@ add_subdirectory_ifdef(CONFIG_NET_BUF net)
add_subdirectory_ifdef(CONFIG_USB usb)
add_subdirectory(random)
add_subdirectory(storage)
add_subdirectory_ifdef(CONFIG_SETTINGS settings)

View file

@ -32,3 +32,5 @@ source "subsys/dfu/Kconfig"
source "subsys/random/Kconfig"
source "subsys/storage/Kconfig"
source "subsys/settings/Kconfig"

View file

@ -0,0 +1,4 @@
add_subdirectory(src)
zephyr_include_directories(
include
)

87
subsys/settings/Kconfig Normal file
View file

@ -0,0 +1,87 @@
#
# Copyright (c) 2018 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
menuconfig SETTINGS
bool
default n
prompt "Enable settings subsystem with non-volatile storage"
depends on FILE_SYSTEM || FCB
select BASE64
help
The settings subsystem allows its users to serialize and
deserialize state in memory into and from non-volatile memory.
It supports several back-ends to store and load serialized data from
and it can do so atomically for all involved modules.
choice
prompt "Storage back-end"
default SETTINGS_FCB if FCB
depends on SETTINGS
help
Storage back-end to be used by the settings subsystem.
config SETTINGS_FCB
bool "FCB"
depends on FCB
help
Use FCB as a settings storage back-end.
config SETTINGS_FS
bool "File System"
depends on FILE_SYSTEM
help
Use a file system as a settings storage back-end.
endchoice
config SETTINGS_FCB_NUM_AREAS
int
default 8
depends on SETTINGS && SETTINGS_FCB
prompt "Number of flash areas used by the settings subsystem "
help
Number of areas to allocate in the settings FCB. A smaller number is
used if the flash hardware cannot support this value.
config SETTINGS_FCB_MAGIC
hex
prompt "FCB magic for the settings subsystem"
default 0xc0ffeeee
depends on SETTINGS && SETTINGS_FCB
help
Magic 32-bit word for to identify valid settings area
config SETTINGS_FCB_FLASH_AREA
int
prompt "Flash area id used for settings"
default 4
depends on SETTINGS && SETTINGS_FCB
help
Id of the Flash area where FCB instance used for settings is
expected to operate.
config SETTINGS_FS_DIR
string
prompt "Serialization directory"
default "/settings"
depends on SETTINGS && SETTINGS_FS
help
Directory where the settings data is stored
config SETTINGS_FS_FILE
string
prompt "Default settings file"
default "/settings/run"
depends on SETTINGS && SETTINGS_FS
help
Full path to the default settings file.
config SETTINGS_FS_MAX_LINES
int
prompt "Compression threshold"
default 32
depends on SETTINGS && SETTINGS_FS
help
'Limit how many items stored in a file before compressing'

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __SETTINGS_FCB_H_
#define __SETTINGS_FCB_H_
#include <fcb.h>
#include "settings/settings.h"
#ifdef __cplusplus
extern "C" {
#endif
struct settings_fcb {
struct settings_store cf_store;
struct fcb cf_fcb;
};
extern int settings_fcb_src(struct settings_fcb *cf);
extern int settings_fcb_dst(struct settings_fcb *cf);
#ifdef __cplusplus
}
#endif
#endif /* __SETTINGS_FCB_H_ */

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __SETTINGS_FILE_H_
#define __SETTINGS_FILE_H_
#include "settings/settings.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SETTINGS_FILE_NAME_MAX 32 /* max length for settings filename */
struct settings_file {
struct settings_store cf_store;
const char *cf_name; /* filename */
int cf_maxlines; /* max # of lines before compressing */
int cf_lines; /* private */
};
/* register file to be source of settings */
int settings_file_src(struct settings_file *cf);
/* settings saves go to a file */
int settings_file_dst(struct settings_file *cf);
#ifdef __cplusplus
}
#endif
#endif /* __SETTINGS_FILE_H_ */

View file

@ -0,0 +1,11 @@
zephyr_include_directories($ENV{ZEPHYR_BASE}/ext/fs/nffs/include)
zephyr_sources(
settings_store.c
settings.c
settings_init.c
settings_line.c
)
zephyr_sources_ifdef(CONFIG_SETTINGS_FS settings_file.c)
zephyr_sources_ifdef(CONFIG_SETTINGS_FCB settings_fcb.c)

View file

@ -0,0 +1,388 @@
/*
* 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 "base64.h"
#include "settings/settings.h"
#include "settings_priv.h"
#include <zephyr/types.h>
/* mbedtls-base64 lib encodes data to null-terminated string */
#define BASE64_ENCODE_SIZE(in_size) ((((((in_size) - 1) / 3) * 4) + 4) + 1)
sys_slist_t settings_handlers;
static u8_t settings_cmd_inited;
void settings_store_init(void);
static void s64_to_dec(char *ptr, int buf_len, s64_t value, int base);
static s64_t dec_to_s64(char *p_str, char **e_ptr);
void settings_init(void)
{
if (!settings_cmd_inited) {
sys_slist_init(&settings_handlers);
settings_store_init();
settings_cmd_inited = 1;
}
}
int settings_register(struct settings_handler *handler)
{
sys_slist_prepend(&settings_handlers, &handler->node);
return 0;
}
/*
* Find settings_handler based on name.
*/
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_value_from_str(char *val_str, enum settings_type type, void *vp,
int maxlen)
{
s32_t val;
s64_t val64;
char *eptr;
if (!val_str) {
goto err;
}
switch (type) {
case SETTINGS_INT8:
case SETTINGS_INT16:
case SETTINGS_INT32:
case SETTINGS_BOOL:
val = strtol(val_str, &eptr, 0);
if (*eptr != '\0') {
goto err;
}
if (type == SETTINGS_BOOL) {
if (val < 0 || val > 1) {
goto err;
}
*(bool *)vp = val;
} else if (type == SETTINGS_INT8) {
if (val < INT8_MIN || val > UINT8_MAX) {
goto err;
}
*(int8_t *)vp = val;
} else if (type == SETTINGS_INT16) {
if (val < INT16_MIN || val > UINT16_MAX) {
goto err;
}
*(int16_t *)vp = val;
} else if (type == SETTINGS_INT32) {
*(s32_t *)vp = val;
}
break;
case SETTINGS_INT64:
val64 = dec_to_s64(val_str, &eptr);
if (*eptr != '\0') {
goto err;
}
*(s64_t *)vp = val64;
break;
case SETTINGS_STRING:
val = strlen(val_str);
if (val + 1 > maxlen) {
goto err;
}
strcpy(vp, val_str);
break;
default:
goto err;
}
return 0;
err:
return -EINVAL;
}
int settings_bytes_from_str(char *val_str, void *vp, int *len)
{
int err;
int rc;
err = base64_decode(vp, *len, &rc, val_str, strlen(val_str));
if (err) {
return -1;
}
*len = rc;
return 0;
}
char *settings_str_from_value(enum settings_type type, void *vp, char *buf,
int buf_len)
{
s32_t val;
if (type == SETTINGS_STRING) {
return vp;
}
switch (type) {
case SETTINGS_INT8:
case SETTINGS_INT16:
case SETTINGS_INT32:
case SETTINGS_BOOL:
if (type == SETTINGS_BOOL) {
val = *(bool *)vp;
} else if (type == SETTINGS_INT8) {
val = *(int8_t *)vp;
} else if (type == SETTINGS_INT16) {
val = *(int16_t *)vp;
} else {
val = *(s32_t *)vp;
}
snprintf(buf, buf_len, "%ld", (long)val);
return buf;
case SETTINGS_INT64:
s64_to_dec(buf, buf_len, *(s64_t *)vp, 10);
return buf;
default:
return NULL;
}
}
static void u64_to_dec(char *ptr, int buf_len, u64_t value, int base)
{
u64_t t = 0, res = 0;
u64_t tmp = value;
int count = 0;
if (ptr == NULL) {
return;
}
if (tmp == 0) {
count++;
}
while (tmp > 0) {
tmp = tmp/base;
count++;
}
ptr += count;
*ptr = '\0';
do {
res = value - base * (t = value / base);
if (res < 10) {
*--ptr = '0' + res;
} else if ((res >= 10) && (res < 16)) {
*--ptr = 'A' - 10 + res;
}
value = t;
} while (value != 0);
}
static void s64_to_dec(char *ptr, int buf_len, s64_t value, int base)
{
u64_t val64;
if (ptr == NULL || buf_len < 1) {
return;
}
if (value < 0) {
*ptr = '-';
ptr++;
buf_len--;
val64 = value * (-1);
} else {
val64 = value;
}
u64_to_dec(ptr, buf_len, val64, base);
}
static s64_t dec_to_s64(char *p_str, char **e_ptr)
{
u64_t val = 0, prev_val = 0;
bool neg = false;
int digit;
if (*p_str == '-') {
neg = true;
p_str++;
} else if (*p_str == '+') {
p_str++;
}
while (1) {
if (*p_str >= '0' && *p_str <= '9') {
digit = *p_str - '0';
} else {
break;
}
val *= 10;
val += digit;
/* this is only a fuse */
if (val < prev_val) {
break;
}
prev_val = val;
p_str++;
}
if (e_ptr != 0)
*e_ptr = p_str;
if (neg) {
return -val;
} else {
return val;
}
}
char *settings_str_from_bytes(void *vp, int vp_len, char *buf, int buf_len)
{
if (BASE64_ENCODE_SIZE(vp_len) > buf_len) {
return NULL;
}
size_t enc_len;
base64_encode(buf, buf_len, &enc_len, vp, vp_len);
return buf;
}
int settings_set_value(char *name, char *val_str)
{
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;
}
return ch->h_set(name_argc - 1, &name_argv[1], val_str);
}
/*
* 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.
*/
char *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 NULL;
}
if (!ch->h_get) {
return NULL;
}
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;
}
}

View file

@ -0,0 +1,257 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <errno.h>
#include <fcb.h>
#include <string.h>
#include "settings/settings.h"
#include "settings/settings_fcb.h"
#include "settings_priv.h"
#define SETTINGS_FCB_VERS 1
struct settings_fcb_load_cb_arg {
load_cb cb;
void *cb_arg;
};
static int settings_fcb_load(struct settings_store *cs, load_cb cb,
void *cb_arg);
static int settings_fcb_save(struct settings_store *cs, const char *name,
const char *value);
static struct settings_store_itf settings_fcb_itf = {
.csi_load = settings_fcb_load,
.csi_save = settings_fcb_save,
};
int settings_fcb_src(struct settings_fcb *cf)
{
int rc;
cf->cf_fcb.f_version = SETTINGS_FCB_VERS;
cf->cf_fcb.f_scratch_cnt = 1;
while (1) {
rc = fcb_init(CONFIG_SETTINGS_FCB_FLASH_AREA, &cf->cf_fcb);
if (rc) {
return -EINVAL;
}
/*
* Check if system was reset in middle of emptying a sector.
* This situation is recognized by checking if the scratch block
* is missing.
*/
if (fcb_free_sector_cnt(&cf->cf_fcb) < 1) {
rc = flash_area_erase(cf->cf_fcb.fap,
cf->cf_fcb.f_active.fe_sector->fs_off,
cf->cf_fcb.f_active.fe_sector->fs_size);
if (rc) {
return -EIO;
}
} else {
break;
}
}
cf->cf_store.cs_itf = &settings_fcb_itf;
settings_src_register(&cf->cf_store);
return 0;
}
int settings_fcb_dst(struct settings_fcb *cf)
{
cf->cf_store.cs_itf = &settings_fcb_itf;
settings_dst_register(&cf->cf_store);
return 0;
}
static int settings_fcb_load_cb(struct fcb_entry_ctx *entry_ctx, void *arg)
{
struct settings_fcb_load_cb_arg *argp;
char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
SETTINGS_EXTRA_LEN];
char *name_str;
char *val_str;
int rc;
int len;
argp = (struct settings_fcb_load_cb_arg *)arg;
len = entry_ctx->loc.fe_data_len;
if (len >= sizeof(buf)) {
len = sizeof(buf) - 1;
}
rc = flash_area_read(entry_ctx->fap,
FCB_ENTRY_FA_DATA_OFF(entry_ctx->loc), buf, len);
if (rc) {
return 0;
}
buf[len] = '\0';
rc = settings_line_parse(buf, &name_str, &val_str);
if (rc) {
return 0;
}
argp->cb(name_str, val_str, argp->cb_arg);
return 0;
}
static int settings_fcb_load(struct settings_store *cs, load_cb cb,
void *cb_arg)
{
struct settings_fcb *cf = (struct settings_fcb *)cs;
struct settings_fcb_load_cb_arg arg;
int rc;
arg.cb = cb;
arg.cb_arg = cb_arg;
rc = fcb_walk(&cf->cf_fcb, 0, settings_fcb_load_cb, &arg);
if (rc) {
return -EINVAL;
}
return 0;
}
static int settings_fcb_var_read(struct fcb_entry_ctx *entry_ctx, char *buf,
char **name, char **val)
{
int rc;
rc = flash_area_read(entry_ctx->fap,
FCB_ENTRY_FA_DATA_OFF(entry_ctx->loc), buf,
entry_ctx->loc.fe_data_len);
if (rc) {
return rc;
}
buf[entry_ctx->loc.fe_data_len] = '\0';
rc = settings_line_parse(buf, name, val);
return rc;
}
static void settings_fcb_compress(struct settings_fcb *cf)
{
int rc;
char buf1[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
SETTINGS_EXTRA_LEN];
char buf2[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
SETTINGS_EXTRA_LEN];
struct fcb_entry_ctx loc1;
struct fcb_entry_ctx loc2;
char *name1, *val1;
char *name2, *val2;
int copy;
rc = fcb_append_to_scratch(&cf->cf_fcb);
if (rc) {
return; /* XXX */
}
loc1.fap = cf->cf_fcb.fap;
loc1.loc.fe_sector = NULL;
loc1.loc.fe_elem_off = 0;
while (fcb_getnext(&cf->cf_fcb, &loc1.loc) == 0) {
if (loc1.loc.fe_sector != cf->cf_fcb.f_oldest) {
break;
}
rc = settings_fcb_var_read(&loc1, buf1, &name1, &val1);
if (rc) {
continue;
}
loc2 = loc1;
copy = 1;
while (fcb_getnext(&cf->cf_fcb, &loc2.loc) == 0) {
rc = settings_fcb_var_read(&loc2, buf2, &name2, &val2);
if (rc) {
continue;
}
if (!strcmp(name1, name2)) {
copy = 0;
break;
}
}
if (!copy) {
continue;
}
/*
* Can't find one. Must copy.
*/
rc = flash_area_read(loc1.fap, FCB_ENTRY_FA_DATA_OFF(loc1.loc),
buf1, loc1.loc.fe_data_len);
if (rc) {
continue;
}
rc = fcb_append(&cf->cf_fcb, loc1.loc.fe_data_len, &loc2.loc);
if (rc) {
continue;
}
rc = flash_area_write(loc2.fap, FCB_ENTRY_FA_DATA_OFF(loc2.loc),
buf1, loc1.loc.fe_data_len);
if (rc) {
continue;
}
fcb_append_finish(&cf->cf_fcb, &loc2.loc);
}
rc = fcb_rotate(&cf->cf_fcb);
__ASSERT(rc == 0, "Failed to fcb rotate.\n");
}
static int settings_fcb_append(struct settings_fcb *cf, char *buf, int len)
{
int rc;
int i;
struct fcb_entry loc;
for (i = 0; i < 10; i++) {
rc = fcb_append(&cf->cf_fcb, len, &loc);
if (rc != FCB_ERR_NOSPACE) {
break;
}
settings_fcb_compress(cf);
}
if (rc) {
return -EINVAL;
}
rc = flash_area_write(cf->cf_fcb.fap, FCB_ENTRY_FA_DATA_OFF(loc),
buf, len);
if (rc) {
return -EINVAL;
}
fcb_append_finish(&cf->cf_fcb, &loc);
return 0;
}
static int settings_fcb_save(struct settings_store *cs, const char *name,
const char *value)
{
struct settings_fcb *cf = (struct settings_fcb *)cs;
char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
SETTINGS_EXTRA_LEN];
int len;
if (!name) {
return -EINVAL;
}
len = settings_line_make(buf, sizeof(buf), name, value);
if (len < 0 || len + 2 > sizeof(buf)) {
return -EINVAL;
}
return settings_fcb_append(cf, buf, len);
}

View file

@ -0,0 +1,302 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <assert.h>
#include <zephyr.h>
#include <fs.h>
#include "settings/settings.h"
#include "settings/settings_file.h"
#include "settings_priv.h"
static int settings_file_load(struct settings_store *cs, load_cb cb,
void *cb_arg);
static int settings_file_save(struct settings_store *cs, const char *name,
const char *value);
static struct settings_store_itf settings_file_itf = {
.csi_load = settings_file_load,
.csi_save = settings_file_save,
};
/*
* Register a file to be a source of configuration.
*/
int settings_file_src(struct settings_file *cf)
{
if (!cf->cf_name) {
return -EINVAL;
}
cf->cf_store.cs_itf = &settings_file_itf;
settings_src_register(&cf->cf_store);
return 0;
}
int settings_file_dst(struct settings_file *cf)
{
if (!cf->cf_name) {
return -EINVAL;
}
cf->cf_store.cs_itf = &settings_file_itf;
settings_dst_register(&cf->cf_store);
return 0;
}
int settings_getnext_line(struct fs_file_t *file, char *buf, int blen,
off_t *loc)
{
int rc;
char *end;
rc = fs_seek(file, *loc, FS_SEEK_SET);
if (rc < 0) {
*loc = 0;
return -1;
}
rc = fs_read(file, buf, blen);
if (rc <= 0) {
*loc = 0;
return -1;
}
if (rc == blen) {
rc--;
}
buf[rc] = '\0';
end = strchr(buf, '\n');
if (end) {
*end = '\0';
} else {
end = strchr(buf, '\0');
}
blen = end - buf;
*loc += (blen + 1);
return blen;
}
/*
* Called to load configuration items. cb must be called for every configuration
* item found.
*/
static int settings_file_load(struct settings_store *cs, load_cb cb,
void *cb_arg)
{
struct settings_file *cf = (struct settings_file *)cs;
struct fs_file_t file;
off_t loc;
char tmpbuf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
SETTINGS_EXTRA_LEN];
char *name_str;
char *val_str;
int rc;
int lines;
rc = fs_open(&file, cf->cf_name);
if (rc != 0) {
return -EINVAL;
}
loc = 0;
lines = 0;
while (1) {
rc = settings_getnext_line(&file, tmpbuf, sizeof(tmpbuf), &loc);
if (loc == 0) {
break;
}
if (rc < 0) {
continue;
}
rc = settings_line_parse(tmpbuf, &name_str, &val_str);
if (rc != 0) {
continue;
}
lines++;
cb(name_str, val_str, cb_arg);
}
rc = fs_close(&file);
cf->cf_lines = lines;
return rc;
}
static void settings_tmpfile(char *dst, const char *src, char *pfx)
{
int len;
int pfx_len;
len = strlen(src);
pfx_len = strlen(pfx);
if (len + pfx_len >= SETTINGS_FILE_NAME_MAX) {
len = SETTINGS_FILE_NAME_MAX - pfx_len - 1;
}
memcpy(dst, src, len);
memcpy(dst + len, pfx, pfx_len);
dst[len + pfx_len] = '\0';
}
static int settings_file_create_or_replace(struct fs_file_t *zfp,
const char *file_name)
{
struct fs_dirent entry;
if (fs_stat(file_name, &entry) == 0) {
if (entry.type == FS_DIR_ENTRY_FILE) {
if (fs_unlink(file_name)) {
return -EIO;
}
} else {
return -EISDIR;
}
}
return fs_open(zfp, file_name);
}
/*
* Try to compress configuration file by keeping unique names only.
*/
void settings_file_compress(struct settings_file *cf)
{
int rc;
struct fs_file_t rf;
struct fs_file_t wf;
char tmp_file[SETTINGS_FILE_NAME_MAX];
char buf1[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
SETTINGS_EXTRA_LEN];
char buf2[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
SETTINGS_EXTRA_LEN];
off_t loc1, loc2;
char *name1, *val1;
char *name2, *val2;
int copy;
int len, len2;
int lines;
if (fs_open(&rf, cf->cf_name) != 0) {
return;
}
settings_tmpfile(tmp_file, cf->cf_name, ".cmp");
if (settings_file_create_or_replace(&wf, tmp_file)) {
fs_close(&rf);
return;
}
loc1 = 0;
lines = 0;
while (1) {
len = settings_getnext_line(&rf, buf1, sizeof(buf1), &loc1);
if (loc1 == 0 || len < 0) {
break;
}
rc = settings_line_parse(buf1, &name1, &val1);
if (rc) {
continue;
}
loc2 = loc1;
copy = 1;
while ((len2 = settings_getnext_line(&rf, buf2, sizeof(buf2),
&loc2)) > 0) {
rc = settings_line_parse(buf2, &name2, &val2);
if (rc) {
continue;
}
if (!strcmp(name1, name2)) {
copy = 0;
break;
}
}
if (!copy) {
continue;
}
/*
* Can't find one. Must copy.
*/
len = settings_line_make(buf2, sizeof(buf2), name1, val1);
if (len < 0 || len + 2 > sizeof(buf2)) {
continue;
}
buf2[len++] = '\n';
if (fs_write(&wf, buf2, len) != len) {
ARG_UNUSED(fs_close(&rf));
ARG_UNUSED(fs_close(&wf));
return;
}
lines++;
}
len = fs_close(&wf);
len2 = fs_close(&rf);
if (len == 0 && len2 == 0 && fs_unlink(cf->cf_name) == 0) {
ARG_UNUSED(fs_rename(tmp_file, cf->cf_name));
cf->cf_lines = lines;
/*
* XXX at settings_file_load(), look for .cmp if actual file does not
* exist.
*/
}
}
/*
* Called to save configuration.
*/
static int settings_file_save(struct settings_store *cs, const char *name,
const char *value)
{
struct settings_file *cf = (struct settings_file *)cs;
struct fs_file_t file;
char buf[SETTINGS_MAX_NAME_LEN + SETTINGS_MAX_VAL_LEN +
SETTINGS_EXTRA_LEN];
int len;
int rc2;
int rc;
if (!name) {
return -EINVAL;
}
if (cf->cf_maxlines && (cf->cf_lines + 1 >= cf->cf_maxlines)) {
/*
* Compress before config file size exceeds
* the max number of lines.
*/
settings_file_compress(cf);
}
len = settings_line_make(buf, sizeof(buf), name, value);
if (len < 0 || len + 2 > sizeof(buf)) {
return -EINVAL;
}
buf[len++] = '\n';
/*
* Open the file to add this one value.
*/
rc = fs_open(&file, cf->cf_name);
if (rc == 0) {
rc = fs_seek(&file, 0, FS_SEEK_END);
if (rc == 0) {
rc2 = fs_write(&file, buf, len);
if (rc2 == len) {
cf->cf_lines++;
}
}
rc2 = fs_close(&file);
if (rc == 0) {
rc = rc2;
}
}
return rc;
}

View file

@ -0,0 +1,112 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
#include <errno.h>
#include "settings/settings.h"
#include "settings/settings_file.h"
#include <zephyr.h>
void settings_init(void);
#ifdef CONFIG_SETTINGS_FS
#include <fs.h>
static struct settings_file config_init_settings_file = {
.cf_name = CONFIG_SETTINGS_FS_FILE,
.cf_maxlines = CONFIG_SETTINGS_FS_MAX_LINES
};
static void settings_init_fs(void)
{
int rc;
rc = settings_file_src(&config_init_settings_file);
if (rc) {
k_panic();
}
rc = settings_file_dst(&config_init_settings_file);
if (rc) {
k_panic();
}
}
#elif defined(CONFIG_SETTINGS_FCB)
#include "fcb.h"
#include "settings/settings_fcb.h"
static struct flash_sector settings_fcb_area[CONFIG_SETTINGS_FCB_NUM_AREAS + 1];
static struct settings_fcb config_init_settings_fcb = {
.cf_fcb.f_magic = CONFIG_SETTINGS_FCB_MAGIC,
.cf_fcb.f_sectors = settings_fcb_area,
};
static void settings_init_fcb(void)
{
u32_t cnt = CONFIG_SETTINGS_FCB_NUM_AREAS + 1;
int rc;
const struct flash_area *fap;
rc = flash_area_get_sectors(CONFIG_SETTINGS_FCB_FLASH_AREA, &cnt,
settings_fcb_area);
if (rc != 0 && rc != -ENOMEM) {
k_panic();
}
config_init_settings_fcb.cf_fcb.f_sector_cnt = cnt;
rc = settings_fcb_src(&config_init_settings_fcb);
if (rc != 0) {
k_panic();
}
rc = flash_area_open(CONFIG_SETTINGS_FCB_FLASH_AREA, &fap);
if (rc == 0) {
rc = flash_area_erase(fap, 0, fap->fa_size);
flash_area_close(fap);
}
if (rc != 0) {
k_panic();
} else {
rc = settings_fcb_src(&config_init_settings_fcb);
}
rc = settings_fcb_dst(&config_init_settings_fcb);
if (rc != 0) {
k_panic();
}
}
#endif
void settings_subsys_init(void)
{
settings_init();
#ifdef CONFIG_SETTINGS_FS
settings_init_fs();
/*
* Must be called after root FS has been initialized.
*/
fs_mkdir(CONFIG_SETTINGS_FS_DIR);
#elif defined(CONFIG_SETTINGS_FCB)
settings_init_fcb();
#endif
}

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ctype.h>
#include <string.h>
#include "settings/settings.h"
#include "settings_priv.h"
int settings_line_parse(char *buf, char **namep, char **valp)
{
char *cp;
enum {
FIND_NAME,
FIND_NAME_END,
FIND_VAL,
FIND_VAL_END
} state = FIND_NAME;
*valp = NULL;
for (cp = buf; *cp != '\0'; cp++) {
switch (state) {
case FIND_NAME:
if (!isspace((unsigned char)*cp)) {
*namep = cp;
state = FIND_NAME_END;
}
break;
case FIND_NAME_END:
if (*cp == '=') {
*cp = '\0';
state = FIND_VAL;
} else if (isspace((unsigned char)*cp)) {
*cp = '\0';
}
break;
case FIND_VAL:
if (!isspace((unsigned char)*cp)) {
*valp = cp;
state = FIND_VAL_END;
}
break;
case FIND_VAL_END:
if (isspace((unsigned char)*cp)) {
*cp = '\0';
}
break;
}
}
if (state == FIND_VAL_END || state == FIND_VAL) {
return 0;
} else {
return -1;
}
}
int settings_line_make(char *dst, int dlen, const char *name, const char *value)
{
int nlen;
int vlen;
int off;
nlen = strlen(name);
if (value) {
vlen = strlen(value);
} else {
vlen = 0;
}
if (nlen + vlen + 2 > dlen) {
return -1;
}
memcpy(dst, name, nlen);
off = nlen;
dst[off++] = '=';
memcpy(dst + off, value, vlen);
off += vlen;
dst[off] = '\0';
return off;
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __SETTINGS_PRIV_H_
#define __SETTINGS_PRIV_H_
#ifdef __cplusplus
extern "C" {
#endif
int settings_cli_register(void);
int settings_nmgr_register(void);
struct mgmt_cbuf;
int settings_cbor_line(struct mgmt_cbuf *cb, char *name, int nlen, char *value,
int vlen);
int settings_line_parse(char *buf, char **namep, char **valp);
int settings_line_make(char *dst, int dlen, const char *name, const char *val);
int settings_line_make2(char *dst, int dlen, const char *name,
const char *value);
/*
* API for config storage.
*/
typedef void (*load_cb)(char *name, char *val, void *cb_arg);
struct settings_store_itf {
int (*csi_load)(struct settings_store *cs, load_cb cb, void *cb_arg);
int (*csi_save_start)(struct settings_store *cs);
int (*csi_save)(struct settings_store *cs, const char *name,
const char *value);
int (*csi_save_end)(struct settings_store *cs);
};
void settings_src_register(struct settings_store *cs);
void settings_dst_register(struct settings_store *cs);
extern sys_slist_t settings_load_srcs;
extern sys_slist_t settings_handlers;
extern struct settings_store *settings_save_dst;
#ifdef __cplusplus
}
#endif
#endif /* __SETTINGS_PRIV_H_ */

View file

@ -0,0 +1,161 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdio.h>
#include <zephyr/types.h>
#include <stddef.h>
#include <sys/types.h>
#include <errno.h>
#include "settings/settings.h"
#include "settings_priv.h"
struct settings_dup_check_arg {
const char *name;
const char *val;
int is_dup;
};
sys_slist_t settings_load_srcs;
struct settings_store *settings_save_dst;
void settings_src_register(struct settings_store *cs)
{
sys_snode_t *prev, *cur;
prev = NULL;
SYS_SLIST_FOR_EACH_NODE(&settings_load_srcs, cur) {
prev = cur;
}
sys_slist_insert(&settings_load_srcs, prev, &cs->cs_next);
}
void settings_dst_register(struct settings_store *cs)
{
settings_save_dst = cs;
}
static void settings_load_cb(char *name, char *val, void *cb_arg)
{
settings_set_value(name, val);
}
int settings_load(void)
{
struct settings_store *cs;
/*
* for every config store
* load config
* apply config
* commit all
*/
SYS_SLIST_FOR_EACH_CONTAINER(&settings_load_srcs, cs, cs_next) {
cs->cs_itf->csi_load(cs, settings_load_cb, NULL);
}
return settings_commit(NULL);
}
static void settings_dup_check_cb(char *name, char *val, void *cb_arg)
{
struct settings_dup_check_arg *cdca = (struct settings_dup_check_arg *)
cb_arg;
if (strcmp(name, cdca->name)) {
return;
}
if (!val) {
if (!cdca->val || cdca->val[0] == '\0') {
cdca->is_dup = 1;
} else {
cdca->is_dup = 0;
}
} else {
if (cdca->val && !strcmp(val, cdca->val)) {
cdca->is_dup = 1;
} else {
cdca->is_dup = 0;
}
}
}
/*
* Append a single value to persisted config. Don't store duplicate value.
*/
int settings_save_one(const char *name, char *value)
{
struct settings_store *cs;
struct settings_dup_check_arg cdca;
cs = settings_save_dst;
if (!cs) {
return -ENOENT;
}
/*
* Check if we're writing the same value again.
*/
cdca.name = name;
cdca.val = value;
cdca.is_dup = 0;
cs->cs_itf->csi_load(cs, settings_dup_check_cb, &cdca);
if (cdca.is_dup == 1) {
return 0;
}
return cs->cs_itf->csi_save(cs, name, value);
}
/*
* Walk through all registered subsystems, and ask them to export their
* config variables. Persist these settings.
*/
static void settings_store_one(char *name, char *value)
{
settings_save_one(name, value);
}
int settings_save(void)
{
struct settings_store *cs;
struct settings_handler *ch;
int rc;
int rc2;
cs = settings_save_dst;
if (!cs) {
return -ENOENT;
}
if (cs->cs_itf->csi_save_start) {
cs->cs_itf->csi_save_start(cs);
}
rc = 0;
SYS_SLIST_FOR_EACH_CONTAINER(&settings_handlers, ch, node) {
if (ch->h_export) {
rc2 = ch->h_export(settings_store_one,
SETTINGS_EXPORT_PERSIST);
if (!rc) {
rc = rc2;
}
}
}
if (cs->cs_itf->csi_save_end) {
cs->cs_itf->csi_save_end(cs);
}
return rc;
}
void settings_store_init(void)
{
sys_slist_init(&settings_load_srcs);
}

View file

@ -0,0 +1,16 @@
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
project(NONE)
FILE(GLOB app_sources src/*.c ../src/*c)
target_sources(app PRIVATE ${app_sources})
zephyr_include_directories(
$ENV{ZEPHYR_BASE}/subsys/settings/include
$ENV{ZEPHYR_BASE}/subsys/settings/src
$ENV{ZEPHYR_BASE}/tests/subsys/settings/fcb/src
)
if(TEST)
target_compile_definitions(app PRIVATE
-DTEST_${TEST}
)
endif()

View file

@ -0,0 +1,14 @@
CONFIG_ZTEST=y
CONFIG_STDOUT_CONSOLE=y
CONFIG_FLASH=y
CONFIG_SOC_FLASH_NRF5=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
CONFIG_ARM_CORE_MPU=n
CONFIG_ARM_MPU=n
CONFIG_ARM_MPU_NRF52X=n
CONFIG_FCB=y
CONFIG_SETTINGS=y
CONFIG_SETTINGS_FCB=y
CONFIG_SETTINGS_FCB_FLASH_AREA=3

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _SETTINGS_TEST_FCB_H
#define _SETTINGS_TEST_FCB_H
#include <stdio.h>
#include <string.h>
#include <ztest.h>
#include "settings/settings.h"
#include "flash_map.h"
#ifdef __cplusplus
#extern "C" {
#endif
#define SETTINGS_TEST_FCB_VAL_STR_CNT 64
#define SETTINGS_TEST_FCB_FLASH_CNT 4
extern u8_t val8;
extern u32_t val32;
extern u64_t val64;
extern int test_get_called;
extern int test_set_called;
extern int test_commit_called;
extern int test_export_block;
extern int c2_var_count;
extern struct flash_sector fcb_sectors[SETTINGS_TEST_FCB_FLASH_CNT];
extern char val_string[SETTINGS_TEST_FCB_VAL_STR_CNT][SETTINGS_MAX_VAL_LEN];
extern char test_ref_value[SETTINGS_TEST_FCB_VAL_STR_CNT][SETTINGS_MAX_VAL_LEN];
extern struct settings_handler c_test_handlers[];
void ctest_clear_call_state(void);
int ctest_get_call_state(void);
void config_wipe_srcs(void);
void config_wipe_fcb(struct flash_sector *fs, int cnt);
void test_config_fill_area(
char test_value[SETTINGS_TEST_FCB_VAL_STR_CNT][SETTINGS_MAX_VAL_LEN],
int iteration);
#ifdef __cplusplus
}
#endif
#endif /* _SETTINGS_TEST_FCB_H */

View file

@ -0,0 +1,88 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test.h"
#include "settings/settings_fcb.h"
void test_config_compress_reset(void)
{
int rc;
struct settings_fcb cf;
struct flash_sector *fa;
int elems[4];
int i;
config_wipe_srcs();
config_wipe_fcb(fcb_sectors, ARRAY_SIZE(fcb_sectors));
cf.cf_fcb.f_magic = CONFIG_SETTINGS_FCB_MAGIC;
cf.cf_fcb.f_sectors = fcb_sectors;
cf.cf_fcb.f_sector_cnt = ARRAY_SIZE(fcb_sectors);
rc = settings_fcb_src(&cf);
zassert_true(rc == 0, "can't register FCB as configuration source\n");
rc = settings_fcb_dst(&cf);
zassert_true(rc == 0,
"can't register FCB as configuration destination\n");
c2_var_count = 1;
memset(elems, 0, sizeof(elems));
for (i = 0; ; i++) {
test_config_fill_area(test_ref_value, i);
memcpy(val_string, test_ref_value, sizeof(val_string));
rc = settings_save();
zassert_true(rc == 0, "fcb write error\n");
if (cf.cf_fcb.f_active.fe_sector == &fcb_sectors[2]) {
/*
* Started using space just before scratch.
*/
break;
}
memset(val_string, 0, sizeof(val_string));
rc = settings_load();
zassert_true(rc == 0, "fcb read error\n");
zassert_true(!memcmp(val_string, test_ref_value,
SETTINGS_MAX_VAL_LEN),
"bad value read\n");
}
fa = cf.cf_fcb.f_active.fe_sector;
rc = fcb_append_to_scratch(&cf.cf_fcb);
zassert_true(rc == 0, "fcb_append_to_scratch call failure\n");
zassert_true(fcb_free_sector_cnt(&cf.cf_fcb) == 0,
"expected non of free sectors\n");
zassert_true(fa != cf.cf_fcb.f_active.fe_sector,
"active page should change\n");
config_wipe_srcs();
memset(&cf, 0, sizeof(cf));
cf.cf_fcb.f_magic = CONFIG_SETTINGS_FCB_MAGIC;
cf.cf_fcb.f_sectors = fcb_sectors;
cf.cf_fcb.f_sector_cnt = ARRAY_SIZE(fcb_sectors);
rc = settings_fcb_src(&cf);
zassert_true(rc == 0, "can't register FCB as configuration source\n");
rc = settings_fcb_dst(&cf);
zassert_true(rc == 0,
"can't register FCB as configuration destination\n");
zassert_true(fcb_free_sector_cnt(&cf.cf_fcb) == 1,
"expected one free sector\n");
zassert_true(fa == cf.cf_fcb.f_active.fe_sector,
"active sector should become free after garbage collection");
c2_var_count = 0;
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test.h"
#include "settings/settings_fcb.h"
void test_config_empty_fcb(void)
{
int rc;
struct settings_fcb cf;
config_wipe_srcs();
config_wipe_fcb(fcb_sectors, ARRAY_SIZE(fcb_sectors));
cf.cf_fcb.f_magic = CONFIG_SETTINGS_FCB_MAGIC;
cf.cf_fcb.f_sectors = fcb_sectors;
cf.cf_fcb.f_sector_cnt = ARRAY_SIZE(fcb_sectors);
rc = settings_fcb_src(&cf);
zassert_true(rc == 0, "settings_fcb_src call should succeed\n");
/*
* No values
*/
settings_load();
config_wipe_srcs();
ctest_clear_call_state();
}

View file

@ -0,0 +1,351 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <string.h>
#include "settings_test.h"
#include "settings_priv.h"
#include "flash_map.h"
u8_t val8;
u32_t val32;
u64_t val64;
int test_get_called;
int test_set_called;
int test_commit_called;
int test_export_block;
int c2_var_count = 1;
char *c1_handle_get(int argc, char **argv, char *val, int val_len_max);
int c1_handle_set(int argc, char **argv, char *val);
int c1_handle_commit(void);
int c1_handle_export(void (*cb)(char *name, char *value),
enum settings_export_tgt tgt);
char *c2_handle_get(int argc, char **argv, char *val, int val_len_max);
int c2_handle_set(int argc, char **argv, char *val);
int c2_handle_export(void (*cb)(char *name, char *value),
enum settings_export_tgt tgt);
char *c3_handle_get(int argc, char **argv, char *val, int val_len_max);
int c3_handle_set(int argc, char **argv, char *val);
int c3_handle_export(void (*cb)(char *name, char *value),
enum settings_export_tgt tgt);
struct settings_handler c_test_handlers[] = {
{
.name = "myfoo",
.h_get = c1_handle_get,
.h_set = c1_handle_set,
.h_commit = c1_handle_commit,
.h_export = c1_handle_export
},
{
.name = "2nd",
.h_get = c2_handle_get,
.h_set = c2_handle_set,
.h_commit = NULL,
.h_export = c2_handle_export
},
{
.name = "3",
.h_get = c3_handle_get,
.h_set = c3_handle_set,
.h_commit = NULL,
.h_export = c3_handle_export
}
};
char val_string[SETTINGS_TEST_FCB_VAL_STR_CNT][SETTINGS_MAX_VAL_LEN];
char test_ref_value[SETTINGS_TEST_FCB_VAL_STR_CNT][SETTINGS_MAX_VAL_LEN];
char *c1_handle_get(int argc, char **argv, char *val, int val_len_max)
{
test_get_called = 1;
if (argc == 1 && !strcmp(argv[0], "mybar")) {
return settings_str_from_value(SETTINGS_INT8, &val8, val,
val_len_max);
}
if (argc == 1 && !strcmp(argv[0], "mybar64")) {
return settings_str_from_value(SETTINGS_INT64, &val64, val,
val_len_max);
}
return NULL;
}
int c1_handle_set(int argc, char **argv, char *val)
{
u8_t newval;
u64_t newval64;
int rc;
test_set_called = 1;
if (argc == 1 && !strcmp(argv[0], "mybar")) {
rc = SETTINGS_VALUE_SET(val, SETTINGS_INT8, newval);
zassert_true(rc == 0, "SETTINGS_VALUE_SET callback");
val8 = newval;
return 0;
}
if (argc == 1 && !strcmp(argv[0], "mybar64")) {
rc = SETTINGS_VALUE_SET(val, SETTINGS_INT64, newval64);
zassert_true(rc == 0, "SETTINGS_VALUE_SET callback");
val64 = newval64;
return 0;
}
return -ENOENT;
}
int c1_handle_commit(void)
{
test_commit_called = 1;
return 0;
}
int c1_handle_export(void (*cb)(char *name, char *value),
enum settings_export_tgt tgt)
{
char value[32];
if (test_export_block) {
return 0;
}
settings_str_from_value(SETTINGS_INT8, &val8, value, sizeof(value));
cb("myfoo/mybar", value);
settings_str_from_value(SETTINGS_INT64, &val64, value, sizeof(value));
cb("myfoo/mybar64", value);
return 0;
}
void ctest_clear_call_state(void)
{
test_get_called = 0;
test_set_called = 0;
test_commit_called = 0;
}
int ctest_get_call_state(void)
{
return test_get_called + test_set_called + test_commit_called;
}
void config_wipe_srcs(void)
{
sys_slist_init(&settings_load_srcs);
settings_save_dst = NULL;
}
struct flash_sector fcb_sectors[SETTINGS_TEST_FCB_FLASH_CNT] = {
[0] = {
.fs_off = 0x00000000,
.fs_size = 16 * 1024
},
[1] = {
.fs_off = 0x00004000,
.fs_size = 16 * 1024
},
[2] = {
.fs_off = 0x00008000,
.fs_size = 16 * 1024
},
[3] = {
.fs_off = 0x0000c000,
.fs_size = 16 * 1024
}
};
void config_wipe_fcb(struct flash_sector *fs, int cnt)
{
const struct flash_area *fap;
int rc;
int i;
rc = flash_area_open(CONFIG_SETTINGS_FCB_FLASH_AREA, &fap);
for (i = 0; i < cnt; i++) {
rc = flash_area_erase(fap, fs[i].fs_off, fs[i].fs_size);
zassert_true(rc == 0, "Can't get flash area\n");
}
}
void
test_config_fill_area(char test_value[SETTINGS_TEST_FCB_VAL_STR_CNT]
[SETTINGS_MAX_VAL_LEN],
int iteration)
{
int i, j;
for (j = 0; j < 64; j++) {
for (i = 0; i < SETTINGS_MAX_VAL_LEN; i++) {
test_value[j][i] = ((j * 2) + i + iteration) % 10 + '0';
}
test_value[j][sizeof(test_value[j]) - 1] = '\0';
}
}
char *c2_var_find(char *name)
{
int idx = 0;
int len;
char *eptr;
len = strlen(name);
zassert_true(len > 6, "string type expected\n");
zassert_true(!strncmp(name, "string", 6), "string type expected\n");
idx = strtoul(&name[6], &eptr, 10);
zassert_true(*eptr == '\0', "EOF\n");
zassert_true(idx < c2_var_count,
"var index greather than any exporter\n");
return val_string[idx];
}
char *c2_handle_get(int argc, char **argv, char *val, int val_len_max)
{
int len;
char *valptr;
if (argc == 1) {
valptr = c2_var_find(argv[0]);
if (!valptr) {
return NULL;
}
len = strlen(val_string[0]);
if (len > val_len_max) {
len = val_len_max;
}
strncpy(val, valptr, len);
}
return NULL;
}
int c2_handle_set(int argc, char **argv, char *val)
{
char *valptr;
if (argc == 1) {
valptr = c2_var_find(argv[0]);
if (!valptr) {
return -ENOENT;
}
if (val) {
strncpy(valptr, val, sizeof(val_string[0]));
} else {
memset(valptr, 0, sizeof(val_string[0]));
}
return 0;
}
return -ENOENT;
}
int c2_handle_export(void (*cb)(char *name, char *value),
enum settings_export_tgt tgt)
{
int i;
char name[32];
for (i = 0; i < c2_var_count; i++) {
snprintf(name, sizeof(name), "2nd/string%d", i);
cb(name, val_string[i]);
}
return 0;
}
char *c3_handle_get(int argc, char **argv, char *val, int val_len_max)
{
if (argc == 1 && !strcmp(argv[0], "v")) {
return settings_str_from_value(SETTINGS_INT32, &val32, val,
val_len_max);
}
return NULL;
}
int c3_handle_set(int argc, char **argv, char *val)
{
u32_t newval;
int rc;
if (argc == 1 && !strcmp(argv[0], "v")) {
rc = SETTINGS_VALUE_SET(val, SETTINGS_INT32, newval);
zassert_true(rc == 0, "SETTINGS_VALUE_SET callback");
val32 = newval;
return 0;
}
return -ENOENT;
}
int c3_handle_export(void (*cb)(char *name, char *value),
enum settings_export_tgt tgt)
{
char value[32];
settings_str_from_value(SETTINGS_INT32, &val32, value, sizeof(value));
cb("3/v", value);
return 0;
}
void config_empty_lookups(void);
void test_config_insert(void);
void test_config_getset_unknown(void);
void test_config_getset_int(void);
void test_config_getset_bytes(void);
void test_config_getset_int64(void);
void test_config_commit(void);
void test_config_empty_fcb(void);
void test_config_save_1_fcb(void);
void test_config_insert2(void);
void test_config_save_2_fcb(void);
void test_config_insert3(void);
void test_config_save_3_fcb(void);
void test_config_compress_reset(void);
void test_config_save_one_fcb(void);
void test_main(void *p1, void *p2, void *p3)
{
ztest_test_suite(test_config_fcb,
/* Config tests */
ztest_unit_test(config_empty_lookups),
ztest_unit_test(test_config_insert),
ztest_unit_test(test_config_getset_unknown),
ztest_unit_test(test_config_getset_int),
ztest_unit_test(test_config_getset_bytes),
ztest_unit_test(test_config_getset_int64),
ztest_unit_test(test_config_commit),
/* FCB as backing storage*/
ztest_unit_test(test_config_empty_fcb),
ztest_unit_test(test_config_save_1_fcb),
ztest_unit_test(test_config_insert2),
ztest_unit_test(test_config_save_2_fcb),
ztest_unit_test(test_config_insert3),
ztest_unit_test(test_config_save_3_fcb),
ztest_unit_test(test_config_compress_reset),
ztest_unit_test(test_config_save_one_fcb)
);
ztest_run_test_suite(test_config_fcb);
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test.h"
#include "settings/settings_fcb.h"
void test_config_save_1_fcb(void)
{
int rc;
struct settings_fcb cf;
config_wipe_srcs();
cf.cf_fcb.f_magic = CONFIG_SETTINGS_FCB_MAGIC;
cf.cf_fcb.f_sectors = fcb_sectors;
cf.cf_fcb.f_sector_cnt = ARRAY_SIZE(fcb_sectors);
rc = settings_fcb_src(&cf);
zassert_true(rc == 0, "can't register FCB as configuration source\n");
rc = settings_fcb_dst(&cf);
zassert_true(rc == 0,
"can't register FCB as configuration destination\n");
val8 = 33;
rc = settings_save();
zassert_true(rc == 0, "fcb write error\n");
val8 = 0;
rc = settings_load();
zassert_true(rc == 0, "fcb redout error\n");
zassert_true(val8 == 33, "bad value read\n");
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test.h"
#include "settings/settings_fcb.h"
#ifdef TEST_LONG
#define TESTS_S2_FCB_ITERATIONS 32
#else
#define TESTS_S2_FCB_ITERATIONS 2
#endif
void test_config_save_2_fcb(void)
{
int rc;
struct settings_fcb cf;
int i;
config_wipe_srcs();
cf.cf_fcb.f_magic = CONFIG_SETTINGS_FCB_MAGIC;
cf.cf_fcb.f_sectors = fcb_sectors;
cf.cf_fcb.f_sector_cnt = ARRAY_SIZE(fcb_sectors);
rc = settings_fcb_src(&cf);
zassert_true(rc == 0, "can't register FCB as configuration source\n");
rc = settings_fcb_dst(&cf);
zassert_true(rc == 0,
"can't register FCB as configuration destination\n");
test_config_fill_area(test_ref_value, 0);
memcpy(val_string, test_ref_value, sizeof(val_string));
val8 = 42;
rc = settings_save();
zassert_true(rc == 0, "fcb write error\n");
val8 = 0;
memset(val_string[0], 0, sizeof(val_string[0]));
rc = settings_load();
zassert_true(rc == 0, "fcb read error\n");
zassert_true(val8 == 42, "bad value read\n");
zassert_true(!strcmp(val_string[0], test_ref_value[0]),
"bad value read\n");
test_export_block = 1;
/*
* Now add the number of settings to max. Keep adjusting the test_data,
* check that rollover happens when it's supposed to.
*/
c2_var_count = 64;
for (i = 0; i < TESTS_S2_FCB_ITERATIONS; i++) {
test_config_fill_area(test_ref_value, i);
memcpy(val_string, test_ref_value, sizeof(val_string));
rc = settings_save();
zassert_true(rc == 0, "fcb write error\n");
memset(val_string, 0, sizeof(val_string));
val8 = 0;
rc = settings_load();
zassert_true(rc == 0, "fcb read error\n");
zassert_true(!memcmp(val_string, test_ref_value,
sizeof(val_string)),
"bad value read\n");
zassert_true(val8 == 42, "bad value read\n");
}
c2_var_count = 0;
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test.h"
#include "settings/settings_fcb.h"
#ifdef TEST_LONG
#define TESTS_S3_FCB_ITERATIONS 4096
#else
#define TESTS_S3_FCB_ITERATIONS 100
#endif
void test_config_save_3_fcb(void)
{
int rc;
struct settings_fcb cf;
int i;
config_wipe_srcs();
config_wipe_fcb(fcb_sectors, ARRAY_SIZE(fcb_sectors));
cf.cf_fcb.f_magic = CONFIG_SETTINGS_FCB_MAGIC;
cf.cf_fcb.f_sectors = fcb_sectors;
cf.cf_fcb.f_sector_cnt = 4;
rc = settings_fcb_src(&cf);
zassert_true(rc == 0, "can't register FCB as configuration source\n");
rc = settings_fcb_dst(&cf);
zassert_true(rc == 0,
"can't register FCB as configuration destination\n");
for (i = 0; i < TESTS_S3_FCB_ITERATIONS; i++) {
val32 = i;
rc = settings_save();
zassert_true(rc == 0, "fcb write error\n");
val32 = 0;
rc = settings_load();
zassert_true(rc == 0, "fcb read error\n");
zassert_true(val32 == i, "bad value read\n");
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test.h"
#include "settings/settings_fcb.h"
void test_config_save_one_fcb(void)
{
int rc;
struct settings_fcb cf;
config_wipe_srcs();
config_wipe_fcb(fcb_sectors, ARRAY_SIZE(fcb_sectors));
cf.cf_fcb.f_magic = CONFIG_SETTINGS_FCB_MAGIC;
cf.cf_fcb.f_sectors = fcb_sectors;
cf.cf_fcb.f_sector_cnt = ARRAY_SIZE(fcb_sectors);
rc = settings_fcb_src(&cf);
zassert_true(rc == 0, "can't register FCB as configuration source\n");
rc = settings_fcb_dst(&cf);
zassert_true(rc == 0,
"can't register FCB as configuration destination\n");
val8 = 33;
rc = settings_save();
zassert_true(rc == 0, "fcb write error\n");
rc = settings_save_one("myfoo/mybar", "42");
zassert_true(rc == 0, "fcb one item write error\n");
rc = settings_load();
zassert_true(rc == 0, "fcb read error\n");
zassert_true(val8 == 42, "bad value read\n");
rc = settings_save_one("myfoo/mybar", "44");
zassert_true(rc == 0, "fcb one item write error\n");
rc = settings_load();
zassert_true(rc == 0, "fcb read error\n");
zassert_true(val8 == 44, "bad value read\n");
}

View file

@ -0,0 +1,5 @@
tests:
test:
build_only: true
platform_whitelist: nrf52840_pca10056, nrf52_pca10040
tags: settings_fcb

View file

@ -0,0 +1,16 @@
include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE)
project(NONE)
FILE(GLOB app_sources src/*.c ../src/*c)
target_sources(app PRIVATE ${app_sources})
zephyr_include_directories(
$ENV{ZEPHYR_BASE}/subsys/settings/include
$ENV{ZEPHYR_BASE}/subsys/settings/src
$ENV{ZEPHYR_BASE}/tests/subsys/settings/nffs/src
)
if(TEST)
target_compile_definitions(app PRIVATE
-DTEST_${TEST}
)
endif()

View file

@ -0,0 +1,28 @@
CONFIG_ZTEST=y
CONFIG_STDOUT_CONSOLE=y
CONFIG_FLASH=y
CONFIG_SOC_FLASH_NRF5=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_FLASH_MAP=y
CONFIG_ARM_CORE_MPU=n
CONFIG_ARM_MPU=n
CONFIG_ARM_MPU_NRF52X=n
CONFIG_ZTEST_STACKSIZE=2048
CONFIG_MAIN_STACK_SIZE=1024
CONFIG_HEAP_MEM_POOL_SIZE=1024
CONFIG_FILE_SYSTEM=y
CONFIG_FILE_SYSTEM_NFFS=y
CONFIG_FS_NFFS_FLASH_DEV_NAME="NRF_FLASH_DRV_NAME"
CONFIG_FS_NFFS_NUM_FILES=4
CONFIG_FS_NFFS_NUM_DIRS=4
CONFIG_FS_NFFS_NUM_INODES=1024
CONFIG_FS_NFFS_NUM_BLOCKS=1024
CONFIG_FS_NFFS_NUM_CACHE_INODES=1
CONFIG_FS_NFFS_NUM_CACHE_BLOCKS=1
CONFIG_FILE_SYSTEM_NFFS=y
CONFIG_NFFS_FILESYSTEM_MAX_AREAS=12
CONFIG_SETTINGS=y
CONFIG_SETTINGS_FS=y

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test.h"
#include <device.h>
#include <fs.h>
/* NFFS work area strcut */
static struct nffs_flash_desc flash_desc;
/* mounting info */
static struct fs_mount_t nffs_mnt = {
.type = FS_NFFS,
.mnt_point = TEST_FS_MPTR,
.fs_data = &flash_desc,
};
void config_setup_nffs(void)
{
struct device *flash_dev;
int rc;
flash_dev = device_get_binding(CONFIG_FS_NFFS_FLASH_DEV_NAME);
zassert_not_null(flash_dev, "Can't bind to the flash device\n");
/* set backend storage dev */
nffs_mnt.storage_dev = flash_dev;
rc = fs_mount(&nffs_mnt);
zassert_true(rc == 0, "mounting nffs [%d]\n", rc);
rc = fs_unlink(TEST_CONFIG_DIR);
zassert_true(rc == 0 || rc == -ENOENT,
"can't delete config directory%d\n", rc);
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _SETTINGS_TEST_FCB_H
#define _SETTINGS_TEST_FCB_H
#include <stdio.h>
#include <string.h>
#include <ztest.h>
#include <fs.h>
#include "settings/settings.h"
#ifdef __cplusplus
#extern "C" {
#endif
#define TEST_FS_MPTR "/nffs"
#define TEST_CONFIG_DIR TEST_FS_MPTR"/config"
extern u8_t val8;
extern u32_t val32;
extern u64_t val64;
extern int test_get_called;
extern int test_set_called;
extern int test_commit_called;
extern int test_export_block;
extern int c2_var_count;
extern struct settings_handler c_test_handlers[];
void ctest_clear_call_state(void);
int ctest_get_call_state(void);
void config_wipe_srcs(void);
int fsutil_read_file(const char *path, off_t offset, size_t len, void *dst,
size_t *out_len);
int fsutil_write_file(const char *path, const void *data, size_t len);
int settings_test_file_strstr(const char *fname, char *string);
#ifdef __cplusplus
}
#endif
#endif /* _SETTINGS_TEST_FCB_H */

View file

@ -0,0 +1,104 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <string.h>
#include "settings_test.h"
#include "settings/settings_file.h"
int file_str_cmp(const char *fname, char *string);
void test_config_compress_file(void)
{
int rc;
struct settings_file cf;
config_wipe_srcs();
rc = fs_mkdir(TEST_CONFIG_DIR);
zassert_true(rc == 0 || rc == -EEXIST, "can't create directory\n");
cf.cf_name = TEST_CONFIG_DIR "/korwin";
cf.cf_maxlines = 24;
rc = settings_file_src(&cf);
zassert_true(rc == 0, "can't register FS as configuration source\n");
rc = settings_file_dst(&cf);
zassert_true(rc == 0,
"can't register FS as configuration destination\n");
val64 = 1125;
for (int i = 0; i < 22; i++) {
val8 = i;
rc = settings_save();
zassert_true(rc == 0, "fs write error\n");
val8 = 0xff;
settings_load();
zassert_true(val8 == i, "Bad value loaded\n");
}
val64 = 37;
rc = settings_save();
zassert_true(rc == 0, "fs write error\n");
/* check 1st compression */
rc = file_str_cmp(cf.cf_name, "myfoo/mybar64=1125\n" \
"myfoo/mybar=21\n" \
"myfoo/mybar64=37\n");
zassert_true(rc == 0, "bad value read\n");
for (int i = 0; i < 21; i++) {
val64 = i;
rc = settings_save();
zassert_true(rc == 0, "fs write error\n");
val64 = 0xff;
settings_load();
zassert_true(val64 == i, "Bad value loaded\n");
}
/* check subsequent compression */
rc = file_str_cmp(cf.cf_name, "myfoo/mybar=21\n" \
"myfoo/mybar64=19\n" \
"myfoo/mybar64=20\n");
zassert_true(rc == 0, "bad value read\n");
}
int file_str_cmp(const char *fname, char *string)
{
int rc;
u32_t len;
u32_t rlen;
char *buf;
struct fs_dirent entry;
rc = fs_stat(fname, &entry);
if (rc) {
return rc;
}
if (entry.size != strlen(string)) {
return -1;
}
len = entry.size;
buf = (char *)k_malloc(len + 1);
zassert_not_null(buf, "out of memory\n");
rc = fsutil_read_file(fname, 0, len, buf, &rlen);
zassert_true(rc == 0, "can't access the file\n'");
zassert_true(rc == 0, "not enough data read\n'");
buf[rlen] = '\0';
if (strcmp(buf, string)) {
return -1;
}
return -0;
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test.h"
#include "settings/settings_file.h"
#include <fs.h>
void test_config_empty_file(void)
{
int rc;
struct settings_file cf_mfg;
struct settings_file cf_running;
const char cf_mfg_test[] = "";
const char cf_running_test[] = "\n\n";
config_wipe_srcs();
cf_mfg.cf_name = TEST_CONFIG_DIR "/mfg";
cf_running.cf_name = TEST_CONFIG_DIR "/running";
rc = settings_file_src(&cf_mfg);
zassert_true(rc == 0, "can't register FS as configuration source\n");
rc = settings_file_src(&cf_running);
zassert_true(rc == 0, "can't register FS as configuration source\n");
/*
* No files
*/
settings_load();
rc = fs_mkdir(TEST_CONFIG_DIR);
zassert_true(rc == 0, "can't create directory\n");
rc = fsutil_write_file(TEST_CONFIG_DIR "/mfg", cf_mfg_test,
sizeof(cf_mfg_test));
zassert_true(rc == 0, "can't write to file\n");
rc = fsutil_write_file(TEST_CONFIG_DIR "/running", cf_running_test,
sizeof(cf_running_test));
zassert_true(rc == 0, "can't write to file\n");
settings_load();
config_wipe_srcs();
ctest_clear_call_state();
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test.h"
#include "settings/settings_file.h"
void test_config_multiple_in_file(void)
{
int rc;
struct settings_file cf_mfg;
const char cf_mfg_test1[] =
"myfoo/mybar=1\n"
"myfoo/mybar=14";
const char cf_mfg_test2[] =
"myfoo/mybar=1\n"
"myfoo/mybar=15\n"
"\n";
config_wipe_srcs();
cf_mfg.cf_name = TEST_CONFIG_DIR "/mfg";
rc = settings_file_src(&cf_mfg);
zassert_true(rc == 0, "can't register FS as configuration source\n");
rc = fsutil_write_file(TEST_CONFIG_DIR "/mfg", cf_mfg_test1,
sizeof(cf_mfg_test1));
zassert_true(rc == 0, "can't write to file\n");
settings_load();
zassert_true(test_set_called, "the SET handler wasn't called\n");
zassert_true(val8 == 14,
"SET handler: was called with wrong parameters\n");
rc = fsutil_write_file(TEST_CONFIG_DIR "/mfg", cf_mfg_test2,
sizeof(cf_mfg_test2));
zassert_true(rc == 0, "can't write to file\n");
settings_load();
zassert_true(test_set_called, "the SET handler wasn't called\n");
zassert_true(val8 == 15,
"SET handler: was called with wrong parameters\n");
}

View file

@ -0,0 +1,233 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <string.h>
#include "settings_test.h"
#include "settings_priv.h"
u8_t val8;
u64_t val64;
int test_get_called;
int test_set_called;
int test_commit_called;
int test_export_block;
int c2_var_count = 1;
char *c1_handle_get(int argc, char **argv, char *val, int val_len_max);
int c1_handle_set(int argc, char **argv, char *val);
int c1_handle_commit(void);
int c1_handle_export(void (*cb)(char *name, char *value),
enum settings_export_tgt tgt);
struct settings_handler c_test_handlers[] = {
{
.name = "myfoo",
.h_get = c1_handle_get,
.h_set = c1_handle_set,
.h_commit = c1_handle_commit,
.h_export = c1_handle_export
},
};
char *c1_handle_get(int argc, char **argv, char *val, int val_len_max)
{
test_get_called = 1;
if (argc == 1 && !strcmp(argv[0], "mybar")) {
return settings_str_from_value(SETTINGS_INT8, &val8, val,
val_len_max);
}
if (argc == 1 && !strcmp(argv[0], "mybar64")) {
return settings_str_from_value(SETTINGS_INT64, &val64, val,
val_len_max);
}
return NULL;
}
int c1_handle_set(int argc, char **argv, char *val)
{
u8_t newval;
u64_t newval64;
int rc;
test_set_called = 1;
if (argc == 1 && !strcmp(argv[0], "mybar")) {
rc = SETTINGS_VALUE_SET(val, SETTINGS_INT8, newval);
zassert_true(rc == 0, "SETTINGS_VALUE_SET callback");
val8 = newval;
return 0;
}
if (argc == 1 && !strcmp(argv[0], "mybar64")) {
rc = SETTINGS_VALUE_SET(val, SETTINGS_INT64, newval64);
zassert_true(rc == 0, "SETTINGS_VALUE_SET callback");
val64 = newval64;
return 0;
}
return -ENOENT;
}
int c1_handle_commit(void)
{
test_commit_called = 1;
return 0;
}
int c1_handle_export(void (*cb)(char *name, char *value),
enum settings_export_tgt tgt)
{
char value[32];
if (test_export_block) {
return 0;
}
settings_str_from_value(SETTINGS_INT8, &val8, value, sizeof(value));
cb("myfoo/mybar", value);
settings_str_from_value(SETTINGS_INT64, &val64, value, sizeof(value));
cb("myfoo/mybar64", value);
return 0;
}
void ctest_clear_call_state(void)
{
test_get_called = 0;
test_set_called = 0;
test_commit_called = 0;
}
int ctest_get_call_state(void)
{
return test_get_called + test_set_called + test_commit_called;
}
void config_wipe_srcs(void)
{
sys_slist_init(&settings_load_srcs);
settings_save_dst = NULL;
}
int fsutil_read_file(const char *path, off_t offset, size_t len, void *dst,
size_t *out_len)
{
struct fs_file_t file;
int rc;
ssize_t r_len = 0;
rc = fs_open(&file, path);
if (rc != 0) {
return rc;
}
r_len = fs_read(&file, dst, len);
if (r_len < 0) {
rc = -EIO;
} else {
*out_len = r_len;
}
fs_close(&file);
return rc;
}
int fsutil_write_file(const char *path, const void *data, size_t len)
{
struct fs_file_t file;
int rc;
rc = fs_open(&file, path);
if (rc != 0) {
return rc;
}
if (fs_write(&file, data, len) != len) {
rc = -EIO;
}
fs_close(&file);
return rc;
}
int settings_test_file_strstr(const char *fname, char *string)
{
int rc;
u32_t len;
u32_t rlen;
char *buf;
struct fs_dirent entry;
rc = fs_stat(fname, &entry);
if (rc) {
return rc;
}
len = entry.size;
buf = (char *)k_malloc(len + 1);
zassert_not_null(buf, "out of memory\n");
rc = fsutil_read_file(fname, 0, len, buf, &rlen);
zassert_true(rc == 0, "can't access the file\n'");
zassert_true(rc == 0, "not enough data read\n'");
buf[rlen] = '\0';
if (strstr(buf, string)) {
return 0;
}
return -1;
}
void config_empty_lookups(void);
void test_config_insert(void);
void test_config_getset_unknown(void);
void test_config_getset_int(void);
void test_config_getset_bytes(void);
void test_config_getset_int64(void);
void test_config_commit(void);
void config_setup_nffs(void);
void test_config_empty_file(void);
void test_config_small_file(void);
void test_config_multiple_in_file(void);
void test_config_save_in_file(void);
void test_config_save_one_file(void);
void test_config_compress_file(void);
void test_main(void *p1, void *p2, void *p3)
{
ztest_test_suite(test_config_fcb,
/* Config tests */
ztest_unit_test(config_empty_lookups),
ztest_unit_test(test_config_insert),
ztest_unit_test(test_config_getset_unknown),
ztest_unit_test(test_config_getset_int),
ztest_unit_test(test_config_getset_bytes),
ztest_unit_test(test_config_getset_int64),
ztest_unit_test(test_config_commit),
/* NFFS as backing storage. */
ztest_unit_test(config_setup_nffs),
ztest_unit_test(test_config_empty_file),
ztest_unit_test(test_config_small_file),
ztest_unit_test(test_config_multiple_in_file),
ztest_unit_test(test_config_save_in_file),
ztest_unit_test(test_config_save_one_file),
ztest_unit_test(test_config_compress_file)
);
ztest_run_test_suite(test_config_fcb);
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test.h"
#include "settings/settings_file.h"
void test_config_save_in_file(void)
{
int rc;
struct settings_file cf;
config_wipe_srcs();
rc = fs_mkdir(TEST_CONFIG_DIR);
zassert_true(rc == 0 || rc == -EEXIST, "can't create directory\n");
cf.cf_name = TEST_CONFIG_DIR "/blah";
rc = settings_file_src(&cf);
zassert_true(rc == 0, "can't register FS as configuration source\n");
rc = settings_file_dst(&cf);
zassert_true(rc == 0,
"can't register FS as configuration destination\n");
val8 = 8;
rc = settings_save();
zassert_true(rc == 0, "fs write error\n");
rc = settings_test_file_strstr(cf.cf_name, "myfoo/mybar=8\n");
zassert_true(rc == 0, "bad value read\n");
val8 = 43;
rc = settings_save();
zassert_true(rc == 0, "fs write error\n");
rc = settings_test_file_strstr(cf.cf_name, "myfoo/mybar=43\n");
zassert_true(rc == 0, "bad value read\n");
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test.h"
#include "settings/settings_file.h"
void test_config_save_one_file(void)
{
int rc;
struct settings_file cf;
config_wipe_srcs();
rc = fs_mkdir(TEST_CONFIG_DIR);
zassert_true(rc == 0 || rc == -EEXIST, "can't create directory\n");
cf.cf_name = TEST_CONFIG_DIR "/blah";
rc = settings_file_src(&cf);
zassert_true(rc == 0, "can't register FS as configuration source\n");
rc = settings_file_dst(&cf);
zassert_true(rc == 0,
"can't register FS as configuration destination\n");
val8 = 33;
rc = settings_save();
zassert_true(rc == 0, "fs write error\n");
rc = settings_save_one("myfoo/mybar", "42");
zassert_equal(rc, 0, "fs one item write error\n");
rc = settings_load();
zassert_true(rc == 0, "fs redout error\n");
zassert_true(val8 == 42, "bad value read\n");
rc = settings_save_one("myfoo/mybar", "44");
zassert_true(rc == 0, "fs one item write error\n");
rc = settings_load();
zassert_true(rc == 0, "fs redout error\n");
zassert_true(val8 == 44, "bad value read\n");
}

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test.h"
#include "settings/settings_file.h"
void test_config_small_file(void)
{
int rc;
struct settings_file cf_mfg;
struct settings_file cf_running;
const char cf_mfg_test[] = "myfoo/mybar=1";
const char cf_running_test[] = " myfoo/mybar = 8 ";
config_wipe_srcs();
cf_mfg.cf_name = TEST_CONFIG_DIR "/mfg";
cf_running.cf_name = TEST_CONFIG_DIR "/running";
rc = settings_file_src(&cf_mfg);
zassert_true(rc == 0, "can't register FS as configuration source\n");
rc = settings_file_src(&cf_running);
zassert_true(rc == 0, "can't register FS as configuration source\n");
rc = fsutil_write_file(TEST_CONFIG_DIR "/mfg", cf_mfg_test,
sizeof(cf_mfg_test));
zassert_true(rc == 0, "can't write to file\n");
settings_load();
zassert_true(test_set_called, "the SET handler wasn't called\n");
zassert_true(val8 == 1,
"SET handler: was called with wrong parameters\n");
ctest_clear_call_state();
rc = fsutil_write_file(TEST_CONFIG_DIR "/running", cf_running_test,
sizeof(cf_running_test));
zassert_true(rc == 0, "can't write to file\n");
settings_load();
zassert_true(test_set_called, "the SET handler wasn't called\n");
zassert_true(val8 == 8,
"SET handler: was called with wrong parameters\n");
ctest_clear_call_state();
}

View file

@ -0,0 +1,5 @@
tests:
test:
build_only: true
platform_whitelist: nrf52840_pca10056, nrf52_pca10040
tags: settings_fs

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test.h"
void config_empty_lookups(void)
{
int rc;
char name[80];
char tmp[64], *str;
strcpy(name, "foo/bar");
rc = settings_set_value(name, "tmp");
zassert_true(rc != 0, "settings_set_value callback");
strcpy(name, "foo/bar");
str = settings_get_value(name, tmp, sizeof(tmp));
zassert_true(str == NULL, "settings_get_value callback");
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test.h"
void test_config_commit(void)
{
char name[80];
int rc;
strcpy(name, "bar");
rc = settings_commit(name);
zassert_true(rc, "commit-nonexisting-tree call should succeed\n");
zassert_true(ctest_get_call_state() == 0,
"a handler was called unexpectedly\n");
rc = settings_commit(NULL);
zassert_true(rc == 0, "commit-All call should succeed");
zassert_true(test_commit_called == 1,
"the COMMIT handler wasn't called\n");
ctest_clear_call_state();
strcpy(name, "myfoo");
rc = settings_commit(name);
zassert_true(rc == 0, "commit-a-tree call should succeed\n");
zassert_true(test_commit_called == 1,
"the COMMIT handler wasn't called\n");
ctest_clear_call_state();
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test.h"
void test_config_getset_bytes(void)
{
char orig[32];
char bytes[32];
char str[48];
char *ret;
int j, i;
int tmp;
int rc;
for (j = 1; j < sizeof(orig); j++) {
for (i = 0; i < j; i++) {
orig[i] = i + j + 1;
}
ret = settings_str_from_bytes(orig, j, str, sizeof(str));
zassert_not_null(ret, "string base64 encodding\n");
tmp = strlen(str);
zassert_true(tmp < sizeof(str), "encoded string is to long\n");
memset(bytes, 0, sizeof(bytes));
tmp = sizeof(bytes);
tmp = sizeof(bytes);
rc = settings_bytes_from_str(str, bytes, &tmp);
zassert_true(rc == 0, "base64 to string decodding\n");
zassert_true(tmp == j, "decoded string bad length\n");
zassert_true(!memcmp(orig, bytes, j),
"decoded string not match to origin\n");
}
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test.h"
void test_config_getset_int(void)
{
char name[80];
char tmp[64], *str;
int rc;
strcpy(name, "myfoo/mybar");
rc = settings_set_value(name, "42");
zassert_true(rc == 0, "can not set key value\n");
zassert_true(test_set_called == 1, "the SET handler wasn't called\n");
zassert_true(val8 == 42,
"SET handler: was called with wrong parameters\n");
ctest_clear_call_state();
strcpy(name, "myfoo/mybar");
str = settings_get_value(name, tmp, sizeof(tmp));
zassert_not_null(str, "the key value should been available\n");
zassert_true(test_get_called == 1, "the GET handler wasn't called\n");
zassert_true(!strcmp("42", tmp), "unexpected value fetched\n");
ctest_clear_call_state();
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test.h"
void test_config_getset_int64(void)
{
char name[80];
char tmp[64], *str;
int rc;
s64_t cmp;
strcpy(name, "myfoo/mybar64");
rc = settings_set_value(name, "-9218247941279444428");
zassert_true(rc == 0, "can't set value\n");
zassert_true(test_set_called == 1, "the SET handler wasn't called\n");
cmp = 0x8012345678901234;
zassert_true(memcmp(&val64, &cmp, sizeof(val64)) == 0,
"SET handler: was called with wrong parameters\n");
ctest_clear_call_state();
strcpy(name, "myfoo/mybar64");
str = settings_get_value(name, tmp, sizeof(tmp));
zassert_not_null(str, "the key value should been available\n");
zassert_true(test_get_called == 1, "the GET handler wasn't called\n");
zassert_true(!strcmp("-9218247941279444428", tmp),
"unexpected value fetched %s\n", tmp);
ctest_clear_call_state();
strcpy(name, "myfoo/mybar64");
rc = settings_set_value(name, "1");
zassert_true(rc == 0, "can't set value\n");
zassert_true(test_set_called == 1, "the SET handler wasn't called\n");
zassert_true(val64 == 1,
"SET handler: was called with wrong parameters\n");
ctest_clear_call_state();
strcpy(name, "myfoo/mybar64");
str = settings_get_value(name, tmp, sizeof(tmp));
zassert_not_null(str, "the key value should been available\n");
zassert_true(test_get_called == 1, "the GET handler wasn't called\n");
zassert_true(!strcmp("1", tmp), "unexpected value fetched\n");
ctest_clear_call_state();
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test.h"
#include <errno.h>
void test_config_getset_unknown(void)
{
char name[80];
char tmp[64], *str;
int rc;
strcpy(name, "foo/bar");
rc = settings_set_value(name, "tmp");
zassert_true(rc != 0, "set value should fail\n");
zassert_true(ctest_get_call_state() == 0,
"a handler was called unexpectedly\n");
strcpy(name, "foo/bar");
str = settings_get_value(name, tmp, sizeof(tmp));
zassert_true(str == NULL, "value should been unreachable\n");
zassert_true(ctest_get_call_state() == 0,
"a handler was called unexpectedly\n");
strcpy(name, "myfoo/bar");
rc = settings_set_value(name, "tmp");
zassert_true(rc == -ENOENT, "unexpected failure retval\n");
zassert_true(test_set_called == 1,
"the GET handler wasn't called\n");
ctest_clear_call_state();
strcpy(name, "myfoo/bar");
str = settings_get_value(name, tmp, sizeof(tmp));
zassert_true(str == NULL, "value should been unreachable\n");
zassert_true(test_get_called == 1,
"the SET handler wasn't called\n");
ctest_clear_call_state();
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2015 Runtime Inc
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "settings_test.h"
void test_config_insert_x(int idx)
{
int rc;
rc = settings_register(&c_test_handlers[idx]);
zassert_true(rc == 0, "settings_register fail");
}
void test_config_insert(void)
{
test_config_insert_x(0);
}
void test_config_insert2(void)
{
test_config_insert_x(1);
}
void test_config_insert3(void)
{
test_config_insert_x(2);
}