| /* |
| * 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 |