Patchwork [4/6] spice-qemu-char: add spiceport chardev

login
register
mail settings
Submitter Marc-André Lureau
Date Nov. 30, 2012, 1:25 p.m.
Message ID <1354281947-20227-5-git-send-email-marcandre.lureau@redhat.com>
Download mbox | patch
Permalink /patch/202956/
State New
Headers show

Comments

Marc-André Lureau - Nov. 30, 2012, 1:25 p.m.
Add a new spice chardev to allow arbitrary communication between the
host and the Spice client (via the spice server).

Examples:

This allows the Spice client to have a special port for the qemu
monitor:

... -chardev spiceport,name=org.qemu.monitor,id=monitorport
    -mon chardev=monitorport

Or to allow arbitrary communication outside of qemu:

... -chardev spiceport,name=org.ovirt.controller,id=...,chardev=ovcsocket
    -chardev socket,server,host=0.0.0.0,port=4242,id=ovcsocket,nowait

The spice client is notified when the qemu socket server has gain or
lost a client. The qemu socket client is disconnected when the spice
client is disconnected.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qemu-char.c       |   1 +
 qemu-config.c     |   3 ++
 qemu-options.hx   |  14 ++++++
 spice-qemu-char.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 trace-events      |   3 ++
 ui/qemu-spice.h   |   1 +
 6 files changed, 161 insertions(+)

Patch

diff --git a/qemu-char.c b/qemu-char.c
index 1414ca1..bc1c74b 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -2782,6 +2782,7 @@  static const struct {
 #endif
 #ifdef CONFIG_SPICE
     { .name = "spicevmc",     .open = qemu_chr_open_spice },
+    { .name = "spiceport",     .open = qemu_chr_open_spice_port },
 #endif
 };
 
diff --git a/qemu-config.c b/qemu-config.c
index 10d1ba4..b4e7af3 100644
--- a/qemu-config.c
+++ b/qemu-config.c
@@ -217,6 +217,9 @@  static QemuOptsList qemu_chardev_opts = {
         },{
             .name = "debug",
             .type = QEMU_OPT_NUMBER,
+        },{
+            .name = "chardev",
+            .type = QEMU_OPT_STRING,
         },
         { /* end of list */ }
     },
diff --git a/qemu-options.hx b/qemu-options.hx
index fbcf079..d2f09f6 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1749,6 +1749,7 @@  DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
 #endif
 #if defined(CONFIG_SPICE)
     "-chardev spicevmc,id=id,name=name[,debug=debug]\n"
+    "-chardev spiceport,id=id,name=name[,chardev=name,debug=debug]\n"
 #endif
     , QEMU_ARCH_ALL
 )
@@ -1776,6 +1777,7 @@  Backend is one of:
 @option{tty},
 @option{parport},
 @option{spicevmc}.
+@option{spiceport}.
 The specific backend will determine the applicable options.
 
 All devices must have an id, which can be any string up to 127 characters long.
@@ -1961,6 +1963,18 @@  required.
 
 Connect to a spice virtual machine channel, such as vdiport.
 
+@item -chardev spiceport ,id=@var{id} ,debug=@var{debug}, name=@var{name}, chardev=@var{chardev}
+
+@option{spiceport} is only available when spice support is built in.
+
+@option{debug} debug level for spicevmc
+
+@option{name} name of spice port to connect to
+
+@option{chardev} name of chardev to connect to
+
+Connect to a spice port, fixme.
+
 @end table
 ETEXI
 
diff --git a/spice-qemu-char.c b/spice-qemu-char.c
index b86e83a..629b500 100644
--- a/spice-qemu-char.c
+++ b/spice-qemu-char.c
@@ -3,6 +3,7 @@ 
 #include "ui/qemu-spice.h"
 #include <spice.h>
 #include <spice-experimental.h>
+#include <spice/protocol.h>
 
 #include "osdep.h"
 
@@ -23,6 +24,7 @@  typedef struct SpiceCharDriver {
     uint8_t               *datapos;
     ssize_t               bufsize, datalen;
     uint32_t              debug;
+    CharDriverState       *chardev;
 } SpiceCharDriver;
 
 static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
@@ -67,6 +69,25 @@  static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
     return bytes;
 }
 
+static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event)
+{
+    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
+    int chr_event;
+
+    switch (event) {
+    case SPICE_PORT_EVENT_BREAK:
+        chr_event = CHR_EVENT_BREAK;
+        break;
+    default:
+        dprintf(scd, 2, "%s: unknown %d\n", __func__, event);
+        return;
+    }
+
+    dprintf(scd, 2, "%s: %d\n", __func__, event);
+    trace_spice_vmc_event(chr_event);
+    qemu_chr_be_event(scd->chr, chr_event);
+}
+
 static void vmc_state(SpiceCharDeviceInstance *sin, int connected)
 {
     SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
@@ -103,6 +124,7 @@  static SpiceCharDeviceInterface vmc_interface = {
     .state              = vmc_state,
     .write              = vmc_write,
     .read               = vmc_read,
+    .event              = vmc_event,
 };
 
 
@@ -116,6 +138,10 @@  static void vmc_register_interface(SpiceCharDriver *scd)
     qemu_spice_add_interface(&scd->sin.base);
     scd->active = true;
     trace_spice_vmc_register_interface(scd);
+
+    if (scd->chardev != NULL && scd->chardev->opened) {
+        spice_server_port_event(&scd->sin, SPICE_PORT_EVENT_OPENED);
+    }
 }
 
 static void vmc_unregister_interface(SpiceCharDriver *scd)
