blob: 0612eac714d2c7229457f3a62b94a687c170f20d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "os/mynewt.h"
#if MYNEWT_VAL(CONFIG_FCB)
#include <fcb/fcb.h>
#include <string.h>
#include "config/config.h"
#include "config/config_store.h"
#include "config/config_fcb.h"
#include "config/config_generic_kv.h"
#include "config_priv.h"
#define CONF_FCB_VERS 1
struct conf_fcb_load_cb_arg {
conf_store_load_cb cb;
void *cb_arg;
};
struct conf_kv_load_cb_arg {
const char *name;
char *value;
size_t len;
};
static int conf_fcb_load(struct conf_store *, conf_store_load_cb cb,
void *cb_arg);
static int conf_fcb_save(struct conf_store *, const char *name,
const char *value);
static struct conf_store_itf conf_fcb_itf = {
.csi_load = conf_fcb_load,
.csi_save = conf_fcb_save,
};
int
conf_fcb_src(struct conf_fcb *cf)
{
int rc;
cf->cf_fcb.f_version = CONF_FCB_VERS;
if (cf->cf_fcb.f_sector_cnt > 1) {
cf->cf_fcb.f_scratch_cnt = 1;
} else {
cf->cf_fcb.f_scratch_cnt = 0;
}
while (1) {
rc = fcb_init(&cf->cf_fcb);
if (rc) {
return OS_INVALID_PARM;
}
/*
* Check if system was reset in middle of emptying a sector. This
* situation is recognized by checking if the scratch block is missing.
*/
if (cf->cf_fcb.f_scratch_cnt &&
fcb_free_sector_cnt(&cf->cf_fcb) < 1) {
flash_area_erase(cf->cf_fcb.f_active.fe_area, 0,
cf->cf_fcb.f_active.fe_area->fa_size);
} else {
break;
}
}
cf->cf_store.cs_itf = &conf_fcb_itf;
conf_src_register(&cf->cf_store);
return OS_OK;
}
int
conf_fcb_dst(struct conf_fcb *cf)
{
cf->cf_store.cs_itf = &conf_fcb_itf;
conf_dst_register(&cf->cf_store);
return OS_OK;
}
static int
conf_fcb_load_cb(struct fcb_entry *loc, void *arg)
{
struct conf_fcb_load_cb_arg *argp;
char buf[CONF_MAX_NAME_LEN + CONF_MAX_VAL_LEN + 32];
char *name_str;
char *val_str;
int rc;
int len;
argp = (struct conf_fcb_load_cb_arg *)arg;
len = loc->fe_data_len;
if (len >= sizeof(buf)) {
len = sizeof(buf) - 1;
}
rc = flash_area_read(loc->fe_area, loc->fe_data_off, buf, len);
if (rc) {
return 0;
}
buf[len] = '\0';
rc = conf_line_parse(buf, &name_str, &val_str);
if (rc) {
return 0;
}
argp->cb(name_str, val_str, argp->cb_arg);
return 0;
}
static int
conf_fcb_load(struct conf_store *cs, conf_store_load_cb cb, void *cb_arg)
{
struct conf_fcb *cf = (struct conf_fcb *)cs;
struct conf_fcb_load_cb_arg arg;
int rc;
arg.cb = cb;
arg.cb_arg = cb_arg;
rc = fcb_walk(&cf->cf_fcb, 0, conf_fcb_load_cb, &arg);
if (rc) {
return OS_EINVAL;
}
return OS_OK;
}
static int
conf_fcb_var_read(struct fcb_entry *loc, char *buf, char **name, char **val)
{
int rc;
rc = flash_area_read(loc->fe_area, loc->fe_data_off, buf, loc->fe_data_len);
if (rc) {
return rc;
}
buf[loc->fe_data_len] = '\0';
rc = conf_line_parse(buf, name, val);
return rc;
}
static void
conf_fcb_compress_internal(struct fcb *fcb,
int (*copy_or_not)(const char *name, const char *val,
void *cn_arg),
void *cn_arg)
{
int rc;
char buf1[CONF_MAX_NAME_LEN + CONF_MAX_VAL_LEN + 32];
char buf2[CONF_MAX_NAME_LEN + CONF_MAX_VAL_LEN + 32];
struct fcb_entry loc1;
struct fcb_entry loc2;
char *name1, *val1;
char *name2, *val2;
int copy;
rc = fcb_append_to_scratch(fcb);
if (rc) {
return; /* XXX */
}
loc1.fe_area = NULL;
loc1.fe_elem_off = 0;
while (fcb_getnext(fcb, &loc1) == 0) {
if (loc1.fe_area != fcb->f_oldest) {
break;
}
rc = conf_fcb_var_read(&loc1, buf1, &name1, &val1);
if (rc) {
continue;
}
if (!val1) {
continue;
}
loc2 = loc1;
copy = 1;
while (fcb_getnext(fcb, &loc2) == 0) {
rc = conf_fcb_var_read(&loc2, buf2, &name2, &val2);
if (rc) {
continue;
}
if (!strcmp(name1, name2)) {
copy = 0;
break;
}
}
if (!copy) {
continue;
}
if (copy_or_not) {
if (copy_or_not(name1, val1, cn_arg)) {
/* Copy rejected */
continue;
}
}
/*
* Can't find one. Must copy.
*/
rc = flash_area_read(loc1.fe_area, loc1.fe_data_off, buf1,
loc1.fe_data_len);
if (rc) {
continue;
}
rc = fcb_append(fcb, loc1.fe_data_len, &loc2);
if (rc) {
continue;
}
rc = flash_area_write(loc2.fe_area, loc2.fe_data_off, buf1,
loc1.fe_data_len);
if (rc) {
continue;
}
fcb_append_finish(fcb, &loc2);
}
rc = fcb_rotate(fcb);
if (rc) {
/* XXXX */
;
}
}
static int
conf_fcb_append(struct fcb *fcb, char *buf, int len)
{
int rc;
int i;
struct fcb_entry loc;
for (i = 0; i < 10; i++) {
rc = fcb_append(fcb, len, &loc);
if (rc != FCB_ERR_NOSPACE) {
break;
}
if (fcb->f_scratch_cnt == 0) {
return OS_ENOMEM;
}
conf_fcb_compress_internal(fcb, NULL, NULL);
}
if (rc) {
return OS_EINVAL;
}
rc = flash_area_write(loc.fe_area, loc.fe_data_off, buf, len);
if (rc) {
return OS_EINVAL;
}
fcb_append_finish(fcb, &loc);
return OS_OK;
}
static int
conf_fcb_save(struct conf_store *cs, const char *name, const char *value)
{
struct conf_fcb *cf = (struct conf_fcb *)cs;
return conf_fcb_kv_save(&cf->cf_fcb, name, value);
}
void
conf_fcb_compress(struct conf_fcb *cf,
int (*copy_or_not)(const char *name, const char *val,
void *cn_arg),
void *cn_arg)
{
conf_fcb_compress_internal(&cf->cf_fcb, copy_or_not, cn_arg);
}
static int
conf_kv_load_cb(struct fcb_entry *loc, void *arg)
{
struct conf_kv_load_cb_arg *cb_arg = arg;
char buf[CONF_MAX_NAME_LEN + CONF_MAX_VAL_LEN + 32];
char *name_str;
char *val_str;
int rc;
int len;
len = loc->fe_data_len;
if (len >= sizeof(buf)) {
len = sizeof(buf) - 1;
}
rc = flash_area_read(loc->fe_area, loc->fe_data_off, buf, len);
if (rc) {
return 0;
}
buf[len] = '\0';
rc = conf_line_parse(buf, &name_str, &val_str);
if (rc) {
return 0;
}
if (strcmp(name_str, cb_arg->name)) {
return 0;
}
strncpy(cb_arg->value, val_str, cb_arg->len);
cb_arg->value[cb_arg->len - 1] = '\0';
return 0;
}
int
conf_fcb_kv_load(struct fcb *fcb, const char *name, char *value, size_t len)
{
struct conf_kv_load_cb_arg arg;
int rc;
arg.name = name;
arg.value = value;
arg.len = len;
rc = fcb_walk(fcb, 0, conf_kv_load_cb, &arg);
if (rc) {
return OS_EINVAL;
}
return OS_OK;
}
int
conf_fcb_kv_save(struct fcb *fcb, const char *name, const char *value)
{
char buf[CONF_MAX_NAME_LEN + CONF_MAX_VAL_LEN + 32];
int len;
if (!name) {
return OS_INVALID_PARM;
}
len = conf_line_make(buf, sizeof(buf), name, value);
if (len < 0 || len + 2 > sizeof(buf)) {
return OS_INVALID_PARM;
}
return conf_fcb_append(fcb, buf, len);
}
#endif