1 /*
2  * Copyright (c) 2018-2021 mcumgr authors
3  * Copyright (c) 2021-2024 Nordic Semiconductor ASA
4  * Copyright (c) 2022 Laird Connectivity
5  *
6  * SPDX-License-Identifier: Apache-2.0
7  */
8 
9 #include <zephyr/sys/util.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/debug/object_tracing.h>
12 #include <zephyr/kernel_structs.h>
13 #include <zephyr/mgmt/mcumgr/mgmt/mgmt.h>
14 #include <zephyr/mgmt/mcumgr/smp/smp.h>
15 #include <zephyr/mgmt/mcumgr/mgmt/handlers.h>
16 #include <zephyr/mgmt/mcumgr/grp/os_mgmt/os_mgmt.h>
17 #include <zephyr/logging/log.h>
18 #include <assert.h>
19 #include <string.h>
20 #include <stdio.h>
21 
22 #include <zcbor_common.h>
23 #include <zcbor_encode.h>
24 #include <zcbor_decode.h>
25 
26 #include <mgmt/mcumgr/util/zcbor_bulk.h>
27 
28 #ifdef CONFIG_REBOOT
29 #include <zephyr/sys/reboot.h>
30 #endif
31 
32 #ifdef CONFIG_MCUMGR_MGMT_NOTIFICATION_HOOKS
33 #include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
34 #endif
35 
36 #ifdef CONFIG_MCUMGR_GRP_OS_DATETIME
37 #include <stdlib.h>
38 #include <zephyr/drivers/rtc.h>
39 #endif
40 
41 #if defined(CONFIG_MCUMGR_GRP_OS_INFO) || defined(CONFIG_MCUMGR_GRP_OS_BOOTLOADER_INFO)
42 #include <stdio.h>
43 #include <zephyr/version.h>
44 #if defined(CONFIG_MCUMGR_GRP_OS_INFO)
45 #include <os_mgmt_processor.h>
46 #endif
47 #if defined(CONFIG_MCUMGR_GRP_OS_BOOTLOADER_INFO)
48 #include <bootutil/boot_status.h>
49 #endif
50 #include <mgmt/mcumgr/util/zcbor_bulk.h>
51 #if defined(CONFIG_NET_HOSTNAME_ENABLE)
52 #include <zephyr/net/hostname.h>
53 #elif defined(CONFIG_BT)
54 #include <zephyr/bluetooth/bluetooth.h>
55 #endif
56 #endif
57 
58 LOG_MODULE_REGISTER(mcumgr_os_grp, CONFIG_MCUMGR_GRP_OS_LOG_LEVEL);
59 
60 #ifdef CONFIG_REBOOT
61 static void os_mgmt_reset_work_handler(struct k_work *work);
62 
63 K_WORK_DELAYABLE_DEFINE(os_mgmt_reset_work, os_mgmt_reset_work_handler);
64 #endif
65 
66 /* This is passed to zcbor_map_start/end_endcode as a number of
67  * expected "columns" (tid, priority, and so on)
68  * The value here does not affect memory allocation is used
69  * to predict how big the map may be. If you increase number
70  * of "columns" the taskstat sends you may need to increase the
71  * value otherwise zcbor_map_end_encode may return with error.
72  */
73 #define TASKSTAT_COLUMNS_MAX	20
74 
75 #ifdef CONFIG_MCUMGR_GRP_OS_TASKSTAT
76 /* Thread iterator information passing structure */
77 struct thread_iterator_info {
78 	zcbor_state_t *zse;
79 	int thread_idx;
80 	bool ok;
81 };
82 #endif
83 
84 #ifdef CONFIG_MCUMGR_GRP_OS_DATETIME
85 /* Iterator for extracting values from the provided datetime string, min and max values are
86  * checked against the provided value, then the offset is added after. If the value is not
87  * within the min and max values, the set operation will be aborted.
88  */
89 struct datetime_parser {
90 	int *value;
91 	int min_value;
92 	int max_value;
93 	int offset;
94 };
95 
96 /* RTC device alias to use for datetime functions, "rtc" */
97 #define RTC_DEVICE DEVICE_DT_GET(DT_ALIAS(rtc))
98 
99 #define RTC_DATETIME_YEAR_OFFSET 1900
100 #define RTC_DATETIME_MONTH_OFFSET 1
101 #define RTC_DATETIME_NUMERIC_BASE 10
102 #define RTC_DATETIME_MS_TO_NS 1000000
103 #define RTC_DATETIME_YEAR_MIN 1900
104 #define RTC_DATETIME_YEAR_MAX 11899
105 #define RTC_DATETIME_MONTH_MIN 1
106 #define RTC_DATETIME_MONTH_MAX 12
107 #define RTC_DATETIME_DAY_MIN 1
108 #define RTC_DATETIME_DAY_MAX 31
109 #define RTC_DATETIME_HOUR_MIN 0
110 #define RTC_DATETIME_HOUR_MAX 23
111 #define RTC_DATETIME_MINUTE_MIN 0
112 #define RTC_DATETIME_MINUTE_MAX 59
113 #define RTC_DATETIME_SECOND_MIN 0
114 #define RTC_DATETIME_SECOND_MAX 59
115 #define RTC_DATETIME_MILLISECOND_MIN 0
116 #define RTC_DATETIME_MILLISECOND_MAX 999
117 
118 /* Size used for datetime creation buffer */
119 #ifdef CONFIG_MCUMGR_GRP_OS_DATETIME_MS
120 #define RTC_DATETIME_STRING_SIZE 32
121 #else
122 #define RTC_DATETIME_STRING_SIZE 26
123 #endif
124 
125 /* Minimum/maximum size of a datetime string that a client can provide */
126 #define RTC_DATETIME_MIN_STRING_SIZE 19
127 #define RTC_DATETIME_MAX_STRING_SIZE 26
128 #endif
129 
130 /* Specifies what the "all" ('a') of info parameter shows */
131 #define OS_MGMT_INFO_FORMAT_ALL                                                               \
132 	OS_MGMT_INFO_FORMAT_KERNEL_NAME | OS_MGMT_INFO_FORMAT_NODE_NAME |                     \
133 		OS_MGMT_INFO_FORMAT_KERNEL_RELEASE | OS_MGMT_INFO_FORMAT_KERNEL_VERSION |     \
134 		(IS_ENABLED(CONFIG_MCUMGR_GRP_OS_INFO_BUILD_DATE_TIME) ?                      \
135 			OS_MGMT_INFO_FORMAT_BUILD_DATE_TIME : 0) |                            \
136 		OS_MGMT_INFO_FORMAT_MACHINE | OS_MGMT_INFO_FORMAT_PROCESSOR |                 \
137 		OS_MGMT_INFO_FORMAT_HARDWARE_PLATFORM | OS_MGMT_INFO_FORMAT_OPERATING_SYSTEM
138 
139 #ifdef CONFIG_MCUMGR_GRP_OS_INFO_BUILD_DATE_TIME
140 extern uint8_t *MCUMGR_GRP_OS_INFO_BUILD_DATE_TIME;
141 #endif
142 
143 /**
144  * Command handler: os echo
145  */
146 #ifdef CONFIG_MCUMGR_GRP_OS_ECHO
os_mgmt_echo(struct smp_streamer * ctxt)147 static int os_mgmt_echo(struct smp_streamer *ctxt)
148 {
149 	bool ok;
150 	zcbor_state_t *zsd = ctxt->reader->zs;
151 	zcbor_state_t *zse = ctxt->writer->zs;
152 	struct zcbor_string data = { 0 };
153 	size_t decoded;
154 
155 	struct zcbor_map_decode_key_val echo_decode[] = {
156 		ZCBOR_MAP_DECODE_KEY_DECODER("d", zcbor_tstr_decode, &data),
157 	};
158 
159 	ok = zcbor_map_decode_bulk(zsd, echo_decode, ARRAY_SIZE(echo_decode), &decoded) == 0;
160 
161 	if (!ok) {
162 		return MGMT_ERR_EINVAL;
163 	}
164 
165 	ok = zcbor_tstr_put_lit(zse, "r")	&&
166 	     zcbor_tstr_encode(zse, &data);
167 
168 	return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
169 }
170 #endif
171 
172 #ifdef CONFIG_MCUMGR_GRP_OS_TASKSTAT
173 
174 #ifdef CONFIG_MCUMGR_GRP_OS_TASKSTAT_USE_THREAD_NAME_FOR_NAME
175 static inline bool
os_mgmt_taskstat_encode_thread_name(zcbor_state_t * zse,int idx,const struct k_thread * thread)176 os_mgmt_taskstat_encode_thread_name(zcbor_state_t *zse, int idx,
177 				    const struct k_thread *thread)
178 {
179 	size_t name_len = strlen(thread->name);
180 
181 	ARG_UNUSED(idx);
182 
183 	if (name_len > CONFIG_MCUMGR_GRP_OS_TASKSTAT_THREAD_NAME_LEN) {
184 		name_len = CONFIG_MCUMGR_GRP_OS_TASKSTAT_THREAD_NAME_LEN;
185 	}
186 
187 	return zcbor_tstr_encode_ptr(zse, thread->name, name_len);
188 }
189 
190 #else
191 static inline bool
os_mgmt_taskstat_encode_thread_name(zcbor_state_t * zse,int idx,const struct k_thread * thread)192 os_mgmt_taskstat_encode_thread_name(zcbor_state_t *zse, int idx,
193 				    const struct k_thread *thread)
194 {
195 	char thread_name[CONFIG_MCUMGR_GRP_OS_TASKSTAT_THREAD_NAME_LEN + 1];
196 
197 #if defined(CONFIG_MCUMGR_GRP_OS_TASKSTAT_USE_THREAD_PRIO_FOR_NAME)
198 	idx = (int)thread->base.prio;
199 #elif defined(CONFIG_MCUMGR_GRP_OS_TASKSTAT_USE_THREAD_IDX_FOR_NAME)
200 	ARG_UNUSED(thread);
201 #else
202 #error Unsupported option for taskstat thread name
203 #endif
204 
205 	snprintf(thread_name, sizeof(thread_name) - 1, "%d", idx);
206 	thread_name[sizeof(thread_name) - 1] = 0;
207 
208 	return zcbor_tstr_put_term(zse, thread_name, sizeof(thread_name));
209 }
210 
211 #endif
212 
213 static inline bool
os_mgmt_taskstat_encode_stack_info(zcbor_state_t * zse,const struct k_thread * thread)214 os_mgmt_taskstat_encode_stack_info(zcbor_state_t *zse,
215 				   const struct k_thread *thread)
216 {
217 #ifdef CONFIG_MCUMGR_GRP_OS_TASKSTAT_STACK_INFO
218 	size_t stack_size = 0;
219 	size_t stack_used = 0;
220 	bool ok = true;
221 
222 #ifdef CONFIG_THREAD_STACK_INFO
223 	stack_size = thread->stack_info.size / 4;
224 
225 #ifdef CONFIG_INIT_STACKS
226 	unsigned int stack_unused;
227 
228 	if (k_thread_stack_space_get(thread, &stack_unused) == 0) {
229 		stack_used = (thread->stack_info.size - stack_unused) / 4;
230 	}
231 #endif /* CONFIG_INIT_STACKS */
232 #endif /* CONFIG_THREAD_STACK_INFO */
233 	ok = zcbor_tstr_put_lit(zse, "stksiz")		&&
234 	     zcbor_uint64_put(zse, stack_size)		&&
235 	     zcbor_tstr_put_lit(zse, "stkuse")		&&
236 	     zcbor_uint64_put(zse, stack_used);
237 
238 	return ok;
239 #else
240 	return true;
241 #endif /* CONFIG_MCUMGR_GRP_OS_TASKSTAT_STACK_INFO */
242 }
243 
244 static inline bool
os_mgmt_taskstat_encode_runtime_info(zcbor_state_t * zse,const struct k_thread * thread)245 os_mgmt_taskstat_encode_runtime_info(zcbor_state_t *zse,
246 				     const struct k_thread *thread)
247 {
248 	bool ok = true;
249 
250 #if defined(CONFIG_SCHED_THREAD_USAGE)
251 	k_thread_runtime_stats_t thread_stats;
252 
253 	k_thread_runtime_stats_get((struct k_thread *)thread, &thread_stats);
254 
255 	ok = zcbor_tstr_put_lit(zse, "runtime") &&
256 	zcbor_uint64_put(zse, thread_stats.execution_cycles);
257 #elif !defined(CONFIG_MCUMGR_GRP_OS_TASKSTAT_ONLY_SUPPORTED_STATS)
258 	ok = zcbor_tstr_put_lit(zse, "runtime") &&
259 	zcbor_uint32_put(zse, 0);
260 #endif
261 
262 	return ok;
263 }
264 
os_mgmt_taskstat_encode_unsupported(zcbor_state_t * zse)265 static inline bool os_mgmt_taskstat_encode_unsupported(zcbor_state_t *zse)
266 {
267 	bool ok = true;
268 
269 	if (!IS_ENABLED(CONFIG_MCUMGR_GRP_OS_TASKSTAT_ONLY_SUPPORTED_STATS)) {
270 		ok = zcbor_tstr_put_lit(zse, "cswcnt")		&&
271 		     zcbor_uint32_put(zse, 0)			&&
272 		     zcbor_tstr_put_lit(zse, "last_checkin")	&&
273 		     zcbor_uint32_put(zse, 0)			&&
274 		     zcbor_tstr_put_lit(zse, "next_checkin")	&&
275 		     zcbor_uint32_put(zse, 0);
276 	} else {
277 		ARG_UNUSED(zse);
278 	}
279 
280 	return ok;
281 }
282 
283 static inline bool
os_mgmt_taskstat_encode_priority(zcbor_state_t * zse,const struct k_thread * thread)284 os_mgmt_taskstat_encode_priority(zcbor_state_t *zse, const struct k_thread *thread)
285 {
286 	return (zcbor_tstr_put_lit(zse, "prio")					&&
287 		IS_ENABLED(CONFIG_MCUMGR_GRP_OS_TASKSTAT_SIGNED_PRIORITY) ?
288 		zcbor_int32_put(zse, (int)thread->base.prio) :
289 		zcbor_uint32_put(zse, (unsigned int)thread->base.prio) & 0xff);
290 }
291 
292 /**
293  * Encodes a single taskstat entry.
294  */
os_mgmt_taskstat_encode_one(const struct k_thread * thread,void * user_data)295 static void os_mgmt_taskstat_encode_one(const struct k_thread *thread, void *user_data)
296 {
297 	/*
298 	 * Threads are sent as map where thread name is key and value is map
299 	 * of thread parameters
300 	 */
301 	struct thread_iterator_info *iterator_ctx = (struct thread_iterator_info *)user_data;
302 
303 	if (iterator_ctx->ok == true) {
304 		iterator_ctx->ok =
305 			os_mgmt_taskstat_encode_thread_name(iterator_ctx->zse,
306 							    iterator_ctx->thread_idx, thread)	&&
307 			zcbor_map_start_encode(iterator_ctx->zse, TASKSTAT_COLUMNS_MAX)		&&
308 			os_mgmt_taskstat_encode_priority(iterator_ctx->zse, thread)		&&
309 			zcbor_tstr_put_lit(iterator_ctx->zse, "tid")				&&
310 			zcbor_uint32_put(iterator_ctx->zse, iterator_ctx->thread_idx)		&&
311 			zcbor_tstr_put_lit(iterator_ctx->zse, "state")				&&
312 			zcbor_uint32_put(iterator_ctx->zse, thread->base.thread_state)		&&
313 			os_mgmt_taskstat_encode_stack_info(iterator_ctx->zse, thread)		&&
314 			os_mgmt_taskstat_encode_runtime_info(iterator_ctx->zse, thread)		&&
315 			os_mgmt_taskstat_encode_unsupported(iterator_ctx->zse)			&&
316 			zcbor_map_end_encode(iterator_ctx->zse, TASKSTAT_COLUMNS_MAX);
317 
318 		++iterator_ctx->thread_idx;
319 	}
320 }
321 
322 /**
323  * Command handler: os taskstat
324  */
os_mgmt_taskstat_read(struct smp_streamer * ctxt)325 static int os_mgmt_taskstat_read(struct smp_streamer *ctxt)
326 {
327 	zcbor_state_t *zse = ctxt->writer->zs;
328 	struct thread_iterator_info iterator_ctx = {
329 		.zse = zse,
330 		.thread_idx = 0,
331 		.ok = true,
332 	};
333 
334 	zcbor_tstr_put_lit(zse, "tasks");
335 	zcbor_map_start_encode(zse, CONFIG_MCUMGR_GRP_OS_TASKSTAT_MAX_NUM_THREADS);
336 
337 	/* Iterate the list of tasks, encoding each. */
338 	k_thread_foreach(os_mgmt_taskstat_encode_one, (void *)&iterator_ctx);
339 
340 	if (!iterator_ctx.ok) {
341 		LOG_ERR("Task iterator status is not OK");
342 	}
343 
344 	if (!iterator_ctx.ok ||
345 	    !zcbor_map_end_encode(zse, CONFIG_MCUMGR_GRP_OS_TASKSTAT_MAX_NUM_THREADS)) {
346 		return MGMT_ERR_EMSGSIZE;
347 	}
348 
349 	return 0;
350 }
351 #endif /* CONFIG_MCUMGR_GRP_OS_TASKSTAT */
352 
353 #ifdef CONFIG_REBOOT
354 /**
355  * Command handler: os reset
356  */
os_mgmt_reset_work_handler(struct k_work * work)357 static void os_mgmt_reset_work_handler(struct k_work *work)
358 {
359 	ARG_UNUSED(work);
360 
361 	sys_reboot(SYS_REBOOT_WARM);
362 }
363 
os_mgmt_reset(struct smp_streamer * ctxt)364 static int os_mgmt_reset(struct smp_streamer *ctxt)
365 {
366 #if defined(CONFIG_MCUMGR_GRP_OS_RESET_HOOK)
367 	zcbor_state_t *zsd = ctxt->reader->zs;
368 	zcbor_state_t *zse = ctxt->writer->zs;
369 	size_t decoded;
370 	enum mgmt_cb_return status;
371 	int32_t err_rc;
372 	uint16_t err_group;
373 
374 	struct os_mgmt_reset_data reboot_data = {
375 		.force = false
376 	};
377 
378 	struct zcbor_map_decode_key_val reset_decode[] = {
379 		ZCBOR_MAP_DECODE_KEY_DECODER("force", zcbor_bool_decode, &reboot_data.force),
380 	};
381 
382 	/* Since this is a core command, if we fail to decode the data, ignore the error and
383 	 * continue with the default parameter of force being false.
384 	 */
385 	(void)zcbor_map_decode_bulk(zsd, reset_decode, ARRAY_SIZE(reset_decode), &decoded);
386 	status = mgmt_callback_notify(MGMT_EVT_OP_OS_MGMT_RESET, &reboot_data,
387 				      sizeof(reboot_data), &err_rc, &err_group);
388 
389 	if (status != MGMT_CB_OK) {
390 		bool ok;
391 
392 		if (status == MGMT_CB_ERROR_RC) {
393 			return err_rc;
394 		}
395 
396 		ok = smp_add_cmd_err(zse, err_group, (uint16_t)err_rc);
397 		return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
398 	}
399 #endif
400 
401 	/* Reboot the system from the system workqueue thread. */
402 	k_work_schedule(&os_mgmt_reset_work, K_MSEC(CONFIG_MCUMGR_GRP_OS_RESET_MS));
403 
404 	return 0;
405 }
406 #endif
407 
408 #ifdef CONFIG_MCUMGR_GRP_OS_MCUMGR_PARAMS
409 static int
os_mgmt_mcumgr_params(struct smp_streamer * ctxt)410 os_mgmt_mcumgr_params(struct smp_streamer *ctxt)
411 {
412 	zcbor_state_t *zse = ctxt->writer->zs;
413 	bool ok;
414 
415 	ok = zcbor_tstr_put_lit(zse, "buf_size")		&&
416 	     zcbor_uint32_put(zse, CONFIG_MCUMGR_TRANSPORT_NETBUF_SIZE)	&&
417 	     zcbor_tstr_put_lit(zse, "buf_count")		&&
418 	     zcbor_uint32_put(zse, CONFIG_MCUMGR_TRANSPORT_NETBUF_COUNT);
419 
420 	return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
421 }
422 #endif
423 
424 #if defined(CONFIG_MCUMGR_GRP_OS_BOOTLOADER_INFO)
425 
426 #if defined(CONFIG_BOOTLOADER_MCUBOOT)
427 #if defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SINGLE_APP)
428 #define BOOTLOADER_MODE MCUBOOT_MODE_SINGLE_SLOT
429 #elif defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_SCRATCH)
430 #define BOOTLOADER_MODE MCUBOOT_MODE_SWAP_USING_SCRATCH
431 #elif defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_OVERWRITE_ONLY)
432 #define BOOTLOADER_MODE MCUBOOT_MODE_UPGRADE_ONLY
433 #elif defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_OFFSET)
434 #define BOOTLOADER_MODE MCUBOOT_MODE_SWAP_USING_OFFSET
435 #elif defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_USING_MOVE) || \
436 defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_SWAP_WITHOUT_SCRATCH)
437 #define BOOTLOADER_MODE MCUBOOT_MODE_SWAP_USING_MOVE
438 #elif defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP)
439 #define BOOTLOADER_MODE MCUBOOT_MODE_DIRECT_XIP
440 #elif defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT)
441 #define BOOTLOADER_MODE MCUBOOT_MODE_DIRECT_XIP_WITH_REVERT
442 #elif defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_FIRMWARE_UPDATER)
443 #define BOOTLOADER_MODE MCUBOOT_MODE_FIRMWARE_LOADER
444 #else
445 #define BOOTLOADER_MODE -1
446 #endif
447 #endif
448 
449 static int
os_mgmt_bootloader_info(struct smp_streamer * ctxt)450 os_mgmt_bootloader_info(struct smp_streamer *ctxt)
451 {
452 	zcbor_state_t *zse = ctxt->writer->zs;
453 	zcbor_state_t *zsd = ctxt->reader->zs;
454 	struct zcbor_string query = { 0 };
455 	size_t decoded;
456 	bool ok = true;
457 	bool has_output = false;
458 
459 #if defined(CONFIG_MCUMGR_GRP_OS_BOOTLOADER_INFO_HOOK)
460 	enum mgmt_cb_return status;
461 	int32_t err_rc;
462 	uint16_t err_group;
463 	struct os_mgmt_bootloader_info_data bootloader_info_data = {
464 		.zse = zse,
465 		.decoded = &decoded,
466 		.query = &query,
467 		.has_output = &has_output
468 	};
469 #endif
470 
471 	struct zcbor_map_decode_key_val bootloader_info[] = {
472 		ZCBOR_MAP_DECODE_KEY_DECODER("query", zcbor_tstr_decode, &query),
473 	};
474 
475 	if (zcbor_map_decode_bulk(zsd, bootloader_info, ARRAY_SIZE(bootloader_info), &decoded)) {
476 		return MGMT_ERR_EINVAL;
477 	}
478 
479 #if defined(CONFIG_MCUMGR_GRP_OS_BOOTLOADER_INFO_HOOK)
480 	status = mgmt_callback_notify(MGMT_EVT_OP_OS_MGMT_BOOTLOADER_INFO, &bootloader_info_data,
481 				      sizeof(bootloader_info_data), &err_rc, &err_group);
482 
483 	if (status != MGMT_CB_OK) {
484 		if (status == MGMT_CB_ERROR_RC) {
485 			return err_rc;
486 		}
487 
488 		ok = smp_add_cmd_err(zse, err_group, (uint16_t)err_rc);
489 
490 		return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
491 	}
492 #endif
493 
494 	/* If no parameter is recognized then just introduce the bootloader. */
495 	if (!has_output) {
496 #if defined(CONFIG_BOOTLOADER_MCUBOOT)
497 		if (decoded == 0) {
498 			ok = zcbor_tstr_put_lit(zse, "bootloader") &&
499 			     zcbor_tstr_put_lit(zse, "MCUboot");
500 			has_output = true;
501 		} else if (zcbor_map_decode_bulk_key_found(bootloader_info,
502 							   ARRAY_SIZE(bootloader_info),
503 			   "query") && (sizeof("mode") - 1) == query.len &&
504 			   memcmp("mode", query.value, query.len) == 0) {
505 
506 			ok = zcbor_tstr_put_lit(zse, "mode") &&
507 			     zcbor_int32_put(zse, BOOTLOADER_MODE);
508 #ifdef CONFIG_MCUBOOT_BOOTLOADER_NO_DOWNGRADE
509 			ok = ok && zcbor_tstr_put_lit(zse, "no-downgrade") &&
510 			     zcbor_bool_encode(zse, &(bool){true});
511 #endif
512 			has_output = true;
513 		}
514 #endif
515 	}
516 
517 	if (!has_output) {
518 		ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_OS, OS_MGMT_ERR_QUERY_YIELDS_NO_ANSWER);
519 	}
520 
521 	return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
522 }
523 #endif
524 
525 #ifdef CONFIG_MCUMGR_GRP_OS_INFO
526 /**
527  * Command handler: os info
528  */
os_mgmt_info(struct smp_streamer * ctxt)529 static int os_mgmt_info(struct smp_streamer *ctxt)
530 {
531 	struct zcbor_string format = { 0 };
532 	uint8_t output[CONFIG_MCUMGR_GRP_OS_INFO_MAX_RESPONSE_SIZE] = { 0 };
533 	zcbor_state_t *zse = ctxt->writer->zs;
534 	zcbor_state_t *zsd = ctxt->reader->zs;
535 	uint32_t format_bitmask = 0;
536 	bool prior_output = false;
537 	size_t i = 0;
538 	size_t decoded;
539 	bool custom_os_name = false;
540 	int rc;
541 	uint16_t output_length = 0;
542 	uint16_t valid_formats = 0;
543 
544 	struct zcbor_map_decode_key_val fs_info_decode[] = {
545 		ZCBOR_MAP_DECODE_KEY_DECODER("format", zcbor_tstr_decode, &format),
546 	};
547 
548 #ifdef CONFIG_MCUMGR_GRP_OS_INFO_CUSTOM_HOOKS
549 	struct os_mgmt_info_check check_data = {
550 		.format = &format,
551 		.format_bitmask = &format_bitmask,
552 		.valid_formats = &valid_formats,
553 		.custom_os_name = &custom_os_name,
554 	};
555 
556 	struct os_mgmt_info_append append_data = {
557 		.format_bitmask = &format_bitmask,
558 		.all_format_specified = false,
559 		.output = output,
560 		.output_length = &output_length,
561 		.buffer_size = sizeof(output),
562 		.prior_output = &prior_output,
563 	};
564 #endif
565 
566 #ifdef CONFIG_MCUMGR_GRP_OS_INFO_CUSTOM_HOOKS
567 	enum mgmt_cb_return status;
568 	int32_t err_rc;
569 	uint16_t err_group;
570 #endif
571 
572 	if (zcbor_map_decode_bulk(zsd, fs_info_decode, ARRAY_SIZE(fs_info_decode), &decoded)) {
573 		return MGMT_ERR_EINVAL;
574 	}
575 
576 	/* Process all input characters in format value */
577 	while (i < format.len) {
578 		switch (format.value[i]) {
579 		case 'a': {
580 #ifdef CONFIG_MCUMGR_GRP_OS_INFO_CUSTOM_HOOKS
581 			append_data.all_format_specified = true;
582 #endif
583 
584 			format_bitmask = OS_MGMT_INFO_FORMAT_ALL;
585 			++valid_formats;
586 			break;
587 		}
588 		case 's': {
589 			format_bitmask |= OS_MGMT_INFO_FORMAT_KERNEL_NAME;
590 			++valid_formats;
591 			break;
592 		}
593 		case 'n': {
594 			format_bitmask |= OS_MGMT_INFO_FORMAT_NODE_NAME;
595 			++valid_formats;
596 			break;
597 		}
598 		case 'r': {
599 			format_bitmask |= OS_MGMT_INFO_FORMAT_KERNEL_RELEASE;
600 			++valid_formats;
601 			break;
602 		}
603 		case 'v': {
604 			format_bitmask |= OS_MGMT_INFO_FORMAT_KERNEL_VERSION;
605 			++valid_formats;
606 			break;
607 		}
608 #ifdef CONFIG_MCUMGR_GRP_OS_INFO_BUILD_DATE_TIME
609 		case 'b': {
610 			format_bitmask |= OS_MGMT_INFO_FORMAT_BUILD_DATE_TIME;
611 			++valid_formats;
612 			break;
613 		}
614 #endif
615 		case 'm': {
616 			format_bitmask |= OS_MGMT_INFO_FORMAT_MACHINE;
617 			++valid_formats;
618 			break;
619 		}
620 		case 'p': {
621 			format_bitmask |= OS_MGMT_INFO_FORMAT_PROCESSOR;
622 			++valid_formats;
623 			break;
624 		}
625 		case 'i': {
626 			format_bitmask |= OS_MGMT_INFO_FORMAT_HARDWARE_PLATFORM;
627 			++valid_formats;
628 			break;
629 		}
630 		case 'o': {
631 			format_bitmask |= OS_MGMT_INFO_FORMAT_OPERATING_SYSTEM;
632 			++valid_formats;
633 			break;
634 		}
635 		default: {
636 			break;
637 		}
638 		}
639 
640 		++i;
641 	}
642 
643 #ifdef CONFIG_MCUMGR_GRP_OS_INFO_CUSTOM_HOOKS
644 	/* Run callbacks to see if any additional handlers will add options */
645 	(void)mgmt_callback_notify(MGMT_EVT_OP_OS_MGMT_INFO_CHECK, &check_data,
646 				   sizeof(check_data), &err_rc, &err_group);
647 #endif
648 
649 	if (valid_formats != format.len) {
650 		/* A provided format specifier is not valid */
651 		bool ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_OS, OS_MGMT_ERR_INVALID_FORMAT);
652 
653 		return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
654 	} else if (format_bitmask == 0) {
655 		/* If no value is provided, use default of kernel name */
656 		format_bitmask = OS_MGMT_INFO_FORMAT_KERNEL_NAME;
657 	}
658 
659 	/* Process all options in order and append to output string */
660 	if (format_bitmask & OS_MGMT_INFO_FORMAT_KERNEL_NAME) {
661 		rc = snprintf(output, (sizeof(output) - output_length), "Zephyr");
662 
663 		if (rc < 0 || rc >= (sizeof(output) - output_length)) {
664 			goto fail;
665 		} else {
666 			output_length += (uint16_t)rc;
667 		}
668 
669 		prior_output = true;
670 	}
671 
672 	if (format_bitmask & OS_MGMT_INFO_FORMAT_NODE_NAME) {
673 		/* Get hostname, if enabled */
674 #if defined(CONFIG_NET_HOSTNAME_ENABLE)
675 		/* From network */
676 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
677 			      (prior_output == true ? " %s" : "%s"), net_hostname_get());
678 #elif defined(CONFIG_BT)
679 		/* From Bluetooth */
680 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
681 			      (prior_output == true ? " %s" : "%s"), bt_get_name());
682 #else
683 		/* Not available */
684 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
685 			      "%sunknown", (prior_output == true ? " " : ""));
686 #endif
687 
688 		if (rc < 0 || rc >= (sizeof(output) - output_length)) {
689 			goto fail;
690 		} else {
691 			output_length += (uint16_t)rc;
692 		}
693 
694 		prior_output = true;
695 		format_bitmask &= ~OS_MGMT_INFO_FORMAT_NODE_NAME;
696 	}
697 
698 	if (format_bitmask & OS_MGMT_INFO_FORMAT_KERNEL_RELEASE) {
699 #ifdef BUILD_VERSION
700 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
701 			      (prior_output == true ? " %s" : "%s"), STRINGIFY(BUILD_VERSION));
702 #else
703 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
704 			      "%sunknown", (prior_output == true ? " " : ""));
705 #endif
706 
707 		if (rc < 0 || rc >= (sizeof(output) - output_length)) {
708 			goto fail;
709 		} else {
710 			output_length += (uint16_t)rc;
711 		}
712 
713 		prior_output = true;
714 		format_bitmask &= ~OS_MGMT_INFO_FORMAT_KERNEL_RELEASE;
715 	}
716 
717 	if (format_bitmask & OS_MGMT_INFO_FORMAT_KERNEL_VERSION) {
718 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
719 			      (prior_output == true ? " %s" : "%s"), KERNEL_VERSION_STRING);
720 
721 		if (rc < 0 || rc >= (sizeof(output) - output_length)) {
722 			goto fail;
723 		} else {
724 			output_length += (uint16_t)rc;
725 		}
726 
727 		prior_output = true;
728 		format_bitmask &= ~OS_MGMT_INFO_FORMAT_KERNEL_VERSION;
729 	}
730 
731 #ifdef CONFIG_MCUMGR_GRP_OS_INFO_BUILD_DATE_TIME
732 	if (format_bitmask & OS_MGMT_INFO_FORMAT_BUILD_DATE_TIME) {
733 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
734 			      (prior_output == true ? " %s" : "%s"),
735 			      MCUMGR_GRP_OS_INFO_BUILD_DATE_TIME);
736 
737 		if (rc < 0 || rc >= (sizeof(output) - output_length)) {
738 			goto fail;
739 		} else {
740 			output_length += (uint16_t)rc;
741 		}
742 
743 		prior_output = true;
744 		format_bitmask &= ~OS_MGMT_INFO_FORMAT_BUILD_DATE_TIME;
745 	}
746 #endif
747 
748 	if (format_bitmask & OS_MGMT_INFO_FORMAT_MACHINE) {
749 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
750 			      (prior_output == true ? " %s" : "%s"), CONFIG_ARCH);
751 
752 		if (rc < 0 || rc >= (sizeof(output) - output_length)) {
753 			goto fail;
754 		} else {
755 			output_length += (uint16_t)rc;
756 		}
757 
758 		prior_output = true;
759 		format_bitmask &= ~OS_MGMT_INFO_FORMAT_MACHINE;
760 	}
761 
762 	if (format_bitmask & OS_MGMT_INFO_FORMAT_PROCESSOR) {
763 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
764 			      (prior_output == true ? " %s" : "%s"), PROCESSOR_NAME);
765 
766 		if (rc < 0 || rc >= (sizeof(output) - output_length)) {
767 			goto fail;
768 		} else {
769 			output_length += (uint16_t)rc;
770 		}
771 
772 		prior_output = true;
773 		format_bitmask &= ~OS_MGMT_INFO_FORMAT_PROCESSOR;
774 	}
775 
776 	if (format_bitmask & OS_MGMT_INFO_FORMAT_HARDWARE_PLATFORM) {
777 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
778 			      (prior_output == true ? " %s%s%s" : "%s%s%s"), CONFIG_BOARD,
779 			      (sizeof(CONFIG_BOARD_REVISION) > 1 ? "@" : ""),
780 			      CONFIG_BOARD_REVISION);
781 
782 		if (rc < 0 || rc >= (sizeof(output) - output_length)) {
783 			goto fail;
784 		} else {
785 			output_length += (uint16_t)rc;
786 		}
787 
788 		prior_output = true;
789 		format_bitmask &= ~OS_MGMT_INFO_FORMAT_HARDWARE_PLATFORM;
790 	}
791 
792 	/* If custom_os_name is not set (by extension code) then return the default OS name of
793 	 * Zephyr
794 	 */
795 	if (format_bitmask & OS_MGMT_INFO_FORMAT_OPERATING_SYSTEM && custom_os_name == false) {
796 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
797 			      "%sZephyr", (prior_output == true ? " " : ""));
798 
799 		if (rc < 0 || rc >= (sizeof(output) - output_length)) {
800 			goto fail;
801 		} else {
802 			output_length += (uint16_t)rc;
803 		}
804 
805 		prior_output = true;
806 		format_bitmask &= ~OS_MGMT_INFO_FORMAT_OPERATING_SYSTEM;
807 	}
808 
809 #ifdef CONFIG_MCUMGR_GRP_OS_INFO_CUSTOM_HOOKS
810 	/* Call custom handler command for additional output/processing */
811 	status = mgmt_callback_notify(MGMT_EVT_OP_OS_MGMT_INFO_APPEND, &append_data,
812 				      sizeof(append_data), &err_rc, &err_group);
813 
814 	if (status != MGMT_CB_OK) {
815 		bool ok;
816 
817 		if (status == MGMT_CB_ERROR_RC) {
818 			return err_rc;
819 		}
820 
821 		ok = smp_add_cmd_err(zse, err_group, (uint16_t)err_rc);
822 		return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
823 	}
824 #endif
825 
826 	if (zcbor_tstr_put_lit(zse, "output") &&
827 	    zcbor_tstr_encode_ptr(zse, output, output_length)) {
828 		return MGMT_ERR_EOK;
829 	}
830 
831 fail:
832 	return MGMT_ERR_EMSGSIZE;
833 }
834 #endif
835 
836 #ifdef CONFIG_MCUMGR_GRP_OS_DATETIME
837 /**
838  * Command handler: os datetime get
839  */
os_mgmt_datetime_read(struct smp_streamer * ctxt)840 static int os_mgmt_datetime_read(struct smp_streamer *ctxt)
841 {
842 	zcbor_state_t *zse = ctxt->writer->zs;
843 	struct rtc_time current_time;
844 	char date_string[RTC_DATETIME_STRING_SIZE];
845 	int rc;
846 	bool ok;
847 
848 #if defined(CONFIG_MCUMGR_GRP_OS_DATETIME_HOOK)
849 	enum mgmt_cb_return status;
850 	int32_t err_rc;
851 	uint16_t err_group;
852 
853 	status = mgmt_callback_notify(MGMT_EVT_OP_OS_MGMT_DATETIME_GET, NULL, 0, &err_rc,
854 				      &err_group);
855 
856 	if (status != MGMT_CB_OK) {
857 		if (status == MGMT_CB_ERROR_RC) {
858 			return err_rc;
859 		}
860 
861 		ok = smp_add_cmd_err(zse, err_group, (uint16_t)err_rc);
862 		return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
863 	}
864 #endif
865 
866 	rc = rtc_get_time(RTC_DEVICE, &current_time);
867 
868 	if (rc == -ENODATA) {
869 		/* RTC not set */
870 		ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_OS, OS_MGMT_ERR_RTC_NOT_SET);
871 		goto finished;
872 	} else if (rc != 0) {
873 		/* Other RTC error */
874 		ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_OS, OS_MGMT_ERR_RTC_COMMAND_FAILED);
875 		goto finished;
876 	}
877 
878 	sprintf(date_string, "%4d-%02d-%02dT%02d:%02d:%02d"
879 #ifdef CONFIG_MCUMGR_GRP_OS_DATETIME_MS
880 		".%03d"
881 #endif
882 		, (uint16_t)(current_time.tm_year + RTC_DATETIME_YEAR_OFFSET),
883 		(uint8_t)(current_time.tm_mon + RTC_DATETIME_MONTH_OFFSET),
884 		(uint8_t)current_time.tm_mday, (uint8_t)current_time.tm_hour,
885 		(uint8_t)current_time.tm_min, (uint8_t)current_time.tm_sec
886 #ifdef CONFIG_MCUMGR_GRP_OS_DATETIME_MS
887 		, (uint16_t)(current_time.tm_nsec / RTC_DATETIME_MS_TO_NS)
888 #endif
889 	);
890 
891 	ok = zcbor_tstr_put_lit(zse, "datetime")				&&
892 	     zcbor_tstr_encode_ptr(zse, date_string, strlen(date_string));
893 
894 finished:
895 	return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
896 }
897 
898 /**
899  * Command handler: os datetime set
900  */
os_mgmt_datetime_write(struct smp_streamer * ctxt)901 static int os_mgmt_datetime_write(struct smp_streamer *ctxt)
902 {
903 	zcbor_state_t *zsd = ctxt->reader->zs;
904 	zcbor_state_t *zse = ctxt->writer->zs;
905 	size_t decoded;
906 	struct zcbor_string datetime = { 0 };
907 	int rc;
908 	uint8_t i = 0;
909 	bool ok = true;
910 	char *pos;
911 	char *new_pos;
912 	char date_string[RTC_DATETIME_MAX_STRING_SIZE];
913 	struct rtc_time new_time = {
914 		.tm_wday = -1,
915 		.tm_yday = -1,
916 		.tm_isdst = -1,
917 		.tm_nsec = 0,
918 	};
919 	struct datetime_parser parser[] = {
920 		{
921 			.value = &new_time.tm_year,
922 			.min_value = RTC_DATETIME_YEAR_MIN,
923 			.max_value = RTC_DATETIME_YEAR_MAX,
924 			.offset = -RTC_DATETIME_YEAR_OFFSET,
925 		},
926 		{
927 			.value = &new_time.tm_mon,
928 			.min_value = RTC_DATETIME_MONTH_MIN,
929 			.max_value = RTC_DATETIME_MONTH_MAX,
930 			.offset = -RTC_DATETIME_MONTH_OFFSET,
931 		},
932 		{
933 			.value = &new_time.tm_mday,
934 			.min_value = RTC_DATETIME_DAY_MIN,
935 			.max_value = RTC_DATETIME_DAY_MAX,
936 		},
937 		{
938 			.value = &new_time.tm_hour,
939 			.min_value = RTC_DATETIME_HOUR_MIN,
940 			.max_value = RTC_DATETIME_HOUR_MAX,
941 		},
942 		{
943 			.value = &new_time.tm_min,
944 			.min_value = RTC_DATETIME_MINUTE_MIN,
945 			.max_value = RTC_DATETIME_MINUTE_MAX,
946 		},
947 		{
948 			.value = &new_time.tm_sec,
949 			.min_value = RTC_DATETIME_SECOND_MIN,
950 			.max_value = RTC_DATETIME_SECOND_MAX,
951 		},
952 	};
953 
954 #if defined(CONFIG_MCUMGR_GRP_OS_DATETIME_HOOK)
955 	enum mgmt_cb_return status;
956 	int32_t err_rc;
957 	uint16_t err_group;
958 #endif
959 
960 	struct zcbor_map_decode_key_val datetime_decode[] = {
961 		ZCBOR_MAP_DECODE_KEY_DECODER("datetime", zcbor_tstr_decode, &datetime),
962 	};
963 
964 	if (zcbor_map_decode_bulk(zsd, datetime_decode, ARRAY_SIZE(datetime_decode), &decoded)) {
965 		return MGMT_ERR_EINVAL;
966 	} else if (datetime.len < RTC_DATETIME_MIN_STRING_SIZE ||
967 		   datetime.len >= RTC_DATETIME_MAX_STRING_SIZE) {
968 		return MGMT_ERR_EINVAL;
969 	}
970 
971 	memcpy(date_string, datetime.value, datetime.len);
972 	date_string[datetime.len] = '\0';
973 
974 	pos = date_string;
975 
976 	while (i < ARRAY_SIZE(parser)) {
977 		if (pos == (date_string + datetime.len)) {
978 			/* Encountered end of string early, this is invalid */
979 			return MGMT_ERR_EINVAL;
980 		}
981 
982 		*parser[i].value = strtol(pos, &new_pos, RTC_DATETIME_NUMERIC_BASE);
983 
984 		if (pos == new_pos) {
985 			/* Missing or unable to convert field */
986 			return MGMT_ERR_EINVAL;
987 		}
988 
989 		if (*parser[i].value < parser[i].min_value ||
990 		    *parser[i].value > parser[i].max_value) {
991 			/* Value is not within the allowed bounds of this field */
992 			return MGMT_ERR_EINVAL;
993 		}
994 
995 		*parser[i].value += parser[i].offset;
996 
997 		/* Skip a character as there is always a delimiter between the fields */
998 		++i;
999 		pos = new_pos + 1;
1000 	}
1001 
1002 #ifdef CONFIG_MCUMGR_GRP_OS_DATETIME_MS
1003 	if (*(pos - 1) == '.' && *pos != '\0') {
1004 		/* Provided value has a ms value, extract it */
1005 		new_time.tm_nsec = strtol(pos, &new_pos, RTC_DATETIME_NUMERIC_BASE);
1006 
1007 		if (new_time.tm_nsec < RTC_DATETIME_MILLISECOND_MIN ||
1008 		    new_time.tm_nsec > RTC_DATETIME_MILLISECOND_MAX) {
1009 			return MGMT_ERR_EINVAL;
1010 		}
1011 
1012 		new_time.tm_nsec *= RTC_DATETIME_MS_TO_NS;
1013 	}
1014 #endif
1015 
1016 #if defined(CONFIG_MCUMGR_GRP_OS_DATETIME_HOOK)
1017 	status = mgmt_callback_notify(MGMT_EVT_OP_OS_MGMT_DATETIME_SET, &new_time,
1018 				      sizeof(new_time), &err_rc, &err_group);
1019 
1020 	if (status != MGMT_CB_OK) {
1021 		if (status == MGMT_CB_ERROR_RC) {
1022 			return err_rc;
1023 		}
1024 
1025 		ok = smp_add_cmd_err(zse, err_group, (uint16_t)err_rc);
1026 		return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
1027 	}
1028 #endif
1029 
1030 	rc = rtc_set_time(RTC_DEVICE, &new_time);
1031 
1032 	if (rc != 0) {
1033 		ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_OS, OS_MGMT_ERR_RTC_COMMAND_FAILED);
1034 	}
1035 
1036 	return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
1037 }
1038 #endif
1039 
1040 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
1041 /*
1042  * @brief	Translate OS mgmt group error code into MCUmgr error code
1043  *
1044  * @param ret	#os_mgmt_err_code_t error code
1045  *
1046  * @return	#mcumgr_err_t error code
1047  */
os_mgmt_translate_error_code(uint16_t err)1048 static int os_mgmt_translate_error_code(uint16_t err)
1049 {
1050 	int rc;
1051 
1052 	switch (err) {
1053 	case OS_MGMT_ERR_INVALID_FORMAT:
1054 		rc = MGMT_ERR_EINVAL;
1055 		break;
1056 
1057 	case OS_MGMT_ERR_QUERY_YIELDS_NO_ANSWER:
1058 	case OS_MGMT_ERR_RTC_NOT_SET:
1059 	case OS_MGMT_ERR_QUERY_RESPONSE_VALUE_NOT_VALID:
1060 		rc = MGMT_ERR_ENOENT;
1061 		break;
1062 
1063 	case OS_MGMT_ERR_UNKNOWN:
1064 	case OS_MGMT_ERR_RTC_COMMAND_FAILED:
1065 	default:
1066 		rc = MGMT_ERR_EUNKNOWN;
1067 	}
1068 
1069 	return rc;
1070 }
1071 #endif
1072 
1073 static const struct mgmt_handler os_mgmt_group_handlers[] = {
1074 #ifdef CONFIG_MCUMGR_GRP_OS_ECHO
1075 	[OS_MGMT_ID_ECHO] = {
1076 		os_mgmt_echo, os_mgmt_echo
1077 	},
1078 #endif
1079 #ifdef CONFIG_MCUMGR_GRP_OS_TASKSTAT
1080 	[OS_MGMT_ID_TASKSTAT] = {
1081 		os_mgmt_taskstat_read, NULL
1082 	},
1083 #endif
1084 
1085 #ifdef CONFIG_MCUMGR_GRP_OS_DATETIME
1086 	[OS_MGMT_ID_DATETIME_STR] = {
1087 		os_mgmt_datetime_read, os_mgmt_datetime_write
1088 	},
1089 #endif
1090 
1091 #ifdef CONFIG_REBOOT
1092 	[OS_MGMT_ID_RESET] = {
1093 		NULL, os_mgmt_reset
1094 	},
1095 #endif
1096 #ifdef CONFIG_MCUMGR_GRP_OS_MCUMGR_PARAMS
1097 	[OS_MGMT_ID_MCUMGR_PARAMS] = {
1098 		os_mgmt_mcumgr_params, NULL
1099 	},
1100 #endif
1101 #ifdef CONFIG_MCUMGR_GRP_OS_INFO
1102 	[OS_MGMT_ID_INFO] = {
1103 		os_mgmt_info, NULL
1104 	},
1105 #endif
1106 #ifdef CONFIG_MCUMGR_GRP_OS_BOOTLOADER_INFO
1107 	[OS_MGMT_ID_BOOTLOADER_INFO] = {
1108 		os_mgmt_bootloader_info, NULL
1109 	},
1110 #endif
1111 };
1112 
1113 #define OS_MGMT_GROUP_SZ ARRAY_SIZE(os_mgmt_group_handlers)
1114 
1115 static struct mgmt_group os_mgmt_group = {
1116 	.mg_handlers = os_mgmt_group_handlers,
1117 	.mg_handlers_count = OS_MGMT_GROUP_SZ,
1118 	.mg_group_id = MGMT_GROUP_ID_OS,
1119 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
1120 	.mg_translate_error = os_mgmt_translate_error_code,
1121 #endif
1122 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS_NAME
1123 	.mg_group_name = "os mgmt",
1124 #endif
1125 };
1126 
os_mgmt_register_group(void)1127 static void os_mgmt_register_group(void)
1128 {
1129 	mgmt_register_group(&os_mgmt_group);
1130 }
1131 
1132 MCUMGR_HANDLER_DEFINE(os_mgmt, os_mgmt_register_group);
1133