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