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