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:
parent
01a9b117fe
commit
d63a10da54
3 changed files with 129 additions and 0 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue