xenvm: drivers: serial: add interrupt-driven API for Xen PV console

This commit adds support of interrupt-driven API for UART-like Xen PV
console driver. It is implemented via Xen event channels. It allows to
send and receive data by chunks (not single symbols) and without
polling.

Signed-off-by: Dmytro Firsov <dmytro_firsov@epam.com>
This commit is contained in:
Dmytro Firsov 2021-09-17 00:10:49 +03:00 committed by Anas Nashif
commit d63a10da54
3 changed files with 129 additions and 0 deletions

View file

@ -7,6 +7,7 @@
config UART_XEN_HVC
bool "Xen hypervisor console UART Driver"
select SERIAL_HAS_DRIVER
select SERIAL_SUPPORT_INTERRUPT
depends on BOARD_XENVM
default y
help

View file

@ -10,6 +10,7 @@
#include <xen/generic.h>
#include <xen/hvm.h>
#include <xen/public/io/console.h>
#include <xen/public/sched.h>
#include <xen/public/xen.h>
#include <device.h>
@ -22,6 +23,10 @@ LOG_MODULE_REGISTER(uart_hvc_xen);
static struct hvc_xen_data hvc_data = {0};
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static void hvc_uart_evtchn_cb(void *priv);
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
static int read_from_ring(const struct device *dev, char *str, int len)
{
int recv = 0;
@ -100,11 +105,125 @@ static void xen_hvc_poll_out(const struct device *dev,
(void) write_to_ring(dev, &c, sizeof(c));
}
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static int xen_hvc_fifo_fill(const struct device *dev, const uint8_t *tx_data,
int len)
{
int ret = 0, sent = 0;
while (len) {
sent = write_to_ring(dev, tx_data, len);
ret += sent;
tx_data += sent;
len -= sent;
if (len) {
/* Need to be able to read it from another domain */
HYPERVISOR_sched_op(SCHEDOP_yield, NULL);
}
}
return ret;
}
static int xen_hvc_fifo_read(const struct device *dev, uint8_t *rx_data,
const int size)
{
return read_from_ring(dev, rx_data, size);
}
static void xen_hvc_irq_tx_enable(const struct device *dev)
{
/*
* Need to explicitly call UART callback on TX enabling to
* process available buffered TX actions, because no HV events
* will be generated on tx_enable.
*/
hvc_uart_evtchn_cb(dev->data);
}
static int xen_hvc_irq_tx_ready(const struct device *dev)
{
return 1;
}
static void xen_hvc_irq_rx_enable(const struct device *dev)
{
/*
* Need to explicitly call UART callback on RX enabling to
* process available buffered RX actions, because no HV events
* will be generated on rx_enable.
*/
hvc_uart_evtchn_cb(dev->data);
}
static int xen_hvc_irq_tx_complete(const struct device *dev)
{
/*
* TX is performed by copying in ring buffer by fifo_fill,
* so it will be always completed.
*/
return 1;
}
static int xen_hvc_irq_rx_ready(const struct device *dev)
{
struct hvc_xen_data *data = dev->data;
/* RX is ready only if data is available in ring buffer */
return (data->intf->in_prod != data->intf->in_cons);
}
static int xen_hvc_irq_is_pending(const struct device *dev)
{
return xen_hvc_irq_rx_ready(dev);
}
static int xen_hvc_irq_update(const struct device *dev)
{
/* Nothing needs to be updated before actual ISR */
return 1;
}
static void xen_hvc_irq_callback_set(const struct device *dev,
uart_irq_callback_user_data_t cb, void *user_data)
{
struct hvc_xen_data *data = dev->data;
data->irq_cb = cb;
data->irq_cb_data = user_data;
}
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
static const struct uart_driver_api xen_hvc_api = {
.poll_in = xen_hvc_poll_in,
.poll_out = xen_hvc_poll_out,
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
.fifo_fill = xen_hvc_fifo_fill,
.fifo_read = xen_hvc_fifo_read,
.irq_tx_enable = xen_hvc_irq_tx_enable,
.irq_tx_ready = xen_hvc_irq_tx_ready,
.irq_rx_enable = xen_hvc_irq_rx_enable,
.irq_tx_complete = xen_hvc_irq_tx_complete,
.irq_rx_ready = xen_hvc_irq_rx_ready,
.irq_is_pending = xen_hvc_irq_is_pending,
.irq_update = xen_hvc_irq_update,
.irq_callback_set = xen_hvc_irq_callback_set,
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
};
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
static void hvc_uart_evtchn_cb(void *priv)
{
struct hvc_xen_data *data = priv;
if (data->irq_cb) {
data->irq_cb(data->dev, data->irq_cb_data);
}
}
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
int xen_console_init(const struct device *dev)
{
int ret = 0;
@ -134,6 +253,10 @@ int xen_console_init(const struct device *dev)
data->intf = (struct xencons_interface *) DEVICE_MMIO_GET(dev);
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
bind_event_channel(data->evtchn, hvc_uart_evtchn_cb, data);
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
LOG_INF("Xen HVC inited successfully\n");
return 0;

View file

@ -16,6 +16,11 @@ struct hvc_xen_data {
const struct device *dev;
struct xencons_interface *intf;
uint64_t evtchn;
#ifdef CONFIG_UART_INTERRUPT_DRIVEN
uart_irq_callback_user_data_t irq_cb;
void *irq_cb_data;
#endif /* CONFIG_UART_INTERRUPT_DRIVEN */
};
int xen_console_init(const struct device *dev);