serial: uart_native_pty: IRQ support
Add support for the interrupt-driven API. Interrupts are emulated using a polling thread. Signed-off-by: Tim Pambor <tim.pambor@codewrights.de> Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
This commit is contained in:
committed by
Benjamin Cabé
parent
0ad98e9edb
commit
120f5a073c
@@ -564,8 +564,7 @@ program ends, one could do:
|
||||
|
||||
$ zephyr.exe --uart_attach_uart_cmd='ln -s %s /tmp/somename' ; rm /tmp/somename
|
||||
|
||||
This driver supports poll mode or async mode with :kconfig:option:`CONFIG_UART_ASYNC_API`.
|
||||
Interrupt mode is not supported.
|
||||
This driver supports poll mode, interrupt mode and async mode.
|
||||
Neither runtime configuration or line control are supported.
|
||||
|
||||
.. _native_tty_uart:
|
||||
|
||||
@@ -7,6 +7,7 @@ config UART_NATIVE_PTY
|
||||
depends on (DT_HAS_ZEPHYR_NATIVE_PTY_UART_ENABLED || DT_HAS_ZEPHYR_NATIVE_POSIX_UART_ENABLED)
|
||||
select SERIAL_HAS_DRIVER
|
||||
select SERIAL_SUPPORT_ASYNC
|
||||
select SERIAL_SUPPORT_INTERRUPT
|
||||
help
|
||||
This enables a PTY based UART driver for the POSIX ARCH with up to 2 UARTs.
|
||||
For the first UART port, the driver can be configured
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <zephyr/drivers/uart.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/atomic.h>
|
||||
#include <cmdline.h> /* native_sim command line options header */
|
||||
#include <posix_native_task.h>
|
||||
#include <nsi_host_trampolines.h>
|
||||
@@ -43,6 +44,7 @@ struct native_pty_status {
|
||||
int out_fd; /* File descriptor used for output */
|
||||
int in_fd; /* File descriptor used for input */
|
||||
bool on_stdinout; /* This UART is connected to a PTY and not STDIN/OUT */
|
||||
bool stdin_disconnected;
|
||||
|
||||
bool auto_attach; /* For PTY, attach a terminal emulator automatically */
|
||||
char *auto_attach_cmd; /* If auto_attach, which command to launch the terminal emulator */
|
||||
@@ -64,8 +66,24 @@ struct native_pty_status {
|
||||
K_KERNEL_STACK_MEMBER(rx_stack, CONFIG_ARCH_POSIX_RECOMMENDED_STACK_SIZE);
|
||||
} async;
|
||||
#endif /* CONFIG_UART_ASYNC_API */
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
struct {
|
||||
bool tx_enabled;
|
||||
bool rx_enabled;
|
||||
uart_irq_callback_user_data_t callback;
|
||||
void *cb_data;
|
||||
char char_store;
|
||||
bool char_ready;
|
||||
atomic_t thread_started;
|
||||
/* Instance-specific IRQ emulation thread. */
|
||||
struct k_thread poll_thread;
|
||||
/* Stack for IRQ emulation thread */
|
||||
K_KERNEL_STACK_MEMBER(poll_stack, CONFIG_ARCH_POSIX_RECOMMENDED_STACK_SIZE);
|
||||
} irq;
|
||||
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
||||
};
|
||||
|
||||
static int np_uart_poll_out_n(struct native_pty_status *d, const unsigned char *buf, size_t len);
|
||||
static void np_uart_poll_out(const struct device *dev, unsigned char out_char);
|
||||
static int np_uart_poll_in(const struct device *dev, unsigned char *p_char);
|
||||
static int np_uart_init(const struct device *dev);
|
||||
@@ -81,6 +99,22 @@ static int np_uart_rx_enable(const struct device *dev, uint8_t *buf, size_t len,
|
||||
static int np_uart_rx_disable(const struct device *dev);
|
||||
#endif /* CONFIG_UART_ASYNC_API */
|
||||
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
static int np_uart_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size);
|
||||
static int np_uart_fifo_read(const struct device *dev, uint8_t *rx_data, const int size);
|
||||
static void np_uart_irq_tx_enable(const struct device *dev);
|
||||
static void np_uart_irq_tx_disable(const struct device *dev);
|
||||
static int np_uart_irq_tx_ready(const struct device *dev);
|
||||
static int np_uart_irq_tx_complete(const struct device *dev);
|
||||
static void np_uart_irq_rx_enable(const struct device *dev);
|
||||
static void np_uart_irq_rx_disable(const struct device *dev);
|
||||
static int np_uart_irq_rx_ready(const struct device *dev);
|
||||
static int np_uart_irq_is_pending(const struct device *dev);
|
||||
static int np_uart_irq_update(const struct device *dev);
|
||||
static void np_uart_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb,
|
||||
void *cb_data);
|
||||
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
||||
|
||||
static DEVICE_API(uart, np_uart_driver_api) = {
|
||||
.poll_out = np_uart_poll_out,
|
||||
.poll_in = np_uart_poll_in,
|
||||
@@ -92,6 +126,20 @@ static DEVICE_API(uart, np_uart_driver_api) = {
|
||||
.rx_enable = np_uart_rx_enable,
|
||||
.rx_disable = np_uart_rx_disable,
|
||||
#endif /* CONFIG_UART_ASYNC_API */
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
.fifo_fill = np_uart_fifo_fill,
|
||||
.fifo_read = np_uart_fifo_read,
|
||||
.irq_tx_enable = np_uart_irq_tx_enable,
|
||||
.irq_tx_disable = np_uart_irq_tx_disable,
|
||||
.irq_tx_ready = np_uart_irq_tx_ready,
|
||||
.irq_tx_complete = np_uart_irq_tx_complete,
|
||||
.irq_rx_enable = np_uart_irq_rx_enable,
|
||||
.irq_rx_disable = np_uart_irq_rx_disable,
|
||||
.irq_rx_ready = np_uart_irq_rx_ready,
|
||||
.irq_is_pending = np_uart_irq_is_pending,
|
||||
.irq_update = np_uart_irq_update,
|
||||
.irq_callback_set = np_uart_irq_callback_set,
|
||||
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
||||
};
|
||||
|
||||
#define NATIVE_PTY_INSTANCE(inst) \
|
||||
@@ -162,6 +210,33 @@ static int np_uart_init(const struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Output len characters towards the serial port
|
||||
*
|
||||
* @param dev UART device struct
|
||||
* @param buf Pointer to the characters to send.
|
||||
* @param len Number of characters to send
|
||||
*/
|
||||
static int np_uart_poll_out_n(struct native_pty_status *d, const unsigned char *buf, size_t len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (d->wait_pts) {
|
||||
while (1) {
|
||||
ret = np_uart_slave_connected(d->out_fd);
|
||||
|
||||
if (ret == 1) {
|
||||
break;
|
||||
}
|
||||
k_sleep(K_MSEC(100));
|
||||
}
|
||||
}
|
||||
|
||||
ret = nsi_host_write(d->out_fd, buf, len);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* @brief Output a character towards the serial port
|
||||
*
|
||||
@@ -170,81 +245,59 @@ static int np_uart_init(const struct device *dev)
|
||||
*/
|
||||
static void np_uart_poll_out(const struct device *dev, unsigned char out_char)
|
||||
{
|
||||
int ret;
|
||||
struct native_pty_status *d = (struct native_pty_status *)dev->data;
|
||||
(void)np_uart_poll_out_n((struct native_pty_status *)dev->data, &out_char, 1);
|
||||
}
|
||||
|
||||
if (d->wait_pts) {
|
||||
while (1) {
|
||||
int rc = np_uart_slave_connected(d->out_fd);
|
||||
/**
|
||||
* @brief Poll the device for up to len input characters
|
||||
*
|
||||
* @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_read_n(struct native_pty_status *data, unsigned char *p_char, int len)
|
||||
{
|
||||
int rc = -1;
|
||||
int in_f = data->in_fd;
|
||||
|
||||
if (rc == 1) {
|
||||
break;
|
||||
}
|
||||
k_sleep(K_MSEC(100));
|
||||
if (len <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (data->on_stdinout) {
|
||||
if (data->stdin_disconnected) {
|
||||
return -1;
|
||||
}
|
||||
rc = np_uart_stdin_read_bottom(in_f, p_char, len);
|
||||
|
||||
if (rc == -2) {
|
||||
data->stdin_disconnected = true;
|
||||
return -1;
|
||||
}
|
||||
|
||||
} else {
|
||||
rc = nsi_host_read(in_f, p_char, len);
|
||||
}
|
||||
|
||||
/* 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_pty_status *)dev->data)->in_fd;
|
||||
static bool disconnected;
|
||||
int rc;
|
||||
|
||||
if (disconnected == true) {
|
||||
return -1;
|
||||
if (rc > 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = np_uart_stdin_poll_in_bottom(in_f, p_char, 1);
|
||||
if (rc == -2) {
|
||||
disconnected = true;
|
||||
}
|
||||
return rc == 1 ? 0 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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_pty_poll_in(const struct device *dev, unsigned char *p_char)
|
||||
{
|
||||
int n = -1;
|
||||
int in_f = ((struct native_pty_status *)dev->data)->in_fd;
|
||||
|
||||
n = nsi_host_read(in_f, p_char, 1);
|
||||
if (n == -1) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int np_uart_poll_in(const struct device *dev, unsigned char *p_char)
|
||||
{
|
||||
if (((struct native_pty_status *)dev->data)->on_stdinout) {
|
||||
return np_uart_stdin_poll_in(dev, p_char);
|
||||
} else {
|
||||
return np_uart_pty_poll_in(dev, p_char);
|
||||
struct native_pty_status *data = dev->data;
|
||||
|
||||
int ret = np_uart_read_n(data, p_char, 1);
|
||||
|
||||
if (ret == -1) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_UART_ASYNC_API
|
||||
@@ -337,8 +390,7 @@ static void native_pty_uart_async_poll_function(void *arg1, void *arg2, void *ar
|
||||
ARG_UNUSED(arg3);
|
||||
|
||||
while (data->async.rx_len) {
|
||||
rc = np_uart_stdin_poll_in_bottom(data->in_fd, data->async.rx_buf,
|
||||
data->async.rx_len);
|
||||
rc = np_uart_read_n(data, data->async.rx_buf, data->async.rx_len);
|
||||
if (rc > 0) {
|
||||
/* Data received */
|
||||
evt.type = UART_RX_RDY;
|
||||
@@ -403,6 +455,222 @@ static int np_uart_rx_disable(const struct device *dev)
|
||||
|
||||
#endif /* CONFIG_UART_ASYNC_API */
|
||||
|
||||
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
|
||||
static void np_uart_irq_handler(const struct device *dev)
|
||||
{
|
||||
struct native_pty_status *data = dev->data;
|
||||
|
||||
if (data->irq.callback) {
|
||||
data->irq.callback(dev, data->irq.cb_data);
|
||||
} else {
|
||||
ERROR("%s: No callback registered\n", __func__);
|
||||
}
|
||||
}
|
||||
|
||||
static void np_uart_irq_read_1_ahead(struct native_pty_status *data)
|
||||
{
|
||||
int ret = np_uart_read_n(data, &data->irq.char_store, 1);
|
||||
|
||||
if (ret == 1) {
|
||||
data->irq.char_ready = true;
|
||||
}
|
||||
|
||||
if (data->stdin_disconnected) {
|
||||
/* There won't be any more data ever */
|
||||
data->irq.rx_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Emulate uart interrupts using a polling thread
|
||||
*/
|
||||
static void np_uart_irq_thread(void *arg1, void *arg2, void *arg3)
|
||||
{
|
||||
ARG_UNUSED(arg2);
|
||||
ARG_UNUSED(arg3);
|
||||
|
||||
struct device *dev = (struct device *)arg1;
|
||||
struct native_pty_status *data = dev->data;
|
||||
|
||||
while (1) {
|
||||
if (data->irq.rx_enabled) {
|
||||
if (!data->irq.char_ready) {
|
||||
np_uart_irq_read_1_ahead(data);
|
||||
}
|
||||
|
||||
if (data->irq.char_ready) {
|
||||
np_uart_irq_handler(dev);
|
||||
}
|
||||
}
|
||||
if (data->irq.tx_enabled) {
|
||||
np_uart_irq_handler(dev);
|
||||
}
|
||||
|
||||
if ((data->irq.tx_enabled) ||
|
||||
((data->irq.rx_enabled) && (data->irq.char_ready))) {
|
||||
/* There is pending work. Let's handle it right away */
|
||||
continue;
|
||||
}
|
||||
|
||||
k_timeout_t wait = K_FOREVER;
|
||||
|
||||
if (data->irq.rx_enabled) {
|
||||
wait = K_MSEC(10);
|
||||
}
|
||||
(void)k_sleep(wait);
|
||||
}
|
||||
}
|
||||
|
||||
static void np_uart_irq_thread_start(const struct device *dev)
|
||||
{
|
||||
struct native_pty_status *data = dev->data;
|
||||
|
||||
/* Create a thread which will wait for data - replacement for IRQ */
|
||||
k_thread_create(&data->irq.poll_thread, data->irq.poll_stack,
|
||||
K_KERNEL_STACK_SIZEOF(data->irq.poll_stack),
|
||||
np_uart_irq_thread,
|
||||
(void *)dev, NULL, NULL,
|
||||
K_HIGHEST_THREAD_PRIO, 0, K_NO_WAIT);
|
||||
}
|
||||
|
||||
static int np_uart_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size)
|
||||
{
|
||||
return np_uart_poll_out_n((struct native_pty_status *)dev->data, tx_data, size);
|
||||
}
|
||||
|
||||
static int np_uart_fifo_read(const struct device *dev, uint8_t *rx_data, int size)
|
||||
{
|
||||
uint32_t len = 0;
|
||||
int ret;
|
||||
struct native_pty_status *data = dev->data;
|
||||
|
||||
if ((size <= 0) || data->stdin_disconnected) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (data->irq.char_ready) {
|
||||
rx_data[0] = data->irq.char_store;
|
||||
rx_data++;
|
||||
size--;
|
||||
len = 1;
|
||||
data->irq.char_ready = false;
|
||||
/* Note this native_sim driver code cannot be interrupted,
|
||||
* so there is no race with np_uart_irq_thread()
|
||||
*/
|
||||
}
|
||||
|
||||
ret = np_uart_read_n(data, rx_data, size);
|
||||
|
||||
if (ret > 0) {
|
||||
len += ret;
|
||||
np_uart_irq_read_1_ahead(data);
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int np_uart_irq_tx_ready(const struct device *dev)
|
||||
{
|
||||
struct native_pty_status *data = dev->data;
|
||||
|
||||
return data->irq.tx_enabled ? 1 : 0;
|
||||
}
|
||||
|
||||
static int np_uart_irq_tx_complete(const struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void np_uart_irq_tx_enable(const struct device *dev)
|
||||
{
|
||||
struct native_pty_status *data = dev->data;
|
||||
|
||||
bool kick_thread = !data->irq.tx_enabled;
|
||||
|
||||
data->irq.tx_enabled = true;
|
||||
|
||||
if (!atomic_set(&data->irq.thread_started, 1)) {
|
||||
np_uart_irq_thread_start(dev);
|
||||
}
|
||||
|
||||
if (kick_thread) {
|
||||
/* Let's ensure the thread wakes to allow the Tx right away */
|
||||
k_wakeup(&data->irq.poll_thread);
|
||||
}
|
||||
}
|
||||
|
||||
static void np_uart_irq_tx_disable(const struct device *dev)
|
||||
{
|
||||
struct native_pty_status *data = dev->data;
|
||||
|
||||
data->irq.tx_enabled = false;
|
||||
}
|
||||
|
||||
static void np_uart_irq_rx_enable(const struct device *dev)
|
||||
{
|
||||
struct native_pty_status *data = dev->data;
|
||||
|
||||
if (data->stdin_disconnected) {
|
||||
/* There won't ever be data => we ignore the request */
|
||||
return;
|
||||
}
|
||||
|
||||
bool kick_thread = !data->irq.rx_enabled;
|
||||
|
||||
data->irq.rx_enabled = true;
|
||||
|
||||
if (!atomic_set(&data->irq.thread_started, 1)) {
|
||||
np_uart_irq_thread_start(dev);
|
||||
}
|
||||
|
||||
if (kick_thread) {
|
||||
/* Let's ensure the thread wakes to try to check for data */
|
||||
k_wakeup(&data->irq.poll_thread);
|
||||
}
|
||||
}
|
||||
|
||||
static void np_uart_irq_rx_disable(const struct device *dev)
|
||||
{
|
||||
struct native_pty_status *data = dev->data;
|
||||
|
||||
data->irq.rx_enabled = false;
|
||||
}
|
||||
|
||||
static int np_uart_irq_rx_ready(const struct device *dev)
|
||||
{
|
||||
struct native_pty_status *data = dev->data;
|
||||
|
||||
if (data->irq.rx_enabled && data->irq.char_ready) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int np_uart_irq_is_pending(const struct device *dev)
|
||||
{
|
||||
return np_uart_irq_rx_ready(dev) ||
|
||||
np_uart_irq_tx_ready(dev);
|
||||
}
|
||||
|
||||
static int np_uart_irq_update(const struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void np_uart_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb,
|
||||
void *cb_data)
|
||||
{
|
||||
struct native_pty_status *data = dev->data;
|
||||
|
||||
data->irq.callback = cb;
|
||||
data->irq.cb_data = cb_data;
|
||||
}
|
||||
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
|
||||
|
||||
|
||||
#define NATIVE_PTY_SET_AUTO_ATTACH_CMD(inst, cmd) \
|
||||
native_pty_status_##inst.auto_attach_cmd = cmd;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include <string.h>
|
||||
#include <pty.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/select.h>
|
||||
#include <poll.h>
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
#include <nsi_tracing.h>
|
||||
@@ -38,39 +38,35 @@
|
||||
* @retval -1 If no character was available to read
|
||||
* @retval -2 if the stdin is disconnected
|
||||
*/
|
||||
int np_uart_stdin_poll_in_bottom(int in_f, unsigned char *p_char, int len)
|
||||
int np_uart_stdin_read_bottom(int in_f, unsigned char *p_char, int len)
|
||||
{
|
||||
if (feof(stdin)) {
|
||||
/*
|
||||
* The stdinput is fed from a file which finished or the user
|
||||
* pressed Ctrl+D
|
||||
*/
|
||||
return -2;
|
||||
}
|
||||
|
||||
int n = -1;
|
||||
|
||||
int ready;
|
||||
fd_set readfds;
|
||||
static struct timeval timeout; /* just zero */
|
||||
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(in_f, &readfds);
|
||||
struct pollfd fds = {.fd = in_f, .events = POLLIN};
|
||||
|
||||
ready = select(in_f+1, &readfds, NULL, NULL, &timeout);
|
||||
ready = poll(&fds, 1, 0);
|
||||
|
||||
if (ready == 0) {
|
||||
return -1;
|
||||
} else if (ready == -1) {
|
||||
ERROR("%s: Error on select ()\n", __func__);
|
||||
ERROR("%s: Error on poll ()\n", __func__);
|
||||
}
|
||||
|
||||
if (len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
n = read(in_f, p_char, len);
|
||||
if ((n == -1) || (n == 0)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return n;
|
||||
if (n == 0) {
|
||||
/* Attempting to read > 0 but getting 0 characters back
|
||||
* indicates we reached EOF
|
||||
*/
|
||||
return -2;
|
||||
} else {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,7 +20,7 @@ extern "C" {
|
||||
|
||||
/* Note: None of these functions are public interfaces. But internal to the native ptty driver */
|
||||
|
||||
int np_uart_stdin_poll_in_bottom(int in_f, unsigned char *p_char, int len);
|
||||
int np_uart_stdin_read_bottom(int in_f, unsigned char *p_char, int len);
|
||||
int np_uart_slave_connected(int fd);
|
||||
int np_uart_open_pty(const char *uart_name, const char *auto_attach_cmd,
|
||||
bool do_auto_attach, bool wait_pts);
|
||||
|
||||
Reference in New Issue
Block a user