qemu/spice-qemu-char.c
<<
>>
Prefs
   1#include "qemu/osdep.h"
   2#include "trace.h"
   3#include "ui/qemu-spice.h"
   4#include "sysemu/char.h"
   5#include <spice.h>
   6#include <spice/protocol.h>
   7
   8
   9typedef struct SpiceCharDriver {
  10    CharDriverState*      chr;
  11    SpiceCharDeviceInstance     sin;
  12    bool                  active;
  13    bool                  blocked;
  14    const uint8_t         *datapos;
  15    int                   datalen;
  16    QLIST_ENTRY(SpiceCharDriver) next;
  17} SpiceCharDriver;
  18
  19typedef struct SpiceCharSource {
  20    GSource               source;
  21    SpiceCharDriver       *scd;
  22} SpiceCharSource;
  23
  24static QLIST_HEAD(, SpiceCharDriver) spice_chars =
  25    QLIST_HEAD_INITIALIZER(spice_chars);
  26
  27static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
  28{
  29    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
  30    ssize_t out = 0;
  31    ssize_t last_out;
  32    uint8_t* p = (uint8_t*)buf;
  33
  34    while (len > 0) {
  35        int can_write = qemu_chr_be_can_write(scd->chr);
  36        last_out = MIN(len, can_write);
  37        if (last_out <= 0) {
  38            break;
  39        }
  40        qemu_chr_be_write(scd->chr, p, last_out);
  41        out += last_out;
  42        len -= last_out;
  43        p += last_out;
  44    }
  45
  46    trace_spice_vmc_write(out, len + out);
  47    return out;
  48}
  49
  50static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
  51{
  52    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
  53    int bytes = MIN(len, scd->datalen);
  54
  55    if (bytes > 0) {
  56        memcpy(buf, scd->datapos, bytes);
  57        scd->datapos += bytes;
  58        scd->datalen -= bytes;
  59        assert(scd->datalen >= 0);
  60    }
  61    if (scd->datalen == 0) {
  62        scd->datapos = 0;
  63        scd->blocked = false;
  64    }
  65    trace_spice_vmc_read(bytes, len);
  66    return bytes;
  67}
  68
  69#if SPICE_SERVER_VERSION >= 0x000c02
  70static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event)
  71{
  72    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
  73    int chr_event;
  74
  75    switch (event) {
  76    case SPICE_PORT_EVENT_BREAK:
  77        chr_event = CHR_EVENT_BREAK;
  78        break;
  79    default:
  80        return;
  81    }
  82
  83    trace_spice_vmc_event(chr_event);
  84    qemu_chr_be_event(scd->chr, chr_event);
  85}
  86#endif
  87
  88static void vmc_state(SpiceCharDeviceInstance *sin, int connected)
  89{
  90    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
  91
  92    if ((scd->chr->be_open && connected) ||
  93        (!scd->chr->be_open && !connected)) {
  94        return;
  95    }
  96
  97    qemu_chr_be_event(scd->chr,
  98                      connected ? CHR_EVENT_OPENED : CHR_EVENT_CLOSED);
  99}
 100
 101static SpiceCharDeviceInterface vmc_interface = {
 102    .base.type          = SPICE_INTERFACE_CHAR_DEVICE,
 103    .base.description   = "spice virtual channel char device",
 104    .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
 105    .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
 106    .state              = vmc_state,
 107    .write              = vmc_write,
 108    .read               = vmc_read,
 109#if SPICE_SERVER_VERSION >= 0x000c02
 110    .event              = vmc_event,
 111#endif
 112#if SPICE_SERVER_VERSION >= 0x000c06
 113    .flags              = SPICE_CHAR_DEVICE_NOTIFY_WRITABLE,
 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
 163static GSourceFuncs 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_vmc_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 spice_port_set_fe_open(struct CharDriverState *chr, int fe_open)
 227{
 228#if SPICE_SERVER_VERSION >= 0x000c02
 229    SpiceCharDriver *s = chr->opaque;
 230
 231    if (fe_open) {
 232        spice_server_port_event(&s->sin, SPICE_PORT_EVENT_OPENED);
 233    } else {
 234        spice_server_port_event(&s->sin, SPICE_PORT_EVENT_CLOSED);
 235    }
 236#endif
 237}
 238
 239static void spice_chr_fe_event(struct CharDriverState *chr, int event)
 240{
 241#if SPICE_SERVER_VERSION >= 0x000c02
 242    SpiceCharDriver *s = chr->opaque;
 243
 244    spice_server_port_event(&s->sin, event);
 245#endif
 246}
 247
 248static void print_allowed_subtypes(void)
 249{
 250    const char** psubtype;
 251    int i;
 252
 253    fprintf(stderr, "allowed names: ");
 254    for(i=0, psubtype = spice_server_char_device_recognized_subtypes();
 255        *psubtype != NULL; ++psubtype, ++i) {
 256        if (i == 0) {
 257            fprintf(stderr, "%s", *psubtype);
 258        } else {
 259            fprintf(stderr, ", %s", *psubtype);
 260        }
 261    }
 262    fprintf(stderr, "\n");
 263}
 264
 265static void spice_chr_accept_input(struct CharDriverState *chr)
 266{
 267    SpiceCharDriver *s = chr->opaque;
 268
 269    spice_server_char_device_wakeup(&s->sin);
 270}
 271
 272static CharDriverState *chr_open(const char *subtype,
 273                                 void (*set_fe_open)(struct CharDriverState *,
 274                                                     int),
 275                                 ChardevCommon *backend,
 276                                 Error **errp)
 277{
 278    CharDriverState *chr;
 279    SpiceCharDriver *s;
 280
 281    chr = qemu_chr_alloc(backend, errp);
 282    if (!chr) {
 283        return NULL;
 284    }
 285    s = g_malloc0(sizeof(SpiceCharDriver));
 286    s->chr = chr;
 287    s->active = false;
 288    s->sin.subtype = g_strdup(subtype);
 289    chr->opaque = s;
 290    chr->chr_write = spice_chr_write;
 291    chr->chr_add_watch = spice_chr_add_watch;
 292    chr->chr_close = spice_chr_close;
 293    chr->chr_set_fe_open = set_fe_open;
 294    chr->explicit_be_open = true;
 295    chr->chr_fe_event = spice_chr_fe_event;
 296    chr->chr_accept_input = spice_chr_accept_input;
 297
 298    QLIST_INSERT_HEAD(&spice_chars, s, next);
 299
 300    return chr;
 301}
 302
 303static CharDriverState *qemu_chr_open_spice_vmc(const char *id,
 304                                                ChardevBackend *backend,
 305                                                ChardevReturn *ret,
 306                                                Error **errp)
 307{
 308    ChardevSpiceChannel *spicevmc = backend->u.spicevmc.data;
 309    const char *type = spicevmc->type;
 310    const char **psubtype = spice_server_char_device_recognized_subtypes();
 311    ChardevCommon *common = qapi_ChardevSpiceChannel_base(spicevmc);
 312
 313    for (; *psubtype != NULL; ++psubtype) {
 314        if (strcmp(type, *psubtype) == 0) {
 315            break;
 316        }
 317    }
 318    if (*psubtype == NULL) {
 319        fprintf(stderr, "spice-qemu-char: unsupported type: %s\n", type);
 320        print_allowed_subtypes();
 321        return NULL;
 322    }
 323
 324    return chr_open(type, spice_vmc_set_fe_open, common, errp);
 325}
 326
 327#if SPICE_SERVER_VERSION >= 0x000c02
 328static CharDriverState *qemu_chr_open_spice_port(const char *id,
 329                                                 ChardevBackend *backend,
 330                                                 ChardevReturn *ret,
 331                                                 Error **errp)
 332{
 333    ChardevSpicePort *spiceport = backend->u.spiceport.data;
 334    const char *name = spiceport->fqdn;
 335    ChardevCommon *common = qapi_ChardevSpicePort_base(spiceport);
 336    CharDriverState *chr;
 337    SpiceCharDriver *s;
 338
 339    if (name == NULL) {
 340        fprintf(stderr, "spice-qemu-char: missing name parameter\n");
 341        return NULL;
 342    }
 343
 344    chr = chr_open("port", spice_port_set_fe_open, common, errp);
 345    if (!chr) {
 346        return NULL;
 347    }
 348    s = chr->opaque;
 349    s->sin.portname = g_strdup(name);
 350
 351    return chr;
 352}
 353
 354void qemu_spice_register_ports(void)
 355{
 356    SpiceCharDriver *s;
 357
 358    QLIST_FOREACH(s, &spice_chars, next) {
 359        if (s->sin.portname == NULL) {
 360            continue;
 361        }
 362        vmc_register_interface(s);
 363    }
 364}
 365#endif
 366
 367static void qemu_chr_parse_spice_vmc(QemuOpts *opts, ChardevBackend *backend,
 368                                     Error **errp)
 369{
 370    const char *name = qemu_opt_get(opts, "name");
 371    ChardevSpiceChannel *spicevmc;
 372
 373    if (name == NULL) {
 374        error_setg(errp, "chardev: spice channel: no name given");
 375        return;
 376    }
 377    spicevmc = backend->u.spicevmc.data = g_new0(ChardevSpiceChannel, 1);
 378    qemu_chr_parse_common(opts, qapi_ChardevSpiceChannel_base(spicevmc));
 379    spicevmc->type = g_strdup(name);
 380}
 381
 382static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
 383                                      Error **errp)
 384{
 385    const char *name = qemu_opt_get(opts, "name");
 386    ChardevSpicePort *spiceport;
 387
 388    if (name == NULL) {
 389        error_setg(errp, "chardev: spice port: no name given");
 390        return;
 391    }
 392    spiceport = backend->u.spiceport.data = g_new0(ChardevSpicePort, 1);
 393    qemu_chr_parse_common(opts, qapi_ChardevSpicePort_base(spiceport));
 394    spiceport->fqdn = g_strdup(name);
 395}
 396
 397static void register_types(void)
 398{
 399    register_char_driver("spicevmc", CHARDEV_BACKEND_KIND_SPICEVMC,
 400                         qemu_chr_parse_spice_vmc, qemu_chr_open_spice_vmc);
 401    register_char_driver("spiceport", CHARDEV_BACKEND_KIND_SPICEPORT,
 402                         qemu_chr_parse_spice_port, qemu_chr_open_spice_port);
 403}
 404
 405type_init(register_types);
 406