From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.5 required=3.0 tests=BAYES_00,DKIM_ADSP_CUSTOM_MED, DKIM_INVALID,DKIM_SIGNED,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,HTML_MESSAGE,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 40E58C433B4 for ; Wed, 5 May 2021 09:55:16 +0000 (UTC) Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 95C9261008 for ; Wed, 5 May 2021 09:55:15 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 95C9261008 Authentication-Results: mail.kernel.org; dmarc=fail (p=none dis=none) header.from=gmail.com Authentication-Results: mail.kernel.org; spf=pass smtp.mailfrom=qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Received: from localhost ([::1]:51922 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1leEFO-0005P8-Kt for qemu-devel@archiver.kernel.org; Wed, 05 May 2021 05:55:14 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:58580) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1leEEU-00045y-2c for qemu-devel@nongnu.org; Wed, 05 May 2021 05:54:18 -0400 Received: from mail-ej1-x633.google.com ([2a00:1450:4864:20::633]:37540) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1leEEQ-000722-It for qemu-devel@nongnu.org; Wed, 05 May 2021 05:54:17 -0400 Received: by mail-ej1-x633.google.com with SMTP id w3so1881639ejc.4 for ; Wed, 05 May 2021 02:54:14 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=czobLS8rA54RvjOlcUbgsqzq2uTc+uLpxYdYNcMkWKY=; b=BcW/gHttW0HJ0MY5UI/ozbJm3SK1mxVJzqDDM8zme0dMKEI9gjAt6WlcdlfC6vxWEA YPez7oNJ67SOLl6ZEBIzcHHlhSjpC3iX3ch/8Hnbv1AOtMOrgO7ITFB4B4bAdAygQyxI bc6i62Px5oAvTc2OQjqfKldCw1TCAVeHDfmqOHTzSqEMmLD9la+lGaSkOVMv+o0xGDv1 8ob9NJ8o7BKhCyQEwMEOv9l0vSfIyCPOVbrUdjrYRq3K2RFaeSMQbbr4esbXE8Tx3NsO hT/rw84WnWa6ImZ6Di9kNLPJWw53reeckIs6Z+DvUazgItpqESlM7J8URadlZ+L99TMK MGeg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=czobLS8rA54RvjOlcUbgsqzq2uTc+uLpxYdYNcMkWKY=; b=ng6OnUqE37lguleaEkYzjF7W79V9y0UMaE7DHlobKBp7DFlh/tAk5idEZdo6aq3bS8 xU7QXx6nbJ7OC3o6f6SnSm1nBq9kcOLMoZuuD0noyP7X7zVtgFz+9ljD0epXLYjfJRvC JWxHWyMlkgUVsZPUBcFr+iimmxGnmIoTeYl4w8sHm5AQhgtAyYUl+GAI8hpe0fRw5Teu 8UXqH3haQzsG3hkzdDTVvZgAWz37biPibTBfcZFmPjtWWrqGN/6J6JZ+/GJjEkTe5a6G 6Nk0UbgwW6RuXBXEpAmp+NDBHa/Yooq8L8Zpxa2vtFM3zLId6r4XScbdl7qgXRAYnaHi qmnQ== X-Gm-Message-State: AOAM531E6mrTUympuE4GbvnghsdyxBNPWGF+9kGlzeghecSP7+B3Nt/j LWS9Mmi2iYwzAyaLFFccDVZvewDBYX2cPGuU81A= X-Google-Smtp-Source: ABdhPJw+MWDkVf8Fj3GHfjJ9lFZCexJwII3i8clEo6z0PD5/Z4eZOSdqerTbajr//1nRLw36+xhs6IkeFXKgbPpr+io= X-Received: by 2002:a17:907:1b1e:: with SMTP id mp30mr26808766ejc.532.1620208453103; Wed, 05 May 2021 02:54:13 -0700 (PDT) MIME-Version: 1.0 References: <20210505060901.828658-1-kraxel@redhat.com> <20210505060901.828658-5-kraxel@redhat.com> In-Reply-To: <20210505060901.828658-5-kraxel@redhat.com> From: =?UTF-8?B?TWFyYy1BbmRyw6kgTHVyZWF1?= Date: Wed, 5 May 2021 13:54:00 +0400 Message-ID: Subject: Re: [PATCH v5 4/9] ui/vdagent: core infrastructure To: Gerd Hoffmann Content-Type: multipart/alternative; boundary="0000000000004f398f05c1922de2" Received-SPF: pass client-ip=2a00:1450:4864:20::633; envelope-from=marcandre.lureau@gmail.com; helo=mail-ej1-x633.google.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Paolo Bonzini , QEMU , Markus Armbruster Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: "Qemu-devel" --0000000000004f398f05c1922de2 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi On Wed, May 5, 2021 at 10:14 AM Gerd Hoffmann wrote: > The vdagent protocol allows the guest agent (spice-vdagent) and the > spice client exchange messages to implement features which require > guest cooperation, for example clipboard support. > > This is a qemu implementation of the spice client side. This allows > the spice guest agent talk to qemu directly when not using the spice > protocol. > > usage: qemu \ > -chardev qemu-vdagent,id=3Dvdagent \ > -device virtserialport,chardev=3Dvdagent,name=3Dcom.redhat.spice.0 > > This patch adds just the protocol basics: initial handshake and > capability negotiation. The following patches will add actual > functionality and also add fields to the initially empty > ChardevVDAgent qapi struct. > > Signed-off-by: Gerd Hoffmann > Acked-by: Markus Armbruster > looks ok to me, but: --- > ui/vdagent.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++++ > qapi/char.json | 17 ++- > ui/meson.build | 1 + > ui/trace-events | 8 ++ > 4 files changed, 348 insertions(+), 1 deletion(-) > create mode 100644 ui/vdagent.c > > diff --git a/ui/vdagent.c b/ui/vdagent.c > new file mode 100644 > index 000000000000..e757a3c9c710 > --- /dev/null > +++ b/ui/vdagent.c > @@ -0,0 +1,323 @@ > +#include "qemu/osdep.h" > +#include "qapi/error.h" > +#include "include/qemu-common.h" > +#include "chardev/char.h" > +#include "trace.h" > + > +#include "qapi/qapi-types-char.h" > + > +#include "spice/vd_agent.h" > + > +struct VDAgentChardev { > + Chardev parent; > + > + /* guest vdagent */ > + uint32_t caps; > + VDIChunkHeader chunk; > + uint32_t chunksize; > + uint8_t *msgbuf; > + uint32_t msgsize; > + uint8_t *xbuf; > + uint32_t xoff, xsize; > +}; > +typedef struct VDAgentChardev VDAgentChardev; > + > +#define TYPE_CHARDEV_QEMU_VDAGENT "chardev-qemu-vdagent" > + > +DECLARE_INSTANCE_CHECKER(VDAgentChardev, QEMU_VDAGENT_CHARDEV, > + TYPE_CHARDEV_QEMU_VDAGENT); > + > +/* ------------------------------------------------------------------ */ > +/* names, for debug logging */ > + > +static const char *cap_name[] =3D { > + [VD_AGENT_CAP_MOUSE_STATE] =3D "mouse-state", > + [VD_AGENT_CAP_MONITORS_CONFIG] =3D "monitors-config", > + [VD_AGENT_CAP_REPLY] =3D "reply", > + [VD_AGENT_CAP_CLIPBOARD] =3D "clipboard", > + [VD_AGENT_CAP_DISPLAY_CONFIG] =3D "display-config", > + [VD_AGENT_CAP_CLIPBOARD_BY_DEMAND] =3D "clipboard-by-dema= nd", > + [VD_AGENT_CAP_CLIPBOARD_SELECTION] =3D "clipboard-selecti= on", > + [VD_AGENT_CAP_SPARSE_MONITORS_CONFIG] =3D > "sparse-monitors-config", > + [VD_AGENT_CAP_GUEST_LINEEND_LF] =3D "guest-lineend-lf"= , > + [VD_AGENT_CAP_GUEST_LINEEND_CRLF] =3D "guest-lineend-crl= f", > + [VD_AGENT_CAP_MAX_CLIPBOARD] =3D "max-clipboard", > + [VD_AGENT_CAP_AUDIO_VOLUME_SYNC] =3D "audio-volume-sync= ", > + [VD_AGENT_CAP_MONITORS_CONFIG_POSITION] =3D > "monitors-config-position", > + [VD_AGENT_CAP_FILE_XFER_DISABLED] =3D "file-xfer-disable= d", > + [VD_AGENT_CAP_FILE_XFER_DETAILED_ERRORS] =3D > "file-xfer-detailed-errors", > +#if 0 > + [VD_AGENT_CAP_GRAPHICS_DEVICE_INFO] =3D > "graphics-device-info", > + [VD_AGENT_CAP_CLIPBOARD_NO_RELEASE_ON_REGRAB] =3D > "clipboard-no-release-on-regrab", > + [VD_AGENT_CAP_CLIPBOARD_GRAB_SERIAL] =3D > "clipboard-grab-serial", > +#endif > +}; > + > +static const char *msg_name[] =3D { > + [VD_AGENT_MOUSE_STATE] =3D "mouse-state", > + [VD_AGENT_MONITORS_CONFIG] =3D "monitors-config", > + [VD_AGENT_REPLY] =3D "reply", > + [VD_AGENT_CLIPBOARD] =3D "clipboard", > + [VD_AGENT_DISPLAY_CONFIG] =3D "display-config", > + [VD_AGENT_ANNOUNCE_CAPABILITIES] =3D "announce-capabilities", > + [VD_AGENT_CLIPBOARD_GRAB] =3D "clipboard-grab", > + [VD_AGENT_CLIPBOARD_REQUEST] =3D "clipboard-request", > + [VD_AGENT_CLIPBOARD_RELEASE] =3D "clipboard-release", > + [VD_AGENT_FILE_XFER_START] =3D "file-xfer-start", > + [VD_AGENT_FILE_XFER_STATUS] =3D "file-xfer-status", > + [VD_AGENT_FILE_XFER_DATA] =3D "file-xfer-data", > + [VD_AGENT_CLIENT_DISCONNECTED] =3D "client-disconnected", > + [VD_AGENT_MAX_CLIPBOARD] =3D "max-clipboard", > + [VD_AGENT_AUDIO_VOLUME_SYNC] =3D "audio-volume-sync", > +#if 0 > + [VD_AGENT_GRAPHICS_DEVICE_INFO] =3D "graphics-device-info", > +#endif > +}; > + > +#define GET_NAME(_m, _v) \ > + (((_v) < ARRAY_SIZE(_m) && (_m[_v])) ? (_m[_v]) : "???") > + > +/* ------------------------------------------------------------------ */ > +/* send messages */ > + > +static void vdagent_send_buf(VDAgentChardev *vd, void *ptr, uint32_t > msgsize) > +{ > + uint8_t *msgbuf =3D ptr; > + uint32_t len, pos =3D 0; > + > + while (pos < msgsize) { > + len =3D qemu_chr_be_can_write(CHARDEV(vd)); > + if (len > msgsize - pos) { > + len =3D msgsize - pos; > + } > + qemu_chr_be_write(CHARDEV(vd), msgbuf + pos, len); > + pos +=3D len; > + } > This looks like it could easily busy loop. Have you thought about fixing this? +} > + > +static void vdagent_send_msg(VDAgentChardev *vd, VDAgentMessage *msg) > +{ > + uint8_t *msgbuf =3D (void *)msg; > + uint32_t msgsize =3D sizeof(VDAgentMessage) + msg->size; > + uint32_t msgoff =3D 0; > + VDIChunkHeader chunk; > + > + trace_vdagent_send(GET_NAME(msg_name, msg->type)); > + > + msg->protocol =3D VD_AGENT_PROTOCOL; > + > + while (msgoff < msgsize) { > + chunk.port =3D VDP_CLIENT_PORT; > + chunk.size =3D msgsize - msgoff; > + if (chunk.size > 1024) { > + chunk.size =3D 1024; > + } > + vdagent_send_buf(vd, &chunk, sizeof(chunk)); > + vdagent_send_buf(vd, msgbuf + msgoff, chunk.size); > + msgoff +=3D chunk.size; > + } > +} > + > +static void vdagent_send_caps(VDAgentChardev *vd) > +{ > + g_autofree VDAgentMessage *msg =3D g_malloc0(sizeof(VDAgentMessage) = + > + > sizeof(VDAgentAnnounceCapabilities) + > + sizeof(uint32_t)); > + > + msg->type =3D VD_AGENT_ANNOUNCE_CAPABILITIES; > + msg->size =3D sizeof(VDAgentAnnounceCapabilities) + sizeof(uint32_t)= ; > + > + vdagent_send_msg(vd, msg); > +} > + > +/* ------------------------------------------------------------------ */ > +/* chardev backend */ > + > +static void vdagent_chr_open(Chardev *chr, > + ChardevBackend *backend, > + bool *be_opened, > + Error **errp) > +{ > +#if defined(HOST_WORDS_BIGENDIAN) > + /* > + * TODO: vdagent protocol is defined to be LE, > + * so we have to byteswap everything on BE hosts. > + */ > + error_setg(errp, "vdagent is not supported on bigendian hosts"); > + return; > +#endif > + > + *be_opened =3D true; > +} > + > +static void vdagent_chr_recv_caps(VDAgentChardev *vd, VDAgentMessage *ms= g) > +{ > + VDAgentAnnounceCapabilities *caps =3D (void *)msg->data; > + int i; > + > + if (msg->size < (sizeof(VDAgentAnnounceCapabilities) + > + sizeof(uint32_t))) { > + return; > + } > + > + for (i =3D 0; i < ARRAY_SIZE(cap_name); i++) { > + if (caps->caps[0] & (1 << i)) { > + trace_vdagent_peer_cap(GET_NAME(cap_name, i)); > + } > + } > + > + vd->caps =3D caps->caps[0]; > + if (caps->request) { > + vdagent_send_caps(vd); > + } > +} > + > +static void vdagent_chr_recv_msg(VDAgentChardev *vd, VDAgentMessage *msg= ) > +{ > + trace_vdagent_recv_msg(GET_NAME(msg_name, msg->type), msg->size); > + > + switch (msg->type) { > + case VD_AGENT_ANNOUNCE_CAPABILITIES: > + vdagent_chr_recv_caps(vd, msg); > + break; > + default: > + break; > + } > +} > + > +static void vdagent_reset_xbuf(VDAgentChardev *vd) > +{ > + g_clear_pointer(&vd->xbuf, g_free); > + vd->xoff =3D 0; > + vd->xsize =3D 0; > +} > + > +static void vdagent_chr_recv_chunk(VDAgentChardev *vd) > +{ > + VDAgentMessage *msg =3D (void *)vd->msgbuf; > + > + if (!vd->xsize) { > + if (vd->msgsize < sizeof(*msg)) { > + error_report("%s: message too small: %d < %zd", __func__, > + vd->msgsize, sizeof(*msg)); > + return; > + } > + if (vd->msgsize =3D=3D msg->size + sizeof(*msg)) { > + vdagent_chr_recv_msg(vd, msg); > + return; > + } > + } > + > + if (!vd->xsize) { > + vd->xsize =3D msg->size + sizeof(*msg); > + vd->xbuf =3D g_malloc0(vd->xsize); > + } > + > + if (vd->xoff + vd->msgsize > vd->xsize) { > + error_report("%s: Oops: %d+%d > %d", __func__, > + vd->xoff, vd->msgsize, vd->xsize); > + vdagent_reset_xbuf(vd); > + return; > + } > + > + memcpy(vd->xbuf + vd->xoff, vd->msgbuf, vd->msgsize); > + vd->xoff +=3D vd->msgsize; > + if (vd->xoff < vd->xsize) { > + return; > + } > + > + msg =3D (void *)vd->xbuf; > + vdagent_chr_recv_msg(vd, msg); > + vdagent_reset_xbuf(vd); > +} > + > +static void vdagent_reset_bufs(VDAgentChardev *vd) > +{ > + memset(&vd->chunk, 0, sizeof(vd->chunk)); > + vd->chunksize =3D 0; > + g_free(vd->msgbuf); > + vd->msgbuf =3D NULL; > + vd->msgsize =3D 0; > +} > + > +static int vdagent_chr_write(Chardev *chr, const uint8_t *buf, int len) > +{ > + VDAgentChardev *vd =3D QEMU_VDAGENT_CHARDEV(chr); > + uint32_t copy, ret =3D len; > + > + while (len) { > + if (vd->chunksize < sizeof(vd->chunk)) { > + copy =3D sizeof(vd->chunk) - vd->chunksize; > + if (copy > len) { > + copy =3D len; > + } > + memcpy((void *)(&vd->chunk) + vd->chunksize, buf, copy); > + vd->chunksize +=3D copy; > + buf +=3D copy; > + len -=3D copy; > + if (vd->chunksize < sizeof(vd->chunk)) { > + break; > + } > + > + assert(vd->msgbuf =3D=3D NULL); > + vd->msgbuf =3D g_malloc0(vd->chunk.size); > + } > + > + copy =3D vd->chunk.size - vd->msgsize; > + if (copy > len) { > + copy =3D len; > + } > + memcpy(vd->msgbuf + vd->msgsize, buf, copy); > + vd->msgsize +=3D copy; > + buf +=3D copy; > + len -=3D copy; > + > + if (vd->msgsize =3D=3D vd->chunk.size) { > + trace_vdagent_recv_chunk(vd->chunk.size); > + vdagent_chr_recv_chunk(vd); > + vdagent_reset_bufs(vd); > + } > + } > + > + return ret; > +} > + > +static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open) > +{ > + VDAgentChardev *vd =3D QEMU_VDAGENT_CHARDEV(chr); > + > + if (!fe_open) { > + trace_vdagent_close(); > + /* reset state */ > + vdagent_reset_bufs(vd); > + vd->caps =3D 0; > + return; > + } > + > + trace_vdagent_open(); > +} > + > +/* ------------------------------------------------------------------ */ > + > +static void vdagent_chr_class_init(ObjectClass *oc, void *data) > +{ > + ChardevClass *cc =3D CHARDEV_CLASS(oc); > + > + cc->open =3D vdagent_chr_open; > + cc->chr_write =3D vdagent_chr_write; > + cc->chr_set_fe_open =3D vdagent_chr_set_fe_open; > +} > + > +static const TypeInfo vdagent_chr_type_info =3D { > + .name =3D TYPE_CHARDEV_QEMU_VDAGENT, > + .parent =3D TYPE_CHARDEV, > + .instance_size =3D sizeof(VDAgentChardev), > + .class_init =3D vdagent_chr_class_init, > +}; > + > +static void register_types(void) > +{ > + type_register_static(&vdagent_chr_type_info); > +} > + > +type_init(register_types); > diff --git a/qapi/char.json b/qapi/char.json > index 6413970fa73b..990801e642bb 100644 > --- a/qapi/char.json > +++ b/qapi/char.json > @@ -390,12 +390,25 @@ > 'data': { '*size': 'int' }, > 'base': 'ChardevCommon' } > > +## > +# @ChardevQemuVDAgent: > +# > +# Configuration info for qemu vdagent implementation. > +# > +# Since: 6.1 > +# > +## > +{ 'struct': 'ChardevQemuVDAgent', > + 'data': { }, > + 'base': 'ChardevCommon', > + 'if': 'defined(CONFIG_SPICE_PROTOCOL)' } > + > ## > # @ChardevBackend: > # > # Configuration info for the new chardev backend. > # > -# Since: 1.4 (testdev since 2.2, wctablet since 2.9) > +# Since: 1.4 (testdev since 2.2, wctablet since 2.9, vdagent since 6.1) > ## > { 'union': 'ChardevBackend', > 'data': { 'file': 'ChardevFile', > @@ -417,6 +430,8 @@ > 'if': 'defined(CONFIG_SPICE)' }, > 'spiceport': { 'type': 'ChardevSpicePort', > 'if': 'defined(CONFIG_SPICE)' }, > + 'qemu-vdagent': { 'type': 'ChardevQemuVDAgent', > + 'if': 'defined(CONFIG_SPICE_PROTOCOL)' }, > 'vc': 'ChardevVC', > 'ringbuf': 'ChardevRingbuf', > # next one is just for compatibility > diff --git a/ui/meson.build b/ui/meson.build > index fc4fb75c2869..bad49fb6de60 100644 > --- a/ui/meson.build > +++ b/ui/meson.build > @@ -14,6 +14,7 @@ softmmu_ss.add(files( > 'qemu-pixman.c', > )) > softmmu_ss.add([spice_headers, files('spice-module.c')]) > +softmmu_ss.add(when: spice_protocol, if_true: files('vdagent.c')) > > softmmu_ss.add(when: 'CONFIG_LINUX', if_true: files('input-linux.c')) > softmmu_ss.add(when: cocoa, if_true: files('cocoa.m')) > diff --git a/ui/trace-events b/ui/trace-events > index 5d1da6f23668..c34cffb0452b 100644 > --- a/ui/trace-events > +++ b/ui/trace-events > @@ -124,3 +124,11 @@ xkeymap_extension(const char *name) "extension '%s'" > xkeymap_vendor(const char *name) "vendor '%s'" > xkeymap_keycodes(const char *name) "keycodes '%s'" > xkeymap_keymap(const char *name) "keymap '%s'" > + > +# vdagent.c > +vdagent_open(void) "" > +vdagent_close(void) "" > +vdagent_send(const char *name) "msg %s" > +vdagent_recv_chunk(uint32_t size) "size %d" > +vdagent_recv_msg(const char *name, uint32_t size) "msg %s, size %d" > +vdagent_peer_cap(const char *name) "cap %s" > -- > 2.31.1 > > > --=20 Marc-Andr=C3=A9 Lureau --0000000000004f398f05c1922de2 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable
Hi

