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    GDBusObjectSkeleton *audio;
  47    QemuDBusDisplay1Audio *iface;
  48    GHashTable *out_listeners;
  49    GHashTable *in_listeners;
  50} DBusAudio;
  51
  52typedef 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
  65typedef struct DBusVoiceIn {
  66    HWVoiceIn hw;
  67    bool enabled;
  68    RateCtl rate;
  69
  70    bool has_volume;
  71    Volume volume;
  72} DBusVoiceIn;
  73
  74static 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(&hw->info, &vo->rate, *size);
  86
  87    return vo->buf + vo->buf_pos;
  88
  89}
  90
  91static 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#ifdef HOST_WORDS_BIGENDIAN
 126#define AUDIO_HOST_BE TRUE
 127#else
 128#define AUDIO_HOST_BE FALSE
 129#endif
 130
 131static void
 132dbus_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
 149static int
 150dbus_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
 168static void
 169dbus_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
 187static void
 188dbus_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
 208static void
 209dbus_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
 229static void
 230dbus_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
 246static void
 247dbus_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
 263static int
 264dbus_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
 282static void
 283dbus_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
 298static void
 299dbus_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
 319static void
 320dbus_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
 336static size_t
 337dbus_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(&hw->info, &vo->rate, 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
 371static void
 372dbus_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
 392static void *
 393dbus_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
 404static void
 405dbus_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
 421static void
 422listener_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
 432static void
 433listener_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
 443static gboolean
 444dbus_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
 572static gboolean
 573dbus_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
 583static gboolean
 584dbus_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
 593static void
 594dbus_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
 617static 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
 634static 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
 648static void register_audio_dbus(void)
 649{
 650    audio_driver_register(&dbus_audio_driver);
 651}
 652type_init(register_audio_dbus);
 653
 654module_dep("ui-dbus")
 655