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