Blob Blame History Raw
From ebf4cebd082442ed2bc11475fde301c18648298d Mon Sep 17 00:00:00 2001
From: Gerd Hoffmann <kraxel@redhat.com>
Date: Tue, 20 Apr 2010 13:33:54 +0200
Subject: [PATCH 16/39] spice: add virtio-serial based vdi port backend.

Adds the spicevmc device.  This is a communication channel between the
spice client and the guest.  It is used to send display information and
mouse events from the spice clients to the guest.
---
 Makefile.target |    1 +
 hw/spice-vmc.c  |  203 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 204 insertions(+), 0 deletions(-)
 create mode 100644 hw/spice-vmc.c

diff --git a/Makefile.target b/Makefile.target
index 4da33b5..90544c5 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -217,6 +217,7 @@ obj-i386-y += pc_piix.o
 obj-i386-y += testdev.o
 obj-i386-y += acpi.o acpi_piix4.o
 obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
+obj-i386-$(CONFIG_SPICE) += spice-vmc.o

 obj-i386-y += pcspk.o i8254.o
 obj-i386-$(CONFIG_KVM_PIT) += i8254-kvm.o
diff --git a/hw/spice-vmc.c b/hw/spice-vmc.c
new file mode 100644
index 0000000..3f6a2bb
--- /dev/null
+++ b/hw/spice-vmc.c
@@ -0,0 +1,203 @@
+/*
+
+ Spice Virtual Machine Channel (VMC).
+
+ A virtio-serial port used for spice to guest communication, over
+ which spice client and a daemon in the guest operating system
+ communicate.
+
+ Replaces the old vdi_port PCI device.
+
+*/
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <spice.h>
+#include <spice-experimental.h>
+
+#include "virtio-serial.h"
+#include "qemu-spice.h"
+
+#define VMC_GUEST_DEVICE_NAME "com.redhat.spice.0"
+#define VMC_DEVICE_NAME       "spicevmc"
+
+#define dprintf(_svc, _level, _fmt, ...)                                \
+    do {                                                                \
+        if (_svc->debug >= _level) {                                    \
+            fprintf(stderr, "svc: " _fmt, ## __VA_ARGS__);              \
+        }                                                               \
+    } while (0)
+
+typedef struct SpiceVirtualChannel {
+    VirtIOSerialPort      port;
+    VMChangeStateEntry    *vmstate;
+    SpiceVDIPortInstance  sin;
+    bool                  active;
+    uint8_t               *buffer;
+    uint8_t               *datapos;
+    ssize_t               bufsize, datalen;
+    uint32_t              debug;
+} SpiceVirtualChannel;
+
+static int vmc_write(SpiceVDIPortInstance *sin, const uint8_t *buf, int len)
+{
+    SpiceVirtualChannel *svc = container_of(sin, SpiceVirtualChannel, sin);
+    ssize_t out;
+
+    out = virtio_serial_write(&svc->port, buf, len);
+    dprintf(svc, 2, "%s: %lu/%d\n", __func__, out, len);
+    return out;
+}
+
+static int vmc_read(SpiceVDIPortInstance *sin, uint8_t *buf, int len)
+{
+    SpiceVirtualChannel *svc = container_of(sin, SpiceVirtualChannel, sin);
+    int bytes = MIN(len, svc->datalen);
+
+    dprintf(svc, 2, "%s: %d/%zd\n", __func__, bytes, svc->datalen);
+    if (bytes) {
+        memcpy(buf, svc->datapos, bytes);
+        svc->datapos += bytes;
+        svc->datalen -= bytes;
+        if (0 == svc->datalen) {
+            virtio_serial_throttle_port(&svc->port, false);
+        }
+    }
+    return bytes;
+}
+
+static SpiceVDIPortInterface vmc_interface = {
+    .base.type          = SPICE_INTERFACE_VDI_PORT,
+    .base.description   = "spice virtual channel vdi port",
+    .base.major_version = SPICE_INTERFACE_VDI_PORT_MAJOR,
+    .base.minor_version = SPICE_INTERFACE_VDI_PORT_MINOR,
+    .write              = vmc_write,
+    .read               = vmc_read,
+};
+
+static void vmc_register_interface(SpiceVirtualChannel *svc)
+{
+    if (svc->active) {
+        return;
+    }
+    dprintf(svc, 1, "%s\n", __func__);
+    svc->sin.base.sif = &vmc_interface.base;
+    spice_server_add_interface(spice_server, &svc->sin.base);
+    svc->active = true;
+}
+
+static void vmc_unregister_interface(SpiceVirtualChannel *svc)
+{
+    if (!svc->active) {
+        return;
+    }
+    dprintf(svc, 1, "%s\n", __func__);
+    spice_server_remove_interface(&svc->sin.base);
+    svc->active = false;
+}
+
+
+static void vmc_change_state_handler(void *opaque, int running, int reason)
+{
+    SpiceVirtualChannel *svc = opaque;
+
+    if (running && svc->active) {
+        spice_server_vdi_port_wakeup(&svc->sin);
+    }
+}
+
+/*
+ * virtio-serial callbacks
+ */
+
+static void vmc_guest_open(VirtIOSerialPort *port)
+{
+    SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
+
+    dprintf(svc, 1, "%s\n", __func__);
+    vmc_register_interface(svc);
+}
+
+static void vmc_guest_close(VirtIOSerialPort *port)
+{
+    SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
+
+    dprintf(svc, 1, "%s\n", __func__);
+    vmc_unregister_interface(svc);
+}
+
+static void vmc_guest_ready(VirtIOSerialPort *port)
+{
+    SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
+
+    dprintf(svc, 1, "%s\n", __func__);
+    if (svc->active)
+        spice_server_vdi_port_wakeup(&svc->sin);
+}
+
+static void vmc_have_data(VirtIOSerialPort *port, const uint8_t *buf, size_t len)
+{
+    SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
+
+    dprintf(svc, 2, "%s: %zd\n", __func__, len);
+    assert(svc->datapos == 0);
+    if (svc->bufsize < len) {
+        svc->bufsize = len;
+        svc->buffer = qemu_realloc(svc->buffer, svc->bufsize);
+    }
+    memcpy(svc->buffer, buf, len);
+    svc->datapos = svc->buffer;
+    svc->datalen = len;
+    virtio_serial_throttle_port(&svc->port, true);
+    spice_server_vdi_port_wakeup(&svc->sin);
+}
+
+static int vmc_initfn(VirtIOSerialDevice *dev)
+{
+    VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev);
+    SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
+
+    if (!using_spice)
+        return -1;
+
+    dprintf(svc, 1, "%s\n", __func__);
+    port->name = qemu_strdup(VMC_GUEST_DEVICE_NAME);
+    svc->vmstate = qemu_add_vm_change_state_handler(
+        vmc_change_state_handler, svc);
+    virtio_serial_open(port);
+    return 0;
+}
+
+static int vmc_exitfn(VirtIOSerialDevice *dev)
+{
+    VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, &dev->qdev);
+    SpiceVirtualChannel *svc = DO_UPCAST(SpiceVirtualChannel, port, port);
+
+    dprintf(svc, 1, "%s\n", __func__);
+    vmc_unregister_interface(svc);
+    qemu_del_vm_change_state_handler(svc->vmstate);
+    virtio_serial_close(port);
+    return 0;
+}
+
+static VirtIOSerialPortInfo vmc_info = {
+    .qdev.name     = VMC_DEVICE_NAME,
+    .qdev.size     = sizeof(SpiceVirtualChannel),
+    .init          = vmc_initfn,
+    .exit          = vmc_exitfn,
+    .guest_open    = vmc_guest_open,
+    .guest_close   = vmc_guest_close,
+    .guest_ready   = vmc_guest_ready,
+    .have_data     = vmc_have_data,
+    .qdev.props = (Property[]) {
+        DEFINE_PROP_UINT32("nr", SpiceVirtualChannel, port.id, VIRTIO_CONSOLE_BAD_ID),
+        DEFINE_PROP_UINT32("debug", SpiceVirtualChannel, debug, 1),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
+static void vmc_register(void)
+{
+    virtio_serial_port_qdev_register(&vmc_info);
+}
+device_init(vmc_register)
-- 
1.7.2.3