From 3cb13b96873b26bf92e43b019011ed0a1def5232 Mon Sep 17 00:00:00 2001 From: Paul Sokolovsky Date: Mon, 10 Apr 2017 10:47:28 +0300 Subject: [PATCH] subsys: console: Add buffered output support to console subsystem Signed-off-by: Paul Sokolovsky --- include/console.h | 20 +++++-- samples/subsys/console/echo/Makefile | 4 ++ samples/subsys/console/echo/prj.conf | 4 ++ samples/subsys/console/echo/src/Makefile | 1 + samples/subsys/console/echo/src/main.c | 16 +++++ samples/subsys/console/echo/testcase.ini | 8 +++ samples/subsys/console/getchar/src/main.c | 2 +- subsys/console/Kconfig | 11 +++- subsys/console/getchar.c | 73 +++++++++++++++++++++-- 9 files changed, 127 insertions(+), 12 deletions(-) create mode 100644 samples/subsys/console/echo/Makefile create mode 100644 samples/subsys/console/echo/prj.conf create mode 100644 samples/subsys/console/echo/src/Makefile create mode 100644 samples/subsys/console/echo/src/main.c create mode 100644 samples/subsys/console/echo/testcase.ini diff --git a/include/console.h b/include/console.h index 5f50bc8562c..2068bc4ac06 100644 --- a/include/console.h +++ b/include/console.h @@ -13,16 +13,17 @@ extern "C" { #endif -/** @brief Initialize console_getchar() call. +/** @brief Initialize console_getchar()/putchar() calls. * * This function should be called once to initialize pull-style - * access to console via console_getchar() function. This function - * supersedes, and incompatible with, callback (push-style) console - * handling (via console_input_fn callback, etc.). + * access to console via console_getchar() function and buffered + * output using console_putchar() function. This function supercedes, + * and incompatible with, callback (push-style) console handling + * (via console_input_fn callback, etc.). * * @return N/A */ -void console_getchar_init(void); +void console_init(void); /** @brief Get next char from console input buffer. * @@ -38,6 +39,15 @@ void console_getchar_init(void); */ u8_t console_getchar(void); +/** @brief Output a char to console (buffered). + * + * Puts a character into console output buffer. It will be sent + * to a console asynchronously, e.g. using an IRQ handler. + * + * @return -1 on output buffer overflow, otherwise 0. + */ +int console_putchar(char c); + /** @brief Initialize console_getline() call. * * This function should be called once to initialize pull-style diff --git a/samples/subsys/console/echo/Makefile b/samples/subsys/console/echo/Makefile new file mode 100644 index 00000000000..4de50f93d4d --- /dev/null +++ b/samples/subsys/console/echo/Makefile @@ -0,0 +1,4 @@ +BOARD ?= qemu_x86 +CONF_FILE = prj.conf + +include ${ZEPHYR_BASE}/Makefile.inc diff --git a/samples/subsys/console/echo/prj.conf b/samples/subsys/console/echo/prj.conf new file mode 100644 index 00000000000..3d80dfbdfc7 --- /dev/null +++ b/samples/subsys/console/echo/prj.conf @@ -0,0 +1,4 @@ +CONFIG_CONSOLE_PULL=y +CONFIG_CONSOLE_GETCHAR=y +CONFIG_CONSOLE_GETCHAR_BUFSIZE=64 +CONFIG_CONSOLE_PUTCHAR_BUFSIZE=512 diff --git a/samples/subsys/console/echo/src/Makefile b/samples/subsys/console/echo/src/Makefile new file mode 100644 index 00000000000..b666967fd57 --- /dev/null +++ b/samples/subsys/console/echo/src/Makefile @@ -0,0 +1 @@ +obj-y += main.o diff --git a/samples/subsys/console/echo/src/main.c b/samples/subsys/console/echo/src/main.c new file mode 100644 index 00000000000..52f805ce491 --- /dev/null +++ b/samples/subsys/console/echo/src/main.c @@ -0,0 +1,16 @@ +#include +#include + +void main(void) +{ + console_init(); + + while (1) { + u8_t c = console_getchar(); + + console_putchar(c); + if (c == '\r') { + console_putchar('\n'); + } + } +} diff --git a/samples/subsys/console/echo/testcase.ini b/samples/subsys/console/echo/testcase.ini new file mode 100644 index 00000000000..283fdd4e2aa --- /dev/null +++ b/samples/subsys/console/echo/testcase.ini @@ -0,0 +1,8 @@ +[test] +tags = samples +build_only = true +# TODO: +# #error "Interrupt not available in uart riscv32-qemu" +# #error "Interrupt-driven Altera JTAG UART not implemented yet" +platform_exclude = qemu_riscv32 qemu_nios2 +filter = CONFIG_UART_CONSOLE diff --git a/samples/subsys/console/getchar/src/main.c b/samples/subsys/console/getchar/src/main.c index 4e6bc6cc974..6a4bf10411b 100644 --- a/samples/subsys/console/getchar/src/main.c +++ b/samples/subsys/console/getchar/src/main.c @@ -4,7 +4,7 @@ void main(void) { - console_getchar_init(); + console_init(); while (1) { u8_t c = console_getchar(); diff --git a/subsys/console/Kconfig b/subsys/console/Kconfig index 4dd5521ade6..3f79fae57f7 100644 --- a/subsys/console/Kconfig +++ b/subsys/console/Kconfig @@ -18,7 +18,7 @@ choice prompt "Console 'get' function selection" config CONSOLE_GETCHAR - bool "Character by character input" + bool "Character by character input and output" select UART_CONSOLE_DEBUG_SERVER_HOOKS select CONSOLE_HANDLER @@ -38,6 +38,15 @@ config CONSOLE_GETCHAR_BUFSIZE default is optimized to save RAM. You may need to increase it e.g. to support large host-side clipboard pastes. +config CONSOLE_PUTCHAR_BUFSIZE + int "console_putchar() buffer size" + default 16 + help + Buffer size for console_putchar(). Must be power of 2. The + default is optimized to save RAM. You may need to increase + it e.g. to support large host-side clipboard pastes (with + echo). + endif # CONSOLE_GETCHAR endif # CONSOLE_PULL diff --git a/subsys/console/getchar.c b/subsys/console/getchar.c index a2725aa525c..609e18b1dc9 100644 --- a/subsys/console/getchar.c +++ b/subsys/console/getchar.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -14,16 +15,58 @@ #error CONFIG_CONSOLE_GETCHAR_BUFSIZE must be power of 2 #endif +#if CONFIG_CONSOLE_PUTCHAR_BUFSIZE & (CONFIG_CONSOLE_PUTCHAR_BUFSIZE - 1) != 0 +#error CONFIG_CONSOLE_PUTCHAR_BUFSIZE must be power of 2 +#endif + static K_SEM_DEFINE(uart_sem, 0, UINT_MAX); static u8_t uart_ringbuf[CONFIG_CONSOLE_GETCHAR_BUFSIZE]; static u8_t i_get, i_put; +static K_SEM_DEFINE(tx_sem, 0, UINT_MAX); +static u8_t tx_ringbuf[CONFIG_CONSOLE_PUTCHAR_BUFSIZE]; +static u8_t tx_get, tx_put; + +static struct device *uart_dev; + +static int console_irq_input_hook(u8_t c); + +static void uart_isr(struct device *dev) +{ + uart_irq_update(dev); + + if (uart_irq_rx_ready(dev)) { + char c; + + while (1) { + if (uart_fifo_read(dev, &c, 1) == 0) { + break; + } + console_irq_input_hook(c); + } + } + + if (uart_irq_tx_ready(dev)) { + if (tx_get == tx_put) { + /* Output buffer empty, don't bother + * us with tx interrupts + */ + uart_irq_tx_disable(dev); + } else { + uart_fifo_fill(dev, &tx_ringbuf[tx_get++], 1); + tx_get &= CONFIG_CONSOLE_PUTCHAR_BUFSIZE - 1; + } + } +} + static int console_irq_input_hook(u8_t c) { int i_next = (i_put + 1) & (CONFIG_CONSOLE_GETCHAR_BUFSIZE - 1); if (i_next == i_get) { - printk("Console buffer overflow - char dropped\n"); + /* Try to give a clue to user that some input was lost */ + console_putchar('~'); + console_putchar('\n'); return 1; } @@ -34,6 +77,26 @@ static int console_irq_input_hook(u8_t c) return 1; } +int console_putchar(char c) +{ + unsigned int key; + int tx_next; + + key = irq_lock(); + tx_next = (tx_put + 1) & (CONFIG_CONSOLE_PUTCHAR_BUFSIZE - 1); + if (tx_next == tx_get) { + irq_unlock(key); + return -1; + } + + tx_ringbuf[tx_put] = c; + tx_put = tx_next; + + irq_unlock(key); + uart_irq_tx_enable(uart_dev); + return 0; +} + u8_t console_getchar(void) { unsigned int key; @@ -48,9 +111,9 @@ u8_t console_getchar(void) return c; } -void console_getchar_init(void) +void console_init(void) { - uart_console_in_debug_hook_install(console_irq_input_hook); - /* All NULLs because we're interested only in the callback above. */ - uart_register_input(NULL, NULL, NULL); + uart_dev = device_get_binding(CONFIG_UART_CONSOLE_ON_DEV_NAME); + uart_irq_callback_set(uart_dev, uart_isr); + uart_irq_rx_enable(uart_dev); }