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