[Spice-devel] [PATCH spice-server v2 5/7] test-stream-device: Factor out VMC emulation

Frediano Ziglio fziglio at redhat.com
Wed Oct 9 08:31:57 UTC 2019


> 
> Allows to reuse code for emulating a character device.
> It will be used for Smardcard test.
> 
> Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
> Acked-by: Victor Toso <victortoso at redhat.com>
> ---
>  server/tests/Makefile.am          |   2 +
>  server/tests/meson.build          |   2 +
>  server/tests/test-stream-device.c | 224 +++++++++---------------------
>  server/tests/vmc-emu.c            | 121 ++++++++++++++++
>  server/tests/vmc-emu.h            |  48 +++++++
>  5 files changed, 236 insertions(+), 161 deletions(-)
>  create mode 100644 server/tests/vmc-emu.c
>  create mode 100644 server/tests/vmc-emu.h
> 

...

> diff --git a/server/tests/vmc-emu.c b/server/tests/vmc-emu.c
> new file mode 100644
> index 000000000..ddac5269c
> --- /dev/null
> +++ b/server/tests/vmc-emu.c
> @@ -0,0 +1,121 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2019 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see
> <http://www.gnu.org/licenses/>.
> +*/
> +
> +#include <config.h>
> +#include <glib.h>
> +
> +#include "vmc-emu.h"
> +
> +// handle writes to the device
> +static int vmc_write(SpiceCharDeviceInstance *sin,
> +                     const uint8_t *buf, int len)
> +{
> +    VmcEmu *const vmc = SPICE_CONTAINEROF(sin, VmcEmu, instance);
> +
> +    // just copy into the buffer
> +    unsigned copy = MIN(sizeof(vmc->write_buf) - vmc->write_pos, len);
> +    memcpy(vmc->write_buf+vmc->write_pos, buf, copy);
> +    vmc->write_pos += copy;
> +    return len;
> +}
> +
> +static int vmc_read(SpiceCharDeviceInstance *sin,
> +                    uint8_t *buf, int len)
> +{
> +    VmcEmu *const vmc = SPICE_CONTAINEROF(sin, VmcEmu, instance);
> +    int ret;
> +
> +    if (vmc->pos >= *vmc->message_sizes_curr && vmc->message_sizes_curr <
> vmc->message_sizes_end) {
> +        ++vmc->message_sizes_curr;
> +    }
> +    if (vmc->message_sizes_curr >= vmc->message_sizes_end || vmc->pos >=
> *vmc->message_sizes_curr) {
> +        return 0;
> +    }
> +    ret = MIN(*vmc->message_sizes_curr - vmc->pos, len);
> +    memcpy(buf, &vmc->message[vmc->pos], ret);
> +    vmc->pos += ret;
> +    // kick off next message read
> +    // currently Qemu kicks the device so we need to do it manually
> +    // here. If not all data are read, the device goes into blocking
> +    // state and we get the wake only when we read from the device
> +    // again
> +    if (vmc->pos >= *vmc->message_sizes_curr) {
> +        spice_server_char_device_wakeup(&vmc->instance);
> +    }
> +    return ret;
> +}
> +
> +static void vmc_state(SpiceCharDeviceInstance *sin,
> +                      int connected)
> +{
> +    VmcEmu *const vmc = SPICE_CONTAINEROF(sin, VmcEmu, instance);
> +    vmc->device_enabled = !!connected;
> +}
> +
> +static const SpiceCharDeviceInterface vmc_interface = {
> +    .base = {
> +        .type          = SPICE_INTERFACE_CHAR_DEVICE,
> +        .description   = "test spice virtual channel char device",
> +        .major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR,
> +        .minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR,
> +    },
> +    .state              = vmc_state,
> +    .write              = vmc_write,
> +    .read               = vmc_read,
> +};
> +
> +VmcEmu *vmc_emu_new(const char *subtype, const char *portname)
> +{
> +    VmcEmu *vmc = g_new0(VmcEmu, 1);
> +    vmc->interface = vmc_interface;
> +    vmc->instance.base.sif = &vmc->interface.base;
> +    vmc->instance.subtype = g_strdup(subtype);
> +    if (portname) {
> +        vmc->instance.portname = g_strdup(portname);
> +    }
> +    vmc_emu_reset(vmc);
> +    return vmc;
> +}
> +
> +void vmc_emu_destroy(VmcEmu *vmc)
> +{
> +    g_free((char *) vmc->instance.portname);
> +    g_free((char *) vmc->instance.subtype);
> +    g_free(vmc);
> +}
> +
> +void vmc_emu_reset(VmcEmu *vmc)
> +{
> +    vmc->pos = 0;
> +    vmc->write_pos = 0;
> +    vmc->message_sizes_curr = vmc->message_sizes;
> +    vmc->message_sizes_end = vmc->message_sizes;
> +}
> +
> +void vmc_emu_add_read_till(VmcEmu *vmc, uint8_t *end)
> +{
> +    g_assert(vmc->message_sizes_end - vmc->message_sizes <
> G_N_ELEMENTS(vmc->message_sizes));
> +    g_assert(end >= vmc->message);
> +    g_assert(end - vmc->message <= G_N_ELEMENTS(vmc->message));
> +    unsigned prev_size =
> +        vmc->message_sizes_end > vmc->message_sizes ?
> vmc->message_sizes_end[-1] : 0;
> +    unsigned size = end - vmc->message;
> +    g_assert(size >= prev_size);
> +    *vmc->message_sizes_end = size;
> +    ++vmc->message_sizes_end;
> +}
> diff --git a/server/tests/vmc-emu.h b/server/tests/vmc-emu.h
> new file mode 100644
> index 000000000..6fcea69d2
> --- /dev/null
> +++ b/server/tests/vmc-emu.h
> @@ -0,0 +1,48 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2019 Red Hat, Inc.
> +
> +   This library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   This library is distributed in the hope that it will be useful,
> +   but WITHOUT ANY WARRANTY; without even the implied warranty of
> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with this library; if not, see
> <http://www.gnu.org/licenses/>.
> +*/
> +#pragma once
> +
> +#include "char-device.h"
> +
> +typedef struct VmcEmu VmcEmu;
> +
> +struct VmcEmu {
> +    SpiceCharDeviceInterface interface;

Locally I renamed this field to vmc_interface to avoid collision on Windows
(detected by Gitlab CI)

> +    SpiceCharDeviceInstance instance;
> +
> +    // device buffer to read from
> +    uint8_t message[2048];
> +    // position to read from
> +    unsigned pos;
> +
> +    // array of limits when the read should return
> +    // the array is defined as [message_sizes_curr, message_sizes_end)
> +    // then the size is reach we move on next one till exausted
> +    unsigned message_sizes[16];
> +    unsigned *message_sizes_end, *message_sizes_curr;
> +
> +    bool device_enabled;
> +
> +    unsigned write_pos;
> +    uint8_t write_buf[2048];
> +};
> +
> +VmcEmu *vmc_emu_new(const char *subtype, const char *portname);
> +void vmc_emu_destroy(VmcEmu *vmc);
> +void vmc_emu_reset(VmcEmu *vmc);
> +void vmc_emu_add_read_till(VmcEmu *vmc, uint8_t *end);

Frediano


More information about the Spice-devel mailing list