-
Notifications
You must be signed in to change notification settings - Fork 294
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added virtio console device support. Signed-off-by: Dan Milea <[email protected]>
- Loading branch information
Dan Milea
committed
Jun 20, 2023
1 parent
9b1b72e
commit e23eafa
Showing
3 changed files
with
230 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 */ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |