ot_setting_delete_subtree returns the information if the subtree had been found in the persistent storage or not before removing. This has matter for otPlatSettingsGet. In other uses cases the return value shoud be ignored. The right place for the check if deletion succeded is in settings_load_subtree_direct and its callback. Currently "ot factoryreset" causes an assert if there is no OpenThread dataset stored in the persistent memory. This PR fixes it. Signed-off-by: Lukasz Maciejonczyk <lukasz.maciejonczyk@nordicsemi.no>
320 lines
6.7 KiB
C
320 lines
6.7 KiB
C
/*
|
|
* Copyright (c) 2019 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <kernel.h>
|
|
#include <logging/log.h>
|
|
#include <settings/settings.h>
|
|
#include <random/rand32.h>
|
|
|
|
#include <openthread/platform/settings.h>
|
|
|
|
LOG_MODULE_REGISTER(net_otPlat_settings, CONFIG_OPENTHREAD_L2_LOG_LEVEL);
|
|
|
|
#define OT_SETTINGS_ROOT_KEY "ot"
|
|
#define OT_SETTINGS_MAX_PATH_LEN 32
|
|
|
|
struct ot_setting_delete_ctx {
|
|
/* Setting subtree to delete. */
|
|
const char *subtree;
|
|
|
|
/* Current entry index, used to iterate over multiple setting
|
|
* instances.
|
|
*/
|
|
int index;
|
|
|
|
/* Target index to delete. -1 to delete entire subtree. */
|
|
int target_index;
|
|
|
|
/* Operation result. */
|
|
int status;
|
|
};
|
|
|
|
static int ot_setting_delete_cb(const char *key, size_t len,
|
|
settings_read_cb read_cb, void *cb_arg,
|
|
void *param)
|
|
{
|
|
int ret;
|
|
char path[OT_SETTINGS_MAX_PATH_LEN];
|
|
struct ot_setting_delete_ctx *ctx =
|
|
(struct ot_setting_delete_ctx *)param;
|
|
|
|
ARG_UNUSED(len);
|
|
ARG_UNUSED(read_cb);
|
|
ARG_UNUSED(cb_arg);
|
|
|
|
if ((ctx->target_index != -1) && (ctx->target_index != ctx->index)) {
|
|
ctx->index++;
|
|
return 0;
|
|
}
|
|
|
|
ret = snprintk(path, sizeof(path), "%s%s%s", ctx->subtree,
|
|
key ? "/" : "", key ? key : "");
|
|
__ASSERT(ret < sizeof(path), "Setting path buffer too small.");
|
|
|
|
LOG_DBG("Removing: %s", log_strdup(path));
|
|
|
|
ret = settings_delete(path);
|
|
if (ret != 0) {
|
|
LOG_ERR("Failed to remove setting %s, ret %d", log_strdup(path),
|
|
ret);
|
|
__ASSERT_NO_MSG(false);
|
|
}
|
|
|
|
ctx->status = 0;
|
|
|
|
if (ctx->target_index == ctx->index) {
|
|
/* Break the loop on index match, otherwise it was -1
|
|
* (delete all).
|
|
*/
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ot_setting_delete_subtree(int key, int index)
|
|
{
|
|
int ret;
|
|
char subtree[OT_SETTINGS_MAX_PATH_LEN];
|
|
struct ot_setting_delete_ctx delete_ctx = {
|
|
.subtree = subtree,
|
|
.status = -ENOENT,
|
|
.target_index = index
|
|
};
|
|
|
|
if (key == -1) {
|
|
ret = snprintk(subtree, sizeof(subtree), "%s",
|
|
OT_SETTINGS_ROOT_KEY);
|
|
} else {
|
|
ret = snprintk(subtree, sizeof(subtree), "%s/%x",
|
|
OT_SETTINGS_ROOT_KEY, key);
|
|
}
|
|
__ASSERT(ret < sizeof(subtree), "Setting path buffer too small.");
|
|
|
|
ret = settings_load_subtree_direct(subtree, ot_setting_delete_cb,
|
|
&delete_ctx);
|
|
if (ret != 0) {
|
|
LOG_ERR("Failed to delete OT subtree %s, index %d, ret %d",
|
|
subtree, index, ret);
|
|
__ASSERT_NO_MSG(false);
|
|
}
|
|
|
|
return delete_ctx.status;
|
|
}
|
|
|
|
static int ot_setting_exists_cb(const char *key, size_t len,
|
|
settings_read_cb read_cb, void *cb_arg,
|
|
void *param)
|
|
{
|
|
bool *exists = (bool *)param;
|
|
|
|
ARG_UNUSED(len);
|
|
ARG_UNUSED(read_cb);
|
|
ARG_UNUSED(cb_arg);
|
|
ARG_UNUSED(key);
|
|
|
|
*exists = true;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static bool ot_setting_exists(const char *path)
|
|
{
|
|
bool exists = false;
|
|
|
|
(void)settings_load_subtree_direct(path, ot_setting_exists_cb, &exists);
|
|
|
|
return exists;
|
|
}
|
|
|
|
struct ot_setting_read_ctx {
|
|
/* Buffer for the setting. */
|
|
uint8_t *value;
|
|
|
|
/* Buffer length on input, setting length read on output. */
|
|
uint16_t *length;
|
|
|
|
/* Current entry index, used to iterate over multiple setting
|
|
* instances.
|
|
*/
|
|
int index;
|
|
|
|
/* Target instnace to read. */
|
|
int target_index;
|
|
|
|
/* Operation result. */
|
|
int status;
|
|
};
|
|
|
|
static int ot_setting_read_cb(const char *key, size_t len,
|
|
settings_read_cb read_cb, void *cb_arg,
|
|
void *param)
|
|
{
|
|
int ret;
|
|
struct ot_setting_read_ctx *ctx = (struct ot_setting_read_ctx *)param;
|
|
|
|
ARG_UNUSED(len);
|
|
ARG_UNUSED(read_cb);
|
|
ARG_UNUSED(cb_arg);
|
|
|
|
if (ctx->target_index != ctx->index) {
|
|
ctx->index++;
|
|
return 0;
|
|
}
|
|
|
|
/* Found setting, break the loop. */
|
|
|
|
if ((ctx->value == NULL) || (ctx->length == NULL)) {
|
|
goto out;
|
|
}
|
|
|
|
if (*(ctx->length) < len) {
|
|
len = *(ctx->length);
|
|
}
|
|
|
|
ret = read_cb(cb_arg, ctx->value, len);
|
|
if (ret <= 0) {
|
|
LOG_ERR("Failed to read the setting, ret: %d", ret);
|
|
ctx->status = -EIO;
|
|
return 1;
|
|
}
|
|
|
|
out:
|
|
if (ctx->length != NULL) {
|
|
*(ctx->length) = len;
|
|
}
|
|
|
|
ctx->status = 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* OpenThread APIs */
|
|
|
|
void otPlatSettingsInit(otInstance *aInstance)
|
|
{
|
|
int ret;
|
|
|
|
ARG_UNUSED(aInstance);
|
|
|
|
ret = settings_subsys_init();
|
|
if (ret != 0) {
|
|
LOG_ERR("settings_subsys_init failed (ret %d)", ret);
|
|
}
|
|
}
|
|
|
|
otError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex,
|
|
uint8_t *aValue, uint16_t *aValueLength)
|
|
{
|
|
int ret;
|
|
char path[OT_SETTINGS_MAX_PATH_LEN];
|
|
struct ot_setting_read_ctx read_ctx = {
|
|
.value = aValue,
|
|
.length = (uint16_t *)aValueLength,
|
|
.status = -ENOENT,
|
|
.target_index = aIndex
|
|
};
|
|
|
|
ARG_UNUSED(aInstance);
|
|
|
|
LOG_DBG("%s Entry aKey %u aIndex %d", __func__, aKey, aIndex);
|
|
|
|
ret = snprintk(path, sizeof(path), "%s/%x", OT_SETTINGS_ROOT_KEY, aKey);
|
|
__ASSERT(ret < sizeof(path), "Setting path buffer too small.");
|
|
|
|
ret = settings_load_subtree_direct(path, ot_setting_read_cb, &read_ctx);
|
|
if (ret != 0) {
|
|
LOG_ERR("Failed to load OT setting aKey %d, aIndex %d, ret %d",
|
|
aKey, aIndex, ret);
|
|
}
|
|
|
|
if (read_ctx.status != 0) {
|
|
LOG_DBG("aKey %u aIndex %d not found", aKey, aIndex);
|
|
return OT_ERROR_NOT_FOUND;
|
|
}
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey,
|
|
const uint8_t *aValue, uint16_t aValueLength)
|
|
{
|
|
int ret;
|
|
char path[OT_SETTINGS_MAX_PATH_LEN];
|
|
|
|
ARG_UNUSED(aInstance);
|
|
|
|
LOG_DBG("%s Entry aKey %u", __func__, aKey);
|
|
|
|
(void)ot_setting_delete_subtree(aKey, -1);
|
|
|
|
ret = snprintk(path, sizeof(path), "%s/%x/%08x", OT_SETTINGS_ROOT_KEY,
|
|
aKey, sys_rand32_get());
|
|
__ASSERT(ret < sizeof(path), "Setting path buffer too small.");
|
|
|
|
ret = settings_save_one(path, aValue, aValueLength);
|
|
if (ret != 0) {
|
|
LOG_ERR("Failed to store setting %d, ret %d", aKey, ret);
|
|
return OT_ERROR_NO_BUFS;
|
|
}
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey,
|
|
const uint8_t *aValue, uint16_t aValueLength)
|
|
{
|
|
int ret;
|
|
char path[OT_SETTINGS_MAX_PATH_LEN];
|
|
|
|
ARG_UNUSED(aInstance);
|
|
|
|
LOG_DBG("%s Entry aKey %u", __func__, aKey);
|
|
|
|
do {
|
|
ret = snprintk(path, sizeof(path), "%s/%x/%08x",
|
|
OT_SETTINGS_ROOT_KEY, aKey, sys_rand32_get());
|
|
__ASSERT(ret < sizeof(path), "Setting path buffer too small.");
|
|
} while (ot_setting_exists(path));
|
|
|
|
ret = settings_save_one(path, aValue, aValueLength);
|
|
if (ret != 0) {
|
|
LOG_ERR("Failed to store setting %d, ret %d", aKey, ret);
|
|
return OT_ERROR_NO_BUFS;
|
|
}
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex)
|
|
{
|
|
int ret;
|
|
|
|
ARG_UNUSED(aInstance);
|
|
|
|
LOG_DBG("%s Entry aKey %u aIndex %d", __func__, aKey, aIndex);
|
|
|
|
ret = ot_setting_delete_subtree(aKey, aIndex);
|
|
if (ret != 0) {
|
|
LOG_DBG("Entry not found aKey %u aIndex %d", aKey, aIndex);
|
|
return OT_ERROR_NOT_FOUND;
|
|
}
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
void otPlatSettingsWipe(otInstance *aInstance)
|
|
{
|
|
ARG_UNUSED(aInstance);
|
|
|
|
(void)ot_setting_delete_subtree(-1, -1);
|
|
}
|
|
|
|
void otPlatSettingsDeinit(otInstance *aInstance)
|
|
{
|
|
ARG_UNUSED(aInstance);
|
|
}
|