mgmt: mcumgr: Rework event callback system

Reworks the event callback system to use a linked list to allow for
chained handlers and support passing a status back to the handler to
indicate if the request should be rejected or allowed. This also
removes the old base callback functionality.

Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
This commit is contained in:
Jamie McCrae 2022-09-23 10:18:40 +01:00 committed by Carles Cufí
commit 6f75c99b8b
6 changed files with 285 additions and 67 deletions

View file

@ -0,0 +1,165 @@
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef H_MCUMGR_CALLBACKS_
#define H_MCUMGR_CALLBACKS_
#include <inttypes.h>
#include <zephyr/sys/slist.h>
#include <mgmt/mgmt.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief MCUmgr callback API
* @defgroup mcumgr_callback_api MCUmgr callback API
* @ingroup mcumgr
* @{
*/
/** @cond INTERNAL_HIDDEN */
/** Event which signfies that all event IDs for a particular group should be enabled. */
#define MGMT_EVT_OP_ID_ALL 0xffff
/** Get event for a particular group and event ID. */
#define MGMT_DEF_EVT_OP_ID(group, event_id) ((group << 16) | BIT(event_id))
/** Get event used for enabling all event IDs of a particular group. */
#define MGMT_DEF_EVT_OP_ALL(group) ((group << 16) | MGMT_EVT_OP_ID_ALL)
/** @endcond */
/** Get group from event. */
#define MGMT_EVT_GET_GROUP(event) ((event >> 16) & MGMT_EVT_OP_ID_ALL)
/** Get event ID from event. */
#define MGMT_EVT_GET_ID(event) (event & MGMT_EVT_OP_ID_ALL)
/**
* @typedef mgmt_cb
* @brief Function to be called on MGMT notification/event.
*
* This callback function is used to notify an application or system about a mcumgr mgmt event.
*
* @param event MGMT_EVT_OP_[...].
* @param rc MGMT_ERR_[...] of the previous handler calls, if it is an error then it
* will be the first error that was returned by a handler (i.e. this handler
* is being called for a notification only, the return code will be ignored).
* @param abort_more Set to true to abort further processing by additional handlers.
* @param data Optional event argument.
* @param data_size Size of optional event argument (0 if no data is provided).
*
* @return MGMT_ERR_[...] of the status to return to the calling code (only checked
* when failed is false).
*/
typedef int32_t (*mgmt_cb)(uint32_t event, int32_t rc, bool *abort_more, void *data,
size_t data_size);
/**
* MGMT event callback group IDs. Note that this is not a 1:1 mapping with MGMT_GROUP_ID_[...]
* values.
*/
enum mgmt_cb_groups {
MGMT_EVT_GRP_ALL = 0,
MGMT_EVT_GRP_SMP,
MGMT_EVT_GRP_USER_CUSTOM_START = MGMT_GROUP_ID_PERUSER,
};
/**
* MGMT event opcodes for all command processing.
*/
enum smp_all_events {
/** Used to enable all events. */
MGMT_EVT_OP_ALL = MGMT_DEF_EVT_OP_ALL(MGMT_EVT_GRP_ALL),
};
/**
* MGMT event opcodes for base SMP command processing.
*/
enum smp_group_events {
/** Callback when a command is received, data is mgmt_evt_op_cmd_arg. */
MGMT_EVT_OP_CMD_RECV = MGMT_DEF_EVT_OP_ID(MGMT_EVT_GRP_SMP, 0),
/** Callback when a a status is updated, data is mgmt_evt_op_cmd_arg. */
MGMT_EVT_OP_CMD_STATUS = MGMT_DEF_EVT_OP_ID(MGMT_EVT_GRP_SMP, 1),
/** Callback when a command has been processed, data is mgmt_evt_op_cmd_arg. */
MGMT_EVT_OP_CMD_DONE = MGMT_DEF_EVT_OP_ID(MGMT_EVT_GRP_SMP, 2),
/** Used to enable all smp_group events. */
MGMT_EVT_OP_CMD_ALL = MGMT_DEF_EVT_OP_ALL(MGMT_EVT_GRP_SMP),
};
/**
* MGMT callback struct
*/
struct mgmt_callback {
/** Entry list node. */
sys_snode_t node;
/** Callback that will be called. */
mgmt_cb callback;
/** MGMT_EVT_[...] Event ID for handler to be called on. */
uint32_t event_id;
};
/**
* MGMT_EVT_OP_CMD_RECV, MGMT_EVT_OP_CMD_STATUS, MGMT_EVT_OP_CMD_DONE arguments
*/
struct mgmt_evt_op_cmd_arg {
/** MGMT_GROUP_ID_[...] */
uint16_t group;
/** Message ID within group */
uint8_t id;
union {
/** MGMT_ERR_[...], used in MGMT_EVT_OP_CMD_DONE */
int err;
/** IMG_MGMT_ID_UPLOAD_STATUS_[...], used in MGMT_EVT_OP_CMD_STATUS */
int status;
};
};
/**
* @brief This function is called to notify registered callbacks about mcumgr notifications/events.
*
* @param event MGMT_EVT_OP_[...].
* @param data Optional event argument.
* @param data_size Size of optional event argument (0 if none).
*
* @return MGMT_ERR_[...] either MGMT_ERR_EOK if all handlers returned it, or the
* error code of the first handler that returned an error.
*/
int32_t mgmt_callback_notify(uint32_t event, void *data, size_t data_size);
/**
* @brief Register event callback function.
*
* @param callback Callback struct.
*/
void mgmt_callback_register(struct mgmt_callback *callback);
/**
* @brief Unregister event callback function.
*
* @param callback Callback struct.
*/
void mgmt_callback_unregister(struct mgmt_callback *callback);
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* H_MCUMGR_CALLBACKS_ */

View file

@ -101,6 +101,30 @@ config MGMT_MAX_DECODING_LEVELS
size of cbor_nb_reader structure by zcbor_state_t size per
one unit selected here.
config MCUMGR_MGMT_NOTIFICATION_HOOKS
bool "MCUmgr notification hook support"
help
With this enabled, applications and parts of code can register for MCUmgr event
notifications which will result in callbacks when a registered event occurs. Note that
this enables the base notification functionality but itself does not enable any
notifications, which must be enabled by selecting other Kconfig options.
To enable notifications in code, mgmt_callback_register() must be called with the
callback function and events that want to be received. Multiple handlers can be
registered and will all be called when registered events occur.
Some callbacks support notifying the calling function of a status, in which to accept
or decline the current operation, by returning false this will signal to the calling
function that the request should be denied, for informal-only notifications or
acceptable, true must be returned by all the registered notification handlers.
config MCUMGR_SMP_COMMAND_STATUS_HOOKS
bool "SMP command status hooks"
depends on MCUMGR_MGMT_NOTIFICATION_HOOKS
help
This will enable SMP command status notification hooks for when an SMP message is
received or processed.
menu "Command Handlers"
rsource "lib/cmd/Kconfig"

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2018-2021 mcumgr authors
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -506,8 +507,11 @@ img_mgmt_upload(struct smp_streamer *ctxt)
end:
img_mgmt_upload_log(req.off == 0, g_img_mgmt_state.off == g_img_mgmt_state.size, rc);
mgmt_evt(MGMT_EVT_OP_CMD_STATUS, MGMT_GROUP_ID_IMAGE, IMG_MGMT_ID_UPLOAD,
&cmd_status_arg);
#if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
(void)mgmt_callback_notify(MGMT_EVT_OP_CMD_STATUS, MGMT_GROUP_ID_IMAGE,
IMG_MGMT_ID_UPLOAD, &cmd_status_arg, false);
#endif
if (rc != 0) {
img_mgmt_dfu_stopped();

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2018-2021 mcumgr authors
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -55,39 +56,6 @@ extern "C" {
#define MGMT_HDR_SIZE 8
/*
* MGMT event opcodes.
*/
#define MGMT_EVT_OP_CMD_RECV 0x01
#define MGMT_EVT_OP_CMD_STATUS 0x02
#define MGMT_EVT_OP_CMD_DONE 0x03
/*
* MGMT_EVT_OP_CMD_STATUS argument
*/
struct mgmt_evt_op_cmd_status_arg {
int status;
};
/*
* MGMT_EVT_OP_CMD_DONE argument
*/
struct mgmt_evt_op_cmd_done_arg {
int err; /* MGMT_ERR_[...] */
};
/** @typedef mgmt_on_evt_cb
* @brief Function to be called on MGMT event.
*
* This callback function is used to notify application about mgmt event.
*
* @param opcode MGMT_EVT_OP_[...].
* @param group MGMT_GROUP_ID_[...].
* @param id Message ID within group.
* @param arg Optional event argument.
*/
typedef void (*mgmt_on_evt_cb)(uint8_t opcode, uint16_t group, uint8_t id, void *arg);
/** @typedef mgmt_alloc_rsp_fn
* @brief Allocates a buffer suitable for holding a response.
*
@ -177,23 +145,6 @@ void mgmt_unregister_group(struct mgmt_group *group);
*/
const struct mgmt_handler *mgmt_find_handler(uint16_t group_id, uint16_t command_id);
/**
* @brief Register event callback function.
*
* @param cb Callback function.
*/
void mgmt_register_evt_cb(mgmt_on_evt_cb cb);
/**
* @brief This function is called to notify about mgmt event.
*
* @param opcode MGMT_EVT_OP_[...].
* @param group MGMT_GROUP_ID_[...].
* @param id Message ID within group.
* @param arg Optional event argument.
*/
void mgmt_evt(uint8_t opcode, uint16_t group, uint8_t id, void *arg);
#ifdef __cplusplus
}
#endif

View file

@ -9,10 +9,18 @@
#include "mgmt/mgmt.h"
#include "smp/smp.h"
static mgmt_on_evt_cb evt_cb;
#ifdef CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS
#include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
#endif
static sys_slist_t mgmt_group_list =
SYS_SLIST_STATIC_INIT(&mgmt_group_list);
#if defined(CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS)
static sys_slist_t mgmt_callback_list =
SYS_SLIST_STATIC_INIT(&mgmt_callback_list);
#endif
void
mgmt_unregister_group(struct mgmt_group *group)
{
@ -62,16 +70,55 @@ mgmt_register_group(struct mgmt_group *group)
sys_slist_append(&mgmt_group_list, &group->node);
}
void
mgmt_register_evt_cb(mgmt_on_evt_cb cb)
#if defined(CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS)
void mgmt_callback_register(struct mgmt_callback *callback)
{
evt_cb = cb;
sys_slist_append(&mgmt_callback_list, &callback->node);
}
void
mgmt_evt(uint8_t opcode, uint16_t group, uint8_t id, void *arg)
void mgmt_callback_unregister(struct mgmt_callback *callback)
{
if (evt_cb) {
evt_cb(opcode, group, id, arg);
}
(void)sys_slist_find_and_remove(&mgmt_callback_list, &callback->node);
}
int32_t mgmt_callback_notify(uint32_t event, void *data, size_t data_size)
{
sys_snode_t *snp, *sns;
bool failed = false;
bool abort_more = false;
int32_t rc;
int32_t return_rc = MGMT_ERR_EOK;
uint16_t group = MGMT_EVT_GET_GROUP(event);
/*
* Search through the linked list for entries that have registered for this event and
* notify them, the first handler to return an error code will have this error returned
* to the calling function, errors returned by additional handlers will be ignored. If
* all notification handlers return MGMT_ERR_EOK then access will be allowed and no error
* will be returned to the calling function. The status of if a previous handler has
* returned an error is provided to the functions through the failed variable, and a
* handler function can set abort_more to true to prevent calling any further handlers.
*/
SYS_SLIST_FOR_EACH_NODE_SAFE(&mgmt_callback_list, snp, sns) {
struct mgmt_callback *loop_group =
CONTAINER_OF(snp, struct mgmt_callback, node);
if (loop_group->event_id == event || loop_group->event_id == MGMT_EVT_OP_ALL ||
(MGMT_EVT_GET_GROUP(loop_group->event_id) == group &&
MGMT_EVT_GET_ID(loop_group->event_id) == MGMT_EVT_OP_ID_ALL)) {
rc = loop_group->callback(event, return_rc, &abort_more, data, data_size);
if (rc != MGMT_ERR_EOK && failed == false) {
failed = true;
return_rc = rc;
}
if (abort_more == true) {
break;
}
}
}
return return_rc;
}
#endif

View file

@ -20,6 +20,10 @@
#include "smp/smp.h"
#include "../../../smp_internal.h"
#ifdef CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS
#include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
#endif
static void cbor_nb_reader_init(struct cbor_nb_reader *cnr, struct net_buf *nb)
{
/* Skip the smp_hdr */
@ -134,6 +138,9 @@ static int smp_handle_single_payload(struct smp_streamer *cbuf, const struct smp
const struct mgmt_handler *handler;
mgmt_handler_fn handler_fn;
int rc;
#if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
struct mgmt_evt_op_cmd_arg cmd_recv;
#endif
handler = mgmt_find_handler(req_hdr->nh_group, req_hdr->nh_id);
if (handler == NULL) {
@ -156,7 +163,14 @@ static int smp_handle_single_payload(struct smp_streamer *cbuf, const struct smp
if (handler_fn) {
*handler_found = true;
zcbor_map_start_encode(cbuf->writer->zs, CONFIG_MGMT_MAX_MAIN_MAP_ENTRIES);
mgmt_evt(MGMT_EVT_OP_CMD_RECV, req_hdr->nh_group, req_hdr->nh_id, NULL);
#if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
cmd_recv.group = req_hdr->nh_group;
cmd_recv.id = req_hdr->nh_id;
cmd_recv.err = MGMT_ERR_EOK;
(void)mgmt_callback_notify(MGMT_EVT_OP_CMD_RECV, &cmd_recv, sizeof(cmd_recv));
#endif
MGMT_CTXT_SET_RC_RSN(cbuf, NULL);
rc = handler_fn(cbuf);
@ -270,7 +284,6 @@ static void smp_on_err(struct smp_streamer *streamer, const struct smp_hdr *req_
int smp_process_request_packet(struct smp_streamer *streamer, void *vreq)
{
struct smp_hdr req_hdr;
struct mgmt_evt_op_cmd_done_arg cmd_done_arg;
void *rsp;
struct net_buf *req = vreq;
bool valid_hdr = false;
@ -278,6 +291,10 @@ int smp_process_request_packet(struct smp_streamer *streamer, void *vreq)
int rc = 0;
const char *rsn = NULL;
#if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
struct mgmt_evt_op_cmd_arg cmd_done_arg;
#endif
rsp = NULL;
while (req->len > 0) {
@ -323,18 +340,28 @@ int smp_process_request_packet(struct smp_streamer *streamer, void *vreq)
/* Trim processed request to free up space for subsequent responses. */
net_buf_pull(req, req_hdr.nh_len);
#if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
cmd_done_arg.group = req_hdr.nh_group;
cmd_done_arg.id = req_hdr.nh_id;
cmd_done_arg.err = MGMT_ERR_EOK;
mgmt_evt(MGMT_EVT_OP_CMD_DONE, req_hdr.nh_group, req_hdr.nh_id,
&cmd_done_arg);
(void)mgmt_callback_notify(MGMT_EVT_OP_CMD_DONE, &cmd_done_arg,
sizeof(cmd_done_arg));
#endif
}
if (rc != 0 && valid_hdr) {
smp_on_err(streamer, &req_hdr, req, rsp, rc, rsn);
if (handler_found) {
#if defined(CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS)
cmd_done_arg.group = req_hdr.nh_group;
cmd_done_arg.id = req_hdr.nh_id;
cmd_done_arg.err = rc;
mgmt_evt(MGMT_EVT_OP_CMD_DONE, req_hdr.nh_group, req_hdr.nh_id,
&cmd_done_arg);
(void)mgmt_callback_notify(MGMT_EVT_OP_CMD_DONE, &cmd_done_arg,
sizeof(cmd_done_arg));
#endif
}
return rc;