1 /*
2 * QEMU DBus audio
3 *
4 * Copyright (c) 2021 Red Hat, Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24
25 #include "qemu/osdep.h"
26 #include "qemu/error-report.h"
27 #include "qemu/host-utils.h"
28 #include "qemu/module.h"
29 #include "qemu/timer.h"
30 #include "qemu/dbus.h"
31
32 #ifdef G_OS_UNIX
33 #include <gio/gunixfdlist.h>
34 #endif
35
36 #include "ui/dbus.h"
37 #include "ui/dbus-display1.h"
38
39 #define AUDIO_CAP "dbus"
40 #include "audio.h"
41 #include "audio_int.h"
42 #include "trace.h"
43
44 #define DBUS_DISPLAY1_AUDIO_PATH DBUS_DISPLAY1_ROOT "/Audio"
45
46 #define DBUS_AUDIO_NSAMPLES 1024 /* could be configured? */
47
48 typedef struct DBusAudio {
49 GDBusObjectManagerServer *server;
50 bool p2p;
51 GDBusObjectSkeleton *audio;
52 QemuDBusDisplay1Audio *iface;
53 GHashTable *out_listeners;
54 GHashTable *in_listeners;
55 } DBusAudio;
56
57 typedef struct DBusVoiceOut {
58 HWVoiceOut hw;
59 bool enabled;
60 RateCtl rate;
61
62 void *buf;
63 size_t buf_pos;
64 size_t buf_size;
65
66 bool has_volume;
67 Volume volume;
68 } DBusVoiceOut;
69
70 typedef struct DBusVoiceIn {
71 HWVoiceIn hw;
72 bool enabled;
73 RateCtl rate;
74
75 bool has_volume;
76 Volume volume;
77 } DBusVoiceIn;
78
dbus_get_buffer_out(HWVoiceOut * hw,size_t * size)79 static void *dbus_get_buffer_out(HWVoiceOut *hw, size_t *size)
80 {
81 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
82
83 if (!vo->buf) {
84 vo->buf_size = hw->samples * hw->info.bytes_per_frame;
85 vo->buf = g_malloc(vo->buf_size);
86 vo->buf_pos = 0;
87 }
88
89 *size = MIN(vo->buf_size - vo->buf_pos, *size);
90 *size = audio_rate_get_bytes(&vo->rate, &hw->info, *size);
91
92 return vo->buf + vo->buf_pos;
93
94 }
95
dbus_put_buffer_out(HWVoiceOut * hw,void * buf,size_t size)96 static size_t dbus_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
97 {
98 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
99 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
100 GHashTableIter iter;
101 QemuDBusDisplay1AudioOutListener *listener = NULL;
102 g_autoptr(GBytes) bytes = NULL;
103 g_autoptr(GVariant) v_data = NULL;
104
105 assert(buf == vo->buf + vo->buf_pos && vo->buf_pos + size <= vo->buf_size);
106 vo->buf_pos += size;
107
108 trace_dbus_audio_put_buffer_out(vo->buf_pos, vo->buf_size);
109
110 if (vo->buf_pos < vo->buf_size) {
111 return size;
112 }
113
114 bytes = g_bytes_new_take(g_steal_pointer(&vo->buf), vo->buf_size);
115 v_data = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
116 g_variant_ref_sink(v_data);
117
118 g_hash_table_iter_init(&iter, da->out_listeners);
119 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
120 qemu_dbus_display1_audio_out_listener_call_write(
121 listener,
122 (uintptr_t)hw,
123 v_data,
124 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
125 }
126
127 return size;
128 }
129
130 #if HOST_BIG_ENDIAN
131 #define AUDIO_HOST_BE TRUE
132 #else
133 #define AUDIO_HOST_BE FALSE
134 #endif
135
136 static void
dbus_init_out_listener(QemuDBusDisplay1AudioOutListener * listener,HWVoiceOut * hw)137 dbus_init_out_listener(QemuDBusDisplay1AudioOutListener *listener,
138 HWVoiceOut *hw)
139 {
140 qemu_dbus_display1_audio_out_listener_call_init(
141 listener,
142 (uintptr_t)hw,
143 hw->info.bits,
144 hw->info.is_signed,
145 hw->info.is_float,
146 hw->info.freq,
147 hw->info.nchannels,
148 hw->info.bytes_per_frame,
149 hw->info.bytes_per_second,
150 hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE,
151 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
152 }
153
154 static int
dbus_init_out(HWVoiceOut * hw,struct audsettings * as,void * drv_opaque)155 dbus_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
156 {
157 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
158 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
159 GHashTableIter iter;
160 QemuDBusDisplay1AudioOutListener *listener = NULL;
161
162 audio_pcm_init_info(&hw->info, as);
163 hw->samples = DBUS_AUDIO_NSAMPLES;
164 audio_rate_start(&vo->rate);
165
166 g_hash_table_iter_init(&iter, da->out_listeners);
167 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
168 dbus_init_out_listener(listener, hw);
169 }
170 return 0;
171 }
172
173 static void
dbus_fini_out(HWVoiceOut * hw)174 dbus_fini_out(HWVoiceOut *hw)
175 {
176 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
177 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
178 GHashTableIter iter;
179 QemuDBusDisplay1AudioOutListener *listener = NULL;
180
181 g_hash_table_iter_init(&iter, da->out_listeners);
182 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
183 qemu_dbus_display1_audio_out_listener_call_fini(
184 listener,
185 (uintptr_t)hw,
186 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
187 }
188
189 g_clear_pointer(&vo->buf, g_free);
190 }
191
192 static void
dbus_enable_out(HWVoiceOut * hw,bool enable)193 dbus_enable_out(HWVoiceOut *hw, bool enable)
194 {
195 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
196 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
197 GHashTableIter iter;
198 QemuDBusDisplay1AudioOutListener *listener = NULL;
199
200 vo->enabled = enable;
201 if (enable) {
202 audio_rate_start(&vo->rate);
203 }
204
205 g_hash_table_iter_init(&iter, da->out_listeners);
206 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
207 qemu_dbus_display1_audio_out_listener_call_set_enabled(
208 listener, (uintptr_t)hw, enable,
209 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
210 }
211 }
212
213 static void
dbus_volume_out_listener(HWVoiceOut * hw,QemuDBusDisplay1AudioOutListener * listener)214 dbus_volume_out_listener(HWVoiceOut *hw,
215 QemuDBusDisplay1AudioOutListener *listener)
216 {
217 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
218 Volume *vol = &vo->volume;
219 g_autoptr(GBytes) bytes = NULL;
220 GVariant *v_vol = NULL;
221
222 if (!vo->has_volume) {
223 return;
224 }
225
226 assert(vol->channels < sizeof(vol->vol));
227 bytes = g_bytes_new(vol->vol, vol->channels);
228 v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
229 qemu_dbus_display1_audio_out_listener_call_set_volume(
230 listener, (uintptr_t)hw, vol->mute, v_vol,
231 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
232 }
233
234 static void
dbus_volume_out(HWVoiceOut * hw,Volume * vol)235 dbus_volume_out(HWVoiceOut *hw, Volume *vol)
236 {
237 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
238 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
239 GHashTableIter iter;
240 QemuDBusDisplay1AudioOutListener *listener = NULL;
241
242 vo->has_volume = true;
243 vo->volume = *vol;
244
245 g_hash_table_iter_init(&iter, da->out_listeners);
246 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
247 dbus_volume_out_listener(hw, listener);
248 }
249 }
250
251 static void
dbus_init_in_listener(QemuDBusDisplay1AudioInListener * listener,HWVoiceIn * hw)252 dbus_init_in_listener(QemuDBusDisplay1AudioInListener *listener, HWVoiceIn *hw)
253 {
254 qemu_dbus_display1_audio_in_listener_call_init(
255 listener,
256 (uintptr_t)hw,
257 hw->info.bits,
258 hw->info.is_signed,
259 hw->info.is_float,
260 hw->info.freq,
261 hw->info.nchannels,
262 hw->info.bytes_per_frame,
263 hw->info.bytes_per_second,
264 hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE,
265 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
266 }
267
268 static int
dbus_init_in(HWVoiceIn * hw,struct audsettings * as,void * drv_opaque)269 dbus_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
270 {
271 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
272 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
273 GHashTableIter iter;
274 QemuDBusDisplay1AudioInListener *listener = NULL;
275
276 audio_pcm_init_info(&hw->info, as);
277 hw->samples = DBUS_AUDIO_NSAMPLES;
278 audio_rate_start(&vo->rate);
279
280 g_hash_table_iter_init(&iter, da->in_listeners);
281 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
282 dbus_init_in_listener(listener, hw);
283 }
284 return 0;
285 }
286
287 static void
dbus_fini_in(HWVoiceIn * hw)288 dbus_fini_in(HWVoiceIn *hw)
289 {
290 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
291 GHashTableIter iter;
292 QemuDBusDisplay1AudioInListener *listener = NULL;
293
294 g_hash_table_iter_init(&iter, da->in_listeners);
295 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
296 qemu_dbus_display1_audio_in_listener_call_fini(
297 listener,
298 (uintptr_t)hw,
299 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
300 }
301 }
302
303 static void
dbus_volume_in_listener(HWVoiceIn * hw,QemuDBusDisplay1AudioInListener * listener)304 dbus_volume_in_listener(HWVoiceIn *hw,
305 QemuDBusDisplay1AudioInListener *listener)
306 {
307 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
308 Volume *vol = &vo->volume;
309 g_autoptr(GBytes) bytes = NULL;
310 GVariant *v_vol = NULL;
311
312 if (!vo->has_volume) {
313 return;
314 }
315
316 assert(vol->channels < sizeof(vol->vol));
317 bytes = g_bytes_new(vol->vol, vol->channels);
318 v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
319 qemu_dbus_display1_audio_in_listener_call_set_volume(
320 listener, (uintptr_t)hw, vol->mute, v_vol,
321 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
322 }
323
324 static void
dbus_volume_in(HWVoiceIn * hw,Volume * vol)325 dbus_volume_in(HWVoiceIn *hw, Volume *vol)
326 {
327 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
328 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
329 GHashTableIter iter;
330 QemuDBusDisplay1AudioInListener *listener = NULL;
331
332 vo->has_volume = true;
333 vo->volume = *vol;
334
335 g_hash_table_iter_init(&iter, da->in_listeners);
336 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
337 dbus_volume_in_listener(hw, listener);
338 }
339 }
340
341 static size_t
dbus_read(HWVoiceIn * hw,void * buf,size_t size)342 dbus_read(HWVoiceIn *hw, void *buf, size_t size)
343 {
344 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
345 /* DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw); */
346 GHashTableIter iter;
347 QemuDBusDisplay1AudioInListener *listener = NULL;
348
349 trace_dbus_audio_read(size);
350
351 /* size = audio_rate_get_bytes(&vo->rate, &hw->info, size); */
352
353 g_hash_table_iter_init(&iter, da->in_listeners);
354 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
355 g_autoptr(GVariant) v_data = NULL;
356 const char *data;
357 gsize n = 0;
358
359 if (qemu_dbus_display1_audio_in_listener_call_read_sync(
360 listener,
361 (uintptr_t)hw,
362 size,
363 G_DBUS_CALL_FLAGS_NONE, -1,
364 &v_data, NULL, NULL)) {
365 data = g_variant_get_fixed_array(v_data, &n, 1);
366 g_warn_if_fail(n <= size);
367 size = MIN(n, size);
368 memcpy(buf, data, size);
369 break;
370 }
371 }
372
373 return size;
374 }
375
376 static void
dbus_enable_in(HWVoiceIn * hw,bool enable)377 dbus_enable_in(HWVoiceIn *hw, bool enable)
378 {
379 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
380 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
381 GHashTableIter iter;
382 QemuDBusDisplay1AudioInListener *listener = NULL;
383
384 vo->enabled = enable;
385 if (enable) {
386 audio_rate_start(&vo->rate);
387 }
388
389 g_hash_table_iter_init(&iter, da->in_listeners);
390 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
391 qemu_dbus_display1_audio_in_listener_call_set_enabled(
392 listener, (uintptr_t)hw, enable,
393 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
394 }
395 }
396
397 static void *
dbus_audio_init(Audiodev * dev,Error ** errp)398 dbus_audio_init(Audiodev *dev, Error **errp)
399 {
400 DBusAudio *da = g_new0(DBusAudio, 1);
401
402 da->out_listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
403 g_free, g_object_unref);
404 da->in_listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
405 g_free, g_object_unref);
406 return da;
407 }
408
409 static void
dbus_audio_fini(void * opaque)410 dbus_audio_fini(void *opaque)
411 {
412 DBusAudio *da = opaque;
413
414 if (da->server) {
415 g_dbus_object_manager_server_unexport(da->server,
416 DBUS_DISPLAY1_AUDIO_PATH);
417 }
418 g_clear_object(&da->audio);
419 g_clear_object(&da->iface);
420 g_clear_pointer(&da->in_listeners, g_hash_table_unref);
421 g_clear_pointer(&da->out_listeners, g_hash_table_unref);
422 g_clear_object(&da->server);
423 g_free(da);
424 }
425
426 static void
listener_out_vanished_cb(GDBusConnection * connection,gboolean remote_peer_vanished,GError * error,DBusAudio * da)427 listener_out_vanished_cb(GDBusConnection *connection,
428 gboolean remote_peer_vanished,
429 GError *error,
430 DBusAudio *da)
431 {
432 char *name = g_object_get_data(G_OBJECT(connection), "name");
433
434 g_hash_table_remove(da->out_listeners, name);
435 }
436
437 static void
listener_in_vanished_cb(GDBusConnection * connection,gboolean remote_peer_vanished,GError * error,DBusAudio * da)438 listener_in_vanished_cb(GDBusConnection *connection,
439 gboolean remote_peer_vanished,
440 GError *error,
441 DBusAudio *da)
442 {
443 char *name = g_object_get_data(G_OBJECT(connection), "name");
444
445 g_hash_table_remove(da->in_listeners, name);
446 }
447
448 static gboolean
dbus_audio_register_listener(AudioState * s,GDBusMethodInvocation * invocation,GUnixFDList * fd_list,GVariant * arg_listener,bool out)449 dbus_audio_register_listener(AudioState *s,
450 GDBusMethodInvocation *invocation,
451 #ifdef G_OS_UNIX
452 GUnixFDList *fd_list,
453 #endif
454 GVariant *arg_listener,
455 bool out)
456 {
457 DBusAudio *da = s->drv_opaque;
458 const char *sender =
459 da->p2p ? "p2p" : g_dbus_method_invocation_get_sender(invocation);
460 g_autoptr(GDBusConnection) listener_conn = NULL;
461 g_autoptr(GError) err = NULL;
462 g_autoptr(GSocket) socket = NULL;
463 g_autoptr(GSocketConnection) socket_conn = NULL;
464 g_autofree char *guid = g_dbus_generate_guid();
465 GHashTable *listeners = out ? da->out_listeners : da->in_listeners;
466 GObject *listener;
467 int fd;
468
469 trace_dbus_audio_register(sender, out ? "out" : "in");
470
471 if (g_hash_table_contains(listeners, sender)) {
472 g_dbus_method_invocation_return_error(invocation,
473 DBUS_DISPLAY_ERROR,
474 DBUS_DISPLAY_ERROR_INVALID,
475 "`%s` is already registered!",
476 sender);
477 return DBUS_METHOD_INVOCATION_HANDLED;
478 }
479
480 #ifdef G_OS_WIN32
481 if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) {
482 return DBUS_METHOD_INVOCATION_HANDLED;
483 }
484 #else
485 fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err);
486 if (err) {
487 g_dbus_method_invocation_return_error(invocation,
488 DBUS_DISPLAY_ERROR,
489 DBUS_DISPLAY_ERROR_FAILED,
490 "Couldn't get peer fd: %s",
491 err->message);
492 return DBUS_METHOD_INVOCATION_HANDLED;
493 }
494 #endif
495
496 socket = g_socket_new_from_fd(fd, &err);
497 if (err) {
498 g_dbus_method_invocation_return_error(invocation,
499 DBUS_DISPLAY_ERROR,
500 DBUS_DISPLAY_ERROR_FAILED,
501 "Couldn't make a socket: %s",
502 err->message);
503 #ifdef G_OS_WIN32
504 closesocket(fd);
505 #else
506 close(fd);
507 #endif
508 return DBUS_METHOD_INVOCATION_HANDLED;
509 }
510 socket_conn = g_socket_connection_factory_create_connection(socket);
511 if (out) {
512 qemu_dbus_display1_audio_complete_register_out_listener(
513 da->iface, invocation
514 #ifdef G_OS_UNIX
515 , NULL
516 #endif
517 );
518 } else {
519 qemu_dbus_display1_audio_complete_register_in_listener(
520 da->iface, invocation
521 #ifdef G_OS_UNIX
522 , NULL
523 #endif
524 );
525 }
526
527 listener_conn =
528 g_dbus_connection_new_sync(
529 G_IO_STREAM(socket_conn),
530 guid,
531 G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER,
532 NULL, NULL, &err);
533 if (err) {
534 error_report("Failed to setup peer connection: %s", err->message);
535 return DBUS_METHOD_INVOCATION_HANDLED;
536 }
537
538 listener = out ?
539 G_OBJECT(qemu_dbus_display1_audio_out_listener_proxy_new_sync(
540 listener_conn,
541 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
542 NULL,
543 "/org/qemu/Display1/AudioOutListener",
544 NULL,
545 &err)) :
546 G_OBJECT(qemu_dbus_display1_audio_in_listener_proxy_new_sync(
547 listener_conn,
548 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
549 NULL,
550 "/org/qemu/Display1/AudioInListener",
551 NULL,
552 &err));
553 if (!listener) {
554 error_report("Failed to setup proxy: %s", err->message);
555 return DBUS_METHOD_INVOCATION_HANDLED;
556 }
557
558 if (out) {
559 HWVoiceOut *hw;
560
561 QLIST_FOREACH(hw, &s->hw_head_out, entries) {
562 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
563 QemuDBusDisplay1AudioOutListener *l =
564 QEMU_DBUS_DISPLAY1_AUDIO_OUT_LISTENER(listener);
565
566 dbus_init_out_listener(l, hw);
567 qemu_dbus_display1_audio_out_listener_call_set_enabled(
568 l, (uintptr_t)hw, vo->enabled,
569 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
570 }
571 } else {
572 HWVoiceIn *hw;
573
574 QLIST_FOREACH(hw, &s->hw_head_in, entries) {
575 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
576 QemuDBusDisplay1AudioInListener *l =
577 QEMU_DBUS_DISPLAY1_AUDIO_IN_LISTENER(listener);
578
579 dbus_init_in_listener(
580 QEMU_DBUS_DISPLAY1_AUDIO_IN_LISTENER(listener), hw);
581 qemu_dbus_display1_audio_in_listener_call_set_enabled(
582 l, (uintptr_t)hw, vo->enabled,
583 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
584 }
585 }
586
587 g_object_set_data_full(G_OBJECT(listener_conn), "name",
588 g_strdup(sender), g_free);
589 g_hash_table_insert(listeners, g_strdup(sender), listener);
590 g_object_connect(listener_conn,
591 "signal::closed",
592 out ? listener_out_vanished_cb : listener_in_vanished_cb,
593 da,
594 NULL);
595
596 return DBUS_METHOD_INVOCATION_HANDLED;
597 }
598
599 static gboolean
dbus_audio_register_out_listener(AudioState * s,GDBusMethodInvocation * invocation,GUnixFDList * fd_list,GVariant * arg_listener)600 dbus_audio_register_out_listener(AudioState *s,
601 GDBusMethodInvocation *invocation,
602 #ifdef G_OS_UNIX
603 GUnixFDList *fd_list,
604 #endif
605 GVariant *arg_listener)
606 {
607 return dbus_audio_register_listener(s, invocation,
608 #ifdef G_OS_UNIX
609 fd_list,
610 #endif
611 arg_listener, true);
612
613 }
614
615 static gboolean
dbus_audio_register_in_listener(AudioState * s,GDBusMethodInvocation * invocation,GUnixFDList * fd_list,GVariant * arg_listener)616 dbus_audio_register_in_listener(AudioState *s,
617 GDBusMethodInvocation *invocation,
618 #ifdef G_OS_UNIX
619 GUnixFDList *fd_list,
620 #endif
621 GVariant *arg_listener)
622 {
623 return dbus_audio_register_listener(s, invocation,
624 #ifdef G_OS_UNIX
625 fd_list,
626 #endif
627 arg_listener, false);
628 }
629
630 static void
dbus_audio_set_server(AudioState * s,GDBusObjectManagerServer * server,bool p2p)631 dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server, bool p2p)
632 {
633 DBusAudio *da = s->drv_opaque;
634
635 g_assert(da);
636 g_assert(!da->server);
637
638 da->server = g_object_ref(server);
639 da->p2p = p2p;
640
641 da->audio = g_dbus_object_skeleton_new(DBUS_DISPLAY1_AUDIO_PATH);
642 da->iface = qemu_dbus_display1_audio_skeleton_new();
643 g_object_connect(da->iface,
644 "swapped-signal::handle-register-in-listener",
645 dbus_audio_register_in_listener, s,
646 "swapped-signal::handle-register-out-listener",
647 dbus_audio_register_out_listener, s,
648 NULL);
649
650 g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(da->audio),
651 G_DBUS_INTERFACE_SKELETON(da->iface));
652 g_dbus_object_manager_server_export(da->server, da->audio);
653 }
654
655 static struct audio_pcm_ops dbus_pcm_ops = {
656 .init_out = dbus_init_out,
657 .fini_out = dbus_fini_out,
658 .write = audio_generic_write,
659 .get_buffer_out = dbus_get_buffer_out,
660 .put_buffer_out = dbus_put_buffer_out,
661 .enable_out = dbus_enable_out,
662 .volume_out = dbus_volume_out,
663
664 .init_in = dbus_init_in,
665 .fini_in = dbus_fini_in,
666 .read = dbus_read,
667 .run_buffer_in = audio_generic_run_buffer_in,
668 .enable_in = dbus_enable_in,
669 .volume_in = dbus_volume_in,
670 };
671
672 static struct audio_driver dbus_audio_driver = {
673 .name = "dbus",
674 .descr = "Timer based audio exposed with DBus interface",
675 .init = dbus_audio_init,
676 .fini = dbus_audio_fini,
677 .set_dbus_server = dbus_audio_set_server,
678 .pcm_ops = &dbus_pcm_ops,
679 .max_voices_out = INT_MAX,
680 .max_voices_in = INT_MAX,
681 .voice_size_out = sizeof(DBusVoiceOut),
682 .voice_size_in = sizeof(DBusVoiceIn)
683 };
684
register_audio_dbus(void)685 static void register_audio_dbus(void)
686 {
687 audio_driver_register(&dbus_audio_driver);
688 }
689 type_init(register_audio_dbus);
690
691 module_dep("ui-dbus")
692