baum: use a common prefix for chr callbacks
[qemu/ar7.git] / spice-qemu-char.c
blob5e5897b189f08f69b69f2eb0a69c1c1509d15398
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>
10 typedef struct SpiceChardev {
11 Chardev parent;
13 SpiceCharDeviceInstance sin;
14 bool active;
15 bool blocked;
16 const uint8_t *datapos;
17 int datalen;
18 QLIST_ENTRY(SpiceChardev) next;
19 } SpiceChardev;
21 typedef struct SpiceCharSource {
22 GSource source;
23 SpiceChardev *scd;
24 } SpiceCharSource;
26 static QLIST_HEAD(, SpiceChardev) spice_chars =
27 QLIST_HEAD_INITIALIZER(spice_chars);
29 static int vmc_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len)
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;
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;
43 qemu_chr_be_write(chr, p, last_out);
44 out += last_out;
45 len -= last_out;
46 p += last_out;
49 trace_spice_vmc_write(out, len + out);
50 return out;
53 static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len)
55 SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
56 int bytes = MIN(len, scd->datalen);
58 if (bytes > 0) {
59 memcpy(buf, scd->datapos, bytes);
60 scd->datapos += bytes;
61 scd->datalen -= bytes;
62 assert(scd->datalen >= 0);
64 if (scd->datalen == 0) {
65 scd->datapos = 0;
66 scd->blocked = false;
68 trace_spice_vmc_read(bytes, len);
69 return bytes;
72 #if SPICE_SERVER_VERSION >= 0x000c02
73 static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event)
75 SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
76 Chardev *chr = (Chardev *)scd;
77 int chr_event;
79 switch (event) {
80 case SPICE_PORT_EVENT_BREAK:
81 chr_event = CHR_EVENT_BREAK;
82 break;
83 default:
84 return;
87 trace_spice_vmc_event(chr_event);
88 qemu_chr_be_event(chr, chr_event);
90 #endif
92 static void vmc_state(SpiceCharDeviceInstance *sin, int connected)
94 SpiceChardev *scd = container_of(sin, SpiceChardev, sin);
95 Chardev *chr = (Chardev *)scd;
97 if ((chr->be_open && connected) ||
98 (!chr->be_open && !connected)) {
99 return;
102 qemu_chr_be_event(chr,
103 connected ? CHR_EVENT_OPENED : CHR_EVENT_CLOSED);
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
123 static void vmc_register_interface(SpiceChardev *scd)
125 if (scd->active) {
126 return;
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);
134 static void vmc_unregister_interface(SpiceChardev *scd)
136 if (!scd->active) {
137 return;
139 spice_server_remove_interface(&scd->sin.base);
140 scd->active = false;
141 trace_spice_vmc_unregister_interface(scd);
144 static gboolean spice_char_source_prepare(GSource *source, gint *timeout)
146 SpiceCharSource *src = (SpiceCharSource *)source;
148 *timeout = -1;
150 return !src->scd->blocked;
153 static gboolean spice_char_source_check(GSource *source)
155 SpiceCharSource *src = (SpiceCharSource *)source;
157 return !src->scd->blocked;
160 static gboolean spice_char_source_dispatch(GSource *source,
161 GSourceFunc callback, gpointer user_data)
163 GIOFunc func = (GIOFunc)callback;
165 return func(NULL, G_IO_OUT, user_data);
168 static GSourceFuncs SpiceCharSourceFuncs = {
169 .prepare = spice_char_source_prepare,
170 .check = spice_char_source_check,
171 .dispatch = spice_char_source_dispatch,
174 static GSource *spice_chr_add_watch(Chardev *chr, GIOCondition cond)
176 SpiceChardev *scd = (SpiceChardev *)chr;
177 SpiceCharSource *src;
179 assert(cond & G_IO_OUT);
181 src = (SpiceCharSource *)g_source_new(&SpiceCharSourceFuncs,
182 sizeof(SpiceCharSource));
183 src->scd = scd;
185 return (GSource *)src;
188 static int spice_chr_write(Chardev *chr, const uint8_t *buf, int len)
190 SpiceChardev *s = (SpiceChardev *)chr;
191 int read_bytes;
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;
204 return read_bytes;
207 static void spice_chr_free(struct Chardev *chr)
209 SpiceChardev *s = (SpiceChardev *)chr;
211 vmc_unregister_interface(s);
212 QLIST_REMOVE(s, next);
214 g_free((char *)s->sin.subtype);
215 #if SPICE_SERVER_VERSION >= 0x000c02
216 g_free((char *)s->sin.portname);
217 #endif
220 static void spice_vmc_set_fe_open(struct Chardev *chr, int fe_open)
222 SpiceChardev *s = (SpiceChardev *)chr;
223 if (fe_open) {
224 vmc_register_interface(s);
225 } else {
226 vmc_unregister_interface(s);
230 static void spice_port_set_fe_open(struct Chardev *chr, int fe_open)
232 #if SPICE_SERVER_VERSION >= 0x000c02
233 SpiceChardev *s = (SpiceChardev *)chr;
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);
240 #endif
243 static void spice_chr_accept_input(struct Chardev *chr)
245 SpiceChardev *s = (SpiceChardev *)chr;
247 spice_server_char_device_wakeup(&s->sin);
250 static Chardev *chr_open(const CharDriver *driver,
251 const char *subtype,
252 ChardevCommon *backend,
253 Error **errp)
255 Chardev *chr;
256 SpiceChardev *s;
258 chr = qemu_chr_alloc(driver, backend, errp);
259 if (!chr) {
260 return NULL;
262 s = (SpiceChardev *)chr;
263 s->active = false;
264 s->sin.subtype = g_strdup(subtype);
266 QLIST_INSERT_HEAD(&spice_chars, s, next);
268 return chr;
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)
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);
283 for (; *psubtype != NULL; ++psubtype) {
284 if (strcmp(type, *psubtype) == 0) {
285 break;
288 if (*psubtype == NULL) {
289 char *subtypes = g_strjoinv(", ",
290 (gchar **)spice_server_char_device_recognized_subtypes());
292 error_setg(errp, "unsupported type name: %s", type);
293 error_append_hint(errp, "allowed spice char type names: %s\n",
294 subtypes);
296 g_free(subtypes);
297 return NULL;
300 *be_opened = false;
301 return chr_open(driver, type, common, errp);
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)
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;
318 if (name == NULL) {
319 error_setg(errp, "missing name parameter");
320 return NULL;
323 chr = chr_open(driver, "port", common, errp);
324 if (!chr) {
325 return NULL;
327 *be_opened = false;
328 s = (SpiceChardev *)chr;
329 s->sin.portname = g_strdup(name);
331 return chr;
334 void qemu_spice_register_ports(void)
336 SpiceChardev *s;
338 QLIST_FOREACH(s, &spice_chars, next) {
339 if (s->sin.portname == NULL) {
340 continue;
342 vmc_register_interface(s);
345 #endif
347 static void qemu_chr_parse_spice_vmc(QemuOpts *opts, ChardevBackend *backend,
348 Error **errp)
350 const char *name = qemu_opt_get(opts, "name");
351 ChardevSpiceChannel *spicevmc;
353 if (name == NULL) {
354 error_setg(errp, "chardev: spice channel: no name given");
355 return;
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);
362 static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend,
363 Error **errp)
365 const char *name = qemu_opt_get(opts, "name");
366 ChardevSpicePort *spiceport;
368 if (name == NULL) {
369 error_setg(errp, "chardev: spice port: no name given");
370 return;
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);
377 static void register_types(void)
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,
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,
401 register_char_driver(&vmc_driver);
402 register_char_driver(&port_driver);
405 type_init(register_types);