mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2026-08-23 02:08:48 +08:00
1. version for v1.2 2. support packed queue 1. support MMIO/PCI over bus 2. new virtio queue api with dma/cpu sync 3. update for virtio device drivers: - net - block - console - input - gpu 4. new virtio device drivers: - 9p - crypto - rng - scmi - scsi Signed-off-by: GuEe-GUI <2991707448@qq.com>
733 lines
18 KiB
C
733 lines
18 KiB
C
/*
|
|
* Copyright (c) 2006-2023, RT-Thread Development Team
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2021-11-11 GuEe-GUI the first version
|
|
* 2023-02-25 GuEe-GUI using virtio dm
|
|
*/
|
|
|
|
#include <rthw.h>
|
|
#include <rtthread.h>
|
|
#include <rtdevice.h>
|
|
|
|
#define DBG_TAG "virtio.dev.console"
|
|
#define DBG_LVL DBG_INFO
|
|
#include <rtdbg.h>
|
|
|
|
#include <cpuport.h>
|
|
|
|
#include "virtio_internal.h"
|
|
#include "virtio_config/virtio-console.h"
|
|
|
|
#define QUEUE_PORT_RX 0
|
|
#define QUEUE_PORT_TX 1
|
|
#define QUEUE_CTRL_RX 2
|
|
#define QUEUE_CTRL_TX 3
|
|
|
|
struct virtio_console;
|
|
|
|
struct console_port
|
|
{
|
|
struct rt_serial_device parent;
|
|
char name[64];
|
|
|
|
char *stream;
|
|
char *rx_stream;
|
|
char *tx_stream;
|
|
rt_bool_t irq_enabled;
|
|
rt_bool_t host_connected;
|
|
struct rt_spinlock lock;
|
|
struct virtio_console *vconsole;
|
|
|
|
struct rt_virtqueue *rx_vq;
|
|
struct rt_virtqueue *tx_vq;
|
|
};
|
|
|
|
#define raw_to_console_port(raw) rt_container_of(raw, struct console_port, parent)
|
|
|
|
struct virtio_console
|
|
{
|
|
struct rt_virtio_device *vdev;
|
|
|
|
rt_uint32_t uid;
|
|
rt_size_t stream_rx_size;
|
|
rt_size_t stream_tx_size;
|
|
|
|
struct rt_spinlock lock;
|
|
struct rt_virtqueue *ctrl_rx_vq;
|
|
struct rt_virtqueue *ctrl_tx_vq;
|
|
|
|
rt_size_t ctrl_packet_size;
|
|
struct virtio_console_control_ext *ctrl_packet;
|
|
|
|
struct rt_work ctrl_work;
|
|
struct rt_work config_work;
|
|
|
|
rt_size_t max_port_nr;
|
|
struct rt_virtqueue **vqs;
|
|
struct console_port *ports;
|
|
};
|
|
|
|
static struct rt_dm_ida virtio_console_ida = RT_DM_IDA_INIT(CUSTOM);
|
|
|
|
rt_inline rt_bool_t console_multiport(struct virtio_console *vconsole)
|
|
{
|
|
return rt_virtio_has_feature(vconsole->vdev, VIRTIO_CONSOLE_F_MULTIPORT);
|
|
}
|
|
|
|
static rt_ssize_t console_send_ctrl_msg(struct virtio_console *vconsole,
|
|
rt_uint32_t port_id, rt_uint16_t event, rt_uint16_t value)
|
|
{
|
|
struct rt_virtqueue *vq;
|
|
struct virtio_console_control ctrl_packet;
|
|
|
|
if (!vconsole || vconsole->max_port_nr <= 1)
|
|
{
|
|
return RT_EOK;
|
|
}
|
|
|
|
vq = vconsole->ctrl_tx_vq;
|
|
rt_spin_lock(&vconsole->lock);
|
|
|
|
ctrl_packet.id = cpu_to_virtio32(vconsole->vdev, port_id);
|
|
ctrl_packet.event = cpu_to_virtio16(vconsole->vdev, event);
|
|
ctrl_packet.value = cpu_to_virtio16(vconsole->vdev, value);
|
|
|
|
rt_virtqueue_add_outbuf(vq, &ctrl_packet, sizeof(ctrl_packet));
|
|
rt_virtqueue_kick(vq);
|
|
|
|
while (!rt_virtqueue_read_buf(vq, RT_NULL))
|
|
{
|
|
rt_hw_cpu_relax();
|
|
}
|
|
|
|
rt_spin_unlock(&vconsole->lock);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static void console_emergency_write(struct virtio_console *vconsole,
|
|
rt_uint32_t emerg_wr)
|
|
{
|
|
rt_virtio_write_config(vconsole->vdev, struct virtio_console_config,
|
|
emerg_wr, emerg_wr);
|
|
}
|
|
|
|
static rt_err_t console_port_configure(struct rt_serial_device *serial,
|
|
struct serial_configure *cfg)
|
|
{
|
|
return RT_EOK;
|
|
}
|
|
|
|
static rt_err_t console_port_control(struct rt_serial_device *serial, int cmd, void *arg)
|
|
{
|
|
struct console_port *port = raw_to_console_port(serial);
|
|
|
|
switch (cmd)
|
|
{
|
|
case RT_DEVICE_CTRL_CLR_INT:
|
|
port->irq_enabled = RT_FALSE;
|
|
break;
|
|
|
|
case RT_DEVICE_CTRL_SET_INT:
|
|
port->irq_enabled = RT_TRUE;
|
|
break;
|
|
}
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static int console_port_putc(struct rt_serial_device *serial, char c)
|
|
{
|
|
rt_base_t level;
|
|
char *tx_stream;
|
|
struct rt_virtqueue *vq;
|
|
struct console_port *port = raw_to_console_port(serial);
|
|
|
|
if (!port->host_connected)
|
|
{
|
|
if (rt_virtio_has_feature(port->vconsole->vdev, VIRTIO_CONSOLE_F_EMERG_WRITE))
|
|
{
|
|
console_emergency_write(port->vconsole, c);
|
|
|
|
if (port->parent.parent.open_flag & RT_DEVICE_FLAG_INT_TX)
|
|
{
|
|
rt_hw_serial_isr(&port->parent, RT_SERIAL_EVENT_TX_DONE);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
vq = port->tx_vq;
|
|
rt_virtqueue_wait_prepare(vq, 1);
|
|
|
|
level = rt_spin_lock_irqsave(&port->lock);
|
|
|
|
tx_stream = &port->tx_stream[rt_virtqueue_next_buf_index(vq)];
|
|
*tx_stream = c;
|
|
rt_virtqueue_add_outbuf(vq, tx_stream, sizeof(*tx_stream));
|
|
rt_virtqueue_kick(vq);
|
|
|
|
if (port->parent.parent.open_flag & ~RT_DEVICE_FLAG_INT_TX)
|
|
{
|
|
while (!rt_virtqueue_read_buf(vq, RT_NULL))
|
|
{
|
|
rt_hw_cpu_relax();
|
|
}
|
|
}
|
|
|
|
rt_spin_unlock_irqrestore(&port->lock, level);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int console_port_getc(struct rt_serial_device *serial)
|
|
{
|
|
rt_base_t level;
|
|
int ch = -1;
|
|
char *rx_stream;
|
|
struct rt_virtqueue *vq;
|
|
struct console_port *port = raw_to_console_port(serial);
|
|
|
|
level = rt_spin_lock_irqsave(&port->lock);
|
|
|
|
vq = port->rx_vq;
|
|
if ((rx_stream = rt_virtqueue_read_buf(vq, RT_NULL)))
|
|
{
|
|
ch = *rx_stream;
|
|
|
|
rt_virtqueue_add_inbuf(vq, rx_stream, sizeof(char));
|
|
|
|
rt_virtqueue_kick(vq);
|
|
}
|
|
|
|
rt_spin_unlock_irqrestore(&port->lock, level);
|
|
|
|
return ch;
|
|
}
|
|
|
|
static const struct rt_uart_ops console_port_ops =
|
|
{
|
|
.configure = console_port_configure,
|
|
.control = console_port_control,
|
|
.putc = console_port_putc,
|
|
.getc = console_port_getc,
|
|
};
|
|
|
|
static rt_err_t console_port_add(struct virtio_console *vconsole,
|
|
struct console_port *port)
|
|
{
|
|
char *stream;
|
|
const char *name;
|
|
rt_uint32_t id = port - vconsole->ports;
|
|
|
|
rt_memset(port, 0, sizeof(*port));
|
|
|
|
if (!(stream = rt_malloc(sizeof(char) * (vconsole->stream_rx_size + vconsole->stream_tx_size))))
|
|
{
|
|
return -RT_ENOMEM;
|
|
}
|
|
|
|
port->stream = stream;
|
|
port->tx_stream = stream;
|
|
port->rx_stream = stream + vconsole->stream_tx_size;
|
|
port->vconsole = vconsole;
|
|
rt_spin_lock_init(&port->lock);
|
|
|
|
if (!port->rx_vq || !port->tx_vq)
|
|
{
|
|
if (id != 0)
|
|
{
|
|
port->rx_vq = vconsole->vqs[4 + (id - 1) * 2 + QUEUE_PORT_RX];
|
|
port->tx_vq = vconsole->vqs[4 + (id - 1) * 2 + QUEUE_PORT_TX];
|
|
}
|
|
else
|
|
{
|
|
port->rx_vq = vconsole->vqs[QUEUE_PORT_RX];
|
|
port->tx_vq = vconsole->vqs[QUEUE_PORT_TX];
|
|
}
|
|
|
|
for (int idx = 0; idx < vconsole->stream_rx_size; ++idx)
|
|
{
|
|
rt_virtqueue_add_inbuf(port->rx_vq, &port->rx_stream[idx], sizeof(char));
|
|
|
|
rt_virtqueue_submit(port->rx_vq);
|
|
}
|
|
|
|
rt_virtqueue_notify(port->rx_vq);
|
|
}
|
|
|
|
console_send_ctrl_msg(vconsole, VIRTIO_CONSOLE_BAD_ID,
|
|
VIRTIO_CONSOLE_DEVICE_READY, 1);
|
|
|
|
if (!console_multiport(port->vconsole))
|
|
{
|
|
port->host_connected = RT_TRUE;
|
|
}
|
|
|
|
port->parent.ops = &console_port_ops;
|
|
port->parent.config = (struct serial_configure)RT_SERIAL_CONFIG_DEFAULT;
|
|
rt_dm_dev_set_name(&port->parent.parent, "vport%up%u", vconsole->uid, id);
|
|
name = rt_dm_dev_get_name(&port->parent.parent);
|
|
|
|
return rt_hw_serial_register(&port->parent, name,
|
|
RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE |
|
|
RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX, vconsole);
|
|
}
|
|
|
|
static void console_port_remove(struct console_port *port, rt_bool_t sync)
|
|
{
|
|
rt_uint32_t port_id;
|
|
|
|
if (!port->vconsole)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Make sure pending buffer is empty */
|
|
while (rt_virtqueue_read_buf(port->tx_vq, RT_NULL) ||
|
|
rt_virtqueue_read_buf(port->rx_vq, RT_NULL))
|
|
{
|
|
rt_thread_yield();
|
|
}
|
|
|
|
port_id = port - port->vconsole->ports;
|
|
|
|
if (sync && port_id != 0)
|
|
{
|
|
console_send_ctrl_msg(port->vconsole, port_id, VIRTIO_CONSOLE_PORT_OPEN, 0);
|
|
}
|
|
|
|
rt_device_unregister(&port->parent.parent);
|
|
|
|
rt_free(port->stream);
|
|
port->vconsole = RT_NULL;
|
|
}
|
|
|
|
static struct console_port *console_find_port_by_vq(struct virtio_console *vconsole,
|
|
struct rt_virtqueue *vq)
|
|
{
|
|
for (int i = 0; i < vconsole->max_port_nr; ++i)
|
|
{
|
|
struct console_port *port = &vconsole->ports[i];
|
|
|
|
if (port->tx_vq == vq || port->rx_vq == vq)
|
|
{
|
|
return port;
|
|
}
|
|
}
|
|
|
|
return RT_NULL;
|
|
}
|
|
|
|
static struct console_port *console_find_port_by_id(struct virtio_console *vconsole,
|
|
rt_uint32_t id)
|
|
{
|
|
return id < vconsole->max_port_nr ? &vconsole->ports[id] : RT_NULL;
|
|
}
|
|
|
|
static void console_resize(struct virtio_console *vconsole,
|
|
rt_uint16_t cols, rt_uint16_t rows)
|
|
{
|
|
LOG_D("%s: [cols, rows] resize [%u, %u]",
|
|
rt_dm_dev_get_name(&vconsole->vdev.parent), cols, rows);
|
|
|
|
/* Port to TTY */
|
|
}
|
|
|
|
static void console_ctrl_work(struct rt_work *work, void *work_data)
|
|
{
|
|
rt_uint32_t port_id;
|
|
struct console_port *port;
|
|
struct virtio_console_control *ctrl_packet;
|
|
struct virtio_console_control_ext *ctrl_packet_ext;
|
|
struct virtio_console *vconsole = work_data;
|
|
struct rt_virtio_device *vdev = vconsole->vdev;
|
|
|
|
_next_ctrl:
|
|
if (!(ctrl_packet_ext = rt_virtqueue_read_buf(vconsole->ctrl_rx_vq, RT_NULL)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
ctrl_packet = &ctrl_packet_ext->ctrl;
|
|
port = console_find_port_by_id(vconsole, virtio32_to_cpu(vdev, ctrl_packet->id));
|
|
port_id = virtio32_to_cpu(vdev, ctrl_packet->id);
|
|
|
|
if (!port->vconsole &&
|
|
cpu_to_virtio16(vdev, ctrl_packet->event) != VIRTIO_CONSOLE_PORT_ADD)
|
|
{
|
|
LOG_D("%s: Port%d in control is invalid", rt_dm_dev_get_name(&vdev->parent),
|
|
port_id);
|
|
|
|
goto _prepare_next_ctrl;
|
|
}
|
|
|
|
switch (virtio16_to_cpu(vdev, ctrl_packet->event))
|
|
{
|
|
case VIRTIO_CONSOLE_PORT_ADD:
|
|
if (port->vconsole)
|
|
{
|
|
LOG_D("%s: Port%u already added", rt_dm_dev_get_name(&vdev->parent),
|
|
port_id);
|
|
|
|
console_send_ctrl_msg(vconsole, port_id, VIRTIO_CONSOLE_PORT_READY, 1);
|
|
break;
|
|
}
|
|
if (port_id >= vconsole->max_port_nr)
|
|
{
|
|
LOG_W("%s: Port%u is out of %u max supported",
|
|
rt_dm_dev_get_name(&vdev->parent), port_id, vconsole->max_port_nr);
|
|
break;
|
|
}
|
|
console_port_add(vconsole, port);
|
|
break;
|
|
|
|
case VIRTIO_CONSOLE_PORT_REMOVE:
|
|
console_port_remove(port, RT_FALSE);
|
|
break;
|
|
|
|
case VIRTIO_CONSOLE_CONSOLE_PORT:
|
|
console_send_ctrl_msg(vconsole, port_id, VIRTIO_CONSOLE_PORT_OPEN, 1);
|
|
break;
|
|
|
|
case VIRTIO_CONSOLE_RESIZE:
|
|
console_resize(vconsole,
|
|
virtio16_to_cpu(vdev, ctrl_packet_ext->resize.cols),
|
|
virtio16_to_cpu(vdev, ctrl_packet_ext->resize.rows));
|
|
break;
|
|
|
|
case VIRTIO_CONSOLE_PORT_OPEN:
|
|
port->host_connected = !!virtio16_to_cpu(vdev, ctrl_packet->value);
|
|
console_send_ctrl_msg(vconsole, port_id, VIRTIO_CONSOLE_PORT_OPEN, 1);
|
|
break;
|
|
|
|
case VIRTIO_CONSOLE_PORT_NAME:
|
|
rt_strncpy(port->name, (const char *)ctrl_packet_ext->data, sizeof(port->name));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
_prepare_next_ctrl:
|
|
rt_virtqueue_add_inbuf(vconsole->ctrl_rx_vq,
|
|
ctrl_packet_ext, sizeof(*ctrl_packet_ext));
|
|
|
|
rt_virtqueue_kick(vconsole->ctrl_rx_vq);
|
|
|
|
goto _next_ctrl;
|
|
}
|
|
|
|
static void console_config_work(struct rt_work *work, void *work_data)
|
|
{
|
|
rt_uint16_t cols, rows;
|
|
struct virtio_console *vconsole = work_data;
|
|
struct rt_virtio_device *vdev = vconsole->vdev;
|
|
|
|
rt_virtio_read_config(vdev, struct virtio_console_config, cols, &cols);
|
|
rt_virtio_read_config(vdev, struct virtio_console_config, rows, &rows);
|
|
|
|
console_resize(vconsole, cols, rows);
|
|
}
|
|
|
|
static void virtio_console_rx_done(struct rt_virtqueue *vq)
|
|
{
|
|
struct virtio_console *vconsole = vq->vdev->priv;
|
|
struct console_port *port = console_find_port_by_vq(vconsole, vq);
|
|
|
|
if (!port)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (port->irq_enabled)
|
|
{
|
|
rt_hw_serial_isr(&port->parent, RT_SERIAL_EVENT_RX_IND);
|
|
}
|
|
else
|
|
{
|
|
/* Give up */
|
|
console_port_getc(&port->parent);
|
|
}
|
|
}
|
|
|
|
static void virtio_console_tx_done(struct rt_virtqueue *vq)
|
|
{
|
|
struct virtio_console *vconsole = vq->vdev->priv;
|
|
struct console_port *port = console_find_port_by_vq(vconsole, vq);
|
|
|
|
if (!port)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (port->parent.parent.open_flag & RT_DEVICE_FLAG_INT_TX)
|
|
{
|
|
if (rt_virtqueue_read_buf(vq, RT_NULL))
|
|
{
|
|
rt_hw_serial_isr(&port->parent, RT_SERIAL_EVENT_TX_DONE);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void virtio_console_ctrl_done(struct rt_virtqueue *vq)
|
|
{
|
|
struct virtio_console *vconsole = vq->vdev->priv;
|
|
|
|
rt_work_submit(&vconsole->ctrl_work, 0);
|
|
}
|
|
|
|
static void virtio_console_config_changed(struct rt_virtio_device *vdev)
|
|
{
|
|
struct virtio_console *vconsole = vdev->priv;
|
|
|
|
if (!console_multiport(vconsole))
|
|
{
|
|
rt_work_submit(&vconsole->config_work, 0);
|
|
}
|
|
}
|
|
|
|
static rt_err_t virtio_console_vq_init(struct virtio_console *vconsole)
|
|
{
|
|
rt_err_t err;
|
|
const char **names;
|
|
rt_size_t vqs_nr = 2;
|
|
rt_virtqueue_callback *cbs;
|
|
|
|
if (vconsole->max_port_nr > 1)
|
|
{
|
|
vqs_nr = 2 * vconsole->max_port_nr + 2;
|
|
}
|
|
|
|
vconsole->vqs = rt_calloc(vqs_nr, sizeof(*vconsole->vqs));
|
|
|
|
if (!vconsole->vqs)
|
|
{
|
|
return -RT_ENOMEM;
|
|
}
|
|
|
|
if (!(names = rt_malloc(sizeof(*names) * vqs_nr)))
|
|
{
|
|
return -RT_ENOMEM;
|
|
}
|
|
|
|
if (!(cbs = rt_malloc(sizeof(*cbs) * vqs_nr)))
|
|
{
|
|
rt_free(names);
|
|
|
|
return -RT_ENOMEM;
|
|
}
|
|
|
|
names[QUEUE_PORT_RX] = "ch-rx";
|
|
names[QUEUE_PORT_TX] = "ch-tx";
|
|
cbs[QUEUE_PORT_RX] = &virtio_console_rx_done;
|
|
cbs[QUEUE_PORT_TX] = &virtio_console_tx_done;
|
|
|
|
if (vqs_nr > 2)
|
|
{
|
|
names[QUEUE_CTRL_RX] = "ctl-rx";
|
|
names[QUEUE_CTRL_TX] = "ctl-tx";
|
|
cbs[QUEUE_CTRL_RX] = &virtio_console_ctrl_done;
|
|
cbs[QUEUE_CTRL_TX] = RT_NULL;
|
|
}
|
|
|
|
for (int i = 4; i < vqs_nr; i += 2)
|
|
{
|
|
names[i + QUEUE_PORT_RX] = "ch-rx";
|
|
names[i + QUEUE_PORT_TX] = "ch-tx";
|
|
cbs[i + QUEUE_PORT_RX] = &virtio_console_rx_done;
|
|
cbs[i + QUEUE_PORT_TX] = &virtio_console_tx_done;
|
|
}
|
|
|
|
if ((err = rt_virtio_virtqueue_install(vconsole->vdev, vqs_nr,
|
|
vconsole->vqs, names, cbs)))
|
|
{
|
|
goto _out_free;
|
|
}
|
|
|
|
vconsole->stream_rx_size = rt_virtqueue_get_virtq_size(vconsole->vqs[QUEUE_PORT_RX]);
|
|
vconsole->stream_tx_size = rt_virtqueue_get_virtq_size(vconsole->vqs[QUEUE_PORT_TX]);
|
|
|
|
if (vqs_nr > 2)
|
|
{
|
|
struct rt_virtqueue *ctrl_rx_vq, *ctrl_tx_vq;
|
|
|
|
ctrl_rx_vq = vconsole->vqs[QUEUE_CTRL_RX];
|
|
ctrl_tx_vq = vconsole->vqs[QUEUE_CTRL_TX];
|
|
|
|
vconsole->ctrl_packet_size = rt_virtqueue_get_virtq_size(ctrl_rx_vq);
|
|
vconsole->ctrl_packet = rt_malloc(sizeof(*vconsole->ctrl_packet) *
|
|
vconsole->ctrl_packet_size);
|
|
|
|
if (!vconsole->ctrl_packet)
|
|
{
|
|
goto _out_free;
|
|
}
|
|
|
|
for (int idx = 0; idx < vconsole->ctrl_packet_size; ++idx)
|
|
{
|
|
rt_virtqueue_add_inbuf(ctrl_rx_vq, &vconsole->ctrl_packet[idx],
|
|
sizeof(vconsole->ctrl_packet[idx]));
|
|
|
|
rt_virtqueue_submit(ctrl_rx_vq);
|
|
}
|
|
|
|
rt_virtqueue_notify(ctrl_rx_vq);
|
|
|
|
vconsole->ctrl_rx_vq = ctrl_rx_vq;
|
|
vconsole->ctrl_tx_vq = ctrl_tx_vq;
|
|
|
|
rt_virtqueue_disable_callback(ctrl_tx_vq);
|
|
}
|
|
|
|
_out_free:
|
|
rt_free(names);
|
|
rt_free(cbs);
|
|
|
|
return err;
|
|
}
|
|
|
|
static void virtio_console_vq_finit(struct virtio_console *vconsole)
|
|
{
|
|
if (vconsole->ports)
|
|
{
|
|
rt_free(vconsole->ports);
|
|
}
|
|
|
|
if (vconsole->vqs)
|
|
{
|
|
if (vconsole->vqs[0])
|
|
{
|
|
rt_virtio_virtqueue_release(vconsole->vdev);
|
|
}
|
|
|
|
rt_free(vconsole->vqs);
|
|
}
|
|
|
|
if (vconsole->ctrl_packet)
|
|
{
|
|
rt_free(vconsole->ctrl_packet);
|
|
}
|
|
}
|
|
|
|
static rt_err_t virtio_console_probe(struct rt_virtio_device *vdev)
|
|
{
|
|
rt_err_t err;
|
|
struct virtio_console *vconsole = rt_calloc(1, sizeof(*vconsole));
|
|
|
|
if (!vconsole)
|
|
{
|
|
return -RT_ENOMEM;
|
|
}
|
|
|
|
vdev->priv = vconsole;
|
|
vconsole->vdev = vdev;
|
|
vdev->parent.user_data = vconsole;
|
|
|
|
rt_virtio_read_config(vdev, struct virtio_console_config,
|
|
max_nr_ports, &vconsole->max_port_nr);
|
|
vconsole->max_port_nr = rt_max_t(rt_size_t, 1, vconsole->max_port_nr);
|
|
|
|
vconsole->ports = rt_malloc(sizeof(*vconsole->ports) * vconsole->max_port_nr);
|
|
|
|
if (!vconsole->ports)
|
|
{
|
|
err = -RT_ENOMEM;
|
|
goto _fail;
|
|
}
|
|
|
|
if ((err = virtio_console_vq_init(vconsole)))
|
|
{
|
|
goto _fail;
|
|
}
|
|
|
|
if ((vconsole->uid = rt_dm_ida_alloc(&virtio_console_ida)) < 0)
|
|
{
|
|
return -RT_EFULL;
|
|
}
|
|
|
|
if (vconsole->ctrl_rx_vq && vconsole->ctrl_tx_vq)
|
|
{
|
|
rt_work_init(&vconsole->ctrl_work, console_ctrl_work, vconsole);
|
|
}
|
|
|
|
rt_work_init(&vconsole->config_work, console_config_work, vconsole);
|
|
|
|
for (int i = 0; i < vconsole->max_port_nr; ++i)
|
|
{
|
|
vconsole->ports[i].vconsole = RT_NULL;
|
|
}
|
|
|
|
if ((err = console_port_add(vconsole, &vconsole->ports[0])))
|
|
{
|
|
goto _fail;
|
|
}
|
|
|
|
return RT_EOK;
|
|
|
|
_fail:
|
|
rt_dm_ida_free(&virtio_console_ida, vconsole->uid);
|
|
virtio_console_vq_finit(vconsole);
|
|
rt_free(vconsole);
|
|
|
|
return err;
|
|
}
|
|
|
|
static rt_err_t virtio_console_remove(struct rt_virtio_device *vdev)
|
|
{
|
|
struct virtio_console *vconsole = vdev->parent.user_data;
|
|
|
|
for (int i = 0; i < vconsole->max_port_nr; ++i)
|
|
{
|
|
console_port_remove(&vconsole->ports[i], RT_TRUE);
|
|
}
|
|
|
|
rt_dm_ida_free(&virtio_console_ida, vconsole->uid);
|
|
|
|
virtio_console_vq_finit(vconsole);
|
|
rt_free(vconsole);
|
|
|
|
return RT_EOK;
|
|
}
|
|
|
|
static const struct rt_virtio_device_id virtio_console_ids[] =
|
|
{
|
|
{ VIRTIO_DEVICE_ID_CONSOLE, VIRTIO_DEVICE_ANY_ID },
|
|
{ /* sentinel */ }
|
|
};
|
|
|
|
static struct rt_virtio_driver virtio_console_driver =
|
|
{
|
|
.ids = virtio_console_ids,
|
|
.features =
|
|
RT_BIT(VIRTIO_CONSOLE_F_SIZE)
|
|
| RT_BIT(VIRTIO_CONSOLE_F_MULTIPORT)
|
|
| RT_BIT(VIRTIO_CONSOLE_F_EMERG_WRITE),
|
|
|
|
.probe = virtio_console_probe,
|
|
.remove = virtio_console_remove,
|
|
.config_changed = virtio_console_config_changed,
|
|
};
|
|
RT_VIRTIO_DRIVER_EXPORT(virtio_console_driver);
|
|
|
|
static const struct rt_virtio_device_id virtio_rproc_serial_ids[] =
|
|
{
|
|
#ifdef RT_VIRTIO_RPROC_SERIAL
|
|
{ VIRTIO_DEVICE_ID_RPROC_SERIAL, VIRTIO_DEVICE_ANY_ID },
|
|
#endif
|
|
{ /* sentinel */ }
|
|
};
|
|
|
|
static struct rt_virtio_driver virtio_rproc_serial_driver =
|
|
{
|
|
.ids = virtio_rproc_serial_ids,
|
|
.probe = virtio_console_probe,
|
|
.remove = virtio_console_remove,
|
|
};
|
|
RT_VIRTIO_DRIVER_EXPORT(virtio_rproc_serial_driver);
|