Skip to content
Permalink
Browse files

settings: adding new nvs backend

Added NVS backend to the Settings subsystem.

Signed-off-by: Kamil Piszczek <Kamil.Piszczek@nordicsemi.no>
  • Loading branch information...
kapi-no authored and ioannisg committed Jun 24, 2019
1 parent 8977d5b commit 88bb7597b13faecae3d44b96a3763cc2edbcf034
@@ -65,6 +65,12 @@ config SETTINGS_FS
help help
Use a file system as a settings storage back-end. Use a file system as a settings storage back-end.


config SETTINGS_NVS
bool "NVS non-volatile storage support"
depends on SETTINGS
help
Enables NVS storage support

config SETTINGS_CUSTOM config SETTINGS_CUSTOM
bool "CUSTOM" bool "CUSTOM"
help help
@@ -111,3 +117,18 @@ config SETTINGS_FS_MAX_LINES
depends on SETTINGS && SETTINGS_FS depends on SETTINGS && SETTINGS_FS
help help
Limit how many items stored in a file before compressing Limit how many items stored in a file before compressing

config SETTINGS_NVS_SECTOR_SIZE_MULT
int "Sector size of the NVS settings area"
default 1
depends on SETTINGS && SETTINGS_NVS
help
The sector size to use for the NVS settings area as a multiple of
FLASH_ERASE_BLOCK_SIZE.

config SETTINGS_NVS_SECTOR_COUNT
int "Sector count of the NVS settings area"
default 8
depends on SETTINGS && SETTINGS_NVS
help
Number of sectors used for the NVS settings area
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2019 Laczen
* Copyright (c) 2019 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef __SETTINGS_NVS_H_
#define __SETTINGS_NVS_H_

#include <nvs/nvs.h>
#include "settings/settings.h"

#ifdef __cplusplus
extern "C" {
#endif

/* In the NVS backend, each setting is stored in two NVS entries:
* 1. setting's name
* 2. setting's value
*
* The NVS entry ID for the setting's value is determined implicitly based on
* the ID of the NVS entry for the setting's name, once that is found. The
* difference between name and value ID is constant and equal to
* NVS_NAME_ID_OFFSET.
*
* Setting's name entries start from NVS_NAMECNT_ID + 1. The entry at
* NVS_NAMECNT_ID is used to store the largest name ID in use.
*
* Deleted records will not be found, only the last record will be
* read.
*/
#define NVS_NAMECNT_ID 0x8000
#define NVS_NAME_ID_OFFSET 0x4000

struct settings_nvs {
struct settings_store cf_store;
struct nvs_fs cf_nvs;
u16_t last_name_id;
const char *flash_dev_name;
};

/* register nvs to be a source of settings */
int settings_nvs_src(struct settings_nvs *cf);

/* register nvs to be the destination of settings */
int settings_nvs_dst(struct settings_nvs *cf);

/* Initialize a nvs backend. */
int settings_nvs_backend_init(struct settings_nvs *cf);


#ifdef __cplusplus
}
#endif

#endif /* __SETTINGS_NVS_H_ */
@@ -10,3 +10,4 @@ zephyr_sources(
zephyr_sources_ifdef(CONFIG_SETTINGS_RUNTIME settings_runtime.c) zephyr_sources_ifdef(CONFIG_SETTINGS_RUNTIME settings_runtime.c)
zephyr_sources_ifdef(CONFIG_SETTINGS_FS settings_file.c) zephyr_sources_ifdef(CONFIG_SETTINGS_FS settings_file.c)
zephyr_sources_ifdef(CONFIG_SETTINGS_FCB settings_fcb.c) zephyr_sources_ifdef(CONFIG_SETTINGS_FCB settings_fcb.c)
zephyr_sources_ifdef(CONFIG_SETTINGS_NVS settings_nvs.c)
@@ -117,6 +117,58 @@ int settings_backend_init(void)


return rc; return rc;
} }

#elif defined(CONFIG_SETTINGS_NVS)
#include <device.h>
#include <flash_map.h>
#include "settings/settings_nvs.h"

