subsys: console: Add pull-style console API support.
This change introduces console_getchar() and console_getline() API calls which can be used to get pending console input (either one char or whole line), or block waiting for one. In this regard, they are similar to well-known ANSI C function getchar/gets/fgets, and are intended to ease porting of existing applications to Zephyr, and indeed, these functions (shaped as an external module) are already used by few applications. The implementation of the functions is structured as a new "console" subsystem. The intention is that further generic console code may be pulled there instead of being in drivers/console/. Besides the functions themselves, initialization code and sample applications are included. At this time, there're may limitations of how these functions can be used. For example, console_getchar() and console_getline() are mutually exclusive, and both are incompatible with callback (push-style) console API (and e.g. with console shell subsystem which uses this API). Again, the intention is to make a first step towards refactoring console subsystem to allow more flexible real-world usage, better reusability and composability. Change-Id: I3f4015bb5b26e0656f82f428b11ba30e980d25a0 Signed-off-by: Paul Sokolovsky <paul.sokolovsky@linaro.org>
This commit is contained in:
parent
9f5f6d6283
commit
542c2b93d0
73
include/console.h
Normal file
73
include/console.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Linaro Limited
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef __CONSOLE_H__
|
||||
#define __CONSOLE_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @brief Initialize console_getchar() call.
|
||||
*
|
||||
* This function should be called once to initialize pull-style
|
||||
* access to console via console_getchar() 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);
|
||||
|
||||
/** @brief Get next char from console input buffer.
|
||||
*
|
||||
* Return next input character from console. If no characters available,
|
||||
* this function will block. This function is similar to ANSI C
|
||||
* getchar() function and is intended to ease porting of existing
|
||||
* software. Before this function can be used, console_getchar_init()
|
||||
* should be called once. This function is incompatible with native
|
||||
* Zephyr callback-based console input processing, shell subsystem,
|
||||
* or console_getline().
|
||||
*
|
||||
* @return A character read, including control characters.
|
||||
*/
|
||||
uint8_t console_getchar(void);
|
||||
|
||||
/** @brief Initialize console_getline() call.
|
||||
*
|
||||
* This function should be called once to initialize pull-style
|
||||
* access to console via console_getline() function. This function
|
||||
* supercedes, and incompatible with, callback (push-style) console
|
||||
* handling (via console_input_fn callback, etc.).
|
||||
*
|
||||
* @return N/A
|
||||
*/
|
||||
void console_getline_init(void);
|
||||
|
||||
/** @brief Get next line from console input buffer.
|
||||
*
|
||||
* Return next input line from console. Until full line is available,
|
||||
* this function will block. This function is similar to ANSI C
|
||||
* gets() function (except a line is returned in system-owned buffer,
|
||||
* and system takes care of the buffer overflow checks) and is
|
||||
* intended to ease porting of existing software. Before this function
|
||||
* can be used, console_getline_init() should be called once. This
|
||||
* function is incompatible with native Zephyr callback-based console
|
||||
* input processing, shell subsystem, or console_getchar().
|
||||
*
|
||||
* @return A pointer to a line read, not including EOL character(s).
|
||||
* A line resides in a system-owned buffer, so an application
|
||||
* should finish any processing of this line immediately
|
||||
* after console_getline() call, or the buffer can be reused.
|
||||
*/
|
||||
char *console_getline(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CONSOLE_H__ */
|
|
@ -4,8 +4,8 @@
|
|||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef __CONSOLE_H__
|
||||
#define __CONSOLE_H__
|
||||
#ifndef __DRIVERS_CONSOLE_CONSOLE_H__
|
||||
#define __DRIVERS_CONSOLE_CONSOLE_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -46,4 +46,4 @@ typedef void (*console_input_fn)(struct k_fifo *avail, struct k_fifo *lines,
|
|||
}
|
||||
#endif
|
||||
|
||||
#endif /* __CONSOLE_H__ */
|
||||
#endif /* __DRIVERS_CONSOLE_CONSOLE_H__ */
|
||||
|
|
|
@ -3,11 +3,12 @@ Samples and Demos
|
|||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:maxdepth: 3
|
||||
:glob:
|
||||
|
||||
kernel
|
||||
basic/*
|
||||
subsys/subsys.rst
|
||||
net/net.rst
|
||||
bluetooth/bluetooth.rst
|
||||
sensor/*
|
||||
|
|
8
samples/subsys/console/console.rst
Normal file
8
samples/subsys/console/console.rst
Normal file
|
@ -0,0 +1,8 @@
|
|||
Console Samples
|
||||
###############
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:glob:
|
||||
|
||||
**/*
|
4
samples/subsys/console/getchar/Makefile
Normal file
4
samples/subsys/console/getchar/Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
BOARD ?= qemu_x86
|
||||
CONF_FILE = prj.conf
|
||||
|
||||
include ${ZEPHYR_BASE}/Makefile.inc
|
40
samples/subsys/console/getchar/README.rst
Normal file
40
samples/subsys/console/getchar/README.rst
Normal file
|
@ -0,0 +1,40 @@
|
|||
.. _console_getchar_sample:
|
||||
|
||||
console_getchar() Sample Application
|
||||
####################################
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
This example shows how to use :cpp:func:`console_getchar()` function.
|
||||
Similar to the well-known ANSI C getchar() function,
|
||||
:cpp:func:`console_getchar()` either returns the next available input
|
||||
character or blocks waiting for one. Using this function, it should be
|
||||
fairly easy to port existing ANSI C, POSIX, or Linux applications which
|
||||
process console input character by character. The sample also allows to
|
||||
see key/character codes as returned by the function.
|
||||
|
||||
If you are interested in line by line console input, see
|
||||
:ref:`console_getline_sample`.
|
||||
|
||||
|
||||
Requirements
|
||||
************
|
||||
|
||||
UART console is required to run this simple.
|
||||
|
||||
|
||||
Building and Running
|
||||
********************
|
||||
|
||||
The easiest way to run this sample is using QEMU:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cd samples/console/getchar
|
||||
$ make BOARD=qemu_x86
|
||||
$ make BOARD=qemu_x86 run
|
||||
|
||||
Now start pressing keys on a keyboard, and they will be printed both as
|
||||
hex values and in character form. Be sure to press Enter, Up/Down, etc.
|
||||
key to check what control characters are produced for them.
|
2
samples/subsys/console/getchar/prj.conf
Normal file
2
samples/subsys/console/getchar/prj.conf
Normal file
|
@ -0,0 +1,2 @@
|
|||
CONFIG_CONSOLE_PULL=y
|
||||
CONFIG_CONSOLE_GETCHAR=y
|
1
samples/subsys/console/getchar/src/Makefile
Normal file
1
samples/subsys/console/getchar/src/Makefile
Normal file
|
@ -0,0 +1 @@
|
|||
obj-y += main.o
|
14
samples/subsys/console/getchar/src/main.c
Normal file
14
samples/subsys/console/getchar/src/main.c
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include <zephyr.h>
|
||||
#include <misc/printk.h>
|
||||
#include <console.h>
|
||||
|
||||
void main(void)
|
||||
{
|
||||
console_getchar_init();
|
||||
|
||||
while (1) {
|
||||
uint8_t c = console_getchar();
|
||||
|
||||
printk("char: [0x%x] %c\n", c, c);
|
||||
}
|
||||
}
|
7
samples/subsys/console/getchar/testcase.ini
Normal file
7
samples/subsys/console/getchar/testcase.ini
Normal file
|
@ -0,0 +1,7 @@
|
|||
[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
|
4
samples/subsys/console/getline/Makefile
Normal file
4
samples/subsys/console/getline/Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
BOARD ?= qemu_x86
|
||||
CONF_FILE = prj.conf
|
||||
|
||||
include ${ZEPHYR_BASE}/Makefile.inc
|
41
samples/subsys/console/getline/README.rst
Normal file
41
samples/subsys/console/getline/README.rst
Normal file
|
@ -0,0 +1,41 @@
|
|||
.. _console_getline_sample:
|
||||
|
||||
console_getline() Sample Application
|
||||
####################################
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
This example shows how to use :cpp:func:`console_getline()` function.
|
||||
Similar to the well-known ANSI C gets() and fgets() functions,
|
||||
:cpp:func:`console_getline()` either returns the next available input
|
||||
line or blocks waiting for one. Using this function, it should be fairly
|
||||
easy to port existing ANSI C, POSIX, or Linux applications which process
|
||||
console input line by line. The sample also allows to see details of how
|
||||
a line is returned by the function.
|
||||
|
||||
If you are interested in character by character console input, see
|
||||
:ref:`console_getchar_sample`.
|
||||
|
||||
|
||||
Requirements
|
||||
************
|
||||
|
||||
UART console is required to run this simple.
|
||||
|
||||
|
||||
Building and Running
|
||||
********************
|
||||
|
||||
The easiest way to run this sample is using QEMU:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cd samples/console/getline
|
||||
$ make BOARD=qemu_x86
|
||||
$ make BOARD=qemu_x86 run
|
||||
|
||||
Now start pressing keys on a keyboard, followed by Enter. The input line
|
||||
will be printed back, with a hex code of the last character, to show that
|
||||
line does not include any special "end of line" characters (like LF, CR,
|
||||
etc.)
|
2
samples/subsys/console/getline/prj.conf
Normal file
2
samples/subsys/console/getline/prj.conf
Normal file
|
@ -0,0 +1,2 @@
|
|||
CONFIG_CONSOLE_PULL=y
|
||||
CONFIG_CONSOLE_GETLINE=y
|
1
samples/subsys/console/getline/src/Makefile
Normal file
1
samples/subsys/console/getline/src/Makefile
Normal file
|
@ -0,0 +1 @@
|
|||
obj-y += main.o
|
16
samples/subsys/console/getline/src/main.c
Normal file
16
samples/subsys/console/getline/src/main.c
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include <string.h>
|
||||
#include <zephyr.h>
|
||||
#include <misc/printk.h>
|
||||
#include <console.h>
|
||||
|
||||
void main(void)
|
||||
{
|
||||
console_getline_init();
|
||||
|
||||
while (1) {
|
||||
char *s = console_getline();
|
||||
|
||||
printk("line: %s\n", s);
|
||||
printk("last char was: 0x%x\n", s[strlen(s) - 1]);
|
||||
}
|
||||
}
|
7
samples/subsys/console/getline/testcase.ini
Normal file
7
samples/subsys/console/getline/testcase.ini
Normal file
|
@ -0,0 +1,7 @@
|
|||
[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
|
8
samples/subsys/subsys.rst
Normal file
8
samples/subsys/subsys.rst
Normal file
|
@ -0,0 +1,8 @@
|
|||
Various Subsystems Samples
|
||||
##########################
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:glob:
|
||||
|
||||
*/*
|
|
@ -11,6 +11,8 @@ source "subsys/usb/Kconfig"
|
|||
|
||||
source "subsys/bluetooth/Kconfig"
|
||||
|
||||
source "subsys/console/Kconfig"
|
||||
|
||||
source "subsys/disk/Kconfig"
|
||||
|
||||
source "subsys/net/Kconfig"
|
||||
|
|
|
@ -3,6 +3,7 @@ obj-$(CONFIG_USB) += usb/
|
|||
obj-$(CONFIG_BLUETOOTH) += bluetooth/
|
||||
obj-$(CONFIG_NET_BUF) += net/
|
||||
obj-$(CONFIG_CONSOLE_SHELL) += shell/
|
||||
obj-$(CONFIG_CONSOLE_PULL) += console/
|
||||
obj-$(CONFIG_DISK_ACCESS) += disk/
|
||||
obj-y += logging/
|
||||
obj-y += debug/
|
||||
|
|
44
subsys/console/Kconfig
Normal file
44
subsys/console/Kconfig
Normal file
|
@ -0,0 +1,44 @@
|
|||
#
|
||||
# Copyright (c) 2017 Linaro Limited
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
menu "Console (pull-style)"
|
||||
|
||||
config CONSOLE_PULL
|
||||
bool
|
||||
default n
|
||||
prompt "Enable pull-style Console access"
|
||||
help
|
||||
Get data from console using getchar/getline calls
|
||||
|
||||
if CONSOLE_PULL
|
||||
choice
|
||||
prompt "Console 'get' function selection"
|
||||
|
||||
config CONSOLE_GETCHAR
|
||||
bool "Character by character input"
|
||||
select UART_CONSOLE_DEBUG_SERVER_HOOKS
|
||||
select CONSOLE_HANDLER
|
||||
|
||||
config CONSOLE_GETLINE
|
||||
bool "Line by line input"
|
||||
select CONSOLE_HANDLER
|
||||
|
||||
endchoice
|
||||
|
||||
if CONSOLE_GETCHAR
|
||||
|
||||
config CONSOLE_GETCHAR_BUFSIZE
|
||||
int "console_getchar() buffer size"
|
||||
default 16
|
||||
help
|
||||
Buffer size for console_getchar(). 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.
|
||||
|
||||
endif # CONSOLE_GETCHAR
|
||||
|
||||
endif # CONSOLE_PULL
|
||||
endmenu
|
2
subsys/console/Makefile
Normal file
2
subsys/console/Makefile
Normal file
|
@ -0,0 +1,2 @@
|
|||
obj-$(CONFIG_CONSOLE_GETCHAR) += getchar.o
|
||||
obj-$(CONFIG_CONSOLE_GETLINE) += getline.o
|
56
subsys/console/getchar.c
Normal file
56
subsys/console/getchar.c
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Linaro Limited.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <uart.h>
|
||||
#include <misc/printk.h>
|
||||
#include <drivers/console/console.h>
|
||||
#include <drivers/console/uart_console.h>
|
||||
|
||||
#if CONFIG_CONSOLE_GETCHAR_BUFSIZE & (CONFIG_CONSOLE_GETCHAR_BUFSIZE - 1) != 0
|
||||
#error CONFIG_CONSOLE_GETCHAR_BUFSIZE must be power of 2
|
||||
#endif
|
||||
|
||||
static K_SEM_DEFINE(uart_sem, 0, UINT_MAX);
|
||||
static uint8_t uart_ringbuf[CONFIG_CONSOLE_GETCHAR_BUFSIZE];
|
||||
static uint8_t i_get, i_put;
|
||||
|
||||
static int console_irq_input_hook(uint8_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");
|
||||
return 1;
|
||||
}
|
||||
|
||||
uart_ringbuf[i_put] = c;
|
||||
i_put = i_next;
|
||||
k_sem_give(&uart_sem);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t console_getchar(void)
|
||||
{
|
||||
unsigned int key;
|
||||
uint8_t c;
|
||||
|
||||
k_sem_take(&uart_sem, K_FOREVER);
|
||||
key = irq_lock();
|
||||
c = uart_ringbuf[i_get++];
|
||||
i_get &= CONFIG_CONSOLE_GETCHAR_BUFSIZE - 1;
|
||||
irq_unlock(key);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void console_getchar_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);
|
||||
}
|
46
subsys/console/getline.c
Normal file
46
subsys/console/getline.c
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Linaro Limited.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr.h>
|
||||
#include <uart.h>
|
||||
#include <drivers/console/console.h>
|
||||
#include <drivers/console/uart_console.h>
|
||||
|
||||
/* While app processes one input line, Zephyr will have another line
|
||||
* buffer to accumulate more console input.
|
||||
*/
|
||||
static struct console_input line_bufs[2];
|
||||
|
||||
static K_FIFO_DEFINE(free_queue);
|
||||
static K_FIFO_DEFINE(used_queue);
|
||||
|
||||
char *console_getline(void)
|
||||
{
|
||||
static struct console_input *cmd;
|
||||
|
||||
/* Recycle cmd buffer returned previous time */
|
||||
if (cmd != NULL) {
|
||||
k_fifo_put(&free_queue, cmd);
|
||||
}
|
||||
|
||||
cmd = k_fifo_get(&used_queue, K_FOREVER);
|
||||
return cmd->line;
|
||||
}
|
||||
|
||||
void console_getline_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sizeof(line_bufs) / sizeof(*line_bufs); i++) {
|
||||
k_fifo_put(&free_queue, &line_bufs[i]);
|
||||
}
|
||||
|
||||
/* Zephyr UART handler takes an empty buffer from free_queue,
|
||||
* stores UART input in it until EOL, and then puts it into
|
||||
* used_queue.
|
||||
*/
|
||||
uart_register_input(&free_queue, &used_queue, NULL);
|
||||
}
|
Loading…
Reference in a new issue