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