static struct settings_nvs default_settings_nvs;

int settings_backend_init(void)
{
int rc;
u16_t cnt = 0;
size_t nvs_sector_size, nvs_size = 0;
const struct flash_area *fa;

rc = flash_area_open(DT_FLASH_AREA_STORAGE_ID, &fa);
if (rc) {
return rc;
}

nvs_sector_size = CONFIG_SETTINGS_NVS_SECTOR_SIZE_MULT *
DT_FLASH_ERASE_BLOCK_SIZE;
while (cnt < CONFIG_SETTINGS_NVS_SECTOR_COUNT) {
nvs_size += nvs_sector_size;
if (nvs_size > fa->fa_size) {
break;
}
cnt++;
}

/* define the nvs file system using the page_info */
default_settings_nvs.cf_nvs.sector_size = nvs_sector_size;
default_settings_nvs.cf_nvs.sector_count = cnt;
default_settings_nvs.cf_nvs.offset = fa->fa_off;
default_settings_nvs.flash_dev_name = fa->fa_dev_name;

rc = settings_nvs_backend_init(&default_settings_nvs);
if (rc) {
return rc;
}

rc = settings_nvs_src(&default_settings_nvs);

if (rc) {
return rc;
}

rc = settings_nvs_dst(&default_settings_nvs);

return rc;
}

#elif defined(CONFIG_SETTINGS_NONE) #elif defined(CONFIG_SETTINGS_NONE)
int settings_backend_init(void) int settings_backend_init(void)
{ {
@@ -0,0 +1,238 @@
/*
* Copyright (c) 2019 Laczen
* Copyright (c) 2019 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <errno.h>
#include <string.h>

#include "settings/settings.h"
#include "settings/settings_nvs.h"
#include "settings_priv.h"

#include <logging/log.h>
LOG_MODULE_DECLARE(settings, CONFIG_SETTINGS_LOG_LEVEL);

struct settings_nvs_read_fn_arg {
struct nvs_fs *fs;
u16_t id;
};

static int settings_nvs_load(struct settings_store *cs, const char *subtree);
static int settings_nvs_save(struct settings_store *cs, const char *name,
const char *value, size_t val_len);

static struct settings_store_itf settings_nvs_itf = {
.csi_load = settings_nvs_load,
.csi_save = settings_nvs_save,
};

static ssize_t settings_nvs_read_fn(void *back_end, void *data, size_t len)
{
struct settings_nvs_read_fn_arg *rd_fn_arg;

rd_fn_arg = (struct settings_nvs_read_fn_arg *)back_end;

return nvs_read(rd_fn_arg->fs, rd_fn_arg->id, data, len);
}

int settings_nvs_src(struct settings_nvs *cf)
{
cf->cf_store.cs_itf = &settings_nvs_itf;
settings_src_register(&cf->cf_store);

return 0;
}

int settings_nvs_dst(struct settings_nvs *cf)
{
cf->cf_store.cs_itf = &settings_nvs_itf;
settings_dst_register(&cf->cf_store);

return 0;
}

static int settings_nvs_load(struct settings_store *cs, const char *subtree)
{
struct settings_nvs *cf = (struct settings_nvs *)cs;
struct settings_nvs_read_fn_arg read_fn_arg;
struct settings_handler *ch;
char name[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1];
char buf;
const char *name_argv;
ssize_t rc1, rc2;
u16_t name_id = NVS_NAMECNT_ID;

name_id = cf->last_name_id + 1;

while (1) {

name_id--;
if (name_id == NVS_NAMECNT_ID) {
break;
}

/* In the NVS backend, each setting item is stored in two NVS
* entries one for the setting's name and one with the
* setting's value.
*/
rc1 = nvs_read(&cf->cf_nvs, name_id, &name, sizeof(name));
rc2 = nvs_read(&cf->cf_nvs, name_id + NVS_NAME_ID_OFFSET,
&buf, sizeof(buf));

if ((rc1 <= 0) && (rc2 <= 0)) {
continue;
}

if ((rc1 <= 0) || (rc2 <= 0)) {
/* Settings item is not stored correctly in the NVS.
* NVS entry for its name or value is either missing
* or deleted. Clean dirty entries to make space for
* future settings item.
*/
if (name_id == cf->last_name_id) {
cf->last_name_id--;
nvs_write(&cf->cf_nvs, NVS_NAMECNT_ID,
&cf->last_name_id, sizeof(u16_t));
}
nvs_delete(&cf->cf_nvs, name_id);
nvs_delete(&cf->cf_nvs, name_id + NVS_NAME_ID_OFFSET);
continue;
}

/* Found a name, this might not include a trailing \0 */
name[rc1] = '\0';

if (subtree && !settings_name_steq(name, subtree, NULL)) {
continue;
}

ch = settings_parse_and_lookup(name, &name_argv);
if (!ch) {
continue;
}

read_fn_arg.fs = &cf->cf_nvs;
read_fn_arg.id = name_id + NVS_NAME_ID_OFFSET;
ch->h_set(name_argv, rc2, settings_nvs_read_fn,
(void *) &read_fn_arg);
}
return 0;
}

