1#include "config-host.h" 2#include "trace.h" 3#include "ui/qemu-spice.h" 4#include <spice.h> 5#include <spice-experimental.h> 6 7#include "osdep.h" 8 9#define dprintf(_scd, _level, _fmt, ...) \ 10 do { \ 11 static unsigned __dprintf_counter = 0; \ 12 if (_scd->debug >= _level) { \ 13 fprintf(stderr, "scd: %3d: " _fmt, ++__dprintf_counter, ## __VA_ARGS__);\ 14 } \ 15 } while (0) 16 17#define VMC_MAX_HOST_WRITE 2048 18 19typedef struct SpiceCharDriver { 20 CharDriverState* chr; 21 SpiceCharDeviceInstance sin; 22 char *subtype; 23 bool active; 24 uint8_t *buffer; 25 uint8_t *datapos; 26 ssize_t bufsize, datalen; 27 uint32_t debug; 28} SpiceCharDriver; 29 30static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len) 31{ 32 SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin); 33 ssize_t out = 0; 34 ssize_t last_out; 35 uint8_t* p = (uint8_t*)buf; 36 37 while (len > 0) { 38 last_out = MIN(len, VMC_MAX_HOST_WRITE); 39 if (qemu_chr_can_read(scd->chr) < last_out) { 40 break; 41 } 42 qemu_chr_read(scd->chr, p, last_out); 43 out += last_out; 44 len -= last_out; 45 p += last_out; 46 } 47 48 dprintf(scd, 3, "%s: %lu/%zd\n", __func__, out, len + out); 49 trace_spice_vmc_write(out, len + out); 50 return out; 51} 52 53static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len) 54{ 55 SpiceCharDriver *scd = container_of(sin, SpiceCharDriver, sin); 56 int bytes = MIN(len, scd->datalen); 57 58 dprintf(scd, 2, "%s: %p %d/%d/%zd\n", __func__, scd->datapos, len, bytes, scd->datalen); 59 if (bytes > 0) { 60 memcpy(buf, scd->datapos, bytes); 61 scd->datapos += bytes; 62 scd->datalen -= bytes; 63 assert(scd->datalen >= 0); 64 if (scd->datalen == 0) { 65 scd->datapos = 0; 66 } 67 } 68 trace_spice_vmc_read(bytes, len); 69 return bytes; 70} 71 72static SpiceCharDeviceInterface vmc_interface = { 73 .base.type = SPICE_INTERFACE_CHAR_DEVICE, 74 .base.description = "spice virtual channel char device", 75 .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR, 76 .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR, 77 .write = vmc_write, 78 .read = vmc_read, 79}; 80 81 82static void vmc_register_interface(SpiceCharDriver *scd) 83{ 84 if (scd->active) { 85 return; 86 } 87 dprintf(scd, 1, "%s\n", __func__); 88 scd->sin.base.sif = &vmc_interface.base; 89 qemu_spice_add_interface(&scd->sin.base); 90 scd->active = true; 91 trace_spice_vmc_register_interface(scd); 92} 93 94static void vmc_unregister_interface(SpiceCharDriver *scd) 95{ 96 if (!scd->active) { 97 return; 98 } 99 dprintf(scd, 1, "%s\n", __func__); 100 spice_server_remove_interface(&scd->sin.base); 101 scd->active = false; 102 trace_spice_vmc_unregister_interface(scd); 103} 104 105 106static int spice_chr_write(CharDriverState *chr, const uint8_t *buf, int len) 107{ 108 SpiceCharDriver *s = chr->opaque; 109 110 dprintf(s, 2, "%s: %d\n", __func__, len); 111 vmc_register_interface(s); 112 assert(s->datalen == 0); 113 if (s->bufsize < len) { 114 s->bufsize = len; 115 s->buffer = qemu_realloc(s->buffer, s->bufsize); 116 } 117 memcpy(s->buffer, buf, len); 118 s->datapos = s->buffer; 119 s->datalen = len; 120 spice_server_char_device_wakeup(&s->sin); 121 return len; 122} 123 124static void spice_chr_close(struct CharDriverState *chr) 125{ 126 SpiceCharDriver *s = chr->opaque; 127 128 printf("%s\n", __func__); 129 vmc_unregister_interface(s); 130 qemu_free(s); 131} 132 133static void spice_chr_guest_open(struct CharDriverState *chr) 134{ 135 SpiceCharDriver *s = chr->opaque; 136 vmc_register_interface(s); 137} 138 139static void spice_chr_guest_close(struct CharDriverState *chr) 140{ 141 SpiceCharDriver *s = chr->opaque; 142 vmc_unregister_interface(s); 143} 144 145static void print_allowed_subtypes(void) 146{ 147 const char** psubtype; 148 int i; 149 150 fprintf(stderr, "allowed names: "); 151 for(i=0, psubtype = spice_server_char_device_recognized_subtypes(); 152 *psubtype != NULL; ++psubtype, ++i) { 153 if (i == 0) { 154 fprintf(stderr, "%s", *psubtype); 155 } else { 156 fprintf(stderr, ", %s", *psubtype); 157 } 158 } 159 fprintf(stderr, "\n"); 160} 161 162int qemu_chr_open_spice(QemuOpts *opts, CharDriverState **_chr) 163{ 164 CharDriverState *chr; 165 SpiceCharDriver *s; 166 const char* name = qemu_opt_get(opts, "name"); 167 uint32_t debug = qemu_opt_get_number(opts, "debug", 0); 168 const char** psubtype = spice_server_char_device_recognized_subtypes(); 169 const char *subtype = NULL; 170 171 if (name == NULL) { 172 fprintf(stderr, "spice-qemu-char: missing name parameter\n"); 173 print_allowed_subtypes(); 174 return -EINVAL; 175 } 176 for(;*psubtype != NULL; ++psubtype) { 177 if (strcmp(name, *psubtype) == 0) { 178 subtype = *psubtype; 179 break; 180 } 181 } 182 if (subtype == NULL) { 183 fprintf(stderr, "spice-qemu-char: unsupported name\n"); 184 print_allowed_subtypes(); 185 return -EINVAL; 186 } 187 188 chr = qemu_mallocz(sizeof(CharDriverState)); 189 s = qemu_mallocz(sizeof(SpiceCharDriver)); 190 s->chr = chr; 191 s->debug = debug; 192 s->active = false; 193 s->sin.subtype = subtype; 194 chr->opaque = s; 195 chr->chr_write = spice_chr_write; 196 chr->chr_close = spice_chr_close; 197 chr->chr_guest_open = spice_chr_guest_open; 198 chr->chr_guest_close = spice_chr_guest_close; 199 200 qemu_chr_generic_open(chr); 201 202 *_chr = chr; 203 return 0; 204} 205