uart: Add driver for posix arch
Added a new UART driver for posix arch boards. The driver can be configured to either attach to a new pseudo-terminal, or to connect to the invoking shell stdin-out. Signed-off-by: Alberto Escolar Piedras <alpi@oticon.com>
This commit is contained in:
parent
3e41ac5f04
commit
013f279111
9 changed files with 461 additions and 21 deletions
|
@ -23,7 +23,7 @@ if (CONFIG_ASAN)
|
|||
zephyr_ld_options(-fsanitize=address)
|
||||
endif ()
|
||||
|
||||
zephyr_compile_definitions(_POSIX_C_SOURCE=200809 _XOPEN_SOURCE _XOPEN_SOURCE_EXTENDED)
|
||||
zephyr_compile_definitions(_POSIX_C_SOURCE=200809 _XOPEN_SOURCE=600 _XOPEN_SOURCE_EXTENDED)
|
||||
|
||||
zephyr_ld_options(
|
||||
-ldl
|
||||
|
|
|
@ -38,10 +38,17 @@ endchoice
|
|||
|
||||
endif # BT_HCI
|
||||
|
||||
if SERIAL
|
||||
|
||||
config UART_NATIVE_POSIX
|
||||
def_bool y
|
||||
|
||||
endif # SERIAL
|
||||
|
||||
if LOG
|
||||
|
||||
config LOG_BACKEND_NATIVE_POSIX
|
||||
def_bool y
|
||||
def_bool y if !SERIAL
|
||||
|
||||
# For native_posix we can log immediately without any problem
|
||||
# Doing so will be nicer for debugging
|
||||
|
@ -55,4 +62,14 @@ config LOG_PROCESS_THREAD
|
|||
|
||||
endif # LOG
|
||||
|
||||
if CONSOLE
|
||||
|
||||
config NATIVE_POSIX_CONSOLE
|
||||
def_bool y if !SERIAL
|
||||
|
||||
config UART_CONSOLE
|
||||
def_bool y if SERIAL
|
||||
|
||||
endif #CONSOLE
|
||||
|
||||
endif # BOARD_NATIVE_POSIX
|
||||
|
|
|
@ -19,6 +19,9 @@ you use native host tools for compiling, debugging, and analyzing your
|
|||
Zephyr application, eliminating the need for architecture-specific
|
||||
target hardware in the early phases of development.
|
||||
|
||||
This board provides a few peripherals such as an Ethernet driver and UART.
|
||||
See `Peripherals`_ for more information.
|
||||
|
||||
Host system dependencies
|
||||
========================
|
||||
|
||||
|
@ -458,8 +461,8 @@ The following peripherals are currently provided with this board:
|
|||
``stdout``.
|
||||
|
||||
- Feed any input from the native application ``stdin`` to a possible
|
||||
running :ref:`Shell`. For more information refer to the section
|
||||
`Shell support`_.
|
||||
running legacy shell. For more information refer to the section
|
||||
`Legacy shell support`_.
|
||||
|
||||
**Clock, timer and system tick model**
|
||||
This model provides the system tick timer. By default
|
||||
|
@ -538,11 +541,40 @@ The following peripherals are currently provided with this board:
|
|||
must be powered down and support Bluetooth Low Energy (i.e. support the
|
||||
Bluetooth specification version 4.0 or greater).
|
||||
|
||||
Shell support
|
||||
*************
|
||||
**UART**
|
||||
An optional UART driver can be compiled with native_posix.
|
||||
For more information refer to the section `UART`_.
|
||||
|
||||
When the :ref:`Shell` subsystem is compiled with your application, the native
|
||||
standard input (``stdin``) will be redirected to the shell.
|
||||
|
||||
UART
|
||||
*****
|
||||
|
||||
This driver can be configured to either create and link the UART to a new
|
||||
pseudoterminal (i.e. ``/dev/pts<nbr>``), or to map the UART input and
|
||||
output to the executable's ``stdin`` and ``stdout``.
|
||||
This is chosen by selecting either
|
||||
:option:`CONFIG_NATIVE_UART_0_ON_OWN_PTY` or
|
||||
:option:`CONFIG_NATIVE_UART_0_ON_STDINOUT`
|
||||
Note that for interactive use, the first option should be chosen. The
|
||||
second option is only intended for automated testing or feeding/piping other
|
||||
processes output to the UART.
|
||||
|
||||
When :option:`CONFIG_NATIVE_UART_0_ON_OWN_PTY` is chosen, the name of the
|
||||
newly created UART pseudo-terminal will be displayed in the console.
|
||||
You may also chose to automatically attach a terminal emulator to it by
|
||||
passing the command line option ``-attach_uart`` to the executable.
|
||||
The command used for attaching to the new shell can be set with the command line
|
||||
option ``-attach_uart_cmd=<"cmd">``. Where the default command is given by
|
||||
:option:`CONFIG_NATIVE_UART_AUTOATTACH_DEFAULT_CMD`.
|
||||
Note that the default command assumes both ``xterm`` and ``screen`` are
|
||||
installed in the system.
|
||||
|
||||
|
||||
Legacy shell support
|
||||
********************
|
||||
|
||||
When the legacy :ref:`Shell` subsystem is compiled with your application,
|
||||
the native standard input (``stdin``) will be redirected to the shell.
|
||||
You may use the shell interactively through the console,
|
||||
by piping another process output to it, or by feeding it a file.
|
||||
|
||||
|
@ -580,16 +612,8 @@ These directives are:
|
|||
Use example
|
||||
===========
|
||||
|
||||
For example, you can build the shell sample app:
|
||||
|
||||
.. zephyr-app-commands::
|
||||
:zephyr-app: samples/subsys/shell/shell_module
|
||||
:host-os: unix
|
||||
:board: native_posix
|
||||
:goals: build
|
||||
:compact:
|
||||
|
||||
And feed it the following set of commands through a pipe:
|
||||
For example, after you have built an application with the legacy shell, you
|
||||
can feed it the following set of commands through a pipe:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
|
|
|
@ -2,5 +2,4 @@ CONFIG_ARCH_POSIX=y
|
|||
CONFIG_SOC_POSIX=y
|
||||
CONFIG_BOARD_NATIVE_POSIX=y
|
||||
CONFIG_CONSOLE=y
|
||||
CONFIG_SERIAL=y
|
||||
CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=1000000
|
||||
|
|
|
@ -255,7 +255,6 @@ config NATIVE_POSIX_CONSOLE
|
|||
bool "Use the host terminal for console"
|
||||
depends on ARCH_POSIX
|
||||
select CONSOLE_HAS_DRIVER
|
||||
default y
|
||||
help
|
||||
Use the host terminal (where the native_posix binary was launched) for the
|
||||
Zephyr console
|
||||
|
@ -263,7 +262,7 @@ config NATIVE_POSIX_CONSOLE
|
|||
config NATIVE_POSIX_STDIN_CONSOLE
|
||||
bool "Use the host terminal stdin"
|
||||
depends on NATIVE_POSIX_CONSOLE
|
||||
default y
|
||||
default y if !NATIVE_UART_0_ON_STDINOUT
|
||||
help
|
||||
Feed the host terminal stdin to the Zephyr console/shell.
|
||||
|
||||
|
|
|
@ -26,3 +26,8 @@ zephyr_library_sources_if_kconfig(usart_mcux_lpc.c)
|
|||
zephyr_library_sources_if_kconfig(uart_psoc6.c)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_USERSPACE uart_handlers.c)
|
||||
|
||||
if(CONFIG_UART_NATIVE_POSIX)
|
||||
zephyr_library_compile_definitions(NO_POSIX_CHEATS)
|
||||
zephyr_library_sources(uart_native_posix.c)
|
||||
endif()
|
||||
|
|
|
@ -71,6 +71,8 @@ source "drivers/serial/Kconfig.imx"
|
|||
|
||||
source "drivers/serial/Kconfig.stellaris"
|
||||
|
||||
source "drivers/serial/Kconfig.native_posix"
|
||||
|
||||
source "drivers/serial/Kconfig.nsim"
|
||||
|
||||
source "drivers/serial/Kconfig.usart_sam"
|
||||
|
|
59
drivers/serial/Kconfig.native_posix
Normal file
59
drivers/serial/Kconfig.native_posix
Normal file
|
@ -0,0 +1,59 @@
|
|||
config UART_NATIVE_POSIX
|
||||
bool "UART driver for native_posix"
|
||||
select SERIAL_HAS_DRIVER
|
||||
depends on ARCH_POSIX
|
||||
help
|
||||
This enables a UART driver for the POSIX ARCH. The driver can be configured
|
||||
to either connect to the terminal from which native_posix was run, or into
|
||||
one decicated pseudoterminal for this UART.
|
||||
|
||||
if UART_NATIVE_POSIX
|
||||
|
||||
config UART_NATIVE_POSIX_PORT_0_NAME
|
||||
string "Port 0 Device Name"
|
||||
default "UART_0"
|
||||
depends on UART_NATIVE_POSIX
|
||||
help
|
||||
This is the device name for UART, and is included in the device
|
||||
struct.
|
||||
|
||||
choice
|
||||
prompt "Native UART Port 0 connection"
|
||||
default NATIVE_UART_0_ON_OWN_PTY
|
||||
|
||||
config NATIVE_UART_0_ON_OWN_PTY
|
||||
bool "Connect the UART to its own pseudo terminal"
|
||||
help
|
||||
Connect the UART to its own pseudoterminal. This is the preferred option
|
||||
for users who want to use Zephyr's shell.
|
||||
Moreover this option does not conflict with any other native_posix backend
|
||||
which may use the calling shell standard input/output.
|
||||
|
||||
config NATIVE_UART_0_ON_STDINOUT
|
||||
bool "Connect the UART to the invoking shell stdin/stdout"
|
||||
help
|
||||
Connect the UART to the stdin & stdout of the calling shell/terminal which
|
||||
invoked the native_posix executable. This is good enough for automated
|
||||
testing, or when feeding from a file/pipe.
|
||||
Note that other, non UART messages, will also be printed to the terminal.
|
||||
This option should NOT be used in conjunction with the legacy shell
|
||||
native_posix backend (NATIVE_POSIX_STDIN_CONSOLE)
|
||||
It is strongly discouraged to try to use this option with the new shell
|
||||
interactively, as the default terminal configuration is NOT appropriate
|
||||
for interactive use.
|
||||
|
||||
endchoice
|
||||
|
||||
config NATIVE_UART_AUTOATTACH_DEFAULT_CMD
|
||||
string "Default command to attach the UART to a new terminal"
|
||||
default "xterm -e screen %s &"
|
||||
help
|
||||
If the native_posix executable is called with the --attach_uart command line
|
||||
option, this will be the default command which will be run to attach a new
|
||||
terminal to it.
|
||||
Note that this command must have one, and only one, '%s' as placeholder for
|
||||
the pseudoterminal device name (e.g. /dev/pts/35)
|
||||
This is only applicable if a UART is configured to use its own PTY with
|
||||
NATIVE_UART_x_ON_OWN_PTY.
|
||||
|
||||
endif #UART_NATIVE_POSIX
|
335
drivers/serial/uart_native_posix.c
Normal file
335
drivers/serial/uart_native_posix.c
Normal file
|
@ -0,0 +1,335 @@
|
|||
/*
|
||||
* Copyright (c) 2018, Oticon A/S
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <pty.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/select.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "uart.h"
|
||||
#include "cmdline.h" /* native_posix command line options header */
|
||||
#include "soc.h"
|
||||
|
||||
/*
|
||||
* UART driver for POSIX ARCH based boards.
|
||||
* It can either be connected to the process STDIN+STDOUT
|
||||
* OR
|
||||
* to a dedicated pseudo terminal
|
||||
*
|
||||
* The 2nd option is the recommended one for interactive use, as the pseudo
|
||||
* terminal driver will be configured in "raw" mode, and will therefore behave
|
||||
* more like a real UART.
|
||||
*
|
||||
* When connected to its own pseudo terminal, it may also auto attach a terminal
|
||||
* emulator to it, if set so from command line.
|
||||
*/
|
||||
|
||||
static int np_uart_stdin_poll_in(struct device *dev, unsigned char *p_char);
|
||||
static int np_uart_tty_poll_in(struct device *dev, unsigned char *p_char);
|
||||
static unsigned char np_uart_poll_out(struct device *dev,
|
||||
unsigned char out_char);
|
||||
|
||||
static bool auto_attach;
|
||||
static const char default_cmd[] = CONFIG_NATIVE_UART_AUTOATTACH_DEFAULT_CMD;
|
||||
static char *auto_attach_cmd;
|
||||
|
||||
static struct uart_driver_api np_uart_driver_api = {
|
||||
.poll_out = np_uart_poll_out,
|
||||
.poll_in = np_uart_tty_poll_in,
|
||||
};
|
||||
|
||||
struct native_uart_status {
|
||||
int out_fd; /* File descriptor used for output */
|
||||
int in_fd; /* File descriptor used for input */
|
||||
};
|
||||
|
||||
#define ERROR posix_print_error_and_exit
|
||||
#define WARN posix_print_warning
|
||||
|
||||
/**
|
||||
* Attempt to connect a terminal emulator to the slave side of the pty
|
||||
* If -attach_uart_cmd=<cmd> is provided as a command line option, <cmd> will be
|
||||
* used. Otherwise, the default command,
|
||||
* CONFIG_NATIVE_UART_AUTOATTACH_DEFAULT_CMD, will be used instead
|
||||
*/
|
||||
static void attach_to_tty(const char *slave_tty)
|
||||
{
|
||||
if (auto_attach_cmd == NULL) {
|
||||
auto_attach_cmd = (char *)default_cmd;
|
||||
}
|
||||
char command[strlen(auto_attach_cmd) + strlen(slave_tty)];
|
||||
|
||||
sprintf(command, auto_attach_cmd, slave_tty);
|
||||
|
||||
int ret = system(command);
|
||||
|
||||
if (ret != 0) {
|
||||
WARN("Could not attach to the UART with \"%s\"\n", command);
|
||||
WARN("The command returned %i\n", WEXITSTATUS(ret));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to allocate and open a new pseudoterminal
|
||||
*
|
||||
* Returns the file descriptor of the master side
|
||||
* If auto_attach was set, it will also attempt to connect a new terminal
|
||||
* emulator to its slave side.
|
||||
*/
|
||||
static int open_tty(void)
|
||||
{
|
||||
int master_pty;
|
||||
char *slave_pty_name;
|
||||
struct termios ter;
|
||||
struct winsize win;
|
||||
int err_nbr;
|
||||
int ret;
|
||||
|
||||
win.ws_col = 80;
|
||||
win.ws_row = 24;
|
||||
|
||||
master_pty = posix_openpt(O_RDWR | O_NOCTTY);
|
||||
if (master_pty == -1) {
|
||||
ERROR("Could not open a new TTY for the UART\n");
|
||||
}
|
||||
ret = grantpt(master_pty);
|
||||
if (ret == -1) {
|
||||
err_nbr = errno;
|
||||
close(master_pty);
|
||||
ERROR("Could not grant access to the slave PTY side (%i)\n",
|
||||
errno);
|
||||
}
|
||||
ret = unlockpt(master_pty);
|
||||
if (ret == -1) {
|
||||
err_nbr = errno;
|
||||
close(master_pty);
|
||||
ERROR("Could not unlock the slave PTY side (%i)\n", errno);
|
||||
}
|
||||
slave_pty_name = ptsname(master_pty);
|
||||
if (slave_pty_name == NULL) {
|
||||
err_nbr = errno;
|
||||
close(master_pty);
|
||||
ERROR("Error getting slave PTY device name (%i)\n", errno);
|
||||
}
|
||||
/* Set the master PTY as non blocking */
|
||||
fcntl(master_pty, F_SETFL, fcntl(master_pty, F_GETFL) | O_NONBLOCK);
|
||||
|
||||
/*
|
||||
* Set terminal in "raw" mode:
|
||||
* Not canonical (no line input)
|
||||
* No signal generation from Ctr+{C|Z..}
|
||||
* No echoing, no input or output processing
|
||||
* No replacing of NL or CR
|
||||
* No flow control
|
||||
*/
|
||||
ret = tcgetattr(master_pty, &ter);
|
||||
if (ret == -1) {
|
||||
ERROR("Could not read terminal driver settings\n");
|
||||
}
|
||||
ter.c_cc[VMIN] = 0;
|
||||
ter.c_cc[VTIME] = 0;
|
||||
ter.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO);
|
||||
ter.c_iflag &= ~(BRKINT | ICRNL | IGNBRK | IGNCR | INLCR | INPCK
|
||||
| ISTRIP | IXON | PARMRK);
|
||||
ter.c_oflag &= ~OPOST;
|
||||
ret = tcsetattr(master_pty, TCSANOW, &ter);
|
||||
if (ret == -1) {
|
||||
ERROR("Could not change terminal driver settings\n");
|
||||
}
|
||||
|
||||
posix_print_trace("UART connected to pseudotty: %s\n", slave_pty_name);
|
||||
|
||||
if (auto_attach) {
|
||||
attach_to_tty(slave_pty_name);
|
||||
}
|
||||
|
||||
return master_pty;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize the native_posix serial port
|
||||
*
|
||||
* @param dev UART device struct
|
||||
*
|
||||
* @return 0 (if it fails catastrophically, the execution is terminated)
|
||||
*/
|
||||
static int np_uart_init(struct device *dev)
|
||||
{
|
||||
struct native_uart_status *d;
|
||||
|
||||
d = (struct native_uart_status *)dev->driver_data;
|
||||
|
||||
if (IS_ENABLED(CONFIG_NATIVE_UART_0_ON_OWN_PTY)) {
|
||||
int tty_fn = open_tty();
|
||||
|
||||
d->in_fd = tty_fn;
|
||||
d->out_fd = tty_fn;
|
||||
np_uart_driver_api.poll_in = np_uart_tty_poll_in;
|
||||
} else { /* NATIVE_UART_0_ON_STDINOUT */
|
||||
d->in_fd = STDIN_FILENO;
|
||||
d->out_fd = STDOUT_FILENO;
|
||||
np_uart_driver_api.poll_in = np_uart_stdin_poll_in;
|
||||
|
||||
if (isatty(STDIN_FILENO)) {
|
||||
WARN("The UART dirver has been configured to map to the"
|
||||
" process stdin&out (NATIVE_UART_0_ON_STDINOUT), "
|
||||
"but stdin seems to be left attached to the shell."
|
||||
" This will most likely NOT behave as you want it "
|
||||
"to. This option is NOT meant for interactive use "
|
||||
"but for piping/feeding from/to files to the UART"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
dev->driver_api = &np_uart_driver_api;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Output a character towards the serial port
|
||||
*
|
||||
* @param dev UART device struct
|
||||
* @param out_char Character to send.
|
||||
*/
|
||||
static unsigned char np_uart_poll_out(struct device *dev,
|
||||
unsigned char out_char)
|
||||
{
|
||||
int ret;
|
||||
struct native_uart_status *d;
|
||||
|
||||
d = (struct native_uart_status *)dev->driver_data;
|
||||
ret = write(d->out_fd, &out_char, 1);
|
||||
|
||||
if (ret != 1) {
|
||||
WARN("%s: a character could not be output\n", __func__);
|
||||
}
|
||||
|
||||
return out_char;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Poll the device for input.
|
||||
*
|
||||
* @param dev UART device structure.
|
||||
* @param p_char Pointer to character.
|
||||
*
|
||||
* @retval 0 If a character arrived and was stored in p_char
|
||||
* @retval -1 If no character was available to read
|
||||
*/
|
||||
static int np_uart_stdin_poll_in(struct device *dev, unsigned char *p_char)
|
||||
{
|
||||
static bool disconnected;
|
||||
|
||||
if (disconnected || feof(stdin)) {
|
||||
/*
|
||||
* The stdinput is fed from a file which finished or the user
|
||||
* pressed Crtl+D
|
||||
*/
|
||||
disconnected = true;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int n = -1;
|
||||
int in_f = ((struct native_uart_status *)dev->driver_data)->in_fd;
|
||||
|
||||
int ready;
|
||||
fd_set readfds;
|
||||
static struct timeval timeout; /* just zero */
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(in_f, &readfds);
|
||||
|
||||
ready = select(in_f+1, &readfds, NULL, NULL, &timeout);
|
||||
|
||||
if (ready == 0) {
|
||||
return -1;
|
||||
} else if (ready == -1) {
|
||||
ERROR("%s: Error on select ()\n", __func__);
|
||||
}
|
||||
|
||||
n = read(in_f, p_char, 1);
|
||||
if (n == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Poll the device for input.
|
||||
*
|
||||
* @param dev UART device structure.
|
||||
* @param p_char Pointer to character.
|
||||
*
|
||||
* @retval 0 If a character arrived and was stored in p_char
|
||||
* @retval -1 If no character was available to read
|
||||
*/
|
||||
static int np_uart_tty_poll_in(struct device *dev, unsigned char *p_char)
|
||||
{
|
||||
int n = -1;
|
||||
int in_f = ((struct native_uart_status *)dev->driver_data)->in_fd;
|
||||
|
||||
n = read(in_f, p_char, 1);
|
||||
if (n == -1) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct native_uart_status native_uart_status_0;
|
||||
|
||||
DEVICE_INIT(uart_native_posix0,
|
||||
CONFIG_UART_NATIVE_POSIX_PORT_0_NAME, &np_uart_init,
|
||||
(void *)&native_uart_status_0, NULL,
|
||||
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
|
||||
|
||||
static void np_add_uart_options(void)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_NATIVE_UART_0_ON_OWN_PTY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
static struct args_struct_t uart_options[] = {
|
||||
/*
|
||||
* Fields:
|
||||
* manual, mandatory, switch,
|
||||
* option_name, var_name ,type,
|
||||
* destination, callback,
|
||||
* description
|
||||
*/
|
||||
{false, false, true,
|
||||
"attach_uart", "", 'b',
|
||||
(void *)&auto_attach, NULL,
|
||||
"Automatically attach to the UART terminal"},
|
||||
{false, false, false,
|
||||
"attach_uart_cmd", "\"cmd\"", 's',
|
||||
(void *)&auto_attach_cmd, NULL,
|
||||
"Command used to automatically attach to the terminal, by "
|
||||
"default: '" CONFIG_NATIVE_UART_AUTOATTACH_DEFAULT_CMD "'"},
|
||||
|
||||
ARG_TABLE_ENDMARKER
|
||||
};
|
||||
|
||||
native_add_command_line_opts(uart_options);
|
||||
}
|
||||
|
||||
static void np_cleanup_uart(void)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_NATIVE_UART_0_ON_OWN_PTY)) {
|
||||
if (native_uart_status_0.in_fd != 0) {
|
||||
close(native_uart_status_0.in_fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NATIVE_TASK(np_add_uart_options, PRE_BOOT_1, 11);
|
||||
NATIVE_TASK(np_cleanup_uart, ON_EXIT, 99);
|
Loading…
Add table
Add a link
Reference in a new issue