@@ -46,6 +46,7 @@ libqos_srcs = files(
'virtio-serial.c',
'virtio-iommu.c',
'virtio-gpio.c',
+ 'virtio-video.c',
'generic-pcihost.c',
# qgraph machines:
new file mode 100644
@@ -0,0 +1,179 @@
+/*
+ * libqos virtio-video driver
+ *
+ * Copyright (c) 2023 Red Hat Inc.
+ *
+ * Authors:
+ * Albert Esteve <aesteve@redhat.com>
+ * (based on virtio-gpio.c)
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "standard-headers/linux/virtio_config.h"
+#include "../libqtest.h"
+#include "qemu/module.h"
+#include "qgraph.h"
+#include "virtio-video.h"
+
+#define NUM_VQUEUES 2
+#define PCI_SLOT 0x04
+#define PCI_FN 0x00
+
+static QGuestAllocator *alloc;
+
+static void virtio_video_cleanup(QVhostUserVideo *video)
+{
+ QVirtioDevice *vdev = video->vdev;
+ int i;
+
+ for (i = 0; i < NUM_VQUEUES; i++) {
+ qvirtqueue_cleanup(vdev->bus, video->queues[i], alloc);
+ }
+ g_free(video->queues);
+}
+
+/*
+ * This handles the VirtIO setup from the point of view of the driver
+ * frontend and therefore doesn't present any vhost specific features
+ * and in fact masks off the re-used bit.
+ */
+static void virtio_video_setup(QVhostUserVideo *video)
+{
+ QVirtioDevice *vdev = video->vdev;
+ uint64_t features;
+ int i;
+
+ features = qvirtio_get_features(vdev);
+ features &= ~QVIRTIO_F_BAD_FEATURE;
+ qvirtio_set_features(vdev, features);
+
+ video->queues = g_new(QVirtQueue *, NUM_VQUEUES);
+ for (i = 0; i < NUM_VQUEUES; i++) {
+ video->queues[i] = qvirtqueue_setup(vdev, alloc, i);
+ }
+ qvirtio_set_driver_ok(vdev);
+}
+
+static void *qvirtio_video_get_driver(QVhostUserVideo *v_video,
+ const char *interface)
+{
+ if (!g_strcmp0(interface, "vhost-user-video")) {
+ return v_video;
+ }
+ if (!g_strcmp0(interface, "virtio")) {
+ return v_video->vdev;
+ }
+
+ g_assert_not_reached();
+}
+
+static void *qvirtio_video_device_get_driver(void *object,
+ const char *interface)
+{
+ QVhostUserVideoDevice *v_video = object;
+ return qvirtio_video_get_driver(&v_video->video, interface);
+}
+
+/* virtio-video (mmio) */
+static void qvirtio_video_device_destructor(QOSGraphObject *obj)
+{
+ QVhostUserVideoDevice *video_dev = (QVhostUserVideoDevice *) obj;
+ virtio_video_cleanup(&video_dev->video);
+}
+
+static void qvirtio_video_device_start_hw(QOSGraphObject *obj)
+{
+ QVhostUserVideoDevice *video_dev = (QVhostUserVideoDevice *) obj;
+ virtio_video_setup(&video_dev->video);
+}
+
+static void *virtio_video_device_create(void *virtio_dev,
+ QGuestAllocator *t_alloc,
+ void *addr)
+{
+ QVhostUserVideoDevice *virtio_device = g_new0(QVhostUserVideoDevice, 1);
+ QVhostUserVideo *interface = &virtio_device->video;
+
+ interface->vdev = virtio_dev;
+ alloc = t_alloc;
+
+ virtio_device->obj.get_driver = qvirtio_video_device_get_driver;
+ virtio_device->obj.start_hw = qvirtio_video_device_start_hw;
+ virtio_device->obj.destructor = qvirtio_video_device_destructor;
+
+ return &virtio_device->obj;
+}
+
+/* virtio-video-pci */
+static void qvirtio_video_pci_destructor(QOSGraphObject *obj)
+{
+ QVhostUserVideoPCI *video_pci = (QVhostUserVideoPCI *) obj;
+ QOSGraphObject *pci_vobj = &video_pci->pci_vdev.obj;
+
+ virtio_video_cleanup(&video_pci->video);
+ qvirtio_pci_destructor(pci_vobj);
+}
+
+static void qvirtio_video_pci_start_hw(QOSGraphObject *obj)
+{
+ QVhostUserVideoPCI *video_pci = (QVhostUserVideoPCI *) obj;
+ QOSGraphObject *pci_vobj = &video_pci->pci_vdev.obj;
+
+ qvirtio_pci_start_hw(pci_vobj);
+ virtio_video_setup(&video_pci->video);
+}
+
+static void *qvirtio_video_pci_get_driver(void *object, const char *interface)
+{
+ QVhostUserVideoPCI *v_video = object;
+
+ if (!g_strcmp0(interface, "pci-device")) {
+ return v_video->pci_vdev.pdev;
+ }
+ return qvirtio_video_get_driver(&v_video->video, interface);
+}
+
+static void *virtio_video_pci_create(void *pci_bus, QGuestAllocator *t_alloc,
+ void *addr)
+{
+ QVhostUserVideoPCI *virtio_spci = g_new0(QVhostUserVideoPCI, 1);
+ QVhostUserVideo *interface = &virtio_spci->video;
+ QOSGraphObject *obj = &virtio_spci->pci_vdev.obj;
+
+ virtio_pci_init(&virtio_spci->pci_vdev, pci_bus, addr);
+ interface->vdev = &virtio_spci->pci_vdev.vdev;
+ alloc = t_alloc;
+
+ obj->get_driver = qvirtio_video_pci_get_driver;
+ obj->start_hw = qvirtio_video_pci_start_hw;
+ obj->destructor = qvirtio_video_pci_destructor;
+
+ return obj;
+}
+
+static void virtio_video_register_nodes(void)
+{
+ QPCIAddress addr = {
+ .devfn = QPCI_DEVFN(PCI_SLOT, PCI_FN),
+ };
+
+ QOSGraphEdgeOptions edge_opts = {
+ .extra_device_opts = "id=video0,chardev=chr-vhost-user-test",
+ };
+
+ /* vhost-user-video-device */
+ qos_node_create_driver("vhost-user-video-device",
+ virtio_video_device_create);
+ qos_node_consumes("vhost-user-video-device", "virtio-bus", &edge_opts);
+ qos_node_produces("vhost-user-video-device", "vhost-user-video");
+
+ /* vhost-user-video-pci */
+ add_qpci_address(&edge_opts, &addr);
+ qos_node_create_driver("vhost-user-video-pci", virtio_video_pci_create);
+ qos_node_consumes("vhost-user-video-pci", "pci-bus", &edge_opts);
+ qos_node_produces("vhost-user-video-pci", "vhost-user-video");
+}
+
+libqos_init(virtio_video_register_nodes);
new file mode 100644
@@ -0,0 +1,39 @@
+/*
+ * libqos virtio-video definitions
+ *
+ * Copyright (c) 2023 Red Hat Inc.
+ *
+ * Authors:
+ * Albert Esteve <aesteve@redhat.com>
+ * (based on virtio-gpio.h)
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef TESTS_LIBQOS_VIRTIO_VIDEO_H
+#define TESTS_LIBQOS_VIRTIO_VIDEO_H
+
+#include "qgraph.h"
+#include "virtio.h"
+#include "virtio-pci.h"
+
+typedef struct QVhostUserVideo QVhostUserVideo;
+typedef struct QVhostUserVideoPCI QVhostUserVideoPCI;
+typedef struct QVhostUserVideoDevice QVhostUserVideoDevice;
+
+struct QVhostUserVideo {
+ QVirtioDevice *vdev;
+ QVirtQueue **queues;
+};
+
+struct QVhostUserVideoPCI {
+ QVirtioPCIDevice pci_vdev;
+ QVhostUserVideo video;
+};
+
+struct QVhostUserVideoDevice {
+ QOSGraphObject obj;
+ QVhostUserVideo video;
+};
+
+#endif /* TESTS_LIBQOS_VIRTIO_VIDEO_H */
@@ -33,6 +33,7 @@
#include "standard-headers/linux/virtio_ids.h"
#include "standard-headers/linux/virtio_net.h"
#include "standard-headers/linux/virtio_gpio.h"
+#include "standard-headers/linux/virtio_video.h"
#ifdef CONFIG_LINUX
#include <sys/vfs.h>
@@ -145,6 +146,7 @@ enum {
enum {
VHOST_USER_NET,
VHOST_USER_GPIO,
+ VHOST_USER_VIDEO,
};
typedef struct TestServer {
@@ -1156,3 +1158,33 @@ static void register_vhost_gpio_test(void)
"vhost-user-gpio", test_read_guest_mem, &opts);
}
libqos_init(register_vhost_gpio_test);
+
+static uint64_t vu_video_get_features(TestServer *s)
+{
+ return 0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES;
+}
+
+static struct vhost_user_ops g_vu_video_ops = {
+ .type = VHOST_USER_VIDEO,
+
+ .append_opts = append_vhost_gpio_opts,
+
+ .get_features = vu_video_get_features,
+ .set_features = vu_net_set_features,
+ .get_protocol_features = vu_gpio_get_protocol_features,
+};
+
+static void register_vhost_video_test(void)
+{
+ QOSGraphTestOptions opts = {
+ .before = vhost_user_test_setup,
+ .subprocess = true,
+ .arg = &g_vu_video_ops,
+ };
+
+ qemu_add_opts(&qemu_chardev_opts);
+
+ qos_add_test("read-guest-mem/memfile",
+ "vhost-user-video", test_read_guest_mem, &opts);
+}
+libqos_init(register_vhost_video_test);
Add some minimal testing of the vhost-user-video backend similar to what is tested for other vhost-user devices. For now it only stresses the backend instantiation and some message passing through the control socket. Signed-off-by: Albert Esteve <aesteve@redhat.com> --- tests/qtest/libqos/meson.build | 1 + tests/qtest/libqos/virtio-video.c | 179 ++++++++++++++++++++++++++++++ tests/qtest/libqos/virtio-video.h | 39 +++++++ tests/qtest/vhost-user-test.c | 32 ++++++ 4 files changed, 251 insertions(+) create mode 100644 tests/qtest/libqos/virtio-video.c create mode 100644 tests/qtest/libqos/virtio-video.h