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