1 /*
2 * Copyright (c) 2020 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log_backend.h>
8 #include <zephyr/logging/log_backend_std.h>
9 #include <zephyr/logging/log_output.h>
10 #include <openthread/platform/logging.h>
11 #include <utils/uart.h>
12 #include <platform-zephyr.h>
13
14 #ifndef CONFIG_LOG_BACKEND_SPINEL_BUFFER_SIZE
15 #define CONFIG_LOG_BACKEND_SPINEL_BUFFER_SIZE 0
16 #endif
17
18 static uint8_t char_buf[CONFIG_LOG_BACKEND_SPINEL_BUFFER_SIZE];
19 static bool panic_mode;
20 static uint16_t last_log_level;
21 static uint32_t log_format_current = CONFIG_LOG_BACKEND_SPINEL_OUTPUT_DEFAULT;
22
23 static int write(uint8_t *data, size_t length, void *ctx);
24
25 LOG_OUTPUT_DEFINE(log_output_spinel, write, char_buf, sizeof(char_buf));
26
is_panic_mode(void)27 static inline bool is_panic_mode(void)
28 {
29 return panic_mode;
30 }
31
process(const struct log_backend * const backend,union log_msg_generic * msg)32 static void process(const struct log_backend *const backend,
33 union log_msg_generic *msg)
34 {
35 /* prevent adding CRLF, which may crash spinel decoding */
36 uint32_t flags = LOG_OUTPUT_FLAG_CRLF_NONE | log_backend_std_get_flags();
37
38 log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
39
40 log_output_func(&log_output_spinel, &msg->log, flags);
41 }
42
format_set(const struct log_backend * const backend,uint32_t log_type)43 static int format_set(const struct log_backend *const backend, uint32_t log_type)
44 {
45 log_format_current = log_type;
46 return 0;
47 }
48
log_backend_spinel_init(struct log_backend const * const backend)49 static void log_backend_spinel_init(struct log_backend const *const backend)
50 {
51 memset(char_buf, '\0', sizeof(char_buf));
52 }
53
panic(struct log_backend const * const backend)54 static void panic(struct log_backend const *const backend)
55 {
56 ARG_UNUSED(backend);
57 panic_mode = true;
58 }
59
dropped(const struct log_backend * const backend,uint32_t cnt)60 static void dropped(const struct log_backend *const backend, uint32_t cnt)
61 {
62 ARG_UNUSED(backend);
63
64 log_backend_std_dropped(&log_output_spinel, cnt);
65 }
66
write(uint8_t * data,size_t length,void * ctx)67 static int write(uint8_t *data, size_t length, void *ctx)
68 {
69 otLogLevel log_level;
70
71 if (is_panic_mode()) {
72 /* In panic mode otPlatLog implemented for Spinel protocol
73 * cannot be used, because it cannot be called from interrupt.
74 * In such situation raw data bytes without encoding are send.
75 */
76 platformUartPanic();
77 otPlatUartSend(data, length);
78 } else {
79 switch (last_log_level) {
80 case LOG_LEVEL_ERR:
81 log_level = OT_LOG_LEVEL_CRIT;
82 break;
83 case LOG_LEVEL_WRN:
84 log_level = OT_LOG_LEVEL_WARN;
85 break;
86 case LOG_LEVEL_INF:
87 log_level = OT_LOG_LEVEL_INFO;
88 break;
89 case LOG_LEVEL_DBG:
90 log_level = OT_LOG_LEVEL_DEBG;
91 break;
92 default:
93 log_level = OT_LOG_LEVEL_NONE;
94 break;
95 }
96 otPlatLog(log_level, OT_LOG_REGION_PLATFORM, "%s", data);
97 }
98
99 /* make sure that buffer will be clean in next attempt */
100 memset(char_buf, '\0', length);
101 return length;
102 }
103
104 const struct log_backend_api log_backend_spinel_api = {
105 .process = process,
106 .panic = panic,
107 .init = log_backend_spinel_init,
108 .dropped = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? NULL : dropped,
109 .format_set = format_set,
110 };
111
112 LOG_BACKEND_DEFINE(log_backend_spinel, log_backend_spinel_api, true);
113