Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2011-2012, 2014-2015 Wind River Systems, Inc.
3 : : *
4 : : * SPDX-License-Identifier: Apache-2.0
5 : : */
6 : :
7 : : /**
8 : : * @file
9 : : * @brief UART-driven console
10 : : *
11 : : *
12 : : * Serial console driver.
13 : : * Hooks into the printk and fputc (for printf) modules. Poll driven.
14 : : */
15 : :
16 : : #include <kernel.h>
17 : :
18 : : #include <stdio.h>
19 : : #include <zephyr/types.h>
20 : : #include <sys/__assert.h>
21 : : #include <errno.h>
22 : : #include <ctype.h>
23 : :
24 : : #include <device.h>
25 : : #include <init.h>
26 : :
27 : : #include <drivers/uart.h>
28 : : #include <drivers/console/console.h>
29 : : #include <drivers/console/uart_console.h>
30 : : #include <toolchain.h>
31 : : #include <linker/sections.h>
32 : : #include <sys/atomic.h>
33 : : #include <sys/printk.h>
34 : : #ifdef CONFIG_UART_CONSOLE_MCUMGR
35 : : #include "mgmt/mcumgr/serial.h"
36 : : #endif
37 : :
38 : : static const struct device *uart_console_dev;
39 : :
40 : : #ifdef CONFIG_UART_CONSOLE_DEBUG_SERVER_HOOKS
41 : :
42 : : static uart_console_in_debug_hook_t debug_hook_in;
43 : : void uart_console_in_debug_hook_install(uart_console_in_debug_hook_t hook)
44 : : {
45 : : debug_hook_in = hook;
46 : : }
47 : :
48 : : static UART_CONSOLE_OUT_DEBUG_HOOK_SIG(debug_hook_out_nop) {
49 : : ARG_UNUSED(c);
50 : : return !UART_CONSOLE_DEBUG_HOOK_HANDLED;
51 : : }
52 : :
53 : : static uart_console_out_debug_hook_t *debug_hook_out = debug_hook_out_nop;
54 : : void uart_console_out_debug_hook_install(uart_console_out_debug_hook_t *hook)
55 : : {
56 : : debug_hook_out = hook;
57 : : }
58 : : #define HANDLE_DEBUG_HOOK_OUT(c) \
59 : : (debug_hook_out(c) == UART_CONSOLE_DEBUG_HOOK_HANDLED)
60 : :
61 : : #endif /* CONFIG_UART_CONSOLE_DEBUG_SERVER_HOOKS */
62 : :
63 : :
64 : : #if defined(CONFIG_PRINTK) || defined(CONFIG_STDOUT_CONSOLE)
65 : : /**
66 : : *
67 : : * @brief Output one character to UART
68 : : *
69 : : * Outputs both line feed and carriage return in the case of a '\n'.
70 : : *
71 : : * @param c Character to output
72 : : *
73 : : * @return The character passed as input.
74 : : */
75 : :
76 : 208948 : static int console_out(int c)
77 : : {
78 : : #ifdef CONFIG_UART_CONSOLE_DEBUG_SERVER_HOOKS
79 : :
80 : : int handled_by_debug_server = HANDLE_DEBUG_HOOK_OUT(c);
81 : :
82 : : if (handled_by_debug_server) {
83 : : return c;
84 : : }
85 : :
86 : : #endif /* CONFIG_UART_CONSOLE_DEBUG_SERVER_HOOKS */
87 : :
88 [ + + ]: 208948 : if ('\n' == c) {
89 : 38 : uart_poll_out(uart_console_dev, '\r');
90 : : }
91 : 208948 : uart_poll_out(uart_console_dev, c);
92 : :
93 : 208948 : return c;
94 : : }
95 : :
96 : : #endif
97 : :
98 : : #if defined(CONFIG_STDOUT_CONSOLE)
99 : : extern void __stdout_hook_install(int (*hook)(int));
100 : : #endif
101 : :
102 : : #if defined(CONFIG_PRINTK)
103 : : extern void __printk_hook_install(int (*fn)(int));
104 : : #endif
105 : :
106 : : #if defined(CONFIG_CONSOLE_HANDLER)
107 : : static struct k_fifo *avail_queue;
108 : : static struct k_fifo *lines_queue;
109 : : static uint8_t (*completion_cb)(char *line, uint8_t len);
110 : :
111 : : /* Control characters */
112 : : #define BS 0x08
113 : : #define ESC 0x1b
114 : : #define DEL 0x7f
115 : :
116 : : /* ANSI escape sequences */
117 : : #define ANSI_ESC '['
118 : : #define ANSI_UP 'A'
119 : : #define ANSI_DOWN 'B'
120 : : #define ANSI_FORWARD 'C'
121 : : #define ANSI_BACKWARD 'D'
122 : : #define ANSI_END 'F'
123 : : #define ANSI_HOME 'H'
124 : : #define ANSI_DEL '~'
125 : :
126 : : static int read_uart(const struct device *uart, uint8_t *buf,
127 : : unsigned int size)
128 : : {
129 : : int rx;
130 : :
131 : : rx = uart_fifo_read(uart, buf, size);
132 : : if (rx < 0) {
133 : : /* Overrun issue. Stop the UART */
134 : : uart_irq_rx_disable(uart);
135 : :
136 : : return -EIO;
137 : : }
138 : :
139 : : return rx;
140 : : }
141 : :
142 : : static inline void cursor_forward(unsigned int count)
143 : : {
144 : : printk("\x1b[%uC", count);
145 : : }
146 : :
147 : : static inline void cursor_backward(unsigned int count)
148 : : {
149 : : printk("\x1b[%uD", count);
150 : : }
151 : :
152 : : static inline void cursor_save(void)
153 : : {
154 : : printk("\x1b[s");
155 : : }
156 : :
157 : : static inline void cursor_restore(void)
158 : : {
159 : : printk("\x1b[u");
160 : : }
161 : :
162 : : static void insert_char(char *pos, char c, uint8_t end)
163 : : {
164 : : char tmp;
165 : :
166 : : /* Echo back to console */
167 : : uart_poll_out(uart_console_dev, c);
168 : :
169 : : if (end == 0U) {
170 : : *pos = c;
171 : : return;
172 : : }
173 : :
174 : : tmp = *pos;
175 : : *(pos++) = c;
176 : :
177 : : cursor_save();
178 : :
179 : : while (end-- > 0) {
180 : : uart_poll_out(uart_console_dev, tmp);
181 : : c = *pos;
182 : : *(pos++) = tmp;
183 : : tmp = c;
184 : : }
185 : :
186 : : /* Move cursor back to right place */
187 : : cursor_restore();
188 : : }
189 : :
190 : : static void del_char(char *pos, uint8_t end)
191 : : {
192 : : uart_poll_out(uart_console_dev, '\b');
193 : :
194 : : if (end == 0U) {
195 : : uart_poll_out(uart_console_dev, ' ');
196 : : uart_poll_out(uart_console_dev, '\b');
197 : : return;
198 : : }
199 : :
200 : : cursor_save();
201 : :
202 : : while (end-- > 0) {
203 : : *pos = *(pos + 1);
204 : : uart_poll_out(uart_console_dev, *(pos++));
205 : : }
206 : :
207 : : uart_poll_out(uart_console_dev, ' ');
208 : :
209 : : /* Move cursor back to right place */
210 : : cursor_restore();
211 : : }
212 : :
213 : : enum {
214 : : ESC_ESC,
215 : : ESC_ANSI,
216 : : ESC_ANSI_FIRST,
217 : : ESC_ANSI_VAL,
218 : : ESC_ANSI_VAL_2,
219 : : #ifdef CONFIG_UART_CONSOLE_MCUMGR
220 : : ESC_MCUMGR_PKT_1,
221 : : ESC_MCUMGR_PKT_2,
222 : : ESC_MCUMGR_FRAG_1,
223 : : ESC_MCUMGR_FRAG_2,
224 : : #endif
225 : : };
226 : :
227 : : static atomic_t esc_state;
228 : : static unsigned int ansi_val, ansi_val_2;
229 : : static uint8_t cur, end;
230 : :
231 : : static void handle_ansi(uint8_t byte, char *line)
232 : : {
233 : : if (atomic_test_and_clear_bit(&esc_state, ESC_ANSI_FIRST)) {
234 : : if (!isdigit(byte)) {
235 : : ansi_val = 1U;
236 : : goto ansi_cmd;
237 : : }
238 : :
239 : : atomic_set_bit(&esc_state, ESC_ANSI_VAL);
240 : : ansi_val = byte - '0';
241 : : ansi_val_2 = 0U;
242 : : return;
243 : : }
244 : :
245 : : if (atomic_test_bit(&esc_state, ESC_ANSI_VAL)) {
246 : : if (isdigit(byte)) {
247 : : if (atomic_test_bit(&esc_state, ESC_ANSI_VAL_2)) {
248 : : ansi_val_2 *= 10U;
249 : : ansi_val_2 += byte - '0';
250 : : } else {
251 : : ansi_val *= 10U;
252 : : ansi_val += byte - '0';
253 : : }
254 : : return;
255 : : }
256 : :
257 : : /* Multi value sequence, e.g. Esc[Line;ColumnH */
258 : : if (byte == ';' &&
259 : : !atomic_test_and_set_bit(&esc_state, ESC_ANSI_VAL_2)) {
260 : : return;
261 : : }
262 : :
263 : : atomic_clear_bit(&esc_state, ESC_ANSI_VAL);
264 : : atomic_clear_bit(&esc_state, ESC_ANSI_VAL_2);
265 : : }
266 : :
267 : : ansi_cmd:
268 : : switch (byte) {
269 : : case ANSI_BACKWARD:
270 : : if (ansi_val > cur) {
271 : : break;
272 : : }
273 : :
274 : : end += ansi_val;
275 : : cur -= ansi_val;
276 : : cursor_backward(ansi_val);
277 : : break;
278 : : case ANSI_FORWARD:
279 : : if (ansi_val > end) {
280 : : break;
281 : : }
282 : :
283 : : end -= ansi_val;
284 : : cur += ansi_val;
285 : : cursor_forward(ansi_val);
286 : : break;
287 : : case ANSI_HOME:
288 : : if (!cur) {
289 : : break;
290 : : }
291 : :
292 : : cursor_backward(cur);
293 : : end += cur;
294 : : cur = 0U;
295 : : break;
296 : : case ANSI_END:
297 : : if (!end) {
298 : : break;
299 : : }
300 : :
301 : : cursor_forward(end);
302 : : cur += end;
303 : : end = 0U;
304 : : break;
305 : : case ANSI_DEL:
306 : : if (!end) {
307 : : break;
308 : : }
309 : :
310 : : cursor_forward(1);
311 : : del_char(&line[cur], --end);
312 : : break;
313 : : default:
314 : : break;
315 : : }
316 : :
317 : : atomic_clear_bit(&esc_state, ESC_ANSI);
318 : : }
319 : :
320 : : #ifdef CONFIG_UART_CONSOLE_MCUMGR
321 : :
322 : : static void clear_mcumgr(void)
323 : : {
324 : : atomic_clear_bit(&esc_state, ESC_MCUMGR_PKT_1);
325 : : atomic_clear_bit(&esc_state, ESC_MCUMGR_PKT_2);
326 : : atomic_clear_bit(&esc_state, ESC_MCUMGR_FRAG_1);
327 : : atomic_clear_bit(&esc_state, ESC_MCUMGR_FRAG_2);
328 : : }
329 : :
330 : : /**
331 : : * These states indicate whether an mcumgr frame is being received.
332 : : */
333 : : #define CONSOLE_MCUMGR_STATE_NONE 1
334 : : #define CONSOLE_MCUMGR_STATE_HEADER 2
335 : : #define CONSOLE_MCUMGR_STATE_PAYLOAD 3
336 : :
337 : : static int read_mcumgr_byte(uint8_t byte)
338 : : {
339 : : bool frag_1;
340 : : bool frag_2;
341 : : bool pkt_1;
342 : : bool pkt_2;
343 : :
344 : : pkt_1 = atomic_test_bit(&esc_state, ESC_MCUMGR_PKT_1);
345 : : pkt_2 = atomic_test_bit(&esc_state, ESC_MCUMGR_PKT_2);
346 : : frag_1 = atomic_test_bit(&esc_state, ESC_MCUMGR_FRAG_1);
347 : : frag_2 = atomic_test_bit(&esc_state, ESC_MCUMGR_FRAG_2);
348 : :
349 : : if (pkt_2 || frag_2) {
350 : : /* Already fully framed. */
351 : : return CONSOLE_MCUMGR_STATE_PAYLOAD;
352 : : }
353 : :
354 : : if (pkt_1) {
355 : : if (byte == MCUMGR_SERIAL_HDR_PKT_2) {
356 : : /* Final framing byte received. */
357 : : atomic_set_bit(&esc_state, ESC_MCUMGR_PKT_2);
358 : : return CONSOLE_MCUMGR_STATE_PAYLOAD;
359 : : }
360 : : } else if (frag_1) {
361 : : if (byte == MCUMGR_SERIAL_HDR_FRAG_2) {
362 : : /* Final framing byte received. */
363 : : atomic_set_bit(&esc_state, ESC_MCUMGR_FRAG_2);
364 : : return CONSOLE_MCUMGR_STATE_PAYLOAD;
365 : : }
366 : : } else {
367 : : if (byte == MCUMGR_SERIAL_HDR_PKT_1) {
368 : : /* First framing byte received. */
369 : : atomic_set_bit(&esc_state, ESC_MCUMGR_PKT_1);
370 : : return CONSOLE_MCUMGR_STATE_HEADER;
371 : : } else if (byte == MCUMGR_SERIAL_HDR_FRAG_1) {
372 : : /* First framing byte received. */
373 : : atomic_set_bit(&esc_state, ESC_MCUMGR_FRAG_1);
374 : : return CONSOLE_MCUMGR_STATE_HEADER;
375 : : }
376 : : }
377 : :
378 : : /* Non-mcumgr byte received. */
379 : : return CONSOLE_MCUMGR_STATE_NONE;
380 : : }
381 : :
382 : : /**
383 : : * @brief Attempts to process a received byte as part of an mcumgr frame.
384 : : *
385 : : * @param cmd The console command currently being received.
386 : : * @param byte The byte just received.
387 : : *
388 : : * @return true if the command being received is an mcumgr frame; false if it
389 : : * is a plain console command.
390 : : */
391 : : static bool handle_mcumgr(struct console_input *cmd, uint8_t byte)
392 : : {
393 : : int mcumgr_state;
394 : :
395 : : mcumgr_state = read_mcumgr_byte(byte);
396 : : if (mcumgr_state == CONSOLE_MCUMGR_STATE_NONE) {
397 : : /* Not an mcumgr command; let the normal console handling
398 : : * process the byte.
399 : : */
400 : : cmd->is_mcumgr = 0;
401 : : return false;
402 : : }
403 : :
404 : : /* The received byte is part of an mcumgr command. Process the byte
405 : : * and return true to indicate that normal console handling should
406 : : * ignore it.
407 : : */
408 : : if (cur + end < sizeof(cmd->line) - 1) {
409 : : cmd->line[cur++] = byte;
410 : : }
411 : : if (mcumgr_state == CONSOLE_MCUMGR_STATE_PAYLOAD && byte == '\n') {
412 : : cmd->line[cur + end] = '\0';
413 : : cmd->is_mcumgr = 1;
414 : : k_fifo_put(lines_queue, cmd);
415 : :
416 : : clear_mcumgr();
417 : : cmd = NULL;
418 : : cur = 0U;
419 : : end = 0U;
420 : : }
421 : :
422 : : return true;
423 : : }
424 : :
425 : : #endif /* CONFIG_UART_CONSOLE_MCUMGR */
426 : :
427 : : static void uart_console_isr(const struct device *unused, void *user_data)
428 : : {
429 : : ARG_UNUSED(unused);
430 : : ARG_UNUSED(user_data);
431 : :
432 : : while (uart_irq_update(uart_console_dev) &&
433 : : uart_irq_is_pending(uart_console_dev)) {
434 : : static struct console_input *cmd;
435 : : uint8_t byte;
436 : : int rx;
437 : :
438 : : if (!uart_irq_rx_ready(uart_console_dev)) {
439 : : continue;
440 : : }
441 : :
442 : : /* Character(s) have been received */
443 : :
444 : : rx = read_uart(uart_console_dev, &byte, 1);
445 : : if (rx < 0) {
446 : : return;
447 : : }
448 : :
449 : : #ifdef CONFIG_UART_CONSOLE_DEBUG_SERVER_HOOKS
450 : : if (debug_hook_in != NULL && debug_hook_in(byte) != 0) {
451 : : /*
452 : : * The input hook indicates that no further processing
453 : : * should be done by this handler.
454 : : */
455 : : return;
456 : : }
457 : : #endif
458 : :
459 : : if (!cmd) {
460 : : cmd = k_fifo_get(avail_queue, K_NO_WAIT);
461 : : if (!cmd) {
462 : : return;
463 : : }
464 : : }
465 : :
466 : : #ifdef CONFIG_UART_CONSOLE_MCUMGR
467 : : /* Divert this byte from normal console handling if it is part
468 : : * of an mcumgr frame.
469 : : */
470 : : if (handle_mcumgr(cmd, byte)) {
471 : : continue;
472 : : }
473 : : #endif /* CONFIG_UART_CONSOLE_MCUMGR */
474 : :
475 : : /* Handle ANSI escape mode */
476 : : if (atomic_test_bit(&esc_state, ESC_ANSI)) {
477 : : handle_ansi(byte, cmd->line);
478 : : continue;
479 : : }
480 : :
481 : : /* Handle escape mode */
482 : : if (atomic_test_and_clear_bit(&esc_state, ESC_ESC)) {
483 : : if (byte == ANSI_ESC) {
484 : : atomic_set_bit(&esc_state, ESC_ANSI);
485 : : atomic_set_bit(&esc_state, ESC_ANSI_FIRST);
486 : : }
487 : :
488 : : continue;
489 : : }
490 : :
491 : : /* Handle special control characters */
492 : : if (!isprint(byte)) {
493 : : switch (byte) {
494 : : case BS:
495 : : case DEL:
496 : : if (cur > 0) {
497 : : del_char(&cmd->line[--cur], end);
498 : : }
499 : : break;
500 : : case ESC:
501 : : atomic_set_bit(&esc_state, ESC_ESC);
502 : : break;
503 : : case '\r':
504 : : cmd->line[cur + end] = '\0';
505 : : uart_poll_out(uart_console_dev, '\r');
506 : : uart_poll_out(uart_console_dev, '\n');
507 : : cur = 0U;
508 : : end = 0U;
509 : : k_fifo_put(lines_queue, cmd);
510 : : cmd = NULL;
511 : : break;
512 : : case '\t':
513 : : if (completion_cb && !end) {
514 : : cur += completion_cb(cmd->line, cur);
515 : : }
516 : : break;
517 : : default:
518 : : break;
519 : : }
520 : :
521 : : continue;
522 : : }
523 : :
524 : : /* Ignore characters if there's no more buffer space */
525 : : if (cur + end < sizeof(cmd->line) - 1) {
526 : : insert_char(&cmd->line[cur++], byte, end);
527 : : }
528 : : }
529 : : }
530 : :
531 : : static void console_input_init(void)
532 : : {
533 : : uint8_t c;
534 : :
535 : : uart_irq_rx_disable(uart_console_dev);
536 : : uart_irq_tx_disable(uart_console_dev);
537 : :
538 : : uart_irq_callback_set(uart_console_dev, uart_console_isr);
539 : :
540 : : /* Drain the fifo */
541 : : while (uart_irq_rx_ready(uart_console_dev)) {
542 : : uart_fifo_read(uart_console_dev, &c, 1);
543 : : }
544 : :
545 : : uart_irq_rx_enable(uart_console_dev);
546 : : }
547 : :
548 : : void uart_register_input(struct k_fifo *avail, struct k_fifo *lines,
549 : : uint8_t (*completion)(char *str, uint8_t len))
550 : : {
551 : : avail_queue = avail;
552 : : lines_queue = lines;
553 : : completion_cb = completion;
554 : :
555 : : console_input_init();
556 : : }
557 : :
558 : : #else
559 : 0 : void uart_register_input(struct k_fifo *avail, struct k_fifo *lines,
560 : : uint8_t (*completion)(char *str, uint8_t len))
561 : : {
562 : : ARG_UNUSED(avail);
563 : : ARG_UNUSED(lines);
564 : : ARG_UNUSED(completion);
565 : 0 : }
566 : : #endif
567 : :
568 : : /**
569 : : * @brief Install printk/stdout hook for UART console output
570 : : */
571 : :
572 : 1 : static void uart_console_hook_install(void)
573 : : {
574 : : #if defined(CONFIG_STDOUT_CONSOLE)
575 : 1 : __stdout_hook_install(console_out);
576 : : #endif
577 : : #if defined(CONFIG_PRINTK)
578 : 1 : __printk_hook_install(console_out);
579 : : #endif
580 : 1 : }
581 : :
582 : : /**
583 : : * @brief Initialize one UART as the console/debug port
584 : : *
585 : : * @return 0 if successful, otherwise failed.
586 : : */
587 : 1 : static int uart_console_init(const struct device *arg)
588 : : {
589 : :
590 : : ARG_UNUSED(arg);
591 : :
592 : : /* Claim console device */
593 : 1 : uart_console_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
594 [ - + ]: 1 : if (!device_is_ready(uart_console_dev)) {
595 : 0 : return -ENODEV;
596 : : }
597 : :
598 : 1 : uart_console_hook_install();
599 : :
600 : 1 : return 0;
601 : : }
602 : :
603 : : /* UART console initializes after the UART device itself */
604 : : SYS_INIT(uart_console_init,
605 : : #if defined(CONFIG_EARLY_CONSOLE)
606 : : PRE_KERNEL_1,
607 : : #else
608 : : POST_KERNEL,
609 : : #endif
610 : : CONFIG_CONSOLE_INIT_PRIORITY);
|