Skip to content

Commit

Permalink
virtio-mmio: console device
Browse files Browse the repository at this point in the history
Added virtio console device support.

Signed-off-by: Dan Milea <dan.milea@windriver.com>
  • Loading branch information
Dan Milea authored and arnopo committed Jul 5, 2023
1 parent 71f037c commit b4c7edd
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 0 deletions.
71 changes: 71 additions & 0 deletions lib/include/openamp/virtio_serial_drv.h
Original file line number Diff line number Diff line change
@@ -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 <openamp/virtqueue.h>
#include <openamp/virtio.h>
#include <metal/device.h>

#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 */

1 change: 1 addition & 0 deletions lib/virtio_mmio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
158 changes: 158 additions & 0 deletions lib/virtio_mmio/virtio_serial_drv.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
* Copyright (c) 2022 Wind River Systems, Inc.
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include <openamp/open_amp.h>
#include <openamp/virtqueue.h>
#include <openamp/virtio.h>
#include <openamp/virtio_drv.h>
#include <metal/device.h>
#include <openamp/virtio_serial_drv.h>

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);
}

0 comments on commit b4c7edd

Please sign in to comment.