qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

dbusaudio.c (20807B)


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