@@ -242,3 +268,116 @@  CharDriverState *qemu_chr_open_spice(QemuOpts *opts)
 
     return chr;
 }
+static int port_can_read(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    SpiceCharDriver *scd = chr->opaque;
+
+    return qemu_chr_be_can_write(scd->chardev);
+}
+
+static void port_read(void *opaque, const uint8_t *buf, int size)
+{
+    CharDriverState *chr = opaque;
+    SpiceCharDriver *scd = chr->opaque;
+
+    dprintf(scd, 2, "%s: %d\n", __func__, size);
+    trace_spice_port_chardev_write(size);
+    qemu_chr_fe_write(scd->chardev, buf, size);
+}
+
+static void port_event(void *opaque, int event)
+{
+    CharDriverState *chr = opaque;
+    SpiceCharDriver *scd = chr->opaque;
+
+    dprintf(scd, 2, "%s: %d\n", __func__, event);
+
+    switch (event) {
+    case CHR_EVENT_CLOSED:
+        if (scd->chardev != NULL) {
+            qemu_chr_remove_clients(scd->chardev);
+        }
+        break;
+    }
+}
+
+static int chardev_can_read(void *opaque)
+{
+    CharDriverState *chr = opaque;
+    SpiceCharDriver *s = chr->opaque;
+
+    if (!chr->opened) {
+        return 0;
+    }
+
+    if (s->datalen != 0) {
+        return 0;
+    }
+
+    /* FIXME: assume spice can take chunks of 4096 */
+    return  4096;
+}
+
+/* Send data from a char device over to the spice port */
+static void chardev_read(void *opaque, const uint8_t *buf, int size)
+{
+    CharDriverState *chr = opaque;
+
+    trace_spice_port_chardev_read(size);
+    /* spicevmc port always send/queue all data */
+    qemu_chr_fe_write(chr, buf, size);
+}
+
+static void chardev_event(void *opaque, int event)
+{
+    CharDriverState *chr = opaque;
+    SpiceCharDriver *scd = chr->opaque;
+
+    dprintf(scd, 2, "%s: %d\n", __func__, event);
+
+    switch (event) {
+    case CHR_EVENT_OPENED:
+        spice_server_port_event(&scd->sin, SPICE_PORT_EVENT_OPENED);
+        break;
+    case CHR_EVENT_CLOSED:
+        spice_server_port_event(&scd->sin, SPICE_PORT_EVENT_CLOSED);
+        break;
+    }
+}
+
+CharDriverState *qemu_chr_open_spice_port(QemuOpts *opts)
+{
+    CharDriverState *chr, *chardev = NULL;
+    SpiceCharDriver *s;
+    const char *name = qemu_opt_get(opts, "name");
+    const char *chrdev = qemu_opt_get(opts, "chardev");
+
+    if (name == NULL) {
+        fprintf(stderr, "spice-qemu-char: missing name parameter\n");
+        return NULL;
+    }
+
+    if (chrdev != NULL) {
+        chardev = qemu_chr_find(chrdev);
+        if (chardev == NULL) {
+            fprintf(stderr, "spice-qemu-char: chardev \"%s\" not found\n",
+                    chrdev);
+            return NULL;
+        }
+    }
+
+    chr = chr_open(opts, "port");
+    s = chr->opaque;
+    s->sin.portname = name;
+
+    if (chardev != NULL) {
+        s->chardev = chardev;
+        qemu_chr_add_handlers(chardev, chardev_can_read,
+                              chardev_read, chardev_event, chr);
+    }
+
+    qemu_chr_add_handlers(chr, port_can_read, port_read, port_event, chr);
+
+    return chr;
+}
diff --git a/trace-events b/trace-events
index 6c6cbf1..26ca363 100644
--- a/trace-events
+++ b/trace-events
@@ -535,6 +535,9 @@  spice_vmc_write(ssize_t out, int len) "spice wrottn %zd of requested %d"
 spice_vmc_read(int bytes, int len) "spice read %d of requested %d"
 spice_vmc_register_interface(void *scd) "spice vmc registered interface %p"
 spice_vmc_unregister_interface(void *scd) "spice vmc unregistered interface %p"
+spice_vmc_event(int event) "spice vmc event %d"
+spice_port_chardev_read(int bytes) "spice port read %d from chardev"
+spice_port_chardev_write(int bytes) "spice port wrote %d to chardev"
 
 # hw/lm32_pic.c
 lm32_pic_raise_irq(void) "Raise CPU interrupt"
diff --git a/ui/qemu-spice.h b/ui/qemu-spice.h
index 3299da8..ab1943a 100644
--- a/ui/qemu-spice.h
+++ b/ui/qemu-spice.h
@@ -46,6 +46,7 @@  void do_info_spice_print(Monitor *mon, const QObject *data);
 void do_info_spice(Monitor *mon, QObject **ret_data);
 
 CharDriverState *qemu_chr_open_spice(QemuOpts *opts);
+CharDriverState *qemu_chr_open_spice_port(QemuOpts *opts);
 
 #else  /* CONFIG_SPICE */
 #include "monitor.h"