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_WITHOUT_SCRATCH)
434 #define BOOTLOADER_MODE MCUBOOT_MODE_SWAP_USING_MOVE
435 #elif defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP)
436 #define BOOTLOADER_MODE MCUBOOT_MODE_DIRECT_XIP
437 #elif defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_DIRECT_XIP_WITH_REVERT)
438 #define BOOTLOADER_MODE MCUBOOT_MODE_DIRECT_XIP_WITH_REVERT
439 #elif defined(CONFIG_MCUBOOT_BOOTLOADER_MODE_FIRMWARE_UPDATER)
440 #define BOOTLOADER_MODE MCUBOOT_MODE_FIRMWARE_LOADER
441 #else
442 #define BOOTLOADER_MODE -1
443 #endif
444 #endif
445 
446 static int
os_mgmt_bootloader_info(struct smp_streamer * ctxt)447 os_mgmt_bootloader_info(struct smp_streamer *ctxt)
448 {
449 	zcbor_state_t *zse = ctxt->writer->zs;
450 	zcbor_state_t *zsd = ctxt->reader->zs;
451 	struct zcbor_string query = { 0 };
452 	size_t decoded;
453 	bool ok;
454 	bool has_output = false;
455 
456 #if defined(CONFIG_MCUMGR_GRP_OS_BOOTLOADER_INFO_HOOK)
457 	enum mgmt_cb_return status;
458 	int32_t err_rc;
459 	uint16_t err_group;
460 	struct os_mgmt_bootloader_info_data bootloader_info_data = {
461 		.zse = zse,
462 		.decoded = &decoded,
463 		.query = &query,
464 		.has_output = &has_output
465 	};
466 #endif
467 
468 	struct zcbor_map_decode_key_val bootloader_info[] = {
469 		ZCBOR_MAP_DECODE_KEY_DECODER("query", zcbor_tstr_decode, &query),
470 	};
471 
472 	if (zcbor_map_decode_bulk(zsd, bootloader_info, ARRAY_SIZE(bootloader_info), &decoded)) {
473 		return MGMT_ERR_EINVAL;
474 	}
475 
476 #if defined(CONFIG_MCUMGR_GRP_OS_BOOTLOADER_INFO_HOOK)
477 	status = mgmt_callback_notify(MGMT_EVT_OP_OS_MGMT_BOOTLOADER_INFO, &bootloader_info_data,
478 				      sizeof(bootloader_info_data), &err_rc, &err_group);
479 
480 	if (status != MGMT_CB_OK) {
481 		if (status == MGMT_CB_ERROR_RC) {
482 			return err_rc;
483 		}
484 
485 		ok = smp_add_cmd_err(zse, err_group, (uint16_t)err_rc);
486 
487 		return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
488 	}
489 #endif
490 
491 	/* If no parameter is recognized then just introduce the bootloader. */
492 	if (!has_output) {
493 #if defined(CONFIG_BOOTLOADER_MCUBOOT)
494 		if (decoded == 0) {
495 			ok = zcbor_tstr_put_lit(zse, "bootloader") &&
496 			     zcbor_tstr_put_lit(zse, "MCUboot");
497 			has_output = true;
498 		} else if (zcbor_map_decode_bulk_key_found(bootloader_info,
499 							   ARRAY_SIZE(bootloader_info),
500 			   "query") && (sizeof("mode") - 1) == query.len &&
501 			   memcmp("mode", query.value, query.len) == 0) {
502 
503 			ok = zcbor_tstr_put_lit(zse, "mode") &&
504 			     zcbor_int32_put(zse, BOOTLOADER_MODE);
505 #ifdef CONFIG_MCUBOOT_BOOTLOADER_NO_DOWNGRADE
506 			ok = ok && zcbor_tstr_put_lit(zse, "no-downgrade") &&
507 			     zcbor_bool_encode(zse, &(bool){true});
508 #endif
509 			has_output = true;
510 		}
511 #endif
512 	}
513 
514 	if (!has_output) {
515 		ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_OS, OS_MGMT_ERR_QUERY_YIELDS_NO_ANSWER);
516 	}
517 
518 	return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
519 }
520 #endif
521 
522 #ifdef CONFIG_MCUMGR_GRP_OS_INFO
523 /**
524  * Command handler: os info
525  */
os_mgmt_info(struct smp_streamer * ctxt)526 static int os_mgmt_info(struct smp_streamer *ctxt)
527 {
528 	struct zcbor_string format = { 0 };
529 	uint8_t output[CONFIG_MCUMGR_GRP_OS_INFO_MAX_RESPONSE_SIZE] = { 0 };
530 	zcbor_state_t *zse = ctxt->writer->zs;
531 	zcbor_state_t *zsd = ctxt->reader->zs;
532 	uint32_t format_bitmask = 0;
533 	bool prior_output = false;
534 	size_t i = 0;
535 	size_t decoded;
536 	bool custom_os_name = false;
537 	int rc;
538 	uint16_t output_length = 0;
539 	uint16_t valid_formats = 0;
540 
541 	struct zcbor_map_decode_key_val fs_info_decode[] = {
542 		ZCBOR_MAP_DECODE_KEY_DECODER("format", zcbor_tstr_decode, &format),
543 	};
544 
545 #ifdef CONFIG_MCUMGR_GRP_OS_INFO_CUSTOM_HOOKS
546 	struct os_mgmt_info_check check_data = {
547 		.format = &format,
548 		.format_bitmask = &format_bitmask,
549 		.valid_formats = &valid_formats,
550 		.custom_os_name = &custom_os_name,
551 	};
552 
553 	struct os_mgmt_info_append append_data = {
554 		.format_bitmask = &format_bitmask,
555 		.all_format_specified = false,
556 		.output = output,
557 		.output_length = &output_length,
558 		.buffer_size = sizeof(output),
559 		.prior_output = &prior_output,
560 	};
561 #endif
562 
563 #ifdef CONFIG_MCUMGR_GRP_OS_INFO_CUSTOM_HOOKS
564 	enum mgmt_cb_return status;
565 	int32_t err_rc;
566 	uint16_t err_group;
567 #endif
568 
569 	if (zcbor_map_decode_bulk(zsd, fs_info_decode, ARRAY_SIZE(fs_info_decode), &decoded)) {
570 		return MGMT_ERR_EINVAL;
571 	}
572 
573 	/* Process all input characters in format value */
574 	while (i < format.len) {
575 		switch (format.value[i]) {
576 		case 'a': {
577 #ifdef CONFIG_MCUMGR_GRP_OS_INFO_CUSTOM_HOOKS
578 			append_data.all_format_specified = true;
579 #endif
580 
581 			format_bitmask = OS_MGMT_INFO_FORMAT_ALL;
582 			++valid_formats;
583 			break;
584 		}
585 		case 's': {
586 			format_bitmask |= OS_MGMT_INFO_FORMAT_KERNEL_NAME;
587 			++valid_formats;
588 			break;
589 		}
590 		case 'n': {
591 			format_bitmask |= OS_MGMT_INFO_FORMAT_NODE_NAME;
592 			++valid_formats;
593 			break;
594 		}
595 		case 'r': {
596 			format_bitmask |= OS_MGMT_INFO_FORMAT_KERNEL_RELEASE;
597 			++valid_formats;
598 			break;
599 		}
600 		case 'v': {
601 			format_bitmask |= OS_MGMT_INFO_FORMAT_KERNEL_VERSION;
602 			++valid_formats;
603 			break;
604 		}
605 #ifdef CONFIG_MCUMGR_GRP_OS_INFO_BUILD_DATE_TIME
606 		case 'b': {
607 			format_bitmask |= OS_MGMT_INFO_FORMAT_BUILD_DATE_TIME;
608 			++valid_formats;
609 			break;
610 		}
611 #endif
612 		case 'm': {
613 			format_bitmask |= OS_MGMT_INFO_FORMAT_MACHINE;
614 			++valid_formats;
615 			break;
616 		}
617 		case 'p': {
618 			format_bitmask |= OS_MGMT_INFO_FORMAT_PROCESSOR;
619 			++valid_formats;
620 			break;
621 		}
622 		case 'i': {
623 			format_bitmask |= OS_MGMT_INFO_FORMAT_HARDWARE_PLATFORM;
624 			++valid_formats;
625 			break;
626 		}
627 		case 'o': {
628 			format_bitmask |= OS_MGMT_INFO_FORMAT_OPERATING_SYSTEM;
629 			++valid_formats;
630 			break;
631 		}
632 		default: {
633 			break;
634 		}
635 		}
636 
637 		++i;
638 	}
639 
640 #ifdef CONFIG_MCUMGR_GRP_OS_INFO_CUSTOM_HOOKS
641 	/* Run callbacks to see if any additional handlers will add options */
642 	(void)mgmt_callback_notify(MGMT_EVT_OP_OS_MGMT_INFO_CHECK, &check_data,
643 				   sizeof(check_data), &err_rc, &err_group);
644 #endif
645 
646 	if (valid_formats != format.len) {
647 		/* A provided format specifier is not valid */
648 		bool ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_OS, OS_MGMT_ERR_INVALID_FORMAT);
649 
650 		return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
651 	} else if (format_bitmask == 0) {
652 		/* If no value is provided, use default of kernel name */
653 		format_bitmask = OS_MGMT_INFO_FORMAT_KERNEL_NAME;
654 	}
655 
656 	/* Process all options in order and append to output string */
657 	if (format_bitmask & OS_MGMT_INFO_FORMAT_KERNEL_NAME) {
658 		rc = snprintf(output, (sizeof(output) - output_length), "Zephyr");
659 
660 		if (rc < 0 || rc >= (sizeof(output) - output_length)) {
661 			goto fail;
662 		} else {
663 			output_length += (uint16_t)rc;
664 		}
665 
666 		prior_output = true;
667 	}
668 
669 	if (format_bitmask & OS_MGMT_INFO_FORMAT_NODE_NAME) {
670 		/* Get hostname, if enabled */
671 #if defined(CONFIG_NET_HOSTNAME_ENABLE)
672 		/* From network */
673 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
674 			      (prior_output == true ? " %s" : "%s"), net_hostname_get());
675 #elif defined(CONFIG_BT)
676 		/* From Bluetooth */
677 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
678 			      (prior_output == true ? " %s" : "%s"), bt_get_name());
679 #else
680 		/* Not available */
681 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
682 			      "%sunknown", (prior_output == true ? " " : ""));
683 #endif
684 
685 		if (rc < 0 || rc >= (sizeof(output) - output_length)) {
686 			goto fail;
687 		} else {
688 			output_length += (uint16_t)rc;
689 		}
690 
691 		prior_output = true;
692 		format_bitmask &= ~OS_MGMT_INFO_FORMAT_NODE_NAME;
693 	}
694 
695 	if (format_bitmask & OS_MGMT_INFO_FORMAT_KERNEL_RELEASE) {
696 #ifdef BUILD_VERSION
697 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
698 			      (prior_output == true ? " %s" : "%s"), STRINGIFY(BUILD_VERSION));
699 #else
700 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
701 			      "%sunknown", (prior_output == true ? " " : ""));
702 #endif
703 
704 		if (rc < 0 || rc >= (sizeof(output) - output_length)) {
705 			goto fail;
706 		} else {
707 			output_length += (uint16_t)rc;
708 		}
709 
710 		prior_output = true;
711 		format_bitmask &= ~OS_MGMT_INFO_FORMAT_KERNEL_RELEASE;
712 	}
713 
714 	if (format_bitmask & OS_MGMT_INFO_FORMAT_KERNEL_VERSION) {
715 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
716 			      (prior_output == true ? " %s" : "%s"), KERNEL_VERSION_STRING);
717 
718 		if (rc < 0 || rc >= (sizeof(output) - output_length)) {
719 			goto fail;
720 		} else {
721 			output_length += (uint16_t)rc;
722 		}
723 
724 		prior_output = true;
725 		format_bitmask &= ~OS_MGMT_INFO_FORMAT_KERNEL_VERSION;
726 	}
727 
728 #ifdef CONFIG_MCUMGR_GRP_OS_INFO_BUILD_DATE_TIME
729 	if (format_bitmask & OS_MGMT_INFO_FORMAT_BUILD_DATE_TIME) {
730 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
731 			      (prior_output == true ? " %s" : "%s"),
732 			      MCUMGR_GRP_OS_INFO_BUILD_DATE_TIME);
733 
734 		if (rc < 0 || rc >= (sizeof(output) - output_length)) {
735 			goto fail;
736 		} else {
737 			output_length += (uint16_t)rc;
738 		}
739 
740 		prior_output = true;
741 		format_bitmask &= ~OS_MGMT_INFO_FORMAT_BUILD_DATE_TIME;
742 	}
743 #endif
744 
745 	if (format_bitmask & OS_MGMT_INFO_FORMAT_MACHINE) {
746 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
747 			      (prior_output == true ? " %s" : "%s"), CONFIG_ARCH);
748 
749 		if (rc < 0 || rc >= (sizeof(output) - output_length)) {
750 			goto fail;
751 		} else {
752 			output_length += (uint16_t)rc;
753 		}
754 
755 		prior_output = true;
756 		format_bitmask &= ~OS_MGMT_INFO_FORMAT_MACHINE;
757 	}
758 
759 	if (format_bitmask & OS_MGMT_INFO_FORMAT_PROCESSOR) {
760 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
761 			      (prior_output == true ? " %s" : "%s"), PROCESSOR_NAME);
762 
763 		if (rc < 0 || rc >= (sizeof(output) - output_length)) {
764 			goto fail;
765 		} else {
766 			output_length += (uint16_t)rc;
767 		}
768 
769 		prior_output = true;
770 		format_bitmask &= ~OS_MGMT_INFO_FORMAT_PROCESSOR;
771 	}
772 
773 	if (format_bitmask & OS_MGMT_INFO_FORMAT_HARDWARE_PLATFORM) {
774 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
775 			      (prior_output == true ? " %s%s%s" : "%s%s%s"), CONFIG_BOARD,
776 			      (sizeof(CONFIG_BOARD_REVISION) > 1 ? "@" : ""),
777 			      CONFIG_BOARD_REVISION);
778 
779 		if (rc < 0 || rc >= (sizeof(output) - output_length)) {
780 			goto fail;
781 		} else {
782 			output_length += (uint16_t)rc;
783 		}
784 
785 		prior_output = true;
786 		format_bitmask &= ~OS_MGMT_INFO_FORMAT_HARDWARE_PLATFORM;
787 	}
788 
789 	/* If custom_os_name is not set (by extension code) then return the default OS name of
790 	 * Zephyr
791 	 */
792 	if (format_bitmask & OS_MGMT_INFO_FORMAT_OPERATING_SYSTEM && custom_os_name == false) {
793 		rc = snprintf(&output[output_length], (sizeof(output) - output_length),
794 			      "%sZephyr", (prior_output == true ? " " : ""));
795 
796 		if (rc < 0 || rc >= (sizeof(output) - output_length)) {
797 			goto fail;
798 		} else {
799 			output_length += (uint16_t)rc;
800 		}
801 
802 		prior_output = true;
803 		format_bitmask &= ~OS_MGMT_INFO_FORMAT_OPERATING_SYSTEM;
804 	}
805 
806 #ifdef CONFIG_MCUMGR_GRP_OS_INFO_CUSTOM_HOOKS
807 	/* Call custom handler command for additional output/processing */
808 	status = mgmt_callback_notify(MGMT_EVT_OP_OS_MGMT_INFO_APPEND, &append_data,
809 				      sizeof(append_data), &err_rc, &err_group);
810 
811 	if (status != MGMT_CB_OK) {
812 		bool ok;
813 
814 		if (status == MGMT_CB_ERROR_RC) {
815 			return err_rc;
816 		}
817 
818 		ok = smp_add_cmd_err(zse, err_group, (uint16_t)err_rc);
819 		return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
820 	}
821 #endif
822 
823 	if (zcbor_tstr_put_lit(zse, "output") &&
824 	    zcbor_tstr_encode_ptr(zse, output, output_length)) {
825 		return MGMT_ERR_EOK;
826 	}
827 
828 fail:
829 	return MGMT_ERR_EMSGSIZE;
830 }
831 #endif
832 
833 #ifdef CONFIG_MCUMGR_GRP_OS_DATETIME
834 /**
835  * Command handler: os datetime get
836  */
os_mgmt_datetime_read(struct smp_streamer * ctxt)837 static int os_mgmt_datetime_read(struct smp_streamer *ctxt)
838 {
839 	zcbor_state_t *zse = ctxt->writer->zs;
840 	struct rtc_time current_time;
841 	char date_string[RTC_DATETIME_STRING_SIZE];
842 	int rc;
843 	bool ok;
844 
845 #if defined(CONFIG_MCUMGR_GRP_OS_DATETIME_HOOK)
846 	enum mgmt_cb_return status;
847 	int32_t err_rc;
848 	uint16_t err_group;
849 
850 	status = mgmt_callback_notify(MGMT_EVT_OP_OS_MGMT_DATETIME_GET, NULL, 0, &err_rc,
851 				      &err_group);
852 
853 	if (status != MGMT_CB_OK) {
854 		if (status == MGMT_CB_ERROR_RC) {
855 			return err_rc;
856 		}
857 
858 		ok = smp_add_cmd_err(zse, err_group, (uint16_t)err_rc);
859 		return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
860 	}
861 #endif
862 
863 	rc = rtc_get_time(RTC_DEVICE, &current_time);
864 
865 	if (rc == -ENODATA) {
866 		/* RTC not set */
867 		ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_OS, OS_MGMT_ERR_RTC_NOT_SET);
868 		goto finished;
869 	} else if (rc != 0) {
870 		/* Other RTC error */
871 		ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_OS, OS_MGMT_ERR_RTC_COMMAND_FAILED);
872 		goto finished;
873 	}
874 
875 	sprintf(date_string, "%4d-%02d-%02dT%02d:%02d:%02d"
876 #ifdef CONFIG_MCUMGR_GRP_OS_DATETIME_MS
877 		".%03d"
878 #endif
879 		, (uint16_t)(current_time.tm_year + RTC_DATETIME_YEAR_OFFSET),
880 		(uint8_t)(current_time.tm_mon + RTC_DATETIME_MONTH_OFFSET),
881 		(uint8_t)current_time.tm_mday, (uint8_t)current_time.tm_hour,
882 		(uint8_t)current_time.tm_min, (uint8_t)current_time.tm_sec
883 #ifdef CONFIG_MCUMGR_GRP_OS_DATETIME_MS
884 		, (uint16_t)(current_time.tm_nsec / RTC_DATETIME_MS_TO_NS)
885 #endif
886 	);
887 
888 	ok = zcbor_tstr_put_lit(zse, "datetime")				&&
889 	     zcbor_tstr_encode_ptr(zse, date_string, strlen(date_string));
890 
891 finished:
892 	return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
893 }
894 
895 /**
896  * Command handler: os datetime set
897  */
os_mgmt_datetime_write(struct smp_streamer * ctxt)898 static int os_mgmt_datetime_write(struct smp_streamer *ctxt)
899 {
900 	zcbor_state_t *zsd = ctxt->reader->zs;
901 	zcbor_state_t *zse = ctxt->writer->zs;
902 	size_t decoded;
903 	struct zcbor_string datetime = { 0 };
904 	int rc;
905 	uint8_t i = 0;
906 	bool ok = true;
907 	char *pos;
908 	char *new_pos;
909 	char date_string[RTC_DATETIME_MAX_STRING_SIZE];
910 	struct rtc_time new_time = {
911 		.tm_wday = -1,
912 		.tm_yday = -1,
913 		.tm_isdst = -1,
914 		.tm_nsec = 0,
915 	};
916 	struct datetime_parser parser[] = {
917 		{
918 			.value = &new_time.tm_year,
919 			.min_value = RTC_DATETIME_YEAR_MIN,
920 			.max_value = RTC_DATETIME_YEAR_MAX,
921 			.offset = -RTC_DATETIME_YEAR_OFFSET,
922 		},
923 		{
924 			.value = &new_time.tm_mon,
925 			.min_value = RTC_DATETIME_MONTH_MIN,
926 			.max_value = RTC_DATETIME_MONTH_MAX,
927 			.offset = -RTC_DATETIME_MONTH_OFFSET,
928 		},
929 		{
930 			.value = &new_time.tm_mday,
931 			.min_value = RTC_DATETIME_DAY_MIN,
932 			.max_value = RTC_DATETIME_DAY_MAX,
933 		},
934 		{
935 			.value = &new_time.tm_hour,
936 			.min_value = RTC_DATETIME_HOUR_MIN,
937 			.max_value = RTC_DATETIME_HOUR_MAX,
938 		},
939 		{
940 			.value = &new_time.tm_min,
941 			.min_value = RTC_DATETIME_MINUTE_MIN,
942 			.max_value = RTC_DATETIME_MINUTE_MAX,
943 		},
944 		{
945 			.value = &new_time.tm_sec,
946 			.min_value = RTC_DATETIME_SECOND_MIN,
947 			.max_value = RTC_DATETIME_SECOND_MAX,
948 		},
949 	};
950 
951 #if defined(CONFIG_MCUMGR_GRP_OS_DATETIME_HOOK)
952 	enum mgmt_cb_return status;
953 	int32_t err_rc;
954 	uint16_t err_group;
955 #endif
956 
957 	struct zcbor_map_decode_key_val datetime_decode[] = {
958 		ZCBOR_MAP_DECODE_KEY_DECODER("datetime", zcbor_tstr_decode, &datetime),
959 	};
960 
961 	if (zcbor_map_decode_bulk(zsd, datetime_decode, ARRAY_SIZE(datetime_decode), &decoded)) {
962 		return MGMT_ERR_EINVAL;
963 	} else if (datetime.len < RTC_DATETIME_MIN_STRING_SIZE ||
964 		   datetime.len >= RTC_DATETIME_MAX_STRING_SIZE) {
965 		return MGMT_ERR_EINVAL;
966 	}
967 
968 	memcpy(date_string, datetime.value, datetime.len);
969 	date_string[datetime.len] = '\0';
970 
971 	pos = date_string;
972 
973 	while (i < ARRAY_SIZE(parser)) {
974 		if (pos == (date_string + datetime.len)) {
975 			/* Encountered end of string early, this is invalid */
976 			return MGMT_ERR_EINVAL;
977 		}
978 
979 		*parser[i].value = strtol(pos, &new_pos, RTC_DATETIME_NUMERIC_BASE);
980 
981 		if (pos == new_pos) {
982 			/* Missing or unable to convert field */
983 			return MGMT_ERR_EINVAL;
984 		}
985 
986 		if (*parser[i].value < parser[i].min_value ||
987 		    *parser[i].value > parser[i].max_value) {
988 			/* Value is not within the allowed bounds of this field */
989 			return MGMT_ERR_EINVAL;
990 		}
991 
992 		*parser[i].value += parser[i].offset;
993 
994 		/* Skip a character as there is always a delimiter between the fields */
995 		++i;
996 		pos = new_pos + 1;
997 	}
998 
999 #ifdef CONFIG_MCUMGR_GRP_OS_DATETIME_MS
1000 	if (*(pos - 1) == '.' && *pos != '\0') {
1001 		/* Provided value has a ms value, extract it */
1002 		new_time.tm_nsec = strtol(pos, &new_pos, RTC_DATETIME_NUMERIC_BASE);
1003 
1004 		if (new_time.tm_nsec < RTC_DATETIME_MILLISECOND_MIN ||
1005 		    new_time.tm_nsec > RTC_DATETIME_MILLISECOND_MAX) {
1006 			return MGMT_ERR_EINVAL;
1007 		}
1008 
1009 		new_time.tm_nsec *= RTC_DATETIME_MS_TO_NS;
1010 	}
1011 #endif
1012 
1013 #if defined(CONFIG_MCUMGR_GRP_OS_DATETIME_HOOK)
1014 	status = mgmt_callback_notify(MGMT_EVT_OP_OS_MGMT_DATETIME_SET, &new_time,
1015 				      sizeof(new_time), &err_rc, &err_group);
1016 
1017 	if (status != MGMT_CB_OK) {
1018 		if (status == MGMT_CB_ERROR_RC) {
1019 			return err_rc;
1020 		}
1021 
1022 		ok = smp_add_cmd_err(zse, err_group, (uint16_t)err_rc);
1023 		return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
1024 	}
1025 #endif
1026 
1027 	rc = rtc_set_time(RTC_DEVICE, &new_time);
1028 
1029 	if (rc != 0) {
1030 		ok = smp_add_cmd_err(zse, MGMT_GROUP_ID_OS, OS_MGMT_ERR_RTC_COMMAND_FAILED);
1031 	}
1032 
1033 	return ok ? MGMT_ERR_EOK : MGMT_ERR_EMSGSIZE;
1034 }
1035 #endif
1036 
1037 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
1038 /*
1039  * @brief	Translate OS mgmt group error code into MCUmgr error code
1040  *
1041  * @param ret	#os_mgmt_err_code_t error code
1042  *
1043  * @return	#mcumgr_err_t error code
1044  */
os_mgmt_translate_error_code(uint16_t err)1045 static int os_mgmt_translate_error_code(uint16_t err)
1046 {
1047 	int rc;
1048 
1049 	switch (err) {
1050 	case OS_MGMT_ERR_INVALID_FORMAT:
1051 		rc = MGMT_ERR_EINVAL;
1052 		break;
1053 
1054 	case OS_MGMT_ERR_QUERY_YIELDS_NO_ANSWER:
1055 	case OS_MGMT_ERR_RTC_NOT_SET:
1056 	case OS_MGMT_ERR_QUERY_RESPONSE_VALUE_NOT_VALID:
1057 		rc = MGMT_ERR_ENOENT;
1058 		break;
1059 
1060 	case OS_MGMT_ERR_UNKNOWN:
1061 	case OS_MGMT_ERR_RTC_COMMAND_FAILED:
1062 	default:
1063 		rc = MGMT_ERR_EUNKNOWN;
1064 	}
1065 
1066 	return rc;
1067 }
1068 #endif
1069 
1070 static const struct mgmt_handler os_mgmt_group_handlers[] = {
1071 #ifdef CONFIG_MCUMGR_GRP_OS_ECHO
1072 	[OS_MGMT_ID_ECHO] = {
1073 		os_mgmt_echo, os_mgmt_echo
1074 	},
1075 #endif
1076 #ifdef CONFIG_MCUMGR_GRP_OS_TASKSTAT
1077 	[OS_MGMT_ID_TASKSTAT] = {
1078 		os_mgmt_taskstat_read, NULL
1079 	},
1080 #endif
1081 
1082 #ifdef CONFIG_MCUMGR_GRP_OS_DATETIME
1083 	[OS_MGMT_ID_DATETIME_STR] = {
1084 		os_mgmt_datetime_read, os_mgmt_datetime_write
1085 	},
1086 #endif
1087 
1088 #ifdef CONFIG_REBOOT
1089 	[OS_MGMT_ID_RESET] = {
1090 		NULL, os_mgmt_reset
1091 	},
1092 #endif
1093 #ifdef CONFIG_MCUMGR_GRP_OS_MCUMGR_PARAMS
1094 	[OS_MGMT_ID_MCUMGR_PARAMS] = {
1095 		os_mgmt_mcumgr_params, NULL
1096 	},
1097 #endif
1098 #ifdef CONFIG_MCUMGR_GRP_OS_INFO
1099 	[OS_MGMT_ID_INFO] = {
1100 		os_mgmt_info, NULL
1101 	},
1102 #endif
1103 #ifdef CONFIG_MCUMGR_GRP_OS_BOOTLOADER_INFO
1104 	[OS_MGMT_ID_BOOTLOADER_INFO] = {
1105 		os_mgmt_bootloader_info, NULL
1106 	},
1107 #endif
1108 };
1109 
1110 #define OS_MGMT_GROUP_SZ ARRAY_SIZE(os_mgmt_group_handlers)
1111 
1112 static struct mgmt_group os_mgmt_group = {
1113 	.mg_handlers = os_mgmt_group_handlers,
1114 	.mg_handlers_count = OS_MGMT_GROUP_SZ,
1115 	.mg_group_id = MGMT_GROUP_ID_OS,
1116 #ifdef CONFIG_MCUMGR_SMP_SUPPORT_ORIGINAL_PROTOCOL
1117 	.mg_translate_error = os_mgmt_translate_error_code,
1118 #endif
1119 #ifdef CONFIG_MCUMGR_GRP_ENUM_DETAILS_NAME
1120 	.mg_group_name = "os mgmt",
1121 #endif
1122 };
1123 
os_mgmt_register_group(void)1124 static void os_mgmt_register_group(void)
1125 {
1126 	mgmt_register_group(&os_mgmt_group);
1127 }
1128 
1129 MCUMGR_HANDLER_DEFINE(os_mgmt, os_mgmt_register_group);
1130