From 013f27911192e6bb9c7c4619965b82badbe68f03 Mon Sep 17 00:00:00 2001 From: Alberto Escolar Piedras Date: Sat, 22 Sep 2018 19:19:59 +0200 Subject: [PATCH] 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 --- arch/posix/CMakeLists.txt | 2 +- boards/posix/native_posix/Kconfig.defconfig | 19 +- boards/posix/native_posix/doc/board.rst | 56 ++- .../posix/native_posix/native_posix_defconfig | 1 - drivers/console/Kconfig | 3 +- drivers/serial/CMakeLists.txt | 5 + drivers/serial/Kconfig | 2 + drivers/serial/Kconfig.native_posix | 59 +++ drivers/serial/uart_native_posix.c | 335 ++++++++++++++++++ 9 files changed, 461 insertions(+), 21 deletions(-) create mode 100644 drivers/serial/Kconfig.native_posix create mode 100644 drivers/serial/uart_native_posix.c diff --git a/arch/posix/CMakeLists.txt b/arch/posix/CMakeLists.txt index a7f4b0402a7..d4e17df902b 100644 --- a/arch/posix/CMakeLists.txt +++ b/arch/posix/CMakeLists.txt @@ -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 diff --git a/boards/posix/native_posix/Kconfig.defconfig b/boards/posix/native_posix/Kconfig.defconfig index 0d4d4583555..e150511af1f 100644 --- a/boards/posix/native_posix/Kconfig.defconfig +++ b/boards/posix/native_posix/Kconfig.defconfig @@ -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 diff --git a/boards/posix/native_posix/doc/board.rst b/boards/posix/native_posix/doc/board.rst index 4846dfffade..b6d7fd5cfbb 100644 --- a/boards/posix/native_posix/doc/board.rst +++ b/boards/posix/native_posix/doc/board.rst @@ -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``), 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 diff --git a/boards/posix/native_posix/native_posix_defconfig b/boards/posix/native_posix/native_posix_defconfig index 3ba44d6bb95..226cf0da763 100644 --- a/boards/posix/native_posix/native_posix_defconfig +++ b/boards/posix/native_posix/native_posix_defconfig @@ -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 diff --git a/drivers/console/Kconfig b/drivers/console/Kconfig index 4ee5ec3adc8..3dd4cc72017 100644 --- a/drivers/console/Kconfig +++ b/drivers/console/Kconfig @@ -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. diff --git a/drivers/serial/CMakeLists.txt b/drivers/serial/CMakeLists.txt index fde8f643b06..8ee2c558d9f 100644 --- a/drivers/serial/CMakeLists.txt +++ b/drivers/serial/CMakeLists.txt @@ -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() diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 540141167d7..7a23adfb5a9 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -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" diff --git a/drivers/serial/Kconfig.native_posix b/drivers/serial/Kconfig.native_posix new file mode 100644 index 00000000000..4f73612481d --- /dev/null +++ b/drivers/serial/Kconfig.native_posix @@ -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 diff --git a/drivers/serial/uart_native_posix.c b/drivers/serial/uart_native_posix.c new file mode 100644 index 00000000000..d6223d59815 --- /dev/null +++ b/drivers/serial/uart_native_posix.c @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2018, Oticon A/S + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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= is provided as a command line option, 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);