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