/* -*- 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 . */ #include #include #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; if (copy && vmc->data_written_cb) { vmc->data_written_cb(vmc); } 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) { auto vmc = g_new0(VmcEmu, 1); vmc->vmc_interface = vmc_interface; vmc->instance.base.sif = &vmc->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(const_cast(vmc->instance.portname)); g_free(const_cast(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; }