/* * Copyright (c) 2018, Oticon A/S * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT zephyr_native_posix_uart #include #include #include #include "cmdline.h" /* native_posix command line options header */ #include "posix_native_task.h" #include "uart_native_ptty_bottom.h" #include "nsi_host_trampolines.h" /* * UART driver for POSIX ARCH based boards. * It can support up to two UARTs. * * For the first UART: * * 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(const struct device *dev, unsigned char *p_char); static int np_uart_tty_poll_in(const struct device *dev, unsigned char *p_char); static void np_uart_poll_out(const struct device *dev, unsigned char out_char); static bool auto_attach; static bool wait_pts; static char *auto_attach_cmd = CONFIG_NATIVE_UART_AUTOATTACH_DEFAULT_CMD; struct native_uart_status { int out_fd; /* File descriptor used for output */ int in_fd; /* File descriptor used for input */ }; static struct native_uart_status native_uart_status_0; static struct uart_driver_api np_uart_driver_api_0 = { .poll_out = np_uart_poll_out, .poll_in = np_uart_tty_poll_in, }; #if defined(CONFIG_UART_NATIVE_POSIX_PORT_1_ENABLE) static struct native_uart_status native_uart_status_1; static struct uart_driver_api np_uart_driver_api_1 = { .poll_out = np_uart_poll_out, .poll_in = np_uart_tty_poll_in, }; #endif /* CONFIG_UART_NATIVE_POSIX_PORT_1_ENABLE */ #define ERROR posix_print_error_and_exit #define WARN posix_print_warning /** * @brief Initialize the first native_posix serial port * * @param dev UART_0 device struct * * @return 0 (if it fails catastrophically, the execution is terminated) */ static int np_uart_0_init(const struct device *dev) { struct native_uart_status *d; d = (struct native_uart_status *)dev->data; if (IS_ENABLED(CONFIG_NATIVE_UART_0_ON_OWN_PTY)) { int tty_fn = np_uart_open_ptty(dev->name, auto_attach_cmd, auto_attach, wait_pts); d->in_fd = tty_fn; d->out_fd = tty_fn; np_uart_driver_api_0.poll_in = np_uart_tty_poll_in; } else { /* NATIVE_UART_0_ON_STDINOUT */ d->in_fd = np_uart_ptty_get_stdin_fileno(); d->out_fd = np_uart_ptty_get_stdout_fileno(); np_uart_driver_api_0.poll_in = np_uart_stdin_poll_in; } return 0; } #if defined(CONFIG_UART_NATIVE_POSIX_PORT_1_ENABLE) /* * Initialize the 2nd UART port. * This port will be always attached to its own new pseudoterminal. */ static int np_uart_1_init(const struct device *dev) { struct native_uart_status *d; int tty_fn; d = (struct native_uart_status *)dev->data; tty_fn = np_uart_open_ptty(dev->name, NULL, false, wait_pts); d->in_fd = tty_fn; d->out_fd = tty_fn; return 0; } #endif /* * @brief Output a character towards the serial port * * @param dev UART device struct * @param out_char Character to send. */ static void np_uart_poll_out(const struct device *dev, unsigned char out_char) { int ret; struct native_uart_status *d = (struct native_uart_status *)dev->data; if (wait_pts) { while (1) { int rc = np_uart_slave_connected(d->out_fd); if (rc == 1) { break; } k_sleep(K_MSEC(100)); } } /* The return value of write() cannot be ignored (there is a warning) * but we do not need the return value for anything. */ ret = nsi_host_write(d->out_fd, &out_char, 1); (void) ret; } /** * @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(const struct device *dev, unsigned char *p_char) { int in_f = ((struct native_uart_status *)dev->data)->in_fd; static bool disconnected; int rc; if (disconnected == true) { return -1; } rc = np_uart_stdin_poll_in_bottom(in_f, p_char); if (rc == -2) { disconnected = true; return -1; } return rc; } /** * @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(const struct device *dev, unsigned char *p_char) { int n = -1; int in_f = ((struct native_uart_status *)dev->data)->in_fd; n = nsi_host_read(in_f, p_char, 1); if (n == -1) { return -1; } return 0; } DEVICE_DT_INST_DEFINE(0, np_uart_0_init, NULL, (void *)&native_uart_status_0, NULL, PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, &np_uart_driver_api_0); #if defined(CONFIG_UART_NATIVE_POSIX_PORT_1_ENABLE) DEVICE_DT_INST_DEFINE(1, np_uart_1_init, NULL, (void *)&native_uart_status_1, NULL, PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, &np_uart_driver_api_1); #endif /* CONFIG_UART_NATIVE_POSIX_PORT_1_ENABLE */ static void auto_attach_cmd_cb(char *argv, int offset) { auto_attach_cmd = &argv[offset]; auto_attach = true; } 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[] = { { .is_switch = true, .option = "attach_uart", .type = 'b', .dest = (void *)&auto_attach, .descript = "Automatically attach to the UART terminal" }, { .option = "attach_uart_cmd", .name = "\"cmd\"", .type = 's', .call_when_found = auto_attach_cmd_cb, .descript = "Command used to automatically attach to the terminal (implies " "auto_attach), by default: " "'" CONFIG_NATIVE_UART_AUTOATTACH_DEFAULT_CMD "'" }, IF_ENABLED(CONFIG_UART_NATIVE_WAIT_PTS_READY_ENABLE, ( { .is_switch = true, .option = "wait_uart", .type = 'b', .dest = (void *)&wait_pts, .descript = "Hold writes to the uart/pts until a client is connected/ready" }, )) 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) { nsi_host_close(native_uart_status_0.in_fd); } } #if defined(CONFIG_UART_NATIVE_POSIX_PORT_1_ENABLE) if (native_uart_status_1.in_fd != 0) { nsi_host_close(native_uart_status_1.in_fd); } #endif } NATIVE_TASK(np_add_uart_options, PRE_BOOT_1, 11); NATIVE_TASK(np_cleanup_uart, ON_EXIT, 99);