static int settings_nvs_save(struct settings_store *cs, const char *name,
const char *value, size_t val_len)
{
struct settings_nvs *cf = (struct settings_nvs *)cs;
char rdname[SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN + 1];
u16_t name_id, write_name_id;
bool delete, write_name;
int rc = 0;

if (!name) {
return -EINVAL;
}

/* Find out if we are doing a delete */
delete = ((value == NULL) || (val_len == 0));

name_id = cf->last_name_id + 1;
write_name_id = cf->last_name_id + 1;
write_name = true;

while (1) {
name_id--;
if (name_id == NVS_NAMECNT_ID) {
break;
}

rc = nvs_read(&cf->cf_nvs, name_id, &rdname, sizeof(rdname));

if (rc < 0) {
/* Error or entry not found */
if (rc == -ENOENT) {
write_name_id = name_id;
}
continue;
}

rdname[rc] = '\0';

if (strcmp(name, rdname)) {
continue;
}

if ((delete) && (name_id == cf->last_name_id)) {
cf->last_name_id--;
rc = nvs_write(&cf->cf_nvs, NVS_NAMECNT_ID,
&cf->last_name_id, sizeof(u16_t));
}

if (delete) {
rc = nvs_delete(&cf->cf_nvs, name_id);
rc = nvs_delete(&cf->cf_nvs, name_id +
NVS_NAME_ID_OFFSET);

return 0;
}
write_name_id = name_id;
write_name = false;
break;
}

if (delete) {
return -ENOENT;
}

/* No free IDs left. */
if (write_name_id == NVS_NAMECNT_ID + NVS_NAME_ID_OFFSET) {
return -ENOMEM;
}

/* write the value */
rc = nvs_write(&cf->cf_nvs, write_name_id + NVS_NAME_ID_OFFSET,
value, val_len);

/* write the name if required */
if (write_name) {
rc = nvs_write(&cf->cf_nvs, write_name_id, name, strlen(name));
}

/* update the last_name_id and write to flash if required*/
if (write_name_id > cf->last_name_id) {
cf->last_name_id = write_name_id;
rc = nvs_write(&cf->cf_nvs, NVS_NAMECNT_ID, &cf->last_name_id,
sizeof(u16_t));
}

if (rc < 0) {
return rc;
}

return 0;
}

/* Initialize the nvs backend. */
int settings_nvs_backend_init(struct settings_nvs *cf)
{
int rc;
u16_t last_name_id;

rc = nvs_init(&cf->cf_nvs, cf->flash_dev_name);
if (rc) {
return rc;
}

rc = nvs_read(&cf->cf_nvs, NVS_NAMECNT_ID, &last_name_id,
sizeof(last_name_id));
if (rc < 0) {
cf->last_name_id = NVS_NAMECNT_ID;
} else {
cf->last_name_id = last_name_id;
}

LOG_DBG("Initialized");
return 0;
}

0 comments on commit 88bb759

Please sign in to comment.
You can’t perform that action at this time.