1 #include "qemu/osdep.h" 2 #include "trace.h" 3 #include "ui/qemu-spice.h" 4 #include "chardev/char.h" 5 #include "qapi/error.h" 6 #include "qemu/error-report.h" 7 #include "qemu/option.h" 8 #include <spice.h> 9 #include <spice/protocol.h> 10 11 12 typedef struct SpiceChardev { 13 Chardev parent; 14 15 SpiceCharDeviceInstance sin; 16 bool active; 17 bool blocked; 18 const uint8_t *datapos; 19 int datalen; 20 QLIST_ENTRY(SpiceChardev) next; 21 } SpiceChardev; 22 23 #define TYPE_CHARDEV_SPICE "chardev-spice" 24 #define TYPE_CHARDEV_SPICEVMC "chardev-spicevmc" 25 #define TYPE_CHARDEV_SPICEPORT "chardev-spiceport" 26 27 #define SPICE_CHARDEV(obj) OBJECT_CHECK(SpiceChardev, (obj), TYPE_CHARDEV_SPICE) 28 29 typedef struct SpiceCharSource { 30 GSource source; 31 SpiceChardev *scd; 32 } SpiceCharSource; 33 34 static QLIST_HEAD(, SpiceChardev) spice_chars = 35 QLIST_HEAD_INITIALIZER(spice_chars); 36 37 static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len) 38 { 39 SpiceChardev *scd = container_of(sin, SpiceChardev, sin); 40 Chardev *chr = CHARDEV(scd); 41 ssize_t out = 0; 42 ssize_t last_out; 43 uint8_t* p = (uint8_t*)buf; 44 45 while (len > 0) { 46 int can_write = qemu_chr_be_can_write(chr); 47 last_out = MIN(len, can_write); 48 if (last_out <= 0) { 49 break; 50 } 51 qemu_chr_be_write(chr, p, last_out); 52 out += last_out; 53 len -= last_out; 54 p += last_out; 55 } 56 57 trace_spice_vmc_write(out, len + out); 58 return out; 59 } 60 61 static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len) 62 { 63 SpiceChardev *scd = container_of(sin, SpiceChardev, sin); 64 int bytes = MIN(len, scd->datalen); 65 66 if (bytes > 0) { 67 memcpy(buf, scd->datapos, bytes); 68 scd->datapos += bytes; 69 scd->datalen -= bytes; 70 assert(scd->datalen >= 0); 71 } 72 if (scd->datalen == 0) { 73 scd->datapos = 0; 74 scd->blocked = false; 75 } 76 trace_spice_vmc_read(bytes, len); 77 return bytes; 78 } 79 80 #if SPICE_SERVER_VERSION >= 0x000c02 81 static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event) 82 { 83 SpiceChardev *scd = container_of(sin, SpiceChardev, sin); 84 Chardev *chr = CHARDEV(scd); 85 int chr_event; 86 87 switch (event) { 88 case SPICE_PORT_EVENT_BREAK: 89 chr_event = CHR_EVENT_BREAK; 90 break; 91 default: 92 return; 93 } 94 95 trace_spice_vmc_event(chr_event); 96 qemu_chr_be_event(chr, chr_event); 97 } 98 #endif 99 100 static void vmc_state(SpiceCharDeviceInstance *sin, int connected) 101 { 102 SpiceChardev *scd = container_of(sin, SpiceChardev, sin); 103 Chardev *chr = CHARDEV(scd); 104 105 if ((chr->be_open && connected) || 106 (!chr->be_open && !connected)) { 107 return; 108 } 109 110 qemu_chr_be_event(chr, 111 connected ? CHR_EVENT_OPENED : CHR_EVENT_CLOSED); 112 } 113 114 static SpiceCharDeviceInterface vmc_interface = { 115 .base.type = SPICE_INTERFACE_CHAR_DEVICE, 116 .base.description = "spice virtual channel char device", 117 .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR, 118 .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR, 119 .state = vmc_state, 120 .write = vmc_write, 121 .read = vmc_read, 122 #if SPICE_SERVER_VERSION >= 0x000c02 123 .event = vmc_event, 124 #endif 125 #if SPICE_SERVER_VERSION >= 0x000c06 126 .flags = SPICE_CHAR_DEVICE_NOTIFY_WRITABLE, 127 #endif 128 }; 129 130 131 static void vmc_register_interface(SpiceChardev *scd) 132 { 133 if (scd->active) { 134 return; 135 } 136 scd->sin.base.sif = &vmc_interface.base; 137 qemu_spice_add_interface(&scd->sin.base); 138 scd->active = true; 139 trace_spice_vmc_register_interface(scd); 140 } 141 142 static void vmc_unregister_interface(SpiceChardev *scd) 143 { 144 if (!scd->active) { 145 return; 146 } 147 spice_server_remove_interface(&scd->sin.base); 148 scd->active = false; 149 trace_spice_vmc_unregister_interface(scd); 150 } 151 152 static gboolean spice_char_source_prepare(GSource *source, gint *timeout) 153 { 154 SpiceCharSource *src = (SpiceCharSource *)source; 155 156 *timeout = -1; 157 158 return !src->scd->blocked; 159 } 160 161 static gboolean spice_char_source_check(GSource *source) 162 { 163 SpiceCharSource *src = (SpiceCharSource *)source; 164 165 return !src->scd->blocked; 166 } 167 168 static gboolean spice_char_source_dispatch(GSource *source, 169 GSourceFunc callback, gpointer user_data) 170 { 171 GIOFunc func = (GIOFunc)callback; 172 173 return func(NULL, G_IO_OUT, user_data); 174 } 175 176 static GSourceFuncs SpiceCharSourceFuncs = { 177 .prepare = spice_char_source_prepare, 178 .check = spice_char_source_check, 179 .dispatch = spice_char_source_dispatch, 180 }; 181 182 static GSource *spice_chr_add_watch(Chardev *chr, GIOCondition cond) 183 { 184 SpiceChardev *scd = SPICE_CHARDEV(chr); 185 SpiceCharSource *src; 186 187 assert(cond & G_IO_OUT); 188 189 src = (SpiceCharSource *)g_source_new(&SpiceCharSourceFuncs, 190 sizeof(SpiceCharSource)); 191 src->scd = scd; 192 193 return (GSource *)src; 194 } 195 196 static int spice_chr_write(Chardev *chr, const uint8_t *buf, int len) 197 { 198 SpiceChardev *s = SPICE_CHARDEV(chr); 199 int read_bytes; 200 201 assert(s->datalen == 0); 202 s->datapos = buf; 203 s->datalen = len; 204 spice_server_char_device_wakeup(&s->sin); 205 read_bytes = len - s->datalen; 206 if (read_bytes != len) { 207 /* We'll get passed in the unconsumed data with the next call */ 208 s->datalen = 0; 209 s->datapos = NULL; 210 s->blocked = true; 211 } 212 return read_bytes; 213 } 214 215 static void char_spice_finalize(Object *obj) 216 { 217 SpiceChardev *s = SPICE_CHARDEV(obj); 218 219 vmc_unregister_interface(s); 220 221 if (s->next.le_prev) { 222 QLIST_REMOVE(s, next); 223 } 224 225 g_free((char *)s->sin.subtype); 226 #if SPICE_SERVER_VERSION >= 0x000c02 227 g_free((char *)s->sin.portname); 228 #endif 229 } 230 231 static void spice_vmc_set_fe_open(struct Chardev *chr, int fe_open) 232 { 233 SpiceChardev *s = SPICE_CHARDEV(chr); 234 if (fe_open) { 235 vmc_register_interface(s); 236 } else { 237 vmc_unregister_interface(s); 238 } 239 } 240 241 static void spice_port_set_fe_open(struct Chardev *chr, int fe_open) 242 { 243 #if SPICE_SERVER_VERSION >= 0x000c02 244 SpiceChardev *s = SPICE_CHARDEV(chr); 245 246 if (fe_open) { 247 spice_server_port_event(&s->sin, SPICE_PORT_EVENT_OPENED); 248 } else { 249 spice_server_port_event(&s->sin, SPICE_PORT_EVENT_CLOSED); 250 } 251 #endif 252 } 253 254 static void spice_chr_accept_input(struct Chardev *chr) 255 { 256 SpiceChardev *s = SPICE_CHARDEV(chr); 257 258 spice_server_char_device_wakeup(&s->sin); 259 } 260 261 static void chr_open(Chardev *chr, const char *subtype) 262 { 263 SpiceChardev *s = SPICE_CHARDEV(chr); 264 265 s->active = false; 266 s->sin.subtype = g_strdup(subtype); 267 268 QLIST_INSERT_HEAD(&spice_chars, s, next); 269 } 270 271 static void qemu_chr_open_spice_vmc(Chardev *chr, 272 ChardevBackend *backend, 273 bool *be_opened, 274 Error **errp) 275 { 276 ChardevSpiceChannel *spicevmc = backend->u.spicevmc.data; 277 const char *type = spicevmc->type; 278 const char **psubtype = spice_server_char_device_recognized_subtypes(); 279 280 for (; *psubtype != NULL; ++psubtype) { 281 if (strcmp(type, *psubtype) == 0) { 282 break; 283 } 284 } 285 if (*psubtype == NULL) { 286 char *subtypes = g_strjoinv(", ", 287 (gchar **)spice_server_char_device_recognized_subtypes()); 288 289 error_setg(errp, "unsupported type name: %s", type); 290 error_append_hint(errp, "allowed spice char type names: %s\n", 291 subtypes); 292 293 g_free(subtypes); 294 return; 295 } 296 297 *be_opened = false; 298 chr_open(chr, type); 299 } 300 301 #if SPICE_SERVER_VERSION >= 0x000c02 302 static void qemu_chr_open_spice_port(Chardev *chr, 303 ChardevBackend *backend, 304 bool *be_opened, 305 Error **errp) 306 { 307 ChardevSpicePort *spiceport = backend->u.spiceport.data; 308 const char *name = spiceport->fqdn; 309 SpiceChardev *s; 310 311 if (name == NULL) { 312 error_setg(errp, "missing name parameter"); 313 return; 314 } 315 316 chr_open(chr, "port"); 317 318 *be_opened = false; 319 s = SPICE_CHARDEV(chr); 320 s->sin.portname = g_strdup(name); 321 } 322 323 void qemu_spice_register_ports(void) 324 { 325 SpiceChardev *s; 326 327 QLIST_FOREACH(s, &spice_chars, next) { 328 if (s->sin.portname == NULL) { 329 continue; 330 } 331 vmc_register_interface(s); 332 } 333 } 334 #endif 335 336 static void qemu_chr_parse_spice_vmc(QemuOpts *opts, ChardevBackend *backend, 337 Error **errp) 338 { 339 const char *name = qemu_opt_get(opts, "name"); 340 ChardevSpiceChannel *spicevmc; 341 342 if (name == NULL) { 343 error_setg(errp, "chardev: spice channel: no name given"); 344 return; 345 } 346 backend->type = CHARDEV_BACKEND_KIND_SPICEVMC; 347 spicevmc = backend->u.spicevmc.data = g_new0(ChardevSpiceChannel, 1); 348 qemu_chr_parse_common(opts, qapi_ChardevSpiceChannel_base(spicevmc)); 349 spicevmc->type = g_strdup(name); 350 } 351 352 static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend, 353 Error **errp) 354 { 355 const char *name = qemu_opt_get(opts, "name"); 356 ChardevSpicePort *spiceport; 357 358 if (name == NULL) { 359 error_setg(errp, "chardev: spice port: no name given"); 360 return; 361 } 362 backend->type = CHARDEV_BACKEND_KIND_SPICEPORT; 363 spiceport = backend->u.spiceport.data = g_new0(ChardevSpicePort, 1); 364 qemu_chr_parse_common(opts, qapi_ChardevSpicePort_base(spiceport)); 365 spiceport->fqdn = g_strdup(name); 366 } 367 368 static void char_spice_class_init(ObjectClass *oc, void *data) 369 { 370 ChardevClass *cc = CHARDEV_CLASS(oc); 371 372 cc->chr_write = spice_chr_write; 373 cc->chr_add_watch = spice_chr_add_watch; 374 cc->chr_accept_input = spice_chr_accept_input; 375 } 376 377 static const TypeInfo char_spice_type_info = { 378 .name = TYPE_CHARDEV_SPICE, 379 .parent = TYPE_CHARDEV, 380 .instance_size = sizeof(SpiceChardev), 381 .instance_finalize = char_spice_finalize, 382 .class_init = char_spice_class_init, 383 .abstract = true, 384 }; 385 386 static void char_spicevmc_class_init(ObjectClass *oc, void *data) 387 { 388 ChardevClass *cc = CHARDEV_CLASS(oc); 389 390 cc->parse = qemu_chr_parse_spice_vmc; 391 cc->open = qemu_chr_open_spice_vmc; 392 cc->chr_set_fe_open = spice_vmc_set_fe_open; 393 } 394 395 static const TypeInfo char_spicevmc_type_info = { 396 .name = TYPE_CHARDEV_SPICEVMC, 397 .parent = TYPE_CHARDEV_SPICE, 398 .class_init = char_spicevmc_class_init, 399 }; 400 401 static void char_spiceport_class_init(ObjectClass *oc, void *data) 402 { 403 ChardevClass *cc = CHARDEV_CLASS(oc); 404 405 cc->parse = qemu_chr_parse_spice_port; 406 cc->open = qemu_chr_open_spice_port; 407 cc->chr_set_fe_open = spice_port_set_fe_open; 408 } 409 410 static const TypeInfo char_spiceport_type_info = { 411 .name = TYPE_CHARDEV_SPICEPORT, 412 .parent = TYPE_CHARDEV_SPICE, 413 .class_init = char_spiceport_class_init, 414 }; 415 416 static void register_types(void) 417 { 418 type_register_static(&char_spice_type_info); 419 type_register_static(&char_spicevmc_type_info); 420 type_register_static(&char_spiceport_type_info); 421 } 422 423 type_init(register_types); 424