qemu/audio/dbusaudio.c
<<
>>
Prefs
   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
  48typedef 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
  57typedef 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
  70typedef struct DBusVoiceIn {
  71    HWVoiceIn hw;
  72    bool enabled;
  73    RateCtl rate;
  74
  75    bool has_volume;
  76    Volume volume;
  77} DBusVoiceIn;
  78
  79static 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
  96static 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
 136static void
 137dbus_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
 154static int
 155dbus_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
 173static void
 174dbus_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
 192static void
 193dbus_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
 213static void
 214dbus_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
 234static void
 235dbus_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
 251static void
 252dbus_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
 268static int
 269dbus_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
 287static void
 288dbus_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
 303static void
 304dbus_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
 324static void
 325dbus_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
 341static size_t
 342dbus_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
 376static void
 377dbus_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
 397static void *
 398dbus_audio_init(Audiodev *dev)
 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
 409static void
 410dbus_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
 426static void
 427listener_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
 437static void
 438listener_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
 448static gboolean
 449dbus_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
 599static gboolean
 600dbus_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
 615static gboolean
 616dbus_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
 630static void
 631dbus_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
 655static 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
 672static 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    .can_be_default  = 1,
 680    .max_voices_out  = INT_MAX,
 681    .max_voices_in   = INT_MAX,
 682    .voice_size_out  = sizeof(DBusVoiceOut),
 683    .voice_size_in   = sizeof(DBusVoiceIn)
 684};
 685
 686static void register_audio_dbus(void)
 687{
 688    audio_driver_register(&dbus_audio_driver);
 689}
 690type_init(register_audio_dbus);
 691
 692module_dep("ui-dbus")
 693