qemu/spice-qemu-char.c
<<
>>
Prefs
   1#include "config-host.h"
   2#include "trace.h"
   3#include "ui/qemu-spice.h"
   4#include <spice.h>
   5#include <spice-experimental.h>
   6
   7#include "osdep.h"
   8
   9#define dprintf(_scd, _level, _fmt, ...)                                \
  10    do {                                                                \
  11        static unsigned __dprintf_counter = 0;                          \
  12        if (_scd->debug >= _level) {                                    \
  13            fprintf(stderr, "scd: %3d: " _fmt, ++__dprintf_counter, ## __VA_ARGS__);\
  14        }                                                               \
  15    } while (0)
  16
  17#define VMC_MAX_HOST_WRITE    2048
  18
  19typedef struct SpiceCharDriver {
  20    CharDriverState*      chr;
  21    SpiceCharDeviceInstance     sin;
  22    char                  *subtype;
  23    bool                  active;
  24    uint8_t               *buffer;
  25    uint8_t               *datapos;
  26    ssize_t               bufsize, datalen;
  27    uint32_t              debug;
  28} SpiceCharDriver;
  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        last_out = MIN(len, VMC_MAX_HOST_WRITE);
  39        if (qemu_chr_be_can_write(scd->chr) < last_out) {
  40            break;
  41        }
  42        qemu_chr_be_write(scd->chr, p, last_out);
  43        out += last_out;
  44        len -= last_out;
  45        p += last_out;
  46    }
  47
  48    dprintf(scd, 3, "%s: %zu/%zd\n", __func__, out, len + out);
  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    dprintf(scd, 2, "%s: %p %d/%d/%zd\n", __func__, scd->datapos, len, bytes, scd->datalen);
  59    if (bytes > 0) {
  60        memcpy(buf, scd->datapos, bytes);
  61        scd->datapos += bytes;
  62        scd->datalen -= bytes;
  63        assert(scd->datalen >= 0);
  64        if (scd->datalen == 0) {
  65            scd->datapos = 0;
  66        }
  67    }
  68    trace_spice_vmc_read(bytes, len);
  69    return bytes;
  70}
  71
  72static void vmc_state(SpiceCharDeviceInstance *sin, int connected)
  73{
  74    SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin);
  75
  76#if SPICE_SERVER_VERSION < 0x000901
  77    /*
  78     * spice-server calls the state callback for the agent channel when the
  79     * spice client connects / disconnects. Given that not the client but
  80     * the server is doing the parsing of the messages this is wrong as the
  81     * server is still listening. Worse, this causes the parser in the server
  82     * to go out of sync, so we ignore state calls for subtype vdagent
  83     * spicevmc chardevs. For the full story see:
  84     * http://lists.freedesktop.org/archives/spice-devel/2011-July/004837.html
  85     */
  86    if (strcmp(sin->subtype, "vdagent") == 0) {
  87        return;
  88    }
  89#endif
  90
  91    if ((scd->chr->opened && connected) ||
  92        (!scd->chr->opened && !connected)) {
  93        return;
  94    }
  95
  96    qemu_chr_be_event(scd->chr,
  97                      connected ? CHR_EVENT_OPENED : CHR_EVENT_CLOSED);
  98}
  99
 100static SpiceCharDeviceInterface vmc_interface = {
 101    .base.type          = SPICE_INTERFACE_CHAR_DEVICE,
 102    .base.description   = "spice virtual channel char device",
 103    .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
 104    .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
 105    .state              = vmc_state,
 106    .write              = vmc_write,
 107    .read               = vmc_read,
 108};
 109
 110
 111static void vmc_register_interface(SpiceCharDriver *scd)
 112{
 113    if (scd->active) {
 114        return;
 115    }
 116    dprintf(scd, 1, "%s\n", __func__);
 117    scd->sin.base.sif = &vmc_interface.base;
 118    qemu_spice_add_interface(&scd->sin.base);
 119    scd->active = true;
 120    trace_spice_vmc_register_interface(scd);
 121}
 122
 123static void vmc_unregister_interface(SpiceCharDriver *scd)
 124{
 125    if (!scd->active) {
 126        return;
 127    }
 128    dprintf(scd, 1, "%s\n", __func__);
 129    spice_server_remove_interface(&scd->sin.base);
 130    scd->active = false;
 131    trace_spice_vmc_unregister_interface(scd);
 132}
 133
 134
 135static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 136{
 137    SpiceCharDriver *s = chr->opaque;
 138
 139    dprintf(s, 2, "%s: %d\n", __func__, len);
 140    vmc_register_interface(s);
 141    assert(s->datalen == 0);
 142    if (s->bufsize < len) {
 143        s->bufsize = len;
 144        s->buffer = g_realloc(s->buffer, s->bufsize);
 145    }
 146    memcpy(s->buffer, buf, len);
 147    s->datapos = s->buffer;
 148    s->datalen = len;
 149    spice_server_char_device_wakeup(&s->sin);
 150    return len;
 151}
 152
 153static void spice_chr_close(struct CharDriverState *chr)
 154{
 155    SpiceCharDriver *s = chr->opaque;
 156
 157    printf("%s\n", __func__);
 158    vmc_unregister_interface(s);
 159    g_free(s);
 160}
 161
 162static void spice_chr_guest_open(struct CharDriverState *chr)
 163{
 164    SpiceCharDriver *s = chr->opaque;
 165    vmc_register_interface(s);
 166}
 167
 168static void spice_chr_guest_close(struct CharDriverState *chr)
 169{
 170    SpiceCharDriver *s = chr->opaque;
 171    vmc_unregister_interface(s);
 172}
 173
 174static void print_allowed_subtypes(void)
 175{
 176    const char** psubtype;
 177    int i;
 178
 179    fprintf(stderr, "allowed names: ");
 180    for(i=0, psubtype = spice_server_char_device_recognized_subtypes();
 181        *psubtype != NULL; ++psubtype, ++i) {
 182        if (i == 0) {
 183            fprintf(stderr, "%s", *psubtype);
 184        } else {
 185            fprintf(stderr, ", %s", *psubtype);
 186        }
 187    }
 188    fprintf(stderr, "\n");
 189}
 190
 191CharDriverState *qemu_chr_open_spice(QemuOpts *opts)
 192{
 193    CharDriverState *chr;
 194    SpiceCharDriver *s;
 195    const char* name = qemu_opt_get(opts, "name");
 196    uint32_t debug = qemu_opt_get_number(opts, "debug", 0);
 197    const char** psubtype = spice_server_char_device_recognized_subtypes();
 198    const char *subtype = NULL;
 199
 200    if (name == NULL) {
 201        fprintf(stderr, "spice-qemu-char: missing name parameter\n");
 202        print_allowed_subtypes();
 203        return NULL;
 204    }
 205    for(;*psubtype != NULL; ++psubtype) {
 206        if (strcmp(name, *psubtype) == 0) {
 207            subtype = *psubtype;
 208            break;
 209        }
 210    }
 211    if (subtype == NULL) {
 212        fprintf(stderr, "spice-qemu-char: unsupported name: %s\n", name);
 213        print_allowed_subtypes();
 214        return NULL;
 215    }
 216
 217    chr = g_malloc0(sizeof(CharDriverState));
 218    s = g_malloc0(sizeof(SpiceCharDriver));
 219    s->chr = chr;
 220    s->debug = debug;
 221    s->active = false;
 222    s->sin.subtype = subtype;
 223    chr->opaque = s;
 224    chr->chr_write = spice_chr_write;
 225    chr->chr_close = spice_chr_close;
 226    chr->chr_guest_open = spice_chr_guest_open;
 227    chr->chr_guest_close = spice_chr_guest_close;
 228
 229#if SPICE_SERVER_VERSION < 0x000901
 230    /* See comment in vmc_state() */
 231    if (strcmp(subtype, "vdagent") == 0) {
 232        qemu_chr_generic_open(chr);
 233    }
 234#endif
 235
 236    return chr;
 237}
 238