Skip to content

Commit d5693ed

Browse files
nordicjmcarlescufi
authored andcommitted
doc: device_mgmt: callbacks: Add initial documentation
Adds the initial documentation describing the mcumgr callback system and how to use it, including API reference and samples. Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
1 parent c2b872b commit d5693ed

File tree

3 files changed

+291
-6
lines changed

3 files changed

+291
-6
lines changed

doc/services/device_mgmt/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Device Management
77
:maxdepth: 1
88

99
mcumgr.rst
10+
mcumgr_callbacks.rst
1011
mcumgr_backporting.rst
1112
smp_protocol.rst
1213
dfu.rst
Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
.. _mcumgr_callbacks:
2+
3+
MCUmgr Callbacks
4+
################
5+
6+
Overview
7+
********
8+
9+
MCUmgr has a customisable callback/notification system that allows application
10+
(and module) code to receive callbacks for MCUmgr events that they are
11+
interested in and react to them or return a status code to the calling function
12+
that provides control over if the action should be allowed or not. An example
13+
of this is with the fs_mgmt group, whereby file access can be gated, the
14+
callback allows the application to inspect the request path and allow or deny
15+
access to said file, or it can rewrite the provided path to a different path
16+
for transparent file redirection support.
17+
18+
Implementation
19+
**************
20+
21+
Enabling
22+
========
23+
24+
The base callback/notification system can be enabled using
25+
:kconfig:option:`CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS` which will compile the
26+
registration and notification system into the code. This will not provide any
27+
callbacks by default as the callbacks that are supported by a build must also
28+
be selected by enabling the Kconfig's for the required callbacks (see
29+
:ref:`mcumgr_cb_events` for further details). A callback function with the
30+
:c:type:`mgmt_cb` type definition can then be declared and registered by
31+
calling :c:func:`mgmt_callback_register` for the desired event inside of a
32+
:c:struct`mgmt_callback` structure. Handlers are called in the order that they
33+
were registered.
34+
35+
With the system enabled, a basic handler can be set up and defined in
36+
application code as per:
37+
38+
.. code-block:: c
39+
40+
#include <zephyr/kernel.h>
41+
#include <mgmt/mgmt.h>
42+
#include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
43+
44+
struct mgmt_callback my_callback;
45+
46+
int32_t my_function(uint32_t event, int32_t rc, bool *abort_more, void *data,
47+
size_t data_size)
48+
{
49+
if (event == MGMT_EVT_OP_CMD_DONE) {
50+
/* This is the event we registered for */
51+
}
52+
53+
/* Return OK status code to continue with acceptance to underlying handler */
54+
return MGMT_ERR_EOK;
55+
}
56+
57+
void main()
58+
{
59+
my_callback.callback = my_function;
60+
my_callback.event_id = MGMT_EVT_OP_CMD_DONE;
61+
mgmt_callback_register(&my_callback);
62+
}
63+
64+
This code registers a handler for the :c:enum:`MGMT_EVT_OP_CMD_DONE` event,
65+
which will be called after a MCUmgr command has been processed and output
66+
generated, note that this requires that
67+
:kconfig:option:`CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS` be enabled to
68+
receive this callback.
69+
70+
Multiple callbacks can be setup to use a single function as a common callback,
71+
and many different functions can be used for each event by registering each
72+
group once, or all notifications for a whole group can be enabled by using one
73+
of the ``MGMT_EVT_OP_*_ALL`` events, alternatively a handler can setup for
74+
every notification by using :c:enum:`MGMT_EVT_OP_ALL`. When setting up
75+
handlers, events can be combined that are in the same group only, for example
76+
5 img_mgmt callbacks can be setup with a single registration call, but to also
77+
setup a callback for an os_mgmt callback, this must be done as a separate
78+
registration. Group IDs are numerical increments, event IDs are bitmask values,
79+
hence the restriction.
80+
81+
.. _mcumgr_cb_events:
82+
83+
Events
84+
======
85+
86+
Events can be selected by enabling their corresponding Kconfig option:
87+
88+
- :kconfig:option:`CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS`
89+
MCUmgr command status (:c:enum:`MGMT_EVT_OP_CMD_RECV`,
90+
:c:enum:`MGMT_EVT_OP_CMD_STATUS`, :c:enum:`MGMT_EVT_OP_CMD_DONE`)
91+
- :kconfig:option:`CONFIG_MCUMGR_GRP_FS_FILE_ACCESS_HOOK`
92+
fs_mgmt file access (:c:enum:`MGMT_EVT_OP_FS_MGMT_FILE_ACCESS`)
93+
- :kconfig:option:`CONFIG_MCUMGR_GRP_IMG_UPLOAD_CHECK_HOOK`
94+
img_mgmt upload check (:c:enum:`MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK`)
95+
- :kconfig:option:`CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS`
96+
img_mgmt upload status (:c:enum:`MGMT_EVT_OP_IMG_MGMT_DFU_STOPPED`,
97+
:c:enum:`MGMT_EVT_OP_IMG_MGMT_DFU_STARTED`,
98+
:c:enum:`MGMT_EVT_OP_IMG_MGMT_DFU_PENDING`,
99+
:c:enum:`MGMT_EVT_OP_IMG_MGMT_DFU_CONFIRMED`)
100+
- :kconfig:option:`CONFIG_MCUMGR_GRP_OS_OS_RESET_HOOK`
101+
os_mgmt reset check (:c:enum:`MGMT_EVT_OP_OS_MGMT_RESET`)
102+
103+
Actions
104+
=======
105+
106+
Some callbacks expect a return status to either allow or disallow an operation,
107+
an example is the fs_mgmt access hook which allows for access to files to be
108+
allowed or denied. With these handlers, the first non-OK error code returned
109+
by a handler will be returned to the MCUmgr client.
110+
111+
An example of selectively denying file access:
112+
113+
.. code-block:: c
114+
115+
#include <zephyr/kernel.h>
116+
#include <mgmt/mgmt.h>
117+
#include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
118+
#include <string.h>
119+
120+
struct mgmt_callback my_callback;
121+
122+
int32_t my_function(uint32_t event, int32_t rc, bool *abort_more, void *data,
123+
size_t data_size)
124+
{
125+
/* Only run this handler if a previous handler has not failed */
126+
if (event == MGMT_EVT_OP_FS_MGMT_FILE_ACCESS && rc == MGMT_ERR_EOK) {
127+
struct fs_mgmt_file_access *fs_data = (struct fs_mgmt_file_access *)data;
128+
129+
/* Check if this is an upload and deny access if it is, otherwise check the
130+
* the path and deny if is matches a name
131+
*/
132+
if (fs_data->upload == true) {
133+
/* Return an access denied error code to the client and abort calling
134+
* further handlers
135+
*/
136+
*abort_more = true;
137+
return MGMT_ERR_EACCESSDENIED;
138+
} else if (strcmp(fs_data->filename, "/lfs1/false_deny.txt") == 0) {
139+
/* Return a no entry error code to the client, call additional handlers
140+
* (which will have failed set to true)
141+
*/
142+
return MGMT_ERR_ENOENT;
143+
}
144+
}
145+
146+
/* Return OK status code to continue with acceptance to underlying handler */
147+
return MGMT_ERR_EOK;
148+
}
149+
150+
void main()
151+
{
152+
my_callback.callback = my_function;
153+
my_callback.event_id = MGMT_EVT_OP_FS_MGMT_FILE_ACCESS;
154+
mgmt_callback_register(&my_callback);
155+
}
156+
157+
This code registers a handler for the :c:enum:`MGMT_EVT_OP_FS_MGMT_FILE_ACCESS`
158+
event, which will be called after a fs_mgmt file read/write command has been
159+
received to check if access to the file should be allowed or not, note that
160+
this requires that :kconfig:option:`CONFIG_MCUMGR_GRP_FS_FILE_ACCESS_HOOK`
161+
be enabled to receive this callback.
162+
163+
MCUmgr Command Callback Usage/Adding New Event Types
164+
====================================================
165+
166+
To add a callback to a MCUmgr command, :c:func:`mgmt_callback_notify` can be
167+
called with the event ID and, optionally, a data struct to pass to the callback
168+
(which can be modified by handlers). If no data needs to be passed back,
169+
``NULL`` can be used instead, and size of the data set to 0.
170+
171+
An example MCUmgr command handler:
172+
173+
.. code-block:: c
174+
175+
#include <zephyr/kernel.h>
176+
#include <zcbor_common.h>
177+
#include <zcbor_encode.h>
178+
#include <mgmt/mgmt.h>
179+
#include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
180+
181+
#define MGMT_EVT_GRP_USER_ONE MGMT_EVT_GRP_USER_CUSTOM_START
182+
183+
enum user_one_group_events {
184+
/** Callback on first post, data is test_struct. */
185+
MGMT_EVT_OP_USER_ONE_FIRST = MGMT_DEF_EVT_OP_ID(MGMT_EVT_GRP_USER_ONE, 0),
186+
187+
/** Callback on second post, data is test_struct. */
188+
MGMT_EVT_OP_USER_ONE_SECOND = MGMT_DEF_EVT_OP_ID(MGMT_EVT_GRP_USER_ONE, 1),
189+
190+
/** Used to enable all user_one events. */
191+
MGMT_EVT_OP_USER_ONE_ALL = MGMT_DEF_EVT_OP_ALL(MGMT_EVT_GRP_USER_ONE),
192+
};
193+
194+
struct test_struct {
195+
uint8_t some_value;
196+
};
197+
198+
static int test_command(struct mgmt_ctxt *ctxt)
199+
{
200+
int rc;
201+
zcbor_state_t *zse = ctxt->cnbe->zs;
202+
bool ok;
203+
struct test_struct test_data = {
204+
.some_value = 8,
205+
};
206+
207+
rc = mgmt_callback_notify(MGMT_EVT_OP_USER_ONE_FIRST, &test_data,
208+
sizeof(test_data));
209+
210+
if (rc != MGMT_ERR_EOK) {
211+
/* A handler returned a failure code */
212+
return rc;
213+
}
214+
215+
/* All handlers returned success codes */
216+
217+
ok = zcbor_tstr_put_lit(zse, "output_value") &&
218+
zcbor_int32_put(zse, 1234);
219+
220+
if (!ok) {
221+
return MGMT_ERR_EMSGSIZE;
222+
}
223+
224+
return MGMT_ERR_EOK;
225+
}
226+
227+
If no response is required for the callback, the function call be called and
228+
casted to void.
229+
230+
.. _mcumgr_cb_migration:
231+
232+
Migration
233+
*********
234+
235+
If there is existing code using the previous callback system(s) in Zephyr 3.2
236+
or earlier, then it will need to be migrated to the new system. To migrate
237+
code, the following callback registration functions will need to be migrated
238+
to register for callbacks using :c:func:`mgmt_callback_register` (note that
239+
:kconfig:option:`CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS` will need to be set to
240+
enable the new notification system in addition to any migrations):
241+
242+
* mgmt_evt
243+
Using :c:enum:`MGMT_EVT_OP_CMD_RECV` if ``MGMT_EVT_OP_CMD_RECV`` was used,
244+
:c:enum:`MGMT_EVT_OP_CMD_STATUS` if ``MGMT_EVT_OP_CMD_STATUS`` was used or
245+
:c:enum:`MGMT_EVT_OP_CMD_DONE` if ``MGMT_EVT_OP_CMD_DONE`` was used, where
246+
the provided data is :c:struct:`mgmt_evt_op_cmd_arg`.
247+
:kconfig:option:`CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS` needs to be set.
248+
* fs_mgmt_register_evt_cb
249+
Using :c:enum:`MGMT_EVT_OP_FS_MGMT_FILE_ACCESS` where the provided data is
250+
:c:struct:`fs_mgmt_file_access`. Instead of returning true to allow the
251+
action or false to deny, a MCUmgr result code needs to be returned,
252+
:c:enum:`MGMT_ERR_EOK` will allow the action, any other return code will
253+
disallow it and return that code to the client
254+
(:c:enum:`MGMT_ERR_EACCESSDENIED` can be used for an access denied error).
255+
:kconfig:option:`CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS` needs to be set.
256+
* img_mgmt_register_callbacks
257+
Using :c:enum:`MGMT_EVT_OP_IMG_MGMT_DFU_STARTED` if ``dfu_started_cb``
258+
was used, :c:enum:`MGMT_EVT_OP_IMG_MGMT_DFU_STOPPED` if ``dfu_stopped_cb``
259+
was used, :c:enum:`MGMT_EVT_OP_IMG_MGMT_DFU_PENDING` if ``dfu_pending_cb``
260+
was used or :c:enum:`MGMT_EVT_OP_IMG_MGMT_DFU_CONFIRMED` if
261+
``dfu_confirmed_cb`` was used. These callbacks do not have any return
262+
status. :kconfig:option:`CONFIG_MCUMGR_GRP_IMG_STATUS_HOOKS` needs to be
263+
set.
264+
* img_mgmt_set_upload_cb
265+
Using :c:enum:`MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK` where the provided data is
266+
:c:struct:`img_mgmt_upload_check`. Instead of returning true to allow the
267+
action or false to deny, a MCUmgr result code needs to be returned,
268+
:c:enum:`MGMT_ERR_EOK` will allow the action, any other return code will
269+
disallow it and return that code to the client
270+
(:c:enum:`MGMT_ERR_EACCESSDENIED` can be used for an access denied error).
271+
:kconfig:option:`CONFIG_MCUMGR_GRP_IMG_UPLOAD_CHECK_HOOK` needs to be set.
272+
* os_mgmt_register_reset_evt_cb
273+
Using :c:enum:`MGMT_EVT_OP_OS_MGMT_RESET`. Instead of returning true to
274+
allow the action or false to deny, a MCUmgr result code needs to be
275+
returned, :c:enum:`MGMT_ERR_EOK` will allow the action, any other return
276+
code will disallow it and return that code to the client
277+
(:c:enum:`MGMT_ERR_EACCESSDENIED` can be used for an access denied error).
278+
:kconfig:option:`CONFIG_MCUMGR_SMP_COMMAND_STATUS_HOOKS` needs to be set
279+
280+
API Reference
281+
*************
282+
283+
.. doxygengroup:: mcumgr_callback_api
284+
:inner:

doc/services/device_mgmt/smp_groups/smp_group_0.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -410,12 +410,12 @@ System reset
410410

411411
Performs reset of system. The device should issue response before resetting so
412412
that the SMP client could receive information that the command has been
413-
accepted. By default, this command is accepted in all conditions, however if the
414-
:kconfig:option:`CONFIG_OS_MGMT_RESET_HOOK` is enabled and an application
415-
registers a callback, the callback will be called when this command is issued
416-
and can be used to perform any necessary tidy operations prior to the module
417-
rebooting, or to reject the reset request outright altogether with an error
418-
response.
413+
accepted. By default, this command is accepted in all conditions, however if
414+
the :kconfig:option:`CONFIG_MCUMGR_GRP_OS_OS_RESET_HOOK` is enabled and an
415+
application registers a callback, the callback will be called when this command
416+
is issued and can be used to perform any necessary tidy operations prior to the
417+
module rebooting, or to reject the reset request outright altogether with an
418+
error response. For details on this functionality, see `ref:`mcumgr_callbacks`.
419419

420420
System reset request
421421
====================

0 commit comments

Comments
 (0)