On Wed, May 5, 2021 at 10:14 AM Gerd Hoffmann= <kraxel@redhat.com> wrote:<= br>
The vdagent prot= ocol allows the guest agent (spice-vdagent) and the
spice client exchange messages to implement features which require
guest cooperation, for example clipboard support.

This is a qemu implementation of the spice client side.=C2=A0 This allows the spice guest agent talk to qemu directly when not using the spice
protocol.

usage: qemu \
=C2=A0 -chardev qemu-vdagent,id=3Dvdagent \
=C2=A0 -device virtserialport,chardev=3Dvdagent,name=3Dcom.redhat.spice.0
This patch adds just the protocol basics: initial handshake and
capability negotiation.=C2=A0 The following patches will add actual
functionality and also add fields to the initially empty
ChardevVDAgent qapi struct.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Acked-by: Markus Armbruster <armbru@redhat.com>

= looks ok to me, but:

---
=C2=A0ui/vdagent.c=C2=A0 =C2=A0 | 323 +++++++++++++++++++++++++++++++++++++= +++++++++++
=C2=A0qapi/char.json=C2=A0 |=C2=A0 17 ++-
=C2=A0ui/meson.build=C2=A0 |=C2=A0 =C2=A01 +
=C2=A0ui/trace-events |=C2=A0 =C2=A08 ++
=C2=A04 files changed, 348 insertions(+), 1 deletion(-)
=C2=A0create mode 100644 ui/vdagent.c

