/* * Copyright (c) 2018 Nordic Semiconductor ASA * Copyright (c) 2015 Runtime Inc * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #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; } if (!val1) { /* No sense to copy empty entry from the oldest sector*/ 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; } rc = fcb_append_finish(&cf->cf_fcb, &loc2.loc); __ASSERT(rc == 0, "Failed to finish fcb_append.\n"); } 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; } return fcb_append_finish(&cf->cf_fcb, &loc); } 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); }