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, ¤t_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