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