| /* |
| * Copyright (c) 2019 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/logging/log.h> |
| #include <zephyr/settings/settings.h> |
| #include <zephyr/random/random.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; |
| |
| /* Indicates if delete subtree root. */ |
| bool delete_subtree_root; |
| }; |
| |
| 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; |
| } |
| |
| if (key == NULL && ctx->delete_subtree_root == false) { |
| 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", path); |
| |
| ret = settings_delete(path); |
| if (ret != 0) { |
| LOG_ERR("Failed to remove setting %s, ret %d", 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, bool delete_subtree_root) |
| { |
| int ret; |
| char subtree[OT_SETTINGS_MAX_PATH_LEN]; |
| struct ot_setting_delete_ctx delete_ctx = { |
| .subtree = subtree, |
| .status = -ENOENT, |
| .target_index = index, |
| .delete_subtree_root = delete_subtree_root, |
| }; |
| |
| 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 instance 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, const uint16_t *aSensitiveKeys, |
| uint16_t aSensitiveKeysLength) |
| { |
| int ret; |
| |
| ARG_UNUSED(aInstance); |
| ARG_UNUSED(aSensitiveKeys); |
| ARG_UNUSED(aSensitiveKeysLength); |
| |
| 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, false); |
| |
| ret = snprintk(path, sizeof(path), "%s/%x", OT_SETTINGS_ROOT_KEY, aKey); |
| __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, true); |
| 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, true); |
| } |
| |
| void otPlatSettingsDeinit(otInstance *aInstance) |
| { |
| ARG_UNUSED(aInstance); |
| } |