xref: /openbmc/qemu/ui/vdagent.c (revision 744c72a8)
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 
23 struct 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 };
53 typedef struct VDAgentChardev VDAgentChardev;
54 
55 #define TYPE_CHARDEV_QEMU_VDAGENT "chardev-qemu-vdagent"
56 
57 DECLARE_INSTANCE_CHECKER(VDAgentChardev, QEMU_VDAGENT_CHARDEV,
58                          TYPE_CHARDEV_QEMU_VDAGENT);
59 
60 /* ------------------------------------------------------------------ */
61 /* names, for debug logging                                           */
62 
63 static 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 
86 static 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 
107 static 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 
113 static 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 
131 static 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 
148 static 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 
178 static 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 
201 static bool have_mouse(VDAgentChardev *vd)
202 {
203     return vd->mouse &&
204         (vd->caps & (1 << VD_AGENT_CAP_MOUSE_STATE));
205 }
206 
207 static 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 
224 static 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 
278 static 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 
287 static 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 
297 static bool have_clipboard(VDAgentChardev *vd)
298 {
299     return vd->clipboard &&
300         (vd->caps & (1 << VD_AGENT_CAP_CLIPBOARD_BY_DEMAND));
301 }
302 
303 static bool have_selection(VDAgentChardev *vd)
304 {
305     return vd->caps & (1 << VD_AGENT_CAP_CLIPBOARD_SELECTION);
306 }
307 
308 static 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 
318 static 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 
349 static 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 
379 static 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 
409 static 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 
436 static 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 
535 static 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 
570 static 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 
601 static 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 
622 static 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 
629 static 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 
668 static 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 
677 static 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 
719 static void vdagent_chr_accept_input(Chardev *chr)
720 {
721     VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(chr);
722 
723     vdagent_send_buf(vd);
724 }
725 
726 static 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 
748 static 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 
764 static 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 
775 static void vdagent_chr_init(Object *obj)
776 {
777     VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(obj);
778 
779     buffer_init(&vd->outbuf, "vdagent-outbuf");
780 }
781 
782 static void vdagent_chr_fini(Object *obj)
783 {
784     VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(obj);
785 
786     buffer_free(&vd->outbuf);
787 }
788 
789 static 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 
798 static void register_types(void)
799 {
800     type_register_static(&vdagent_chr_type_info);
801 }
802 
803 type_init(register_types);
804