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