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