diff --git a/ui/vdagent.c b/ui/vdagent.c
new file mode 100644
index 000000000000..e757a3c9c710
--- /dev/null
+++ b/ui/vdagent.c
@@ -0,0 +1,323 @@
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "include/qemu-common.h"
+#include "chardev/char.h"
+#include "trace.h"
+
+#include "qapi/qapi-types-char.h"
+
+#include "spice/vd_agent.h"
+
+struct VDAgentChardev {
+=C2=A0 =C2=A0 Chardev parent;
+
+=C2=A0 =C2=A0 /* guest vdagent */
+=C2=A0 =C2=A0 uint32_t caps;
+=C2=A0 =C2=A0 VDIChunkHeader chunk;
+=C2=A0 =C2=A0 uint32_t chunksize;
+=C2=A0 =C2=A0 uint8_t *msgbuf;
+=C2=A0 =C2=A0 uint32_t msgsize;
+=C2=A0 =C2=A0 uint8_t *xbuf;
+=C2=A0 =C2=A0 uint32_t xoff, xsize;
+};
+typedef struct VDAgentChardev VDAgentChardev;
+
+#define TYPE_CHARDEV_QEMU_VDAGENT "chardev-qemu-vdagent"
+
+DECLARE_INSTANCE_CHECKER(VDAgentChardev, QEMU_VDAGENT_CHARDEV,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0TYPE_CHARDEV_QEMU_VDAGENT);
+
+/* ------------------------------------------------------------------ */ +/* names, for debug logging=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0*/
+
+static const char *cap_name[] =3D {
+=C2=A0 =C2=A0 [VD_AGENT_CAP_MOUSE_STATE]=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =3D "mouse-state",
+=C2=A0 =C2=A0 [VD_AGENT_CAP_MONITORS_CONFIG]=C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =3D "monitors-config",
+=C2=A0 =C2=A0 [VD_AGENT_CAP_REPLY]=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =3D "reply",=
+=C2=A0 =C2=A0 [VD_AGENT_CAP_CLIPBOARD]=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =3D "clipboard",
+=C2=A0 =C2=A0 [VD_AGENT_CAP_DISPLAY_CONFIG]=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=3D "display-config",
+=C2=A0 =C2=A0 [VD_AGENT_CAP_CLIPBOARD_BY_DEMAND]=C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =3D "clipboard-by-demand",
+=C2=A0 =C2=A0 [VD_AGENT_CAP_CLIPBOARD_SELECTION]=C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =3D "clipboard-selection",
+=C2=A0 =C2=A0 [VD_AGENT_CAP_SPARSE_MONITORS_CONFIG]=C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0=3D "sparse-monitors-config",
+=C2=A0 =C2=A0 [VD_AGENT_CAP_GUEST_LINEEND_LF]=C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0=3D "guest-lineend-lf",
+=C2=A0 =C2=A0 [VD_AGENT_CAP_GUEST_LINEEND_CRLF]=C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0=3D "guest-lineend-crlf",
+=C2=A0 =C2=A0 [VD_AGENT_CAP_MAX_CLIPBOARD]=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =3D "max-clipboard",
+=C2=A0 =C2=A0 [VD_AGENT_CAP_AUDIO_VOLUME_SYNC]=C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =3D "audio-volume-sync",
+=C2=A0 =C2=A0 [VD_AGENT_CAP_MONITORS_CONFIG_POSITION]=C2=A0 =C2=A0 =C2=A0 = =C2=A0=3D "monitors-config-position",
+=C2=A0 =C2=A0 [VD_AGENT_CAP_FILE_XFER_DISABLED]=C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0=3D "file-xfer-disabled",
+=C2=A0 =C2=A0 [VD_AGENT_CAP_FILE_XFER_DETAILED_ERRORS]=C2=A0 =C2=A0 =C2=A0= =3D "file-xfer-detailed-errors",
+#if 0
+=C2=A0 =C2=A0 [VD_AGENT_CAP_GRAPHICS_DEVICE_INFO]=C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0=3D "graphics-device-info",
+=C2=A0 =C2=A0 [VD_AGENT_CAP_CLIPBOARD_NO_RELEASE_ON_REGRAB] =3D "clip= board-no-release-on-regrab",
+=C2=A0 =C2=A0 [VD_AGENT_CAP_CLIPBOARD_GRAB_SERIAL]=C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =3D "clipboard-grab-serial",
+#endif
+};
+
+static const char *msg_name[] =3D {
+=C2=A0 =C2=A0 [VD_AGENT_MOUSE_STATE]=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0=3D "mouse-state",
+=C2=A0 =C2=A0 [VD_AGENT_MONITORS_CONFIG]=C2=A0 =C2=A0 =C2=A0 =C2=A0=3D &qu= ot;monitors-config",
+=C2=A0 =C2=A0 [VD_AGENT_REPLY]=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0=3D "reply",
+=C2=A0 =C2=A0 [VD_AGENT_CLIPBOARD]=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0=3D "clipboard",
+=C2=A0 =C2=A0 [VD_AGENT_DISPLAY_CONFIG]=C2=A0 =C2=A0 =C2=A0 =C2=A0 =3D &qu= ot;display-config",
+=C2=A0 =C2=A0 [VD_AGENT_ANNOUNCE_CAPABILITIES] =3D "announce-capabili= ties",
+=C2=A0 =C2=A0 [VD_AGENT_CLIPBOARD_GRAB]=C2=A0 =C2=A0 =C2=A0 =C2=A0 =3D &qu= ot;clipboard-grab",
+=C2=A0 =C2=A0 [VD_AGENT_CLIPBOARD_REQUEST]=C2=A0 =C2=A0 =C2=A0=3D "cl= ipboard-request",
+=C2=A0 =C2=A0 [VD_AGENT_CLIPBOARD_RELEASE]=C2=A0 =C2=A0 =C2=A0=3D "cl= ipboard-release",
+=C2=A0 =C2=A0 [VD_AGENT_FILE_XFER_START]=C2=A0 =C2=A0 =C2=A0 =C2=A0=3D &qu= ot;file-xfer-start",
+=C2=A0 =C2=A0 [VD_AGENT_FILE_XFER_STATUS]=C2=A0 =C2=A0 =C2=A0 =3D "fi= le-xfer-status",
+=C2=A0 =C2=A0 [VD_AGENT_FILE_XFER_DATA]=C2=A0 =C2=A0 =C2=A0 =C2=A0 =3D &qu= ot;file-xfer-data",
+=C2=A0 =C2=A0 [VD_AGENT_CLIENT_DISCONNECTED]=C2=A0 =C2=A0=3D "client-= disconnected",
+=C2=A0 =C2=A0 [VD_AGENT_MAX_CLIPBOARD]=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =3D "max-clipboard",
+=C2=A0 =C2=A0 [VD_AGENT_AUDIO_VOLUME_SYNC]=C2=A0 =C2=A0 =C2=A0=3D "au= dio-volume-sync",
+#if 0
+=C2=A0 =C2=A0 [VD_AGENT_GRAPHICS_DEVICE_INFO]=C2=A0 =3D "graphics-dev= ice-info",
+#endif
+};
+
+#define GET_NAME(_m, _v) \
+=C2=A0 =C2=A0 (((_v) < ARRAY_SIZE(_m) && (_m[_v])) ? (_m[_v]) := "???")
+
+/* ------------------------------------------------------------------ */ +/* send messages=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 */
+
+static void vdagent_send_buf(VDAgentChardev *vd, void *ptr, uint32_t msgsi= ze)
+{
+=C2=A0 =C2=A0 uint8_t *msgbuf =3D ptr;
+=C2=A0 =C2=A0 uint32_t len, pos =3D 0;
+
+=C2=A0 =C2=A0 while (pos < msgsize) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 len =3D qemu_chr_be_can_write(CHARDEV(vd)); +=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (len > msgsize - pos) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 len =3D msgsize - pos;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 qemu_chr_be_write(CHARDEV(vd), msgbuf + pos, l= en);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 pos +=3D len;
+=C2=A0 =C2=A0 }

