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