spice: add chardev (v5)

Adding a chardev backend for spice, where spice determines what
to do with it based on the name attribute given during chardev creation.
For usage by spice vdagent in conjunction with a properly named
virtio-serial device, and future smartcard channel usage.

Example usage:
 qemu -device virtio-serial -chardev spicevmc,name=vdagent,id=vdagent \
 -device virtserialport,chardev=vdagent,name=com.redhat.spice.0

v4->v5:
 * add tracing events
 * fix missing comma
 * fix help string to show debug is optional

v3->v4:
 * updated commit message

v1->v3 changes: (v2 had a wrong commit message)
 * removed spice-qemu-char.h, folded into ui/qemu-spice.h
 * removed dead IOCTL code
 * removed comment
 * removed ifdef CONFIG_SPICE from qemu-config.c and qemu-options.hx help.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
Alon Levy 2011-01-19 10:49:50 +02:00 committed by Gerd Hoffmann
parent 710fc4f5f1
commit cbcc6336ce
7 changed files with 225 additions and 2 deletions

View File

@ -105,7 +105,7 @@ common-obj-$(CONFIG_BRLAPI) += baum.o
common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o common-obj-$(CONFIG_POSIX) += migration-exec.o migration-unix.o migration-fd.o
common-obj-$(CONFIG_WIN32) += version.o common-obj-$(CONFIG_WIN32) += version.o
common-obj-$(CONFIG_SPICE) += ui/spice-core.o ui/spice-input.o ui/spice-display.o common-obj-$(CONFIG_SPICE) += ui/spice-core.o ui/spice-input.o ui/spice-display.o spice-qemu-char.o
audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
audio-obj-$(CONFIG_SDL) += sdlaudio.o audio-obj-$(CONFIG_SDL) += sdlaudio.o

View File

@ -97,6 +97,7 @@
#endif #endif
#include "qemu_socket.h" #include "qemu_socket.h"
#include "ui/qemu-spice.h"
#define READ_BUF_LEN 4096 #define READ_BUF_LEN 4096
@ -2495,6 +2496,9 @@ static const struct {
|| defined(__FreeBSD_kernel__) || defined(__FreeBSD_kernel__)
{ .name = "parport", .open = qemu_chr_open_pp }, { .name = "parport", .open = qemu_chr_open_pp },
#endif #endif
#ifdef CONFIG_SPICE
{ .name = "spicevmc", .open = qemu_chr_open_spice },
#endif
}; };
CharDriverState *qemu_chr_open_opts(QemuOpts *opts, CharDriverState *qemu_chr_open_opts(QemuOpts *opts,

View File

@ -146,6 +146,12 @@ static QemuOptsList qemu_chardev_opts = {
},{ },{
.name = "signal", .name = "signal",
.type = QEMU_OPT_BOOL, .type = QEMU_OPT_BOOL,
},{
.name = "name",
.type = QEMU_OPT_STRING,
},{
.name = "debug",
.type = QEMU_OPT_NUMBER,
}, },
{ /* end of list */ } { /* end of list */ }
}, },

View File

@ -1367,6 +1367,9 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev,
#endif #endif
#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) #if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__)
"-chardev parport,id=id,path=path[,mux=on|off]\n" "-chardev parport,id=id,path=path[,mux=on|off]\n"
#endif
#if defined(CONFIG_SPICE)
"-chardev spicevmc,id=id,name=name[,debug=debug]\n"
#endif #endif
, QEMU_ARCH_ALL , QEMU_ARCH_ALL
) )
@ -1392,7 +1395,8 @@ Backend is one of:
@option{stdio}, @option{stdio},
@option{braille}, @option{braille},
@option{tty}, @option{tty},
@option{parport}. @option{parport},
@option{spicevmc}.
The specific backend will determine the applicable options. The specific backend will determine the applicable options.
All devices must have an id, which can be any string up to 127 characters long. All devices must have an id, which can be any string up to 127 characters long.
@ -1568,6 +1572,16 @@ Connect to a local parallel port.
@option{path} specifies the path to the parallel port device. @option{path} is @option{path} specifies the path to the parallel port device. @option{path} is
required. required.
#if defined(CONFIG_SPICE)
@item -chardev spicevmc ,id=@var{id} ,debug=@var{debug}, name=@var{name}
@option{debug} debug level for spicevmc
@option{name} name of spice channel to connect to
Connect to a spice virtual machine channel, such as vdiport.
#endif
@end table @end table
ETEXI ETEXI

190
spice-qemu-char.c Normal file
View File

@ -0,0 +1,190 @@
#include "config-host.h"
#include "trace.h"
#include "ui/qemu-spice.h"
#include <spice.h>
#include <spice-experimental.h>
#include "osdep.h"
#define dprintf(_scd, _level, _fmt, ...) \
do { \
static unsigned __dprintf_counter = 0; \
if (_scd->debug >= _level) { \
fprintf(stderr, "scd: %3d: " _fmt, ++__dprintf_counter, ## __VA_ARGS__);\
} \
} while (0)
#define VMC_MAX_HOST_WRITE 2048
typedef struct SpiceCharDriver {
CharDriverState* chr;
SpiceCharDeviceInstance sin;
char *subtype;
bool active;
uint8_t *buffer;
uint8_t *datapos;
ssize_t bufsize, datalen;
uint32_t debug;
} SpiceCharDriver;
static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
{
SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
ssize_t out = 0;
ssize_t last_out;
uint8_t* p = (uint8_t*)buf;
while (len > 0) {
last_out = MIN(len, VMC_MAX_HOST_WRITE);
qemu_chr_read(scd->chr, p, last_out);
if (last_out > 0) {
out += last_out;
len -= last_out;
p += last_out;
} else {
break;
}
}
dprintf(scd, 3, "%s: %lu/%zd\n", __func__, out, len + out);
trace_spice_vmc_write(out, len + out);
return out;
}
static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
{
SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
int bytes = MIN(len, scd->datalen);
dprintf(scd, 2, "%s: %p %d/%d/%zd\n", __func__, scd->datapos, len, bytes, scd->datalen);
if (bytes > 0) {
memcpy(buf, scd->datapos, bytes);
scd->datapos += bytes;
scd->datalen -= bytes;
assert(scd->datalen >= 0);
if (scd->datalen == 0) {
scd->datapos = 0;
}
}
trace_spice_vmc_read(bytes, len);
return bytes;
}
static SpiceCharDeviceInterface vmc_interface = {
.base.type = SPICE_INTERFACE_CHAR_DEVICE,
.base.description = "spice virtual channel char device",
.base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
.base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
.write = vmc_write,
.read = vmc_read,
};
static void vmc_register_interface(SpiceCharDriver *scd)
{
if (scd->active) {
return;
}
dprintf(scd, 1, "%s\n", __func__);
scd->sin.base.sif = &vmc_interface.base;
qemu_spice_add_interface(&scd->sin.base);
scd->active = true;
trace_spice_vmc_register_interface(scd);
}
static void vmc_unregister_interface(SpiceCharDriver *scd)
{
if (!scd->active) {
return;
}
dprintf(scd, 1, "%s\n", __func__);
spice_server_remove_interface(&scd->sin.base);
scd->active = false;
trace_spice_vmc_unregister_interface(scd);
}
static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
SpiceCharDriver *s = chr->opaque;
dprintf(s, 2, "%s: %d\n", __func__, len);
vmc_register_interface(s);
assert(s->datalen == 0);
if (s->bufsize < len) {
s->bufsize = len;
s->buffer = qemu_realloc(s->buffer, s->bufsize);
}
memcpy(s->buffer, buf, len);
s->datapos = s->buffer;
s->datalen = len;
spice_server_char_device_wakeup(&s->sin);
return len;
}
static void spice_chr_close(struct CharDriverState *chr)
{
SpiceCharDriver *s = chr->opaque;
printf("%s\n", __func__);
vmc_unregister_interface(s);
qemu_free(s);
}
static void print_allowed_subtypes(void)
{
const char** psubtype;
int i;
fprintf(stderr, "allowed names: ");
for(i=0, psubtype = spice_server_char_device_recognized_subtypes();
*psubtype != NULL; ++psubtype, ++i) {
if (i == 0) {
fprintf(stderr, "%s", *psubtype);
} else {
fprintf(stderr, ", %s", *psubtype);
}
}
fprintf(stderr, "\n");
}
CharDriverState *qemu_chr_open_spice(QemuOpts *opts)
{
CharDriverState *chr;
SpiceCharDriver *s;
const char* name = qemu_opt_get(opts, "name");
uint32_t debug = qemu_opt_get_number(opts, "debug", 0);
const char** psubtype = spice_server_char_device_recognized_subtypes();
const char *subtype = NULL;
if (name == NULL) {
fprintf(stderr, "spice-qemu-char: missing name parameter\n");
print_allowed_subtypes();
return NULL;
}
for(;*psubtype != NULL; ++psubtype) {
if (strcmp(name, *psubtype) == 0) {
subtype = *psubtype;
break;
}
}
if (subtype == NULL) {
fprintf(stderr, "spice-qemu-char: unsupported name\n");
print_allowed_subtypes();
return NULL;
}
chr = qemu_mallocz(sizeof(CharDriverState));
s = qemu_mallocz(sizeof(SpiceCharDriver));
s->chr = chr;
s->debug = debug;
s->active = false;
s->sin.subtype = subtype;
chr->opaque = s;
chr->chr_write = spice_chr_write;
chr->chr_close = spice_chr_close;
qemu_chr_generic_open(chr);
return chr;
}

View File

@ -224,3 +224,9 @@ disable qed_aio_write_data(void *s, void *acb, int ret, uint64_t offset, size_t
disable qed_aio_write_prefill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64"" disable qed_aio_write_prefill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64""
disable qed_aio_write_postfill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64"" disable qed_aio_write_postfill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64""
disable qed_aio_write_main(void *s, void *acb, int ret, uint64_t offset, size_t len) "s %p acb %p ret %d offset %"PRIu64" len %zu" disable qed_aio_write_main(void *s, void *acb, int ret, uint64_t offset, size_t len) "s %p acb %p ret %d offset %"PRIu64" len %zu"
# spice-qemu-char.c
disable spice_vmc_write(ssize_t out, int len) "spice wrottn %lu of requested %zd"
disable spice_vmc_read(int bytes, int len) "spice read %lu of requested %zd"
disable spice_vmc_register_interface(void *scd) "spice vmc registered interface %p"
disable spice_vmc_unregister_interface(void *scd) "spice vmc unregistered interface %p"

View File

@ -24,6 +24,7 @@
#include "qemu-option.h" #include "qemu-option.h"
#include "qemu-config.h" #include "qemu-config.h"
#include "qemu-char.h"
extern int using_spice; extern int using_spice;
@ -41,6 +42,8 @@ int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,
void do_info_spice_print(Monitor *mon, const QObject *data); void do_info_spice_print(Monitor *mon, const QObject *data);
void do_info_spice(Monitor *mon, QObject **ret_data); void do_info_spice(Monitor *mon, QObject **ret_data);
CharDriverState *qemu_chr_open_spice(QemuOpts *opts);
#else /* CONFIG_SPICE */ #else /* CONFIG_SPICE */
#define using_spice 0 #define using_spice 0