1 /*
2 * Copyright (c) 2018 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_core.h>
9 #include <zephyr/logging/log_output.h>
10 #include <zephyr/logging/log_output_dict.h>
11 #include <zephyr/logging/log_backend_std.h>
12 #include <zephyr/logging/log.h>
13 #include <zephyr/device.h>
14 #include <zephyr/drivers/uart.h>
15 #include <zephyr/sys/util_macro.h>
16 #include <zephyr/sys/__assert.h>
17 #include <zephyr/pm/device.h>
18 #include <zephyr/pm/device_runtime.h>
19 LOG_MODULE_REGISTER(log_uart);
20
21 /* Fixed size to avoid auto-added trailing '\0'.
22 * Used if CONFIG_LOG_BACKEND_UART_OUTPUT_DICTIONARY_HEX.
23 */
24 static const char LOG_HEX_SEP[10] = "##ZLOGV1##";
25
26 static const struct device *const uart_dev =
27 DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
28 static struct k_sem sem;
29 static volatile bool in_panic;
30 static bool use_async;
31 static uint32_t log_format_current = CONFIG_LOG_BACKEND_UART_OUTPUT_DEFAULT;
32
uart_callback(const struct device * dev,struct uart_event * evt,void * user_data)33 static void uart_callback(const struct device *dev,
34 struct uart_event *evt,
35 void *user_data)
36 {
37 switch (evt->type) {
38 case UART_TX_DONE:
39 k_sem_give(&sem);
40 break;
41 default:
42 break;
43 }
44 }
45
dict_char_out_hex(uint8_t * data,size_t length)46 static void dict_char_out_hex(uint8_t *data, size_t length)
47 {
48 for (size_t i = 0; i < length; i++) {
49 char c;
50 uint8_t x;
51
52 /* upper 8-bit */
53 x = data[i] >> 4;
54 (void)hex2char(x, &c);
55 uart_poll_out(uart_dev, c);
56
57 /* lower 8-bit */
58 x = data[i] & 0x0FU;
59 (void)hex2char(x, &c);
60 uart_poll_out(uart_dev, c);
61 }
62 }
63
char_out(uint8_t * data,size_t length,void * ctx)64 static int char_out(uint8_t *data, size_t length, void *ctx)
65 {
66 ARG_UNUSED(ctx);
67 int err;
68
69 if (pm_device_runtime_is_enabled(uart_dev) && !k_is_in_isr()) {
70 if (pm_device_runtime_get(uart_dev) < 0) {
71 /* Enabling the UART instance has failed but this
72 * function MUST return the number of bytes consumed.
73 */
74 return length;
75 }
76 }
77
78 if (IS_ENABLED(CONFIG_LOG_BACKEND_UART_OUTPUT_DICTIONARY_HEX)) {
79 dict_char_out_hex(data, length);
80 goto cleanup;
81 }
82
83 if (!IS_ENABLED(CONFIG_LOG_BACKEND_UART_ASYNC) || in_panic || !use_async) {
84 for (size_t i = 0; i < length; i++) {
85 uart_poll_out(uart_dev, data[i]);
86 }
87 goto cleanup;
88 }
89
90 err = uart_tx(uart_dev, data, length, SYS_FOREVER_US);
91 __ASSERT_NO_MSG(err == 0);
92
93 err = k_sem_take(&sem, K_FOREVER);
94 __ASSERT_NO_MSG(err == 0);
95
96 (void)err;
97 cleanup:
98 if (pm_device_runtime_is_enabled(uart_dev) && !k_is_in_isr()) {
99 /* As errors cannot be returned, ignore the return value */
100 (void)pm_device_runtime_put(uart_dev);
101 }
102
103 return length;
104 }
105
106 static uint8_t uart_output_buf[CONFIG_LOG_BACKEND_UART_BUFFER_SIZE];
107 LOG_OUTPUT_DEFINE(log_output_uart, char_out, uart_output_buf, sizeof(uart_output_buf));
108
process(const struct log_backend * const backend,union log_msg_generic * msg)109 static void process(const struct log_backend *const backend,
110 union log_msg_generic *msg)
111 {
112 uint32_t flags = log_backend_std_get_flags();
113
114 log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
115
116 log_output_func(&log_output_uart, &msg->log, flags);
117 }
118
format_set(const struct log_backend * const backend,uint32_t log_type)119 static int format_set(const struct log_backend *const backend, uint32_t log_type)
120 {
121 log_format_current = log_type;
122 return 0;
123 }
124
log_backend_uart_init(struct log_backend const * const backend)125 static void log_backend_uart_init(struct log_backend const *const backend)
126 {
127 __ASSERT_NO_MSG(device_is_ready(uart_dev));
128
129 if (IS_ENABLED(CONFIG_LOG_BACKEND_UART_OUTPUT_DICTIONARY_HEX)) {
130 /* Print a separator so the output can be fed into
131 * log parser directly. This is useful when capturing
132 * from UART directly where there might be other output
133 * (e.g. bootloader).
134 */
135 for (int i = 0; i < sizeof(LOG_HEX_SEP); i++) {
136 uart_poll_out(uart_dev, LOG_HEX_SEP[i]);
137 }
138
139 return;
140 }
141
142 if (IS_ENABLED(CONFIG_LOG_BACKEND_UART_ASYNC)) {
143 int err = uart_callback_set(uart_dev, uart_callback, NULL);
144
145 if (err == 0) {
146 use_async = true;
147 k_sem_init(&sem, 0, 1);
148 } else {
149 LOG_WRN("Failed to initialize asynchronous mode (err:%d). "
150 "Fallback to polling.", err);
151 }
152 }
153 }
154
panic(struct log_backend const * const backend)155 static void panic(struct log_backend const *const backend)
156 {
157 /* Ensure that the UART device is in active mode */
158 #if defined(CONFIG_PM_DEVICE_RUNTIME)
159 if (pm_device_runtime_is_enabled(uart_dev)) {
160 if (k_is_in_isr()) {
161 /* pm_device_runtime_get cannot be used from ISRs */
162 pm_device_action_run(uart_dev, PM_DEVICE_ACTION_RESUME);
163 } else {
164 pm_device_runtime_get(uart_dev);
165 }
166 }
167 #elif defined(CONFIG_PM_DEVICE)
168 enum pm_device_state pm_state;
169 int rc;
170
171 rc = pm_device_state_get(uart_dev, &pm_state);
172 if ((rc == 0) && (pm_state == PM_DEVICE_STATE_SUSPENDED)) {
173 pm_device_action_run(uart_dev, PM_DEVICE_ACTION_RESUME);
174 }
175 #endif /* CONFIG_PM_DEVICE */
176
177 in_panic = true;
178 log_backend_std_panic(&log_output_uart);
179 }
180
dropped(const struct log_backend * const backend,uint32_t cnt)181 static void dropped(const struct log_backend *const backend, uint32_t cnt)
182 {
183 ARG_UNUSED(backend);
184
185 if (IS_ENABLED(CONFIG_LOG_BACKEND_UART_OUTPUT_DICTIONARY)) {
186 log_dict_output_dropped_process(&log_output_uart, cnt);
187 } else {
188 log_backend_std_dropped(&log_output_uart, cnt);
189 }
190 }
191
192 const struct log_backend_api log_backend_uart_api = {
193 .process = process,
194 .panic = panic,
195 .init = log_backend_uart_init,
196 .dropped = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? NULL : dropped,
197 .format_set = format_set,
198 };
199
200 LOG_BACKEND_DEFINE(log_backend_uart, log_backend_uart_api,
201 IS_ENABLED(CONFIG_LOG_BACKEND_UART_AUTOSTART));
202