xref: /qemu/audio/dbusaudio.c (revision 3cc72cdb)
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 
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 
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(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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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
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
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
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
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
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
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
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 
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