This looks like it cou= ld easily busy loop. Have you thought about fixing this?

=
+}
+
+static void vdagent_send_msg(VDAgentChardev *vd, VDAgentMessage *msg)
+{
+=C2=A0 =C2=A0 uint8_t *msgbuf =3D (void *)msg;
+=C2=A0 =C2=A0 uint32_t msgsize =3D sizeof(VDAgentMessage) + msg->size;<= br> +=C2=A0 =C2=A0 uint32_t msgoff =3D 0;
+=C2=A0 =C2=A0 VDIChunkHeader chunk;
+
+=C2=A0 =C2=A0 trace_vdagent_send(GET_NAME(msg_name, msg->type));
+
+=C2=A0 =C2=A0 msg->protocol =3D VD_AGENT_PROTOCOL;
+
+=C2=A0 =C2=A0 while (msgoff < msgsize) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 chunk.port =3D VDP_CLIENT_PORT;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 chunk.size =3D msgsize - msgoff;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (chunk.size > 1024) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 chunk.size =3D 1024;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 vdagent_send_buf(vd, &chunk, sizeof(chunk)= );
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 vdagent_send_buf(vd, msgbuf + msgoff, chunk.si= ze);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 msgoff +=3D chunk.size;
+=C2=A0 =C2=A0 }
+}
+
+static void vdagent_send_caps(VDAgentChardev *vd)
+{
+=C2=A0 =C2=A0 g_autofree VDAgentMessage *msg =3D g_malloc0(sizeof(VDAgentM= essage) +
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0sizeof(VDAgentAnnounceCapabilities) +
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0sizeof(uint32_t));
+
+=C2=A0 =C2=A0 msg->type =3D VD_AGENT_ANNOUNCE_CAPABILITIES;
+=C2=A0 =C2=A0 msg->size =3D sizeof(VDAgentAnnounceCapabilities) + sizeo= f(uint32_t);
+
+=C2=A0 =C2=A0 vdagent_send_msg(vd, msg);
+}
+
+/* ------------------------------------------------------------------ */ +/* chardev backend=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 */
+
+static void vdagent_chr_open(Chardev *chr,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ChardevBackend *backend,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0bool *be_opened,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0Error **errp)
+{
+#if defined(HOST_WORDS_BIGENDIAN)
+=C2=A0 =C2=A0 /*
+=C2=A0 =C2=A0 =C2=A0* TODO: vdagent protocol is defined to be LE,
+=C2=A0 =C2=A0 =C2=A0* so we have to byteswap everything on BE hosts.
+=C2=A0 =C2=A0 =C2=A0*/
+=C2=A0 =C2=A0 error_setg(errp, "vdagent is not supported on bigendian= hosts");
+=C2=A0 =C2=A0 return;
+#endif
+
+=C2=A0 =C2=A0 *be_opened =3D true;
+}
+
+static void vdagent_chr_recv_caps(VDAgentChardev *vd, VDAgentMessage *msg)=
+{
+=C2=A0 =C2=A0 VDAgentAnnounceCapabilities *caps =3D (void *)msg->data;<= br> +=C2=A0 =C2=A0 int i;
+
+=C2=A0 =C2=A0 if (msg->size < (sizeof(VDAgentAnnounceCapabilities) +=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0sizeof(uint32_t))) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 for (i =3D 0; i < ARRAY_SIZE(cap_name); i++) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (caps->caps[0] & (1 << i)) { +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 trace_vdagent_peer_cap(GET_NAME(= cap_name, i));
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 vd->caps =3D caps->caps[0];
+=C2=A0 =C2=A0 if (caps->request) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 vdagent_send_caps(vd);
+=C2=A0 =C2=A0 }
+}
+
+static void vdagent_chr_recv_msg(VDAgentChardev *vd, VDAgentMessage *msg)<= br> +{
+=C2=A0 =C2=A0 trace_vdagent_recv_msg(GET_NAME(msg_name, msg->type), msg= ->size);
+
+=C2=A0 =C2=A0 switch (msg->type) {
+=C2=A0 =C2=A0 case VD_AGENT_ANNOUNCE_CAPABILITIES:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 vdagent_chr_recv_caps(vd, msg);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 default:
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 }
+}
+
+static void vdagent_reset_xbuf(VDAgentChardev *vd)
+{
+=C2=A0 =C2=A0 g_clear_pointer(&vd->xbuf, g_free);
+=C2=A0 =C2=A0 vd->xoff =3D 0;
+=C2=A0 =C2=A0 vd->xsize =3D 0;
+}
+
+static void vdagent_chr_recv_chunk(VDAgentChardev *vd)
+{
+=C2=A0 =C2=A0 VDAgentMessage *msg =3D (void *)vd->msgbuf;
+
+=C2=A0 =C2=A0 if (!vd->xsize) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (vd->msgsize < sizeof(*msg)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 error_report("%s: message t= oo small: %d < %zd", __func__,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0vd->msgsize, sizeof(*msg));
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (vd->msgsize =3D=3D msg->size + sizeo= f(*msg)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 vdagent_chr_recv_msg(vd, msg); +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 if (!vd->xsize) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 vd->xsize =3D msg->size + sizeof(*msg);<= br> +=C2=A0 =C2=A0 =C2=A0 =C2=A0 vd->xbuf =3D g_malloc0(vd->xsize);
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 if (vd->xoff + vd->msgsize > vd->xsize) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 error_report("%s: Oops: %d+%d > %d&quo= t;, __func__,
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0vd->xoff, vd->msgsize, vd->xsize);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 vdagent_reset_xbuf(vd);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 memcpy(vd->xbuf + vd->xoff, vd->msgbuf, vd->msgs= ize);
+=C2=A0 =C2=A0 vd->xoff +=3D vd->msgsize;
+=C2=A0 =C2=A0 if (vd->xoff < vd->xsize) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 msg =3D (void *)vd->xbuf;
+=C2=A0 =C2=A0 vdagent_chr_recv_msg(vd, msg);
+=C2=A0 =C2=A0 vdagent_reset_xbuf(vd);
+}
+
+static void vdagent_reset_bufs(VDAgentChardev *vd)
+{
+=C2=A0 =C2=A0 memset(&vd->chunk, 0, sizeof(vd->chunk));
+=C2=A0 =C2=A0 vd->chunksize =3D 0;
+=C2=A0 =C2=A0 g_free(vd->msgbuf);
+=C2=A0 =C2=A0 vd->msgbuf =3D NULL;
+=C2=A0 =C2=A0 vd->msgsize =3D 0;
+}
+
+static int vdagent_chr_write(Chardev *chr, const uint8_t *buf, int len) +{
+=C2=A0 =C2=A0 VDAgentChardev *vd =3D QEMU_VDAGENT_CHARDEV(chr);
+=C2=A0 =C2=A0 uint32_t copy, ret =3D len;
+
+=C2=A0 =C2=A0 while (len) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (vd->chunksize < sizeof(vd->chunk)= ) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 copy =3D sizeof(vd->chunk) - = vd->chunksize;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (copy > len) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 copy =3D len;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 memcpy((void *)(&vd->chun= k) + vd->chunksize, buf, copy);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 vd->chunksize +=3D copy;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 buf +=3D copy;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 len -=3D copy;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 if (vd->chunksize < sizeof= (vd->chunk)) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 break;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 assert(vd->msgbuf =3D=3D NULL= );
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 vd->msgbuf =3D g_malloc0(vd-&= gt;chunk.size);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 copy =3D vd->chunk.size - vd->msgsize; +=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (copy > len) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 copy =3D len;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 memcpy(vd->msgbuf + vd->msgsize, buf, co= py);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 vd->msgsize +=3D copy;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 buf +=3D copy;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 len -=3D copy;
+
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 if (vd->msgsize =3D=3D vd->chunk.size) {=
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 trace_vdagent_recv_chunk(vd->= chunk.size);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 vdagent_chr_recv_chunk(vd);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 vdagent_reset_bufs(vd);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 }
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 return ret;
+}
+
+static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open)
+{
+=C2=A0 =C2=A0 VDAgentChardev *vd =3D QEMU_VDAGENT_CHARDEV(chr);
+
+=C2=A0 =C2=A0 if (!fe_open) {
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 trace_vdagent_close();
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 /* reset state */
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 vdagent_reset_bufs(vd);
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 vd->caps =3D 0;
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 return;
+=C2=A0 =C2=A0 }
+
+=C2=A0 =C2=A0 trace_vdagent_open();
+}
+
+/* ------------------------------------------------------------------ */ +
+static void vdagent_chr_class_init(ObjectClass *oc, void *data)
+{
+=C2=A0 =C2=A0 ChardevClass *cc =3D CHARDEV_CLASS(oc);
+
+=C2=A0 =C2=A0 cc->open=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =3D vdagent_chr_open;
+=C2=A0 =C2=A0 cc->chr_write=C2=A0 =C2=A0 =C2=A0 =C2=A0 =3D vdagent_chr_= write;
+=C2=A0 =C2=A0 cc->chr_set_fe_open=C2=A0 =3D vdagent_chr_set_fe_open; +}
+
+static const TypeInfo vdagent_chr_type_info =3D {
+=C2=A0 =C2=A0 .name =3D TYPE_CHARDEV_QEMU_VDAGENT,
+=C2=A0 =C2=A0 .parent =3D TYPE_CHARDEV,
+=C2=A0 =C2=A0 .instance_size =3D sizeof(VDAgentChardev),
+=C2=A0 =C2=A0 .class_init =3D vdagent_chr_class_init,
+};
+
+static void register_types(void)
+{
+=C2=A0 =C2=A0 type_register_static(&vdagent_chr_type_info);
+}
+
+type_init(register_types);
diff --git a/qapi/char.json b/qapi/char.json
index 6413970fa73b..990801e642bb 100644
--- a/qapi/char.json
+++ b/qapi/char.json
@@ -390,12 +390,25 @@
=C2=A0 =C2=A0'data': { '*size': 'int' },
=C2=A0 =C2=A0'base': 'ChardevCommon' }

