@@ -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
};
@@ -217,6 +217,9 @@ static QemuOptsList qemu_chardev_opts = {
},{
.name = "debug",
.type = QEMU_OPT_NUMBER,
+ },{
+ .name = "chardev",
+ .type = QEMU_OPT_STRING,
},
{ /* end of list */ }
},
@@ -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
@@ -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;
+}
@@ -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"
@@ -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"
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(+)