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