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