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