qemu/ui/vdagent.c
<<
>>
Prefs
   1#include "qemu/osdep.h"
   2#include "qapi/error.h"
   3#include "include/qemu-common.h"
   4#include "chardev/char.h"
   5#include "qemu/buffer.h"
   6#include "qemu/option.h"
   7#include "qemu/units.h"
   8#include "hw/qdev-core.h"
   9#include "ui/clipboard.h"
  10#include "ui/console.h"
  11#include "ui/input.h"
  12#include "trace.h"
  13
  14#include "qapi/qapi-types-char.h"
  15#include "qapi/qapi-types-ui.h"
  16
  17#include "spice/vd_agent.h"
  18
  19#define VDAGENT_BUFFER_LIMIT (1 * MiB)
  20#define VDAGENT_MOUSE_DEFAULT true
  21#define VDAGENT_CLIPBOARD_DEFAULT false
  22
  23struct VDAgentChardev {
  24    Chardev parent;
  25
  26    /* config */
  27    bool mouse;
  28    bool clipboard;
  29
  30    /* guest vdagent */
  31    uint32_t caps;
  32    VDIChunkHeader chunk;
  33    uint32_t chunksize;
  34    uint8_t *msgbuf;
  35    uint32_t msgsize;
  36    uint8_t *xbuf;
  37    uint32_t xoff, xsize;
  38    Buffer outbuf;
  39
  40    /* mouse */
  41    DeviceState mouse_dev;
  42    uint32_t mouse_x;
  43    uint32_t mouse_y;
  44    uint32_t mouse_btn;
  45    uint32_t mouse_display;
  46    QemuInputHandlerState *mouse_hs;
  47
  48    /* clipboard */
  49    QemuClipboardPeer cbpeer;
  50    QemuClipboardInfo *cbinfo[QEMU_CLIPBOARD_SELECTION__COUNT];
  51    uint32_t cbpending[QEMU_CLIPBOARD_SELECTION__COUNT];
  52};
  53typedef struct VDAgentChardev VDAgentChardev;
  54
  55#define TYPE_CHARDEV_QEMU_VDAGENT "chardev-qemu-vdagent"
  56
  57DECLARE_INSTANCE_CHECKER(VDAgentChardev, QEMU_VDAGENT_CHARDEV,
  58                         TYPE_CHARDEV_QEMU_VDAGENT);
  59
  60/* ------------------------------------------------------------------ */
  61/* names, for debug logging                                           */
  62
  63static const char *cap_name[] = {
  64    [VD_AGENT_CAP_MOUSE_STATE]                    = "mouse-state",
  65    [VD_AGENT_CAP_MONITORS_CONFIG]                = "monitors-config",
  66    [VD_AGENT_CAP_REPLY]                          = "reply",
  67    [VD_AGENT_CAP_CLIPBOARD]                      = "clipboard",
  68    [VD_AGENT_CAP_DISPLAY_CONFIG]                 = "display-config",
  69    [VD_AGENT_CAP_CLIPBOARD_BY_DEMAND]            = "clipboard-by-demand",
  70    [VD_AGENT_CAP_CLIPBOARD_SELECTION]            = "clipboard-selection",
  71    [VD_AGENT_CAP_SPARSE_MONITORS_CONFIG]         = "sparse-monitors-config",
  72    [VD_AGENT_CAP_GUEST_LINEEND_LF]               = "guest-lineend-lf",
  73    [VD_AGENT_CAP_GUEST_LINEEND_CRLF]             = "guest-lineend-crlf",
  74    [VD_AGENT_CAP_MAX_CLIPBOARD]                  = "max-clipboard",
  75    [VD_AGENT_CAP_AUDIO_VOLUME_SYNC]              = "audio-volume-sync",
  76    [VD_AGENT_CAP_MONITORS_CONFIG_POSITION]       = "monitors-config-position",
  77    [VD_AGENT_CAP_FILE_XFER_DISABLED]             = "file-xfer-disabled",
  78    [VD_AGENT_CAP_FILE_XFER_DETAILED_ERRORS]      = "file-xfer-detailed-errors",
  79#if 0
  80    [VD_AGENT_CAP_GRAPHICS_DEVICE_INFO]           = "graphics-device-info",
  81    [VD_AGENT_CAP_CLIPBOARD_NO_RELEASE_ON_REGRAB] = "clipboard-no-release-on-regrab",
  82    [VD_AGENT_CAP_CLIPBOARD_GRAB_SERIAL]          = "clipboard-grab-serial",
  83#endif
  84};
  85
  86static const char *msg_name[] = {
  87    [VD_AGENT_MOUSE_STATE]           = "mouse-state",
  88    [VD_AGENT_MONITORS_CONFIG]       = "monitors-config",
  89    [VD_AGENT_REPLY]                 = "reply",
  90    [VD_AGENT_CLIPBOARD]             = "clipboard",
  91    [VD_AGENT_DISPLAY_CONFIG]        = "display-config",
  92    [VD_AGENT_ANNOUNCE_CAPABILITIES] = "announce-capabilities",
  93    [VD_AGENT_CLIPBOARD_GRAB]        = "clipboard-grab",
  94    [VD_AGENT_CLIPBOARD_REQUEST]     = "clipboard-request",
  95    [VD_AGENT_CLIPBOARD_RELEASE]     = "clipboard-release",
  96    [VD_AGENT_FILE_XFER_START]       = "file-xfer-start",
  97    [VD_AGENT_FILE_XFER_STATUS]      = "file-xfer-status",
  98    [VD_AGENT_FILE_XFER_DATA]        = "file-xfer-data",
  99    [VD_AGENT_CLIENT_DISCONNECTED]   = "client-disconnected",
 100    [VD_AGENT_MAX_CLIPBOARD]         = "max-clipboard",
 101    [VD_AGENT_AUDIO_VOLUME_SYNC]     = "audio-volume-sync",
 102#if 0
 103    [VD_AGENT_GRAPHICS_DEVICE_INFO]  = "graphics-device-info",
 104#endif
 105};
 106
 107static const char *sel_name[] = {
 108    [VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD] = "clipboard",
 109    [VD_AGENT_CLIPBOARD_SELECTION_PRIMARY]   = "primary",
 110    [VD_AGENT_CLIPBOARD_SELECTION_SECONDARY] = "secondary",
 111};
 112
 113static const char *type_name[] = {
 114    [VD_AGENT_CLIPBOARD_NONE]       = "none",
 115    [VD_AGENT_CLIPBOARD_UTF8_TEXT]  = "text",
 116    [VD_AGENT_CLIPBOARD_IMAGE_PNG]  = "png",
 117    [VD_AGENT_CLIPBOARD_IMAGE_BMP]  = "bmp",
 118    [VD_AGENT_CLIPBOARD_IMAGE_TIFF] = "tiff",
 119    [VD_AGENT_CLIPBOARD_IMAGE_JPG]  = "jpg",
 120#if 0
 121    [VD_AGENT_CLIPBOARD_FILE_LIST]  = "files",
 122#endif
 123};
 124
 125#define GET_NAME(_m, _v) \
 126    (((_v) < ARRAY_SIZE(_m) && (_m[_v])) ? (_m[_v]) : "???")
 127
 128/* ------------------------------------------------------------------ */
 129/* send messages                                                      */
 130
 131static void vdagent_send_buf(VDAgentChardev *vd)
 132{
 133    uint32_t len;
 134
 135    while (!buffer_empty(&vd->outbuf)) {
 136        len = qemu_chr_be_can_write(CHARDEV(vd));
 137        if (len == 0) {
 138            return;
 139        }
 140        if (len > vd->outbuf.offset) {
 141            len = vd->outbuf.offset;
 142        }
 143        qemu_chr_be_write(CHARDEV(vd), vd->outbuf.buffer, len);
 144        buffer_advance(&vd->outbuf, len);
 145    }
 146}
 147
 148static void vdagent_send_msg(VDAgentChardev *vd, VDAgentMessage *msg)
 149{
 150    uint8_t *msgbuf = (void *)msg;
 151    uint32_t msgsize = sizeof(VDAgentMessage) + msg->size;
 152    uint32_t msgoff = 0;
 153    VDIChunkHeader chunk;
 154
 155    trace_vdagent_send(GET_NAME(msg_name, msg->type));
 156
 157    msg->protocol = VD_AGENT_PROTOCOL;
 158
 159    if (vd->outbuf.offset + msgsize > VDAGENT_BUFFER_LIMIT) {
 160        error_report("buffer full, dropping message");
 161        return;
 162    }
 163
 164    while (msgoff < msgsize) {
 165        chunk.port = VDP_CLIENT_PORT;
 166        chunk.size = msgsize - msgoff;
 167        if (chunk.size > 1024) {
 168            chunk.size = 1024;
 169        }
 170        buffer_reserve(&vd->outbuf, sizeof(chunk) + chunk.size);
 171        buffer_append(&vd->outbuf, &chunk, sizeof(chunk));
 172        buffer_append(&vd->outbuf, msgbuf + msgoff, chunk.size);
 173        msgoff += chunk.size;
 174    }
 175    vdagent_send_buf(vd);
 176}
 177
 178static void vdagent_send_caps(VDAgentChardev *vd)
 179{
 180    g_autofree VDAgentMessage *msg = g_malloc0(sizeof(VDAgentMessage) +
 181                                               sizeof(VDAgentAnnounceCapabilities) +
 182                                               sizeof(uint32_t));
 183    VDAgentAnnounceCapabilities *caps = (void *)msg->data;
 184
 185    msg->type = VD_AGENT_ANNOUNCE_CAPABILITIES;
 186    msg->size = sizeof(VDAgentAnnounceCapabilities) + sizeof(uint32_t);
 187    if (vd->mouse) {
 188        caps->caps[0] |= (1 << VD_AGENT_CAP_MOUSE_STATE);
 189    }
 190    if (vd->clipboard) {
 191        caps->caps[0] |= (1 << VD_AGENT_CAP_CLIPBOARD_BY_DEMAND);
 192        caps->caps[0] |= (1 << VD_AGENT_CAP_CLIPBOARD_SELECTION);
 193    }
 194
 195    vdagent_send_msg(vd, msg);
 196}
 197
 198/* ------------------------------------------------------------------ */
 199/* mouse events                                                       */
 200
 201static bool have_mouse(VDAgentChardev *vd)
 202{
 203    return vd->mouse &&
 204        (vd->caps & (1 << VD_AGENT_CAP_MOUSE_STATE));
 205}
 206
 207static void vdagent_send_mouse(VDAgentChardev *vd)
 208{
 209    g_autofree VDAgentMessage *msg = g_malloc0(sizeof(VDAgentMessage) +
 210                                               sizeof(VDAgentMouseState));
 211    VDAgentMouseState *mouse = (void *)msg->data;
 212
 213    msg->type = VD_AGENT_MOUSE_STATE;
 214    msg->size = sizeof(VDAgentMouseState);
 215
 216    mouse->x          = vd->mouse_x;
 217    mouse->y          = vd->mouse_y;
 218    mouse->buttons    = vd->mouse_btn;
 219    mouse->display_id = vd->mouse_display;
 220
 221    vdagent_send_msg(vd, msg);
 222}
 223
 224static void vdagent_pointer_event(DeviceState *dev, QemuConsole *src,
 225                                  InputEvent *evt)
 226{
 227    static const int bmap[INPUT_BUTTON__MAX] = {
 228        [INPUT_BUTTON_LEFT]        = VD_AGENT_LBUTTON_MASK,
 229        [INPUT_BUTTON_RIGHT]       = VD_AGENT_RBUTTON_MASK,
 230        [INPUT_BUTTON_MIDDLE]      = VD_AGENT_MBUTTON_MASK,
 231        [INPUT_BUTTON_WHEEL_UP]    = VD_AGENT_UBUTTON_MASK,
 232        [INPUT_BUTTON_WHEEL_DOWN]  = VD_AGENT_DBUTTON_MASK,
 233#ifdef VD_AGENT_EBUTTON_MASK
 234        [INPUT_BUTTON_SIDE]        = VD_AGENT_SBUTTON_MASK,
 235        [INPUT_BUTTON_EXTRA]       = VD_AGENT_EBUTTON_MASK,
 236#endif
 237    };
 238
 239    VDAgentChardev *vd = container_of(dev, struct VDAgentChardev, mouse_dev);
 240    InputMoveEvent *move;
 241    InputBtnEvent *btn;
 242    uint32_t xres, yres;
 243
 244    switch (evt->type) {
 245    case INPUT_EVENT_KIND_ABS:
 246        move = evt->u.abs.data;
 247        xres = qemu_console_get_width(src, 1024);
 248        yres = qemu_console_get_height(src, 768);
 249        if (move->axis == INPUT_AXIS_X) {
 250            vd->mouse_x = qemu_input_scale_axis(move->value,
 251                                                INPUT_EVENT_ABS_MIN,
 252                                                INPUT_EVENT_ABS_MAX,
 253                                                0, xres);
 254        } else if (move->axis == INPUT_AXIS_Y) {
 255            vd->mouse_y = qemu_input_scale_axis(move->value,
 256                                                INPUT_EVENT_ABS_MIN,
 257                                                INPUT_EVENT_ABS_MAX,
 258                                                0, yres);
 259        }
 260        vd->mouse_display = qemu_console_get_index(src);
 261        break;
 262
 263    case INPUT_EVENT_KIND_BTN:
 264        btn = evt->u.btn.data;
 265        if (btn->down) {
 266            vd->mouse_btn |= bmap[btn->button];
 267        } else {
 268            vd->mouse_btn &= ~bmap[btn->button];
 269        }
 270        break;
 271
 272    default:
 273        /* keep gcc happy */
 274        break;
 275    }
 276}
 277
 278static void vdagent_pointer_sync(DeviceState *dev)
 279{
 280    VDAgentChardev *vd = container_of(dev, struct VDAgentChardev, mouse_dev);
 281
 282    if (vd->caps & (1 << VD_AGENT_CAP_MOUSE_STATE)) {
 283        vdagent_send_mouse(vd);
 284    }
 285}
 286
 287static QemuInputHandler vdagent_mouse_handler = {
 288    .name  = "vdagent mouse",
 289    .mask  = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS,
 290    .event = vdagent_pointer_event,
 291    .sync  = vdagent_pointer_sync,
 292};
 293
 294/* ------------------------------------------------------------------ */
 295/* clipboard                                                          */
 296
 297static bool have_clipboard(VDAgentChardev *vd)
 298{
 299    return vd->clipboard &&
 300        (vd->caps & (1 << VD_AGENT_CAP_CLIPBOARD_BY_DEMAND));
 301}
 302
 303static bool have_selection(VDAgentChardev *vd)
 304{
 305    return vd->caps & (1 << VD_AGENT_CAP_CLIPBOARD_SELECTION);
 306}
 307
 308static uint32_t type_qemu_to_vdagent(enum QemuClipboardType type)
 309{
 310    switch (type) {
 311    case QEMU_CLIPBOARD_TYPE_TEXT:
 312        return VD_AGENT_CLIPBOARD_UTF8_TEXT;
 313    default:
 314        return VD_AGENT_CLIPBOARD_NONE;
 315    }
 316}
 317
 318static void vdagent_send_clipboard_grab(VDAgentChardev *vd,
 319                                        QemuClipboardInfo *info)
 320{
 321    g_autofree VDAgentMessage *msg =
 322        g_malloc0(sizeof(VDAgentMessage) +
 323                  sizeof(uint32_t) * (QEMU_CLIPBOARD_TYPE__COUNT + 1));
 324    uint8_t *s = msg->data;
 325    uint32_t *data = (uint32_t *)msg->data;
 326    uint32_t q, type;
 327
 328    if (have_selection(vd)) {
 329        *s = info->selection;
 330        data++;
 331        msg->size += sizeof(uint32_t);
 332    } else if (info->selection != QEMU_CLIPBOARD_SELECTION_CLIPBOARD) {
 333        return;
 334    }
 335
 336    for (q = 0; q < QEMU_CLIPBOARD_TYPE__COUNT; q++) {
 337        type = type_qemu_to_vdagent(q);
 338        if (type != VD_AGENT_CLIPBOARD_NONE && info->types[q].available) {
 339            *data = type;
 340            data++;
 341            msg->size += sizeof(uint32_t);
 342        }
 343    }
 344
 345    msg->type = VD_AGENT_CLIPBOARD_GRAB;
 346    vdagent_send_msg(vd, msg);
 347}
 348
 349static void vdagent_send_clipboard_data(VDAgentChardev *vd,
 350                                        QemuClipboardInfo *info,
 351                                        QemuClipboardType type)
 352{
 353    g_autofree VDAgentMessage *msg = g_malloc0(sizeof(VDAgentMessage) +
 354                                               sizeof(uint32_t) * 2 +
 355                                               info->types[type].size);
 356
 357    uint8_t *s = msg->data;
 358    uint32_t *data = (uint32_t *)msg->data;
 359
 360    if (have_selection(vd)) {
 361        *s = info->selection;
 362        data++;
 363        msg->size += sizeof(uint32_t);
 364    } else if (info->selection != QEMU_CLIPBOARD_SELECTION_CLIPBOARD) {
 365        return;
 366    }
 367
 368    *data = type_qemu_to_vdagent(type);
 369    data++;
 370    msg->size += sizeof(uint32_t);
 371
 372    memcpy(data, info->types[type].data, info->types[type].size);
 373    msg->size += info->types[type].size;
 374
 375    msg->type = VD_AGENT_CLIPBOARD;
 376    vdagent_send_msg(vd, msg);
 377}
 378
 379static void vdagent_clipboard_notify(Notifier *notifier, void *data)
 380{
 381    VDAgentChardev *vd = container_of(notifier, VDAgentChardev, cbpeer.update);
 382    QemuClipboardInfo *info = data;
 383    QemuClipboardSelection s = info->selection;
 384    QemuClipboardType type;
 385    bool self_update = info->owner == &vd->cbpeer;
 386
 387    if (info != vd->cbinfo[s]) {
 388        qemu_clipboard_info_unref(vd->cbinfo[s]);
 389        vd->cbinfo[s] = qemu_clipboard_info_ref(info);
 390        vd->cbpending[s] = 0;
 391        if (!self_update) {
 392            vdagent_send_clipboard_grab(vd, info);
 393        }
 394        return;
 395    }
 396
 397    if (self_update) {
 398        return;
 399    }
 400
 401    for (type = 0; type < QEMU_CLIPBOARD_TYPE__COUNT; type++) {
 402        if (vd->cbpending[s] & (1 << type)) {
 403            vd->cbpending[s] &= ~(1 << type);
 404            vdagent_send_clipboard_data(vd, info, type);
 405        }
 406    }
 407}
 408
 409static void vdagent_clipboard_request(QemuClipboardInfo *info,
 410                                      QemuClipboardType qtype)
 411{
 412    VDAgentChardev *vd = container_of(info->owner, VDAgentChardev, cbpeer);
 413    g_autofree VDAgentMessage *msg = g_malloc0(sizeof(VDAgentMessage) +
 414                                               sizeof(uint32_t) * 2);
 415    uint32_t type = type_qemu_to_vdagent(qtype);
 416    uint8_t *s = msg->data;
 417    uint32_t *data = (uint32_t *)msg->data;
 418
 419    if (type == VD_AGENT_CLIPBOARD_NONE) {
 420        return;
 421    }
 422
 423    if (have_selection(vd)) {
 424        *s = info->selection;
 425        data++;
 426        msg->size += sizeof(uint32_t);
 427    }
 428
 429    *data = type;
 430    msg->size += sizeof(uint32_t);
 431
 432    msg->type = VD_AGENT_CLIPBOARD_REQUEST;
 433    vdagent_send_msg(vd, msg);
 434}
 435
 436static void vdagent_chr_recv_clipboard(VDAgentChardev *vd, VDAgentMessage *msg)
 437{
 438    uint8_t s = VD_AGENT_CLIPBOARD_SELECTION_CLIPBOARD;
 439    uint32_t size = msg->size;
 440    void *data = msg->data;
 441    QemuClipboardInfo *info;
 442    QemuClipboardType type;
 443
 444    if (have_selection(vd)) {
 445        if (size < 4) {
 446            return;
 447        }
 448        s = *(uint8_t *)data;
 449        if (s >= QEMU_CLIPBOARD_SELECTION__COUNT) {
 450            return;
 451        }
 452        data += 4;
 453        size -= 4;
 454    }
 455
 456    switch (msg->type) {
 457    case VD_AGENT_CLIPBOARD_GRAB:
 458        trace_vdagent_cb_grab_selection(GET_NAME(sel_name, s));
 459        info = qemu_clipboard_info_new(&vd->cbpeer, s);
 460        if (size > sizeof(uint32_t) * 10) {
 461            /*
 462             * spice has 6 types as of 2021. Limiting to 10 entries
 463             * so we we have some wiggle room.
 464             */
 465            return;
 466        }
 467        while (size >= sizeof(uint32_t)) {
 468            trace_vdagent_cb_grab_type(GET_NAME(type_name, *(uint32_t *)data));
 469            switch (*(uint32_t *)data) {
 470            case VD_AGENT_CLIPBOARD_UTF8_TEXT:
 471                info->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true;
 472                break;
 473            default:
 474                break;
 475            }
 476            data += sizeof(uint32_t);
 477            size -= sizeof(uint32_t);
 478        }
 479        qemu_clipboard_update(info);
 480        qemu_clipboard_info_unref(info);
 481        break;
 482    case VD_AGENT_CLIPBOARD_REQUEST:
 483        if (size < sizeof(uint32_t)) {
 484            return;
 485        }
 486        switch (*(uint32_t *)data) {
 487        case VD_AGENT_CLIPBOARD_UTF8_TEXT:
 488            type = QEMU_CLIPBOARD_TYPE_TEXT;
 489            break;
 490        default:
 491            return;
 492        }
 493        if (vd->cbinfo[s] &&
 494            vd->cbinfo[s]->types[type].available &&
 495            vd->cbinfo[s]->owner != &vd->cbpeer) {
 496            if (vd->cbinfo[s]->types[type].data) {
 497                vdagent_send_clipboard_data(vd, vd->cbinfo[s], type);
 498            } else {
 499                vd->cbpending[s] |= (1 << type);
 500                qemu_clipboard_request(vd->cbinfo[s], type);
 501            }
 502        }
 503        break;
 504    case VD_AGENT_CLIPBOARD: /* data */
 505        if (size < sizeof(uint32_t)) {
 506            return;
 507        }
 508        switch (*(uint32_t *)data) {
 509        case VD_AGENT_CLIPBOARD_UTF8_TEXT:
 510            type = QEMU_CLIPBOARD_TYPE_TEXT;
 511            break;
 512        default:
 513            return;
 514        }
 515        data += 4;
 516        size -= 4;
 517        qemu_clipboard_set_data(&vd->cbpeer, vd->cbinfo[s], type,
 518                                size, data, true);
 519        break;
 520    case VD_AGENT_CLIPBOARD_RELEASE: /* data */
 521        if (vd->cbinfo[s] &&
 522            vd->cbinfo[s]->owner == &vd->cbpeer) {
 523            /* set empty clipboard info */
 524            info = qemu_clipboard_info_new(NULL, s);
 525            qemu_clipboard_update(info);
 526            qemu_clipboard_info_unref(info);
 527        }
 528        break;
 529    }
 530}
 531
 532/* ------------------------------------------------------------------ */
 533/* chardev backend                                                    */
 534
 535static void vdagent_chr_open(Chardev *chr,
 536                             ChardevBackend *backend,
 537                             bool *be_opened,
 538                             Error **errp)
 539{
 540    VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(chr);
 541    ChardevQemuVDAgent *cfg = backend->u.qemu_vdagent.data;
 542
 543#if defined(HOST_WORDS_BIGENDIAN)
 544    /*
 545     * TODO: vdagent protocol is defined to be LE,
 546     * so we have to byteswap everything on BE hosts.
 547     */
 548    error_setg(errp, "vdagent is not supported on bigendian hosts");
 549    return;
 550#endif
 551
 552    vd->mouse = VDAGENT_MOUSE_DEFAULT;
 553    if (cfg->has_mouse) {
 554        vd->mouse = cfg->mouse;
 555    }
 556
 557    vd->clipboard = VDAGENT_CLIPBOARD_DEFAULT;
 558    if (cfg->has_clipboard) {
 559        vd->clipboard = cfg->clipboard;
 560    }
 561
 562    if (vd->mouse) {
 563        vd->mouse_hs = qemu_input_handler_register(&vd->mouse_dev,
 564                                                   &vdagent_mouse_handler);
 565    }
 566
 567    *be_opened = true;
 568}
 569
 570static void vdagent_chr_recv_caps(VDAgentChardev *vd, VDAgentMessage *msg)
 571{
 572    VDAgentAnnounceCapabilities *caps = (void *)msg->data;
 573    int i;
 574
 575    if (msg->size < (sizeof(VDAgentAnnounceCapabilities) +
 576                     sizeof(uint32_t))) {
 577        return;
 578    }
 579
 580    for (i = 0; i < ARRAY_SIZE(cap_name); i++) {
 581        if (caps->caps[0] & (1 << i)) {
 582            trace_vdagent_peer_cap(GET_NAME(cap_name, i));
 583        }
 584    }
 585
 586    vd->caps = caps->caps[0];
 587    if (caps->request) {
 588        vdagent_send_caps(vd);
 589    }
 590    if (have_mouse(vd) && vd->mouse_hs) {
 591        qemu_input_handler_activate(vd->mouse_hs);
 592    }
 593    if (have_clipboard(vd) && vd->cbpeer.update.notify == NULL) {
 594        vd->cbpeer.name = "vdagent";
 595        vd->cbpeer.update.notify = vdagent_clipboard_notify;
 596        vd->cbpeer.request = vdagent_clipboard_request;
 597        qemu_clipboard_peer_register(&vd->cbpeer);
 598    }
 599}
 600
 601static void vdagent_chr_recv_msg(VDAgentChardev *vd, VDAgentMessage *msg)
 602{
 603    trace_vdagent_recv_msg(GET_NAME(msg_name, msg->type), msg->size);
 604
 605    switch (msg->type) {
 606    case VD_AGENT_ANNOUNCE_CAPABILITIES:
 607        vdagent_chr_recv_caps(vd, msg);
 608        break;
 609    case VD_AGENT_CLIPBOARD:
 610    case VD_AGENT_CLIPBOARD_GRAB:
 611    case VD_AGENT_CLIPBOARD_REQUEST:
 612    case VD_AGENT_CLIPBOARD_RELEASE:
 613        if (have_clipboard(vd)) {
 614            vdagent_chr_recv_clipboard(vd, msg);
 615        }
 616        break;
 617    default:
 618        break;
 619    }
 620}
 621
 622static void vdagent_reset_xbuf(VDAgentChardev *vd)
 623{
 624    g_clear_pointer(&vd->xbuf, g_free);
 625    vd->xoff = 0;
 626    vd->xsize = 0;
 627}
 628
 629static void vdagent_chr_recv_chunk(VDAgentChardev *vd)
 630{
 631    VDAgentMessage *msg = (void *)vd->msgbuf;
 632
 633    if (!vd->xsize) {
 634        if (vd->msgsize < sizeof(*msg)) {
 635            error_report("%s: message too small: %d < %zd", __func__,
 636                         vd->msgsize, sizeof(*msg));
 637            return;
 638        }
 639        if (vd->msgsize == msg->size + sizeof(*msg)) {
 640            vdagent_chr_recv_msg(vd, msg);
 641            return;
 642        }
 643    }
 644
 645    if (!vd->xsize) {
 646        vd->xsize = msg->size + sizeof(*msg);
 647        vd->xbuf = g_malloc0(vd->xsize);
 648    }
 649
 650    if (vd->xoff + vd->msgsize > vd->xsize) {
 651        error_report("%s: Oops: %d+%d > %d", __func__,
 652                     vd->xoff, vd->msgsize, vd->xsize);
 653        vdagent_reset_xbuf(vd);
 654        return;
 655    }
 656
 657    memcpy(vd->xbuf + vd->xoff, vd->msgbuf, vd->msgsize);
 658    vd->xoff += vd->msgsize;
 659    if (vd->xoff < vd->xsize) {
 660        return;
 661    }
 662
 663    msg = (void *)vd->xbuf;
 664    vdagent_chr_recv_msg(vd, msg);
 665    vdagent_reset_xbuf(vd);
 666}
 667
 668static void vdagent_reset_bufs(VDAgentChardev *vd)
 669{
 670    memset(&vd->chunk, 0, sizeof(vd->chunk));
 671    vd->chunksize = 0;
 672    g_free(vd->msgbuf);
 673    vd->msgbuf = NULL;
 674    vd->msgsize = 0;
 675}
 676
 677static int vdagent_chr_write(Chardev *chr, const uint8_t *buf, int len)
 678{
 679    VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(chr);
 680    uint32_t copy, ret = len;
 681
 682    while (len) {
 683        if (vd->chunksize < sizeof(vd->chunk)) {
 684            copy = sizeof(vd->chunk) - vd->chunksize;
 685            if (copy > len) {
 686                copy = len;
 687            }
 688            memcpy((void *)(&vd->chunk) + vd->chunksize, buf, copy);
 689            vd->chunksize += copy;
 690            buf += copy;
 691            len -= copy;
 692            if (vd->chunksize < sizeof(vd->chunk)) {
 693                break;
 694            }
 695
 696            assert(vd->msgbuf == NULL);
 697            vd->msgbuf = g_malloc0(vd->chunk.size);
 698        }
 699
 700        copy = vd->chunk.size - vd->msgsize;
 701        if (copy > len) {
 702            copy = len;
 703        }
 704        memcpy(vd->msgbuf + vd->msgsize, buf, copy);
 705        vd->msgsize += copy;
 706        buf += copy;
 707        len -= copy;
 708
 709        if (vd->msgsize == vd->chunk.size) {
 710            trace_vdagent_recv_chunk(vd->chunk.size);
 711            vdagent_chr_recv_chunk(vd);
 712            vdagent_reset_bufs(vd);
 713        }
 714    }
 715
 716    return ret;
 717}
 718
 719static void vdagent_chr_accept_input(Chardev *chr)
 720{
 721    VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(chr);
 722
 723    vdagent_send_buf(vd);
 724}
 725
 726static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open)
 727{
 728    VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(chr);
 729
 730    if (!fe_open) {
 731        trace_vdagent_close();
 732        /* reset state */
 733        vdagent_reset_bufs(vd);
 734        vd->caps = 0;
 735        if (vd->mouse_hs) {
 736            qemu_input_handler_deactivate(vd->mouse_hs);
 737        }
 738        if (vd->cbpeer.update.notify) {
 739            qemu_clipboard_peer_unregister(&vd->cbpeer);
 740            memset(&vd->cbpeer, 0, sizeof(vd->cbpeer));
 741        }
 742        return;
 743    }
 744
 745    trace_vdagent_open();
 746}
 747
 748static void vdagent_chr_parse(QemuOpts *opts, ChardevBackend *backend,
 749                              Error **errp)
 750{
 751    ChardevQemuVDAgent *cfg;
 752
 753    backend->type = CHARDEV_BACKEND_KIND_QEMU_VDAGENT;
 754    cfg = backend->u.qemu_vdagent.data = g_new0(ChardevQemuVDAgent, 1);
 755    qemu_chr_parse_common(opts, qapi_ChardevQemuVDAgent_base(cfg));
 756    cfg->has_mouse = true;
 757    cfg->mouse = qemu_opt_get_bool(opts, "mouse", VDAGENT_MOUSE_DEFAULT);
 758    cfg->has_clipboard = true;
 759    cfg->clipboard = qemu_opt_get_bool(opts, "clipboard", VDAGENT_CLIPBOARD_DEFAULT);
 760}
 761
 762/* ------------------------------------------------------------------ */
 763
 764static void vdagent_chr_class_init(ObjectClass *oc, void *data)
 765{
 766    ChardevClass *cc = CHARDEV_CLASS(oc);
 767
 768    cc->parse            = vdagent_chr_parse;
 769    cc->open             = vdagent_chr_open;
 770    cc->chr_write        = vdagent_chr_write;
 771    cc->chr_set_fe_open  = vdagent_chr_set_fe_open;
 772    cc->chr_accept_input = vdagent_chr_accept_input;
 773}
 774
 775static void vdagent_chr_init(Object *obj)
 776{
 777    VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(obj);
 778
 779    buffer_init(&vd->outbuf, "vdagent-outbuf");
 780}
 781
 782static void vdagent_chr_fini(Object *obj)
 783{
 784    VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(obj);
 785
 786    buffer_free(&vd->outbuf);
 787}
 788
 789static const TypeInfo vdagent_chr_type_info = {
 790    .name = TYPE_CHARDEV_QEMU_VDAGENT,
 791    .parent = TYPE_CHARDEV,
 792    .instance_size = sizeof(VDAgentChardev),
 793    .instance_init = vdagent_chr_init,
 794    .instance_finalize = vdagent_chr_fini,
 795    .class_init = vdagent_chr_class_init,
 796};
 797
 798static void register_types(void)
 799{
 800    type_register_static(&vdagent_chr_type_info);
 801}
 802
 803type_init(register_types);
 804