+##
+# @ChardevQemuVDAgent:
+#
+# Configuration info for qemu vdagent implementation.
+#
+# Since: 6.1
+#
+##
+{ 'struct': 'ChardevQemuVDAgent',
+=C2=A0 'data': { },
+=C2=A0 'base': 'ChardevCommon',
+=C2=A0 'if': 'defined(CONFIG_SPICE_PROTOCOL)' }
+
=C2=A0##
=C2=A0# @ChardevBackend:
=C2=A0#
=C2=A0# Configuration info for the new chardev backend.
=C2=A0#
-# Since: 1.4 (testdev since 2.2, wctablet since 2.9)
+# Since: 1.4 (testdev since 2.2, wctablet since 2.9, vdagent since 6.1) =C2=A0##
=C2=A0{ 'union': 'ChardevBackend',
=C2=A0 =C2=A0'data': { 'file': 'ChardevFile',
@@ -417,6 +430,8 @@
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0'if': 'defined(CONFIG_SPICE)' }, =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0'spiceport': { '= ;type': 'ChardevSpicePort',
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 'if': 'defined(CONFIG_SPICE)' }, +=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 'qemu-vdagent': { 't= ype': 'ChardevQemuVDAgent',
+=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 'if': 'defined(CONFIG_SPICE_PRO= TOCOL)' },
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0'vc': 'ChardevV= C',
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0'ringbuf': 'Cha= rdevRingbuf',
=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0# next one is just for comp= atibility
diff --git a/ui/meson.build b/ui/meson.build
index fc4fb75c2869..bad49fb6de60 100644
--- a/ui/meson.build
+++ b/ui/meson.build
@@ -14,6 +14,7 @@ softmmu_ss.add(files(
=C2=A0 =C2=A0'qemu-pixman.c',
=C2=A0))
=C2=A0softmmu_ss.add([spice_headers, files('spice-module.c')])
+softmmu_ss.add(when: spice_protocol, if_true: files('vdagent.c'))<= br>
=C2=A0softmmu_ss.add(when: 'CONFIG_LINUX', if_true: files('inpu= t-linux.c'))
=C2=A0softmmu_ss.add(when: cocoa, if_true: files('cocoa.m'))
diff --git a/ui/trace-events b/ui/trace-events
index 5d1da6f23668..c34cffb0452b 100644
--- a/ui/trace-events
+++ b/ui/trace-events
@@ -124,3 +124,11 @@ xkeymap_extension(const char *name) "extension &#= 39;%s'"
=C2=A0xkeymap_vendor(const char *name) "vendor '%s'"
=C2=A0xkeymap_keycodes(const char *name) "keycodes '%s'"<= br> =C2=A0xkeymap_keymap(const char *name) "keymap '%s'"
+
+# vdagent.c
+vdagent_open(void) ""
+vdagent_close(void) ""
+vdagent_send(const char *name) "msg %s"
+vdagent_recv_chunk(uint32_t size) "size %d"
+vdagent_recv_msg(const char *name, uint32_t size) "msg %s, size %d&qu= ot;
+vdagent_peer_cap(const char *name) "cap %s"
--
2.31.1




--
Marc-Andr=C3=A9 Lureau
--0000000000004f398f05c1922de2--