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_free(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 print_allowed_subtypes(void)
 240{
 241    const char** psubtype;
 242    int i;
 243
 244    fprintf(stderr, "allowed names: ");
 245    for(i=0, psubtype = spice_server_char_device_recognized_subtypes();
 246        *psubtype != NULL; ++psubtype, ++i) {
 247        if (i == 0) {
 248            fprintf(stderr, "%s", *psubtype);
 249        } else {
 250            fprintf(stderr, ", %s", *psubtype);
 251        }
 252    }
 253    fprintf(stderr, "\n");
 254}
 255
 256static void spice_chr_accept_input(struct CharDriverState *chr)
 257{
 258    SpiceCharDriver *s = chr->opaque;
 259
 260    spice_server_char_device_wakeup(&s->sin);
 261}
 262
 263static CharDriverState *chr_open(const char *subtype,
 264                                 void (*set_fe_open)(struct CharDriverState *,
 265                                                     int),
 266                                 ChardevCommon *backend,
 267                                 Error **errp)
 268{
 269    CharDriverState *chr;
 270    SpiceCharDriver *s;
 271
 272    chr = qemu_chr_alloc(backend, errp);
 273    if (!chr) {
 274        return NULL;
 275    }
 276    s = g_malloc0(sizeof(SpiceCharDriver));
 277    s->chr = chr;
 278    s->active = false;
 279    s->sin.subtype = g_strdup(subtype);
 280    chr->opaque = s;
 281    chr->chr_write = spice_chr_write;
 282    chr->chr_add_watch = spice_chr_add_watch;
 283    chr->chr_free = spice_chr_free;
 284    chr->chr_set_fe_open = set_fe_open;
 285    chr->chr_accept_input = spice_chr_accept_input;
 286
 287    QLIST_INSERT_HEAD(&spice_chars, s, next);
 288
 289    return chr;
 290}
 291
 292static CharDriverState *qemu_chr_open_spice_vmc(const char *id,
 293                                                ChardevBackend *backend,
 294                                                ChardevReturn *ret,
 295                                                bool *be_opened,
 296                                                Error **errp)
 297{
 298    ChardevSpiceChannel *spicevmc = backend->u.spicevmc.data;
 299    const char *type = spicevmc->type;
 300    const char **psubtype = spice_server_char_device_recognized_subtypes();
 301    ChardevCommon *common = qapi_ChardevSpiceChannel_base(spicevmc);
 302
 303    for (; *psubtype != NULL; ++psubtype) {
 304        if (strcmp(type, *psubtype) == 0) {
 305            break;
 306        }
 307    }
 308    if (*psubtype == NULL) {
 309        fprintf(stderr, "spice-qemu-char: unsupported type: %s\n", type);
 310        print_allowed_subtypes();
 311        return NULL;
 312    }
 313
 314    *be_opened = false;
 315    return chr_open(type, spice_vmc_set_fe_open, common, errp);
 316}
 317
 318#if SPICE_SERVER_VERSION >= 0x000c02
 319static CharDriverState *qemu_chr_open_spice_port(const char *id,
 320                                                 ChardevBackend *backend,
 321                                                 ChardevReturn *ret,
 322                                                 bool *be_opened,
 323                                                 Error **errp)
 324{
 325    ChardevSpicePort *spiceport = backend->u.spiceport.data;
 326    const char *name = spiceport->fqdn;
 327    ChardevCommon *common = qapi_ChardevSpicePort_base(spiceport);
 328    CharDriverState *chr;
 329    SpiceCharDriver *s;
 330
 331    if (name == NULL) {
 332        fprintf(stderr, "spice-qemu-char: missing name parameter\n");
 333        return NULL;
 334    }
 335
 336    chr = chr_open("port", spice_port_set_fe_open, common, errp);
 337    if (!chr) {
 338        return NULL;
 339    }
 340    *be_opened = false;
 341    s = chr->opaque;
 342    s->sin.portname = g_strdup(name);
 343
 344    return chr;
 345}
 346
 347void qemu_spice_register_ports(void)
 348{
 349    SpiceCharDriver *s;
 350
 351    QLIST_FOREACH(s, &spice_chars, next) {
 352        if (s->sin.portname == NULL) {
 353            continue;
 354        }
 355        vmc_register_interface(s);
 356    }
 357}
 358#endif
 359
 360static void qemu_chr_parse_spice_vmc(QemuOpts *opts, ChardevBackend *backend,
 361                                     Error **errp)
 362{
 363    const char *name = qemu_opt_get(opts, "name");
 364    ChardevSpiceChannel *spicevmc;
 365
 366    if (name == NULL) {
 367        error_setg(errp, "chardev: spice channel: no name given");
 368        return;
 369    }
 370    spicevmc = backend->u.spicevmc.data = g_new0(ChardevSpiceChannel, 1);
 371    qemu_chr_parse_common(opts, qapi_ChardevSpiceChannel_base(spicevmc));
 372    spicevmc->type = g_strdup(name);
 373}
 374
 375static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
 376                                      Error **errp)
 377{
 378    const char *name = qemu_opt_get(opts, "name");
 379    ChardevSpicePort *spiceport;
 380
 381    if (name == NULL) {
 382        error_setg(errp, "chardev: spice port: no name given");
 383        return;
 384    }
 385    spiceport = backend->u.spiceport.data = g_new0(ChardevSpicePort, 1);
 386    qemu_chr_parse_common(opts, qapi_ChardevSpicePort_base(spiceport));
 387    spiceport->fqdn = g_strdup(name);
 388}
 389
 390static void register_types(void)
 391{
 392    register_char_driver("spicevmc", CHARDEV_BACKEND_KIND_SPICEVMC,
 393                         qemu_chr_parse_spice_vmc, qemu_chr_open_spice_vmc);
 394    register_char_driver("spiceport", CHARDEV_BACKEND_KIND_SPICEPORT,
 395                         qemu_chr_parse_spice_port, qemu_chr_open_spice_port);
 396}
 397
 398type_init(register_types);
 399