qemu/spice-qemu-char.c
<<
>>
Prefs
   1#include "config-host.h"
   2#include "trace.h"
   3#include "ui/qemu-spice.h"
   4#include "sysemu/char.h"
   5#include <spice.h>
   6#include <spice-experimental.h>
   7#include <spice/protocol.h>
   8
   9#include "qemu/osdep.h"
  10
  11typedef struct SpiceCharDriver {
  12    CharDriverState*      chr;
  13    SpiceCharDeviceInstance     sin;
  14    char                  *subtype;
  15    bool                  active;
  16    bool                  blocked;
  17    const uint8_t         *datapos;
  18    int                   datalen;
  19    QLIST_ENTRY(SpiceCharDriver) next;
  20} SpiceCharDriver;
  21
  22typedef struct SpiceCharSource {
  23    GSource               source;
  24    SpiceCharDriver       *scd;
  25} SpiceCharSource;
  26
  27static QLIST_HEAD(, SpiceCharDriver) spice_chars =
  28    QLIST_HEAD_INITIALIZER(spice_chars);
  29
  30static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
  31{
  32    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
  33    ssize_t out = 0;
  34    ssize_t last_out;
  35    uint8_t* p = (uint8_t*)buf;
  36
  37    while (len > 0) {
  38        int can_write = qemu_chr_be_can_write(scd->chr);
  39        last_out = MIN(len, can_write);
  40        if (last_out <= 0) {
  41            break;
  42        }
  43        qemu_chr_be_write(scd->chr, p, last_out);
  44        out += last_out;
  45        len -= last_out;
  46        p += last_out;
  47    }
  48
  49    trace_spice_vmc_write(out, len + out);
  50    return out;
  51}
  52
  53static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
  54{
  55    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
  56    int bytes = MIN(len, scd->datalen);
  57
  58    if (bytes > 0) {
  59        memcpy(buf, scd->datapos, bytes);
  60        scd->datapos += bytes;
  61        scd->datalen -= bytes;
  62        assert(scd->datalen >= 0);
  63    }
  64    if (scd->datalen == 0) {
  65        scd->datapos = 0;
  66        scd->blocked = false;
  67    }
  68    trace_spice_vmc_read(bytes, len);
  69    return bytes;
  70}
  71
  72#if SPICE_SERVER_VERSION >= 0x000c02
  73static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event)
  74{
  75    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
  76    int chr_event;
  77
  78    switch (event) {
  79    case SPICE_PORT_EVENT_BREAK:
  80        chr_event = CHR_EVENT_BREAK;
  81        break;
  82    default:
  83        return;
  84    }
  85
  86    trace_spice_vmc_event(chr_event);
  87    qemu_chr_be_event(scd->chr, chr_event);
  88}
  89#endif
  90
  91static void vmc_state(SpiceCharDeviceInstance *sin, int connected)
  92{
  93    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
  94
  95    if ((scd->chr->be_open && connected) ||
  96        (!scd->chr->be_open && !connected)) {
  97        return;
  98    }
  99
 100    qemu_chr_be_event(scd->chr,
 101                      connected ? CHR_EVENT_OPENED : CHR_EVENT_CLOSED);
 102}
 103
 104static SpiceCharDeviceInterface vmc_interface = {
 105    .base.type          = SPICE_INTERFACE_CHAR_DEVICE,
 106    .base.description   = "spice virtual channel char device",
 107    .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
 108    .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
 109    .state              = vmc_state,
 110    .write              = vmc_write,
 111    .read               = vmc_read,
 112#if SPICE_SERVER_VERSION >= 0x000c02
 113    .event              = vmc_event,
 114#endif
 115};
 116
 117
 118static void vmc_register_interface(SpiceCharDriver *scd)
 119{
 120    if (scd->active) {
 121        return;
 122    }
 123    scd->sin.base.sif = &vmc_interface.base;
 124    qemu_spice_add_interface(&scd->sin.base);
 125    scd->active = true;
 126    trace_spice_vmc_register_interface(scd);
 127}
 128
 129static void vmc_unregister_interface(SpiceCharDriver *scd)
 130{
 131    if (!scd->active) {
 132        return;
 133    }
 134    spice_server_remove_interface(&scd->sin.base);
 135    scd->active = false;
 136    trace_spice_vmc_unregister_interface(scd);
 137}
 138
 139static gboolean spice_char_source_prepare(GSource *source, gint *timeout)
 140{
 141    SpiceCharSource *src = (SpiceCharSource *)source;
 142
 143    *timeout = -1;
 144
 145    return !src->scd->blocked;
 146}
 147
 148static gboolean spice_char_source_check(GSource *source)
 149{
 150    SpiceCharSource *src = (SpiceCharSource *)source;
 151
 152    return !src->scd->blocked;
 153}
 154
 155static gboolean spice_char_source_dispatch(GSource *source,
 156    GSourceFunc callback, gpointer user_data)
 157{
 158    GIOFunc func = (GIOFunc)callback;
 159
 160    return func(NULL, G_IO_OUT, user_data);
 161}
 162
 163GSourceFuncs SpiceCharSourceFuncs = {
 164    .prepare  = spice_char_source_prepare,
 165    .check    = spice_char_source_check,
 166    .dispatch = spice_char_source_dispatch,
 167};
 168
 169static GSource *spice_chr_add_watch(CharDriverState *chr, GIOCondition cond)
 170{
 171    SpiceCharDriver *scd = chr->opaque;
 172    SpiceCharSource *src;
 173
 174    assert(cond == G_IO_OUT);
 175
 176    src = (SpiceCharSource *)g_source_new(&SpiceCharSourceFuncs,
 177                                          sizeof(SpiceCharSource));
 178    src->scd = scd;
 179
 180    return (GSource *)src;
 181}
 182
 183static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 184{
 185    SpiceCharDriver *s = chr->opaque;
 186    int read_bytes;
 187
 188    assert(s->datalen == 0);
 189    s->datapos = buf;
 190    s->datalen = len;
 191    spice_server_char_device_wakeup(&s->sin);
 192    read_bytes = len - s->datalen;
 193    if (read_bytes != len) {
 194        /* We'll get passed in the unconsumed data with the next call */
 195        s->datalen = 0;
 196        s->datapos = NULL;
 197        s->blocked = true;
 198    }
 199    return read_bytes;
 200}
 201
 202static void spice_chr_close(struct CharDriverState *chr)
 203{
 204    SpiceCharDriver *s = chr->opaque;
 205
 206    vmc_unregister_interface(s);
 207    QLIST_REMOVE(s, next);
 208
 209    g_free((char *)s->sin.subtype);
 210#if SPICE_SERVER_VERSION >= 0x000c02
 211    g_free((char *)s->sin.portname);
 212#endif
 213    g_free(s);
 214}
 215
 216static void spice_chr_set_fe_open(struct CharDriverState *chr, int fe_open)
 217{
 218    SpiceCharDriver *s = chr->opaque;
 219    if (fe_open) {
 220        vmc_register_interface(s);
 221    } else {
 222        vmc_unregister_interface(s);
 223    }
 224}
 225
 226static void print_allowed_subtypes(void)
 227{
 228    const char** psubtype;
 229    int i;
 230
 231    fprintf(stderr, "allowed names: ");
 232    for(i=0, psubtype = spice_server_char_device_recognized_subtypes();
 233        *psubtype != NULL; ++psubtype, ++i) {
 234        if (i == 0) {
 235            fprintf(stderr, "%s", *psubtype);
 236        } else {
 237            fprintf(stderr, ", %s", *psubtype);
 238        }
 239    }
 240    fprintf(stderr, "\n");
 241}
 242
 243static CharDriverState *chr_open(const char *subtype)
 244{
 245    CharDriverState *chr;
 246    SpiceCharDriver *s;
 247
 248    chr = g_malloc0(sizeof(CharDriverState));
 249    s = g_malloc0(sizeof(SpiceCharDriver));
 250    s->chr = chr;
 251    s->active = false;
 252    s->sin.subtype = g_strdup(subtype);
 253    chr->opaque = s;
 254    chr->chr_write = spice_chr_write;
 255    chr->chr_add_watch = spice_chr_add_watch;
 256    chr->chr_close = spice_chr_close;
 257    chr->chr_set_fe_open = spice_chr_set_fe_open;
 258    chr->explicit_be_open = true;
 259
 260    QLIST_INSERT_HEAD(&spice_chars, s, next);
 261
 262    return chr;
 263}
 264
 265CharDriverState *qemu_chr_open_spice_vmc(const char *type)
 266{
 267    const char **psubtype = spice_server_char_device_recognized_subtypes();
 268
 269    if (type == NULL) {
 270        fprintf(stderr, "spice-qemu-char: missing name parameter\n");
 271        print_allowed_subtypes();
 272        return NULL;
 273    }
 274    for (; *psubtype != NULL; ++psubtype) {
 275        if (strcmp(type, *psubtype) == 0) {
 276            break;
 277        }
 278    }
 279    if (*psubtype == NULL) {
 280        fprintf(stderr, "spice-qemu-char: unsupported type: %s\n", type);
 281        print_allowed_subtypes();
 282        return NULL;
 283    }
 284
 285    return chr_open(type);
 286}
 287
 288#if SPICE_SERVER_VERSION >= 0x000c02
 289CharDriverState *qemu_chr_open_spice_port(const char *name)
 290{
 291    CharDriverState *chr;
 292    SpiceCharDriver *s;
 293
 294    if (name == NULL) {
 295        fprintf(stderr, "spice-qemu-char: missing name parameter\n");
 296        return NULL;
 297    }
 298
 299    chr = chr_open("port");
 300    s = chr->opaque;
 301    s->sin.portname = g_strdup(name);
 302
 303    return chr;
 304}
 305
 306void qemu_spice_register_ports(void)
 307{
 308    SpiceCharDriver *s;
 309
 310    QLIST_FOREACH(s, &spice_chars, next) {
 311        if (s->sin.portname == NULL) {
 312            continue;
 313        }
 314        vmc_register_interface(s);
 315    }
 316}
 317#endif
 318
 319static void qemu_chr_parse_spice_vmc(QemuOpts *opts, ChardevBackend *backend,
 320                                     Error **errp)
 321{
 322    const char *name = qemu_opt_get(opts, "name");
 323
 324    if (name == NULL) {
 325        error_setg(errp, "chardev: spice channel: no name given");
 326        return;
 327    }
 328    backend->spicevmc = g_new0(ChardevSpiceChannel, 1);
 329    backend->spicevmc->type = g_strdup(name);
 330}
 331
 332static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
 333                                      Error **errp)
 334{
 335    const char *name = qemu_opt_get(opts, "name");
 336
 337    if (name == NULL) {
 338        error_setg(errp, "chardev: spice port: no name given");
 339        return;
 340    }
 341    backend->spiceport = g_new0(ChardevSpicePort, 1);
 342    backend->spiceport->fqdn = g_strdup(name);
 343}
 344
 345static void register_types(void)
 346{
 347    register_char_driver_qapi("spicevmc", CHARDEV_BACKEND_KIND_SPICEVMC,
 348                              qemu_chr_parse_spice_vmc);
 349    register_char_driver_qapi("spiceport", CHARDEV_BACKEND_KIND_SPICEPORT,
 350                              qemu_chr_parse_spice_port);
 351}
 352
 353type_init(register_types);
 354