From d63a10da54745326572d2a4887f77ece6bce4404 Mon Sep 17 00:00:00 2001 From: Dmytro Firsov Date: Fri, 17 Sep 2021 00:10:49 +0300 Subject: [PATCH] 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 --- drivers/serial/Kconfig.xen | 1 + drivers/serial/uart_hvc_xen.c | 123 ++++++++++++++++++++++++++++++++++ include/xen/console.h | 5 ++ 3 files changed, 129 insertions(+) diff --git a/drivers/serial/Kconfig.xen b/drivers/serial/Kconfig.xen index e95ddb7c458..82b191d8b35 100644 --- a/drivers/serial/Kconfig.xen +++ b/drivers/serial/Kconfig.xen @@ -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 diff --git a/drivers/serial/uart_hvc_xen.c b/drivers/serial/uart_hvc_xen.c index 9b0004bc792..d593b2edd0b 100644 --- a/drivers/serial/uart_hvc_xen.c +++ b/drivers/serial/uart_hvc_xen.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -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; diff --git a/include/xen/console.h b/include/xen/console.h index 2e617b04d1d..87e63c47729 100644 --- a/include/xen/console.h +++ b/include/xen/console.h @@ -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);