From e23eafadd1965f8f31e2f7569ef0f8f0665d3b59 Mon Sep 17 00:00:00 2001 From: Dan Milea Date: Tue, 20 Jun 2023 15:52:43 +0300 Subject: [PATCH] virtio-mmio: console device Added virtio console device support. Signed-off-by: Dan Milea --- lib/include/openamp/virtio_serial_drv.h | 71 +++++++++++ lib/virtio_mmio/CMakeLists.txt | 1 + lib/virtio_mmio/virtio_serial_drv.c | 158 ++++++++++++++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 lib/include/openamp/virtio_serial_drv.h create mode 100644 lib/virtio_mmio/virtio_serial_drv.c diff --git a/lib/include/openamp/virtio_serial_drv.h b/lib/include/openamp/virtio_serial_drv.h new file mode 100644 index 000000000..b5942cd60 --- /dev/null +++ b/lib/include/openamp/virtio_serial_drv.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2022 Wind River Systems, Inc. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef VIRTIO_SERIAL_H +#define VIRTIO_SERIAL_H + +#include +#include +#include + +#define VQIN_SIZE 4 +#define VQOUT_SIZE 4 + +struct virtio_serial_chan { + atomic_long tx_inuse; + atomic_long rx_inuse; + bool rxpoll_active; + bool txpoll_active; + uint8_t rxpoll; + uint8_t txpoll; +}; + +struct virtio_serial_data { + struct virtio_serial_chan *chan0; +}; + +typedef void (*con_funcptr)(void *); + +/** + * @brief Initialize a VIRTIO console device. + * + * @param[in] vdev Pointer to virtio_device structure. + * @param[in] vqs Array of pointers to the virtqueues used by the device. + * @param[in] vq_names Array of pointers to the virtqueues names. + * @param[in] cbs Array of function pointers to call on virtqueue kick. + * @param[in] cb_args Array of pointers to parameters for kick callbacks. + * @param[in] vq_count Number of virtqueues the device uses. + * + * @return int 0 for success. + */ + +int virtio_serial_init(struct virtio_device *vdev, struct virtqueue **vqs, char **vq_names, + con_funcptr *cbs, void **cb_args, int vq_count); + +/** + * @brief Poll console input queue for data. + * + * @param[in] vdev Pointer to virtio_device structure. + * @param[out] p_char Output char. + * + * @return int 0 for success. + */ + +int virtio_serial_poll_in(const struct virtio_device *vdev, unsigned char *p_char); + +/** + * @brief Send data routine. + * + * @param[in] vdev Pointer to virtio_device structure. + * @param[in] out_char Character to send. + * + * @return int 0 for success. + */ + +void virtio_serial_poll_out(const struct virtio_device *vdev, unsigned char out_char); + +#endif /* VIRTIO_SERIAL_H */ + diff --git a/lib/virtio_mmio/CMakeLists.txt b/lib/virtio_mmio/CMakeLists.txt index 3bceb6272..711a3aaf4 100644 --- a/lib/virtio_mmio/CMakeLists.txt +++ b/lib/virtio_mmio/CMakeLists.txt @@ -2,4 +2,5 @@ if (WITH_VIRTIO_MMIO_DRV) collect (PROJECT_LIB_SOURCES virtio_mmio_drv.c) collect (PROJECT_LIB_SOURCES virtio_rng_drv.c) collect (PROJECT_LIB_SOURCES virtio_net_drv.c) +collect (PROJECT_LIB_SOURCES virtio_serial_drv.c) endif (WITH_VIRTIO_MMIO_DRV) diff --git a/lib/virtio_mmio/virtio_serial_drv.c b/lib/virtio_mmio/virtio_serial_drv.c new file mode 100644 index 000000000..528f6771d --- /dev/null +++ b/lib/virtio_mmio/virtio_serial_drv.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2022 Wind River Systems, Inc. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include + +int virtio_serial_init(struct virtio_device *vdev, struct virtqueue **vqs, char **vq_names, + void (**cbs)(void *), void **cb_args, int vq_count) +{ + uint32_t devid, features; + struct virtqueue *vq = NULL; + int i; + + if (!vdev) { + return -EINVAL; + } + + vdev->vrings_info = metal_allocate_memory(sizeof(struct virtio_vring_info) * vq_count); + if (!vdev->vrings_info) { + return -ENOMEM; + } + + devid = virtio_get_devid(vdev); + if (devid != VIRTIO_ID_CONSOLE) { + metal_log(METAL_LOG_ERROR, "Expected devid %04x, got %04x\n", + VIRTIO_ID_CONSOLE, devid); + metal_free_memory(vdev->vrings_info); + return -ENODEV; + } + virtio_device_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER); + virtio_device_set_features(vdev, 0/*VIRTIO_F_NOTIFY_ON_EMPTY*/); + features = virtio_device_get_features(vdev); + metal_log(METAL_LOG_DEBUG, "features: %08x\n", features); + + for (i = 0; i < vq_count; i++) { + /* TODO: update API for compatibility with other transports like + * remoteproc virtio + */ + vq = virtio_drv_setup_virtqueue( + vdev, + i, + vqs[i], + cbs[i], + cb_args[i], + vq_names[i] + ); + + if (!vq) { + return -1; + } + } + + virtio_device_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER_OK); + + for (i = 0; i < vq_count; i++) { + virtqueue_kick(vq); + } + + return 0; +} + +int virtio_serial_poll_in(const struct virtio_device *vdev, unsigned char *p_char) +{ + struct virtio_serial_chan *chan = NULL; + struct virtqueue *vq = NULL; + int ret = -1; + long exp = 0; + void *cookie = NULL; + struct virtqueue_buf vb[1] = {0}; + + if (!vdev || !vdev->priv) { + return -EINVAL; + } + + chan = ((struct virtio_serial_data *)(vdev->priv))->chan0; + + if (!chan) { + return -EINVAL; + } + + vq = vdev->vrings_info[0].vq; + + if (!atomic_compare_exchange_strong(&chan->rx_inuse, &exp, 1)) { + return ret; + } + + if (chan->rxpoll_active) { + cookie = virtqueue_get_buffer(vq, NULL, NULL); + + if (!cookie) { + /* Nothing received yet */ + atomic_store(&chan->rx_inuse, 0); + return -1; + } + *p_char = chan->rxpoll; + ret = 0; + } + + vb[0].buf = &chan->rxpoll; + vb[0].len = 1; + + virtqueue_add_buffer(vq, vb, 0, 1, (void *)(uintptr_t)1); + virtqueue_kick(vq); + chan->rxpoll_active = true; + atomic_store(&chan->rx_inuse, 0); + + return ret; +} + +void virtio_serial_poll_out(const struct virtio_device *vdev, unsigned char out_char) +{ + struct virtio_serial_chan *chan = NULL; + struct virtqueue *vq = NULL; + struct virtqueue_buf vb[1] = {0}; + uint8_t *data = NULL; + long exp = 0; + + if (!vdev) { + return; + } + + chan = ((struct virtio_serial_data *)(vdev->priv))->chan0; + + if (!chan) { + return; + } + + data = &chan->txpoll; + vq = vdev->vrings_info[1].vq; + + if (!atomic_compare_exchange_strong(&chan->tx_inuse, &exp, 1)) { + return; + } + + if (virtqueue_full(vq)) { + virtqueue_get_buffer(vq, NULL, NULL); + } + + if (virtqueue_full(vq)) { + atomic_store(&chan->tx_inuse, 0); + return; + } + + data[0] = out_char; + vb[0].buf = data; + vb[0].len = 1; + + virtqueue_add_buffer(vq, vb, 1, 0, (void *)(uintptr_t)1); + virtqueue_kick(vq); + atomic_store(&chan->tx_inuse, 0); +}