1 /*
2 * Copyright (c) 2018-2021 mcumgr authors
3 * Copyright (c) 2022-2024 Nordic Semiconductor ASA
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include <assert.h>
9 #include <zephyr/sys/slist.h>
10 #include <zephyr/sys/byteorder.h>
11 #include <zephyr/device.h>
12 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
13 #include <zephyr/mgmt/mcumgr/mgmt/handlers.h>
14 #include <zephyr/sys/iterable_sections.h>
15 #include <string.h>
16
17 #ifdef CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS
18 #include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
19 #endif
20
21 static sys_slist_t mgmt_group_list =
22 SYS_SLIST_STATIC_INIT(&mgmt_group_list);
23
24 #if defined(CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS)
25 static sys_slist_t mgmt_callback_list =
26 SYS_SLIST_STATIC_INIT(&mgmt_callback_list);
27 #endif
28
29 void
mgmt_unregister_group(struct mgmt_group * group)30 mgmt_unregister_group(struct mgmt_group *group)
31 {
32 (void)sys_slist_find_and_remove(&mgmt_group_list, &group->node);
33 }
34
35 const struct mgmt_handler *
mgmt_find_handler(uint16_t group_id,uint16_t command_id)36 mgmt_find_handler(uint16_t group_id, uint16_t command_id)
37 {
38 struct mgmt_group *group = NULL;
39 sys_snode_t *snp, *sns;
40
41 /*
42 * Find the group with the specified group id, if one exists
43 * check the handler for the command id and make sure
44 * that is not NULL. If that is not set, look for the group
45 * with a command id that is set
46 */
47 SYS_SLIST_FOR_EACH_NODE_SAFE(&mgmt_group_list, snp, sns) {
48 struct mgmt_group *loop_group =
49 CONTAINER_OF(snp, struct mgmt_group, node);
50 if (loop_group->mg_group_id == group_id) {
51 if (command_id >= loop_group->mg_handlers_count) {
52 break;
53 }
54
55 if (!loop_group->mg_handlers[command_id].mh_read &&
56 !loop_group->mg_handlers[command_id].mh_write) {
57 continue;
58 }
59
60 group = loop_group;
61 break;
62 }
63 }
64
65 if (group == NULL) {
66 return NULL;
67 }
68
69 return &group->mg_handlers[command_id];
70 }
71
72 const struct mgmt_group *
mgmt_find_group(uint16_t group_id)73 mgmt_find_group(uint16_t group_id)
74 {
75 sys_snode_t *snp, *sns;
76
77 /*
78 * Find the group with the specified group id
79 */
80 SYS_SLIST_FOR_EACH_NODE_SAFE(&mgmt_group_list, snp, sns) {
81 struct mgmt_group *loop_group =
82 CONTAINER_OF(snp, struct mgmt_group, node);
83 if (loop_group->mg_group_id == group_id) {
84 return loop_group;
85 }
86 }
87
88 return NULL;
89 }
90
91 const struct mgmt_handler *
mgmt_get_handler(const struct mgmt_group * group,uint16_t command_id)92 mgmt_get_handler(const struct mgmt_group *group, uint16_t command_id)
93 {
94 if (command_id >= group->mg_handlers_count) {
95 return NULL;
96 }
97
98 if (!group->mg_handlers[command_id].mh_read &&
99 !group->mg_handlers[command_id].mh_write) {
100 return NULL;
101 }
102
103 return &group->mg_handlers[command_id];
104 }
105
106 #if defined(CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL)
mgmt_find_error_translation_function(uint16_t group_id)107 smp_translate_error_fn mgmt_find_error_translation_function(uint16_t group_id)
108 {
109 struct mgmt_group *group = NULL;
110 sys_snode_t *snp, *sns;
111
112 /* Find the group with the specified group ID. */
113 SYS_SLIST_FOR_EACH_NODE_SAFE(&mgmt_group_list, snp, sns) {
114 struct mgmt_group *loop_group =
115 CONTAINER_OF(snp, struct mgmt_group, node);
116 if (loop_group->mg_group_id == group_id) {
117 group = loop_group;
118 break;
119 }
120 }
121
122 if (group == NULL) {
123 return NULL;
124 }
125
126 return group->mg_translate_error;
127 }
128 #endif
129
130 void
mgmt_register_group(struct mgmt_group * group)131 mgmt_register_group(struct mgmt_group *group)
132 {
133 sys_slist_append(&mgmt_group_list, &group->node);
134 }
135
136 #if defined(CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS)
mgmt_callback_register(struct mgmt_callback * callback)137 void mgmt_callback_register(struct mgmt_callback *callback)
138 {
139 sys_slist_append(&mgmt_callback_list, &callback->node);
140 }
141
mgmt_callback_unregister(struct mgmt_callback * callback)142 void mgmt_callback_unregister(struct mgmt_callback *callback)
143 {
144 (void)sys_slist_find_and_remove(&mgmt_callback_list, &callback->node);
145 }
146
mgmt_callback_notify(uint32_t event,void * data,size_t data_size,int32_t * err_rc,uint16_t * err_group)147 enum mgmt_cb_return mgmt_callback_notify(uint32_t event, void *data, size_t data_size,
148 int32_t *err_rc, uint16_t *err_group)
149 {
150 sys_snode_t *snp, *sns;
151 bool failed = false;
152 bool abort_more = false;
153 uint16_t group = MGMT_EVT_GET_GROUP(event);
154 enum mgmt_cb_return return_status = MGMT_CB_OK;
155
156 *err_rc = MGMT_ERR_EOK;
157 *err_group = 0;
158
159 /*
160 * Search through the linked list for entries that have registered for this event and
161 * notify them, the first handler to return an error code will have this error returned
162 * to the calling function, errors returned by additional handlers will be ignored. If
163 * all notification handlers return MGMT_ERR_EOK then access will be allowed and no error
164 * will be returned to the calling function. The status of if a previous handler has
165 * returned an error is provided to the functions through the failed variable, and a
166 * handler function can set abort_more to true to prevent calling any further handlers.
167 */
168 SYS_SLIST_FOR_EACH_NODE_SAFE(&mgmt_callback_list, snp, sns) {
169 struct mgmt_callback *loop_group =
170 CONTAINER_OF(snp, struct mgmt_callback, node);
171
172 if (loop_group->event_id == MGMT_EVT_OP_ALL ||
173 (MGMT_EVT_GET_GROUP(loop_group->event_id) == group &&
174 (MGMT_EVT_GET_ID(event) & MGMT_EVT_GET_ID(loop_group->event_id)) ==
175 MGMT_EVT_GET_ID(event))) {
176 int32_t cached_rc = *err_rc;
177 uint16_t cached_group = *err_group;
178 enum mgmt_cb_return status;
179
180 status = loop_group->callback(event, return_status, &cached_rc,
181 &cached_group, &abort_more, data,
182 data_size);
183
184 __ASSERT((status <= MGMT_CB_ERROR_ERR),
185 "Invalid status returned by MCUmgr handler: %d", status);
186
187 if (status != MGMT_CB_OK && failed == false) {
188 failed = true;
189 return_status = status;
190 *err_rc = cached_rc;
191
192 if (status == MGMT_CB_ERROR_ERR) {
193 *err_group = cached_group;
194 }
195 }
196
197 if (abort_more == true) {
198 break;
199 }
200 }
201 }
202
203 return return_status;
204 }
205
mgmt_evt_get_index(uint32_t event)206 uint8_t mgmt_evt_get_index(uint32_t event)
207 {
208 uint8_t index = 0;
209
210 event &= MGMT_EVT_OP_ID_ALL;
211 __ASSERT((event != 0), "Event cannot be 0.");
212
213 while (index < 16) {
214 if (event & 0x1) {
215 break;
216 }
217
218 ++index;
219 event = event >> 1;
220 }
221
222 event = event >> 1;
223 __ASSERT((event == 0), "Event cannot contain multiple values.");
224
225 return index;
226 }
227 #endif
228
mgmt_groups_foreach(mgmt_groups_cb_t user_cb,void * user_data)229 void mgmt_groups_foreach(mgmt_groups_cb_t user_cb, void *user_data)
230 {
231 sys_snode_t *snp, *sns;
232 bool ok;
233
234 SYS_SLIST_FOR_EACH_NODE_SAFE(&mgmt_group_list, snp, sns) {
235 const struct mgmt_group *group = CONTAINER_OF(snp, struct mgmt_group, node);
236
237 ok = user_cb(group, user_data);
238
239 if (!ok) {
240 return;
241 }
242 }
243 }
244
245 /* Processes all registered MCUmgr handlers at start up and registers them */
mcumgr_handlers_init(void)246 static int mcumgr_handlers_init(void)
247 {
248
249 STRUCT_SECTION_FOREACH(mcumgr_handler, handler) {
250 if (handler->init) {
251 handler->init();
252 }
253 }
254
255 return 0;
256 }
257
258 SYS_INIT(mcumgr_handlers_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
259