1 /*
2  * Copyright (c) 2018 omSquare s.r.o.
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_backend_std.h>
11 #include <SEGGER_RTT.h>
12 
13 #ifndef CONFIG_LOG_BACKEND_RTT_BUFFER_SIZE
14 #define CONFIG_LOG_BACKEND_RTT_BUFFER_SIZE 0
15 #endif
16 
17 #ifndef CONFIG_LOG_BACKEND_RTT_MESSAGE_SIZE
18 #define CONFIG_LOG_BACKEND_RTT_MESSAGE_SIZE 0
19 #endif
20 
21 #ifndef CONFIG_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE
22 #define CONFIG_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE 0
23 #endif
24 
25 #ifndef CONFIG_LOG_BACKEND_RTT_RETRY_DELAY_MS
26 /* Long enough to detect host presence */
27 #define CONFIG_LOG_BACKEND_RTT_RETRY_DELAY_MS 10
28 #endif
29 
30 #ifndef CONFIG_LOG_BACKEND_RTT_RETRY_CNT
31 /* Big enough to detect host presence */
32 #define CONFIG_LOG_BACKEND_RTT_RETRY_CNT 10
33 #endif
34 
35 #if defined(CONFIG_LOG_BACKEND_RTT_OUTPUT_DICTIONARY)
36 static const uint8_t LOG_HEX_SEP[10] = "##ZLOGV1##";
37 #endif
38 
39 #define DROP_MAX 99
40 
41 #define DROP_MSG "messages dropped:    \r\n"
42 
43 #define DROP_MSG_LEN (sizeof(DROP_MSG) - 1)
44 
45 #define MESSAGE_SIZE CONFIG_LOG_BACKEND_RTT_MESSAGE_SIZE
46 
47 #define CHAR_BUF_SIZE \
48 	((IS_ENABLED(CONFIG_LOG_BACKEND_RTT_MODE_BLOCK) && \
49 	 !IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE)) ? \
50 		CONFIG_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE : 1)
51 
52 #define RTT_LOCK() \
53 	COND_CODE_0(CONFIG_LOG_BACKEND_RTT_BUFFER, (SEGGER_RTT_LOCK()), ())
54 
55 #define RTT_UNLOCK() \
56 	COND_CODE_0(CONFIG_LOG_BACKEND_RTT_BUFFER, (SEGGER_RTT_UNLOCK()), ())
57 
58 #define RTT_BUFFER_SIZE \
59 	COND_CODE_0(CONFIG_LOG_BACKEND_RTT_BUFFER, \
60 		(0), (CONFIG_LOG_BACKEND_RTT_BUFFER_SIZE))
61 
62 
63 static const char *drop_msg = DROP_MSG;
64 static uint8_t rtt_buf[RTT_BUFFER_SIZE];
65 static uint8_t line_buf[MESSAGE_SIZE + DROP_MSG_LEN];
66 static uint8_t *line_pos;
67 static uint8_t char_buf[CHAR_BUF_SIZE];
68 static int drop_cnt;
69 static int drop_warn;
70 static bool panic_mode;
71 static bool host_present;
72 
73 static int data_out_block_mode(uint8_t *data, size_t length, void *ctx);
74 static int data_out_drop_mode(uint8_t *data, size_t length, void *ctx);
75 
76 static int char_out_drop_mode(uint8_t data);
77 static int line_out_drop_mode(void);
78 static uint32_t log_format_current = CONFIG_LOG_BACKEND_RTT_OUTPUT_DEFAULT;
79 
is_sync_mode(void)80 static inline bool is_sync_mode(void)
81 {
82 	return IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) || panic_mode;
83 }
84 
is_panic_mode(void)85 static inline bool is_panic_mode(void)
86 {
87 	return panic_mode;
88 }
89 
data_out_drop_mode(uint8_t * data,size_t length,void * ctx)90 static int data_out_drop_mode(uint8_t *data, size_t length, void *ctx)
91 {
92 	(void) ctx;
93 	uint8_t *pos;
94 
95 	if (is_sync_mode()) {
96 		return data_out_block_mode(data, length, ctx);
97 	}
98 
99 	for (pos = data; pos < data + length; pos++) {
100 		if (char_out_drop_mode(*pos)) {
101 			break;
102 		}
103 	}
104 
105 	return (int) (pos - data);
106 }
107 
char_out_drop_mode(uint8_t data)108 static int char_out_drop_mode(uint8_t data)
109 {
110 	if (data == '\n') {
111 		if (line_out_drop_mode()) {
112 			return 1;
113 		}
114 		line_pos = line_buf;
115 		return 0;
116 	}
117 
118 	if (line_pos < line_buf + MESSAGE_SIZE - 1) {
119 		*line_pos++ = data;
120 	}
121 
122 	/* not enough space in line buffer, we have to wait for EOL */
123 	return 0;
124 }
125 
line_out_drop_mode(void)126 static int line_out_drop_mode(void)
127 {
128 	/* line cannot be empty */
129 	__ASSERT_NO_MSG(line_pos > line_buf);
130 
131 	/* Handle the case if line contains only '\n' */
132 	if (line_pos - line_buf == 1) {
133 		line_pos++;
134 	}
135 
136 	*(line_pos - 1) = '\r';
137 	*line_pos++ = '\n';
138 
139 	if (drop_cnt > 0 && !drop_warn) {
140 		int cnt = MIN(drop_cnt, DROP_MAX);
141 
142 		__ASSERT_NO_MSG(line_pos - line_buf <= MESSAGE_SIZE);
143 
144 		memmove(line_buf + DROP_MSG_LEN, line_buf, line_pos - line_buf);
145 		(void)memcpy(line_buf, drop_msg, DROP_MSG_LEN);
146 		line_pos += DROP_MSG_LEN;
147 		drop_warn = 1;
148 
149 
150 		if (cnt < 10) {
151 			line_buf[DROP_MSG_LEN - 2] = ' ';
152 			line_buf[DROP_MSG_LEN - 3] = (uint8_t) ('0' + cnt);
153 			line_buf[DROP_MSG_LEN - 4] = ' ';
154 		} else {
155 			line_buf[DROP_MSG_LEN - 2] = (uint8_t) ('0' + cnt % 10);
156 			line_buf[DROP_MSG_LEN - 3] = (uint8_t) ('0' + cnt / 10);
157 			line_buf[DROP_MSG_LEN - 4] = '>';
158 		}
159 	}
160 
161 	int ret;
162 
163 	RTT_LOCK();
164 	ret = SEGGER_RTT_WriteSkipNoLock(CONFIG_LOG_BACKEND_RTT_BUFFER,
165 					 line_buf, line_pos - line_buf);
166 	RTT_UNLOCK();
167 
168 	if (ret == 0) {
169 		drop_cnt++;
170 	} else {
171 		drop_cnt = 0;
172 		drop_warn = 0;
173 	}
174 
175 	return 0;
176 }
177 
on_failed_write(int retry_cnt)178 static void on_failed_write(int retry_cnt)
179 {
180 	if (retry_cnt == 0) {
181 		host_present = false;
182 	} else if (is_sync_mode()) {
183 		k_busy_wait(USEC_PER_MSEC *
184 				CONFIG_LOG_BACKEND_RTT_RETRY_DELAY_MS);
185 	} else {
186 		k_msleep(CONFIG_LOG_BACKEND_RTT_RETRY_DELAY_MS);
187 	}
188 }
189 
on_write(int retry_cnt)190 static void on_write(int retry_cnt)
191 {
192 	host_present = true;
193 	if (is_panic_mode()) {
194 		/* In panic mode block on each write until host reads it. This
195 		 * way it is ensured that if system resets all messages are read
196 		 * by the host. While pending on data being read by the host we
197 		 * must also detect situation where host is disconnected.
198 		 */
199 		while (SEGGER_RTT_HasDataUp(CONFIG_LOG_BACKEND_RTT_BUFFER) &&
200 			host_present) {
201 			on_failed_write(retry_cnt--);
202 		}
203 	}
204 
205 }
206 
data_out_block_mode(uint8_t * data,size_t length,void * ctx)207 static int data_out_block_mode(uint8_t *data, size_t length, void *ctx)
208 {
209 	int ret = 0;
210 	/* This function is also called in drop mode for synchronous operation
211 	 * in that case retry is undesired */
212 	int retry_cnt = IS_ENABLED(CONFIG_LOG_BACKEND_RTT_MODE_BLOCK) ?
213 			 CONFIG_LOG_BACKEND_RTT_RETRY_CNT : 1;
214 
215 	do {
216 		if (!is_sync_mode()) {
217 			RTT_LOCK();
218 			ret = SEGGER_RTT_WriteSkipNoLock(CONFIG_LOG_BACKEND_RTT_BUFFER,
219 							 data, length);
220 			RTT_UNLOCK();
221 		} else {
222 			ret = SEGGER_RTT_WriteSkipNoLock(CONFIG_LOG_BACKEND_RTT_BUFFER,
223 							 data, length);
224 		}
225 
226 		if (ret) {
227 			on_write(retry_cnt);
228 		} else if (host_present) {
229 			retry_cnt--;
230 			on_failed_write(retry_cnt);
231 		} else {
232 		}
233 	} while ((ret == 0) && host_present);
234 
235 	return ((ret == 0) && host_present) ? 0 : length;
236 }
237 
data_out_overwrite_mode(uint8_t * data,size_t length,void * ctx)238 static int data_out_overwrite_mode(uint8_t *data, size_t length, void *ctx)
239 {
240 	if (!is_sync_mode()) {
241 		RTT_LOCK();
242 		SEGGER_RTT_WriteWithOverwriteNoLock(CONFIG_LOG_BACKEND_RTT_BUFFER,
243 						    data, length);
244 
245 		RTT_UNLOCK();
246 	} else {
247 		SEGGER_RTT_WriteWithOverwriteNoLock(CONFIG_LOG_BACKEND_RTT_BUFFER,
248 						    data, length);
249 	}
250 
251 	return length;
252 }
253 
254 static const log_output_func_t logging_func =
255 	IS_ENABLED(CONFIG_LOG_BACKEND_RTT_MODE_BLOCK)       ? data_out_block_mode
256 	: IS_ENABLED(CONFIG_LOG_BACKEND_RTT_MODE_OVERWRITE) ? data_out_overwrite_mode
257 							    : data_out_drop_mode;
258 
data_out(uint8_t * data,size_t length,void * ctx)259 static int data_out(uint8_t *data, size_t length, void *ctx)
260 {
261 #if defined(CONFIG_LOG_BACKEND_RTT_OUTPUT_DICTIONARY)
262 	for (size_t i = 0; i < length; i++) {
263 		char c[2];
264 		uint8_t x[2];
265 
266 		/* upper 8-bit */
267 		x[0] = data[i] >> 4;
268 		(void)hex2char(x[0], &c[0]);
269 
270 		/* lower 8-bit */
271 		x[1] = data[i] & 0x0FU;
272 		(void)hex2char(x[1], &c[1]);
273 		logging_func(c, sizeof(c), ctx);
274 	}
275 	return length;
276 #endif
277 
278 	return logging_func(data, length, ctx);
279 }
280 
281 LOG_OUTPUT_DEFINE(log_output_rtt, data_out, char_buf, sizeof(char_buf));
282 
log_backend_rtt_cfg(void)283 static void log_backend_rtt_cfg(void)
284 {
285 	SEGGER_RTT_ConfigUpBuffer(CONFIG_LOG_BACKEND_RTT_BUFFER, "Logger",
286 				  rtt_buf, sizeof(rtt_buf),
287 				  SEGGER_RTT_MODE_NO_BLOCK_SKIP);
288 }
289 
log_backend_rtt_init(struct log_backend const * const backend)290 static void log_backend_rtt_init(struct log_backend const *const backend)
291 {
292 	if (CONFIG_LOG_BACKEND_RTT_BUFFER > 0) {
293 		log_backend_rtt_cfg();
294 	}
295 
296 #if defined(CONFIG_LOG_BACKEND_RTT_OUTPUT_DICTIONARY)
297 	logging_func((uint8_t *)LOG_HEX_SEP, sizeof(LOG_HEX_SEP), NULL);
298 #endif
299 
300 	host_present = true;
301 	line_pos = line_buf;
302 }
303 
panic(struct log_backend const * const backend)304 static void panic(struct log_backend const *const backend)
305 {
306 	panic_mode = true;
307 	log_backend_std_panic(&log_output_rtt);
308 }
309 
dropped(const struct log_backend * const backend,uint32_t cnt)310 static void dropped(const struct log_backend *const backend, uint32_t cnt)
311 {
312 	ARG_UNUSED(backend);
313 
314 	log_backend_std_dropped(&log_output_rtt, cnt);
315 }
316 
process(const struct log_backend * const backend,union log_msg_generic * msg)317 static void process(const struct log_backend *const backend,
318 		union log_msg_generic *msg)
319 {
320 	uint32_t flags = log_backend_std_get_flags();
321 
322 	log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
323 
324 	log_output_func(&log_output_rtt, &msg->log, flags);
325 }
326 
format_set(const struct log_backend * const backend,uint32_t log_type)327 static int format_set(const struct log_backend *const backend, uint32_t log_type)
328 {
329 	log_format_current = log_type;
330 	return 0;
331 }
332 
333 const struct log_backend_api log_backend_rtt_api = {
334 	.process = process,
335 	.panic = panic,
336 	.init = log_backend_rtt_init,
337 	.dropped = IS_ENABLED(CONFIG_LOG_MODE_IMMEDIATE) ? NULL : dropped,
338 	.format_set = format_set,
339 };
340 
341 LOG_BACKEND_DEFINE(log_backend_rtt, log_backend_rtt_api, true);
342