mgmt: mcumgr: Add settings management

Adds a settings management group to MCUmgr which allows for
manipulation of the zephyr settings from a remote device.
Includes callback hooks to secure access from an application.

Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
This commit is contained in:
Jamie McCrae 2023-06-19 08:31:38 +01:00 committed by Fabio Baltieri
parent 6728af6458
commit 55527d8733
9 changed files with 759 additions and 4 deletions

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef H_SETTINGS_MGMT_
#define H_SETTINGS_MGMT_
#ifdef __cplusplus
extern "C" {
#endif
/**
* Command IDs for settings management group.
*/
#define SETTINGS_MGMT_ID_READ_WRITE 0
#define SETTINGS_MGMT_ID_DELETE 1
#define SETTINGS_MGMT_ID_COMMIT 2
#define SETTINGS_MGMT_ID_LOAD_SAVE 3
/**
* Command result codes for settings management group.
*/
enum settings_mgmt_ret_code_t {
/** No error, this is implied if there is no ret value in the response. */
SETTINGS_MGMT_ERR_OK = 0,
/** Unknown error occurred. */
SETTINGS_MGMT_ERR_UNKNOWN,
/** The provided key name is too long to be used. */
SETTINGS_MGMT_ERR_KEY_TOO_LONG,
/** The provided key name does not exist. */
SETTINGS_MGMT_ERR_KEY_NOT_FOUND,
/** The provided key name does not support being read. */
SETTINGS_MGMT_ERR_READ_NOT_SUPPORTED,
};
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,70 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef H_MCUMGR_SETTINGS_MGMT_CALLBACKS_
#define H_MCUMGR_SETTINGS_MGMT_CALLBACKS_
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief MCUmgr settings_mgmt callback API
* @defgroup mcumgr_callback_api_settings_mgmt MCUmgr settings_mgmt callback API
* @ingroup mcumgr_callback_api
* @{
*/
enum settings_mgmt_access_types {
SETTINGS_ACCESS_READ,
SETTINGS_ACCESS_WRITE,
SETTINGS_ACCESS_DELETE,
SETTINGS_ACCESS_COMMIT,
SETTINGS_ACCESS_LOAD,
SETTINGS_ACCESS_SAVE,
};
/**
* Structure provided in the #MGMT_EVT_OP_SETTINGS_MGMT_ACCESS notification callback: This
* callback function is used to notify the application about a pending setting
* read/write/delete/load/save/commit request and to authorise or deny it. Access will be allowed
* so long as no handlers return an error, if one returns an error then access will be denied.
*/
struct settings_mgmt_access {
/** Type of access */
enum settings_mgmt_access_types access;
/**
* Key name for accesses (only set for SETTINGS_ACCESS_READ, SETTINGS_ACCESS_WRITE and
* SETTINGS_ACCESS_DELETE). Note that this can be changed by handlers to redirect settings
* access if needed (as long as it does not exceed the maximum setting string size) if
* CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_STACK is selected, of maximum size
* CONFIG_MCUMGR_GRP_SETTINGS_NAME_LEN.
*
* Note: This string *must* be NULL terminated.
*/
#ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
const char *name;
#else
char *name;
#endif
/** Data provided by the user (only set for SETTINGS_ACCESS_WRITE) */
const uint8_t *val;
/** Length of data provided by the user (only set for SETTINGS_ACCESS_WRITE) */
const size_t *val_length;
};
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
* Copyright (c) 2022-2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -23,6 +23,10 @@
#include <zephyr/mgmt/mcumgr/grp/os_mgmt/os_mgmt_callbacks.h>
#endif
#ifdef CONFIG_MCUMGR_GRP_SETTINGS
#include <zephyr/mgmt/mcumgr/grp/settings_mgmt/settings_mgmt_callbacks.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
@ -113,6 +117,7 @@ enum mgmt_cb_groups {
MGMT_EVT_GRP_OS,
MGMT_EVT_GRP_IMG,
MGMT_EVT_GRP_FS,
MGMT_EVT_GRP_SETTINGS,
MGMT_EVT_GRP_USER_CUSTOM_START = MGMT_GROUP_ID_PERUSER,
};
@ -196,6 +201,17 @@ enum os_mgmt_group_events {
MGMT_EVT_OP_OS_MGMT_ALL = MGMT_DEF_EVT_OP_ALL(MGMT_EVT_GRP_OS),
};
/**
* MGMT event opcodes for settings management group.
*/
enum settings_mgmt_group_events {
/** Callback when a setting is read/written/deleted. */
MGMT_EVT_OP_SETTINGS_MGMT_ACCESS = MGMT_DEF_EVT_OP_ID(MGMT_EVT_GRP_SETTINGS, 0),
/** Used to enable all settings_mgmt_group events. */
MGMT_EVT_OP_SETTINGS_MGMT_ALL = MGMT_DEF_EVT_OP_ALL(MGMT_EVT_GRP_SETTINGS),
};
/**
* MGMT callback struct
*/

View file

@ -23,6 +23,12 @@ extern "C" {
* @{
*/
/**
* Used at end of MCUmgr handlers to return an error if the message size limit was reached,
* or OK if it was not
*/
#define MGMT_RETURN_CHECK(ok) ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE
/** Opcodes; encoded in first byte of header. */
enum mcumgr_op_t {
/** Read op-code */
@ -52,8 +58,8 @@ enum mcumgr_group_t {
/** Statistic management group, used for retieving statistics */
MGMT_GROUP_ID_STAT,
/** System configuration group (unused) */
MGMT_GROUP_ID_CONFIG,
/** Settings management (config) group, used for reading/writing settings */
MGMT_GROUP_ID_SETTINGS,
/** Log management group (unused) */
MGMT_GROUP_ID_LOG,

View file

@ -1,6 +1,6 @@
#
# Copyright (c) 2018-2021 mcumgr authors
# Copyright (c) 2022 Nordic Semiconductor ASA
# Copyright (c) 2022-2023 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
@ -13,3 +13,4 @@ add_subdirectory_ifdef(CONFIG_MCUMGR_GRP_OS_CLIENT os_mgmt_client)
add_subdirectory_ifdef(CONFIG_MCUMGR_GRP_STAT stat_mgmt)
add_subdirectory_ifdef(CONFIG_MCUMGR_GRP_SHELL shell_mgmt)
add_subdirectory_ifdef(CONFIG_MCUMGR_GRP_ZBASIC zephyr_basic)
add_subdirectory_ifdef(CONFIG_MCUMGR_GRP_SETTINGS settings_mgmt)

View file

@ -17,3 +17,5 @@ rsource "shell_mgmt/Kconfig"
rsource "stat_mgmt/Kconfig"
rsource "zephyr_basic/Kconfig"
rsource "settings_mgmt/Kconfig"

View file

@ -0,0 +1,18 @@
#
# Copyright (c) 2023 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#
# Settings management group public API is exposed by MCUmgr API
# interface, when settings management is enabled.
zephyr_library(mgmt_mcumgr_grp_settings)
zephyr_library_sources(src/settings_mgmt.c)
if(CONFIG_MCUMGR_GRP_SETTINGS AND NOT CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK)
message(WARNING "Note: MCUmgr settings management is enabled but settings access hooks are "
"disabled, this is an insecure configuration and not recommended for production "
"use, as all settings on the device can be manipulated by a remote device. See "
"https://docs.zephyrproject.org/latest/services/device_mgmt/mcumgr_callbacks.html "
"for details on enabling and using MCUmgr hooks.")
endif()

View file

@ -0,0 +1,68 @@
# Copyright Nordic Semiconductor ASA 2023. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
menuconfig MCUMGR_GRP_SETTINGS
bool "MCUmgr handlers for settings management"
depends on SETTINGS
depends on SETTINGS_RUNTIME
select MCUMGR_SMP_CBOR_MIN_DECODING_LEVEL_2
select MCUMGR_SMP_CBOR_MIN_ENCODING_LEVEL_2 if ZCBOR_CANONICAL
select MCUMGR_SMP_CBOR_MIN_ENCODING_LEVEL_1
help
Enables MCUmgr handlers for settings manipulation.
if MCUMGR_GRP_SETTINGS
choice MCUMGR_GRP_SETTINGS_BUFFER_TYPE
prompt "Buffer type"
default MCUMGR_GRP_SETTINGS_BUFFER_TYPE_STACK
help
Selects if the stack or heap will be used for variables that are
needed when processing requests.
config MCUMGR_GRP_SETTINGS_BUFFER_TYPE_STACK
bool "Stack (fixed size)"
help
Use a fixed size stack buffer, any user-supplied values longer than
this will be rejected.
Note that stack usage for parameter storage alone will be
MCUMGR_GRP_SETTINGS_NAME_LEN + MCUMGR_GRP_SETTINGS_VALUE_LEN,
therefore the MCUmgr stack should be appropriately sized.
config MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
bool "Heap (dynamic size)"
depends on COMMON_LIBC_MALLOC_ARENA_SIZE > 0
help
Use dynamic heap memory allocation through malloc, if there is
insufficient heap memory for the allocation then the request will be
rejected.
endchoice
config MCUMGR_GRP_SETTINGS_NAME_LEN
int "Maximum setting name length"
default 32
help
Maximum length of a key to lookup, this will be the size of the
variable if placed on the stack or the maximum allocated size of the
variable if placed on the heap.
config MCUMGR_GRP_SETTINGS_VALUE_LEN
int "Maximum setting value length"
default 32
help
Maximum length of a value to read, this will be the size of the
variable if placed on the stack or the allocated size of the
variable if placed on the heap (settings does not support getting
the size of a value prior to looking it up).
config MCUMGR_GRP_SETTINGS_ACCESS_HOOK
bool "Settings access hook"
depends on MCUMGR_MGMT_NOTIFICATION_HOOKS
help
Allows applications to control settings management access by
registering for a callback which is then triggered whenever a
settings read or write attempt is made.
endif

View file

@ -0,0 +1,528 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/sys/util.h>
#include <zephyr/settings/settings.h>
#include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
#include <zephyr/mgmt/mcumgr/smp/smp.h>
#include <zephyr/mgmt/mcumgr/mgmt/handlers.h>
#include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
#include <zephyr/mgmt/mcumgr/grp/settings_mgmt/settings_mgmt.h>
#include <mgmt/mcumgr/util/zcbor_bulk.h>
#include <assert.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <zcbor_common.h>
#include <zcbor_decode.h>
#include <zcbor_encode.h>
#define LOG_LEVEL CONFIG_MCUMGR_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(settings_mgmt);
/**
* Command handler: settings read
*/
static int settings_mgmt_read(struct smp_streamer *ctxt)
{
int rc;
zcbor_state_t *zse = ctxt->writer->zs;
zcbor_state_t *zsd = ctxt->reader->zs;
bool ok;
size_t decoded;
struct zcbor_string key = { 0 };
uint32_t max_size = CONFIG_MCUMGR_GRP_SETTINGS_VALUE_LEN;
bool limited_size = false;
#ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
char *key_name = NULL;
uint8_t *data = NULL;
#else
char key_name[CONFIG_MCUMGR_GRP_SETTINGS_NAME_LEN] = { 0 };
uint8_t data[CONFIG_MCUMGR_GRP_SETTINGS_VALUE_LEN] = { 0 };
#endif
struct zcbor_map_decode_key_val settings_read_decode[] = {
ZCBOR_MAP_DECODE_KEY_DECODER("name", zcbor_tstr_decode, &key),
ZCBOR_MAP_DECODE_KEY_DECODER("max_size", zcbor_uint32_decode, &max_size),
};
ok = zcbor_map_decode_bulk(zsd, settings_read_decode, ARRAY_SIZE(settings_read_decode),
&decoded) == 0;
if (!ok || key.len == 0) {
return MGMT_ERR_EINVAL;
}
/* Check if the length of the user supplied key is too large for the configuration
* to allow and return an error if so
*/
if (key.len >= CONFIG_MCUMGR_GRP_SETTINGS_NAME_LEN) {
ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SETTINGS,
SETTINGS_MGMT_ERR_KEY_TOO_LONG);
goto end;
}
if (max_size > CONFIG_MCUMGR_GRP_SETTINGS_VALUE_LEN) {
max_size = CONFIG_MCUMGR_GRP_SETTINGS_VALUE_LEN;
limited_size = true;
}
#ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
key_name = (char *)malloc(key.len + 1);
data = (uint8_t *)malloc(max_size);
if (data == NULL || key_name == NULL) {
if (key_name != NULL) {
free(key_name);
}
return MGMT_ERR_ENOMEM;
}
#endif
memcpy(key_name, key.value, key.len);
key_name[key.len] = 0;
if (IS_ENABLED(CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK)) {
/* Send request to application to check if access should be allowed or not */
struct settings_mgmt_access settings_access_data = {
.access = SETTINGS_ACCESS_READ,
.name = key_name,
};
enum mgmt_cb_return status;
int32_t ret_rc;
uint16_t ret_group;
status = mgmt_callback_notify(MGMT_EVT_OP_SETTINGS_MGMT_ACCESS,
&settings_access_data, sizeof(settings_access_data),
&ret_rc, &ret_group);
if (status != MGMT_CB_OK) {
bool ok;
if (status == MGMT_CB_ERROR_RC) {
return ret_rc;
}
ok = smp_add_cmd_err(zse, ret_group, (uint16_t)ret_rc);
goto end;
}
}
/* Read the settings key */
rc = settings_runtime_get(key_name, data, max_size);
if (rc < 0) {
if (rc == -EINVAL) {
rc = SETTINGS_MGMT_ERR_KEY_NOT_FOUND;
} else if (rc == -ENOTSUP) {
rc = SETTINGS_MGMT_ERR_READ_NOT_SUPPORTED;
} else {
rc = SETTINGS_MGMT_ERR_UNKNOWN;
}
ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SETTINGS, (uint16_t)rc);
goto end;
}
ok = zcbor_tstr_put_lit(zse, "val") &&
zcbor_bstr_encode_ptr(zse, data, rc);
if (ok && limited_size) {
ok = zcbor_tstr_put_lit(zse, "max_size") &&
zcbor_uint32_put(zse, CONFIG_MCUMGR_GRP_SETTINGS_VALUE_LEN);
}
end:
#ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
free(key_name);
free(data);
#endif
return MGMT_RETURN_CHECK(ok);
}
/**
* Command handler: settings write
*/
static int settings_mgmt_write(struct smp_streamer *ctxt)
{
int rc;
zcbor_state_t *zse = ctxt->writer->zs;
zcbor_state_t *zsd = ctxt->reader->zs;
bool ok;
struct zcbor_string data = { 0 };
size_t decoded;
struct zcbor_string key = { 0 };
#ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
char *key_name = NULL;
#else
char key_name[CONFIG_MCUMGR_GRP_SETTINGS_NAME_LEN];
#endif
struct zcbor_map_decode_key_val settings_write_decode[] = {
ZCBOR_MAP_DECODE_KEY_DECODER("name", zcbor_tstr_decode, &key),
ZCBOR_MAP_DECODE_KEY_DECODER("val", zcbor_bstr_decode, &data),
};
ok = zcbor_map_decode_bulk(zsd, settings_write_decode, ARRAY_SIZE(settings_write_decode),
&decoded) == 0;
if (!ok || key.len == 0) {
return MGMT_ERR_EINVAL;
}
/* Check if the length of the user supplied key is too large for the configuration
* to allow and return an error if so
*/
if (key.len >= CONFIG_MCUMGR_GRP_SETTINGS_NAME_LEN) {
ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SETTINGS,
SETTINGS_MGMT_ERR_KEY_TOO_LONG);
goto end;
}
#ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
key_name = (char *)malloc(key.len + 1);
if (key_name == NULL) {
return MGMT_ERR_ENOMEM;
}
#endif
memcpy(key_name, key.value, key.len);
key_name[key.len] = 0;
if (IS_ENABLED(CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK)) {
/* Send request to application to check if access should be allowed or not */
struct settings_mgmt_access settings_access_data = {
.access = SETTINGS_ACCESS_WRITE,
.name = key_name,
.val = data.value,
.val_length = &data.len,
};
enum mgmt_cb_return status;
int32_t ret_rc;
uint16_t ret_group;
status = mgmt_callback_notify(MGMT_EVT_OP_SETTINGS_MGMT_ACCESS,
&settings_access_data, sizeof(settings_access_data),
&ret_rc, &ret_group);
if (status != MGMT_CB_OK) {
bool ok;
if (status == MGMT_CB_ERROR_RC) {
return ret_rc;
}
ok = smp_add_cmd_err(zse, ret_group, (uint16_t)ret_rc);
goto end;
}
}
/* Update the settings data */
rc = settings_runtime_set(key_name, data.value, data.len);
if (rc < 0) {
if (rc == -EINVAL) {
rc = SETTINGS_MGMT_ERR_KEY_NOT_FOUND;
} else {
rc = SETTINGS_MGMT_ERR_UNKNOWN;
}
ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SETTINGS, (uint16_t)rc);
}
end:
#ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
free(key_name);
#endif
return MGMT_RETURN_CHECK(ok);
}
/**
* Command handler: settings delete
*/
static int settings_mgmt_delete(struct smp_streamer *ctxt)
{
zcbor_state_t *zse = ctxt->writer->zs;
zcbor_state_t *zsd = ctxt->reader->zs;
int rc;
bool ok;
size_t decoded;
struct zcbor_string key = { 0 };
#ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
char *key_name = NULL;
#else
char key_name[CONFIG_MCUMGR_GRP_SETTINGS_NAME_LEN];
#endif
struct zcbor_map_decode_key_val settings_delete_decode[] = {
ZCBOR_MAP_DECODE_KEY_DECODER("name", zcbor_tstr_decode, &key),
};
ok = zcbor_map_decode_bulk(zsd, settings_delete_decode, ARRAY_SIZE(settings_delete_decode),
&decoded) == 0;
if (!ok || key.len == 0) {
return MGMT_ERR_EINVAL;
}
/* Check if the length of the user supplied key is too large for the configuration
* to allow and return an error if so
*/
if (key.len >= CONFIG_MCUMGR_GRP_SETTINGS_NAME_LEN) {
ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SETTINGS,
SETTINGS_MGMT_ERR_KEY_TOO_LONG);
goto end;
}
#ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
key_name = (char *)malloc(key.len + 1);
if (key_name == NULL) {
return MGMT_ERR_ENOMEM;
}
#endif
memcpy(key_name, key.value, key.len);
key_name[key.len] = 0;
if (IS_ENABLED(CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK)) {
/* Send request to application to check if access should be allowed or not */
struct settings_mgmt_access settings_access_data = {
.access = SETTINGS_ACCESS_DELETE,
.name = key_name,
};
enum mgmt_cb_return status;
int32_t ret_rc;
uint16_t ret_group;
status = mgmt_callback_notify(MGMT_EVT_OP_SETTINGS_MGMT_ACCESS,
&settings_access_data, sizeof(settings_access_data),
&ret_rc, &ret_group);
if (status != MGMT_CB_OK) {
bool ok;
if (status == MGMT_CB_ERROR_RC) {
return ret_rc;
}
ok = smp_add_cmd_err(zse, ret_group, (uint16_t)ret_rc);
goto end;
}
}
/* Delete requested key from settings */
rc = settings_delete(key_name);
#ifdef CONFIG_MCUMGR_GRP_SETTINGS_BUFFER_TYPE_HEAP
free(key_name);
#endif
if (rc < 0) {
if (rc == -ENOENT) {
rc = SETTINGS_MGMT_ERR_KEY_NOT_FOUND;
} else {
rc = SETTINGS_MGMT_ERR_UNKNOWN;
}
ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_SETTINGS, (uint16_t)rc);
}
end:
return MGMT_RETURN_CHECK(ok);
}
/**
* Command handler: settings commit
*/
static int settings_mgmt_commit(struct smp_streamer *ctxt)
{
bool ok = true;
if (IS_ENABLED(CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK)) {
/* Send request to application to check if access should be allowed or not */
struct settings_mgmt_access settings_access_data = {
.access = SETTINGS_ACCESS_COMMIT,
};
zcbor_state_t *zse = ctxt->writer->zs;
enum mgmt_cb_return status;
int32_t ret_rc;
uint16_t ret_group;
status = mgmt_callback_notify(MGMT_EVT_OP_SETTINGS_MGMT_ACCESS,
&settings_access_data, sizeof(settings_access_data),
&ret_rc, &ret_group);
if (status != MGMT_CB_OK) {
bool ok;
if (status == MGMT_CB_ERROR_RC) {
return ret_rc;
}
ok = smp_add_cmd_err(zse, ret_group, (uint16_t)ret_rc);
goto end;
}
}
settings_commit();
end:
return MGMT_RETURN_CHECK(ok);
}
/**
* Command handler: settings load
*/
static int settings_mgmt_load(struct smp_streamer *ctxt)
{
bool ok = true;
if (IS_ENABLED(CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK)) {
/* Send request to application to check if access should be allowed or not */
struct settings_mgmt_access settings_access_data = {
.access = SETTINGS_ACCESS_LOAD,
};
zcbor_state_t *zse = ctxt->writer->zs;
enum mgmt_cb_return status;
int32_t ret_rc;
uint16_t ret_group;
status = mgmt_callback_notify(MGMT_EVT_OP_SETTINGS_MGMT_ACCESS,
&settings_access_data, sizeof(settings_access_data),
&ret_rc, &ret_group);
if (status != MGMT_CB_OK) {
bool ok;
if (status == MGMT_CB_ERROR_RC) {
return ret_rc;
}
ok = smp_add_cmd_err(zse, ret_group, (uint16_t)ret_rc);
goto end;
}
}
settings_load();
end:
return MGMT_RETURN_CHECK(ok);
}
/**
* Command handler: settings save
*/
static int settings_mgmt_save(struct smp_streamer *ctxt)
{
bool ok = true;
if (IS_ENABLED(CONFIG_MCUMGR_GRP_SETTINGS_ACCESS_HOOK)) {
/* Send request to application to check if access should be allowed or not */
struct settings_mgmt_access settings_access_data = {
.access = SETTINGS_ACCESS_SAVE,
};
zcbor_state_t *zse = ctxt->writer->zs;
enum mgmt_cb_return status;
int32_t ret_rc;
uint16_t ret_group;
status = mgmt_callback_notify(MGMT_EVT_OP_SETTINGS_MGMT_ACCESS,
&settings_access_data, sizeof(settings_access_data),
&ret_rc, &ret_group);
if (status != MGMT_CB_OK) {
bool ok;
if (status == MGMT_CB_ERROR_RC) {
return ret_rc;
}
ok = smp_add_cmd_err(zse, ret_group, (uint16_t)ret_rc);
goto end;
}
}
settings_save();
end:
return MGMT_RETURN_CHECK(ok);
}
#ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
static int settings_mgmt_translate_error_code(uint16_t ret)
{
int rc;
switch (ret) {
case SETTINGS_MGMT_ERR_KEY_TOO_LONG:
rc = MGMT_ERR_EINVAL;
break;
case SETTINGS_MGMT_ERR_KEY_NOT_FOUND:
case SETTINGS_MGMT_ERR_READ_NOT_SUPPORTED:
rc = MGMT_ERR_ENOENT;
break;
case SETTINGS_MGMT_ERR_UNKNOWN:
default:
rc = MGMT_ERR_EUNKNOWN;
}
return rc;
}
#endif
static const struct mgmt_handler settings_mgmt_handlers[] = {
[SETTINGS_MGMT_ID_READ_WRITE] = {
.mh_read = settings_mgmt_read,
.mh_write = settings_mgmt_write,
},
[SETTINGS_MGMT_ID_DELETE] = {
.mh_read = NULL,
.mh_write = settings_mgmt_delete,
},
[SETTINGS_MGMT_ID_COMMIT] = {
.mh_read = NULL,
.mh_write = settings_mgmt_commit,
},
[SETTINGS_MGMT_ID_LOAD_SAVE] = {
.mh_read = settings_mgmt_load,
.mh_write = settings_mgmt_save,
},
};
static struct mgmt_group settings_mgmt_group = {
.mg_handlers = settings_mgmt_handlers,
.mg_handlers_count = ARRAY_SIZE(settings_mgmt_handlers),
.mg_group_id = MGMT_GROUP_ID_SETTINGS,
#ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
.mg_translate_error = settings_mgmt_translate_error_code,
#endif
};
static void settings_mgmt_register_group(void)
{
mgmt_register_group(&settings_mgmt_group);
}
MCUMGR_HANDLER_DEFINE(settings_mgmt, settings_mgmt_register_group);