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