| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * QEMU DBus audio | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2021 Red Hat, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | 
					
						
							|  |  |  |  * of this software and associated documentation files (the "Software"), to deal | 
					
						
							|  |  |  |  * in the Software without restriction, including without limitation the rights | 
					
						
							|  |  |  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
					
						
							|  |  |  |  * copies of the Software, and to permit persons to whom the Software is | 
					
						
							|  |  |  |  * furnished to do so, subject to the following conditions: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The above copyright notice and this permission notice shall be included in | 
					
						
							|  |  |  |  * all copies or substantial portions of the Software. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
					
						
							|  |  |  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
					
						
							|  |  |  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | 
					
						
							|  |  |  |  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
					
						
							|  |  |  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
					
						
							|  |  |  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
					
						
							|  |  |  |  * THE SOFTWARE. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "qemu/osdep.h"
 | 
					
						
							|  |  |  | #include "qemu/error-report.h"
 | 
					
						
							|  |  |  | #include "qemu/host-utils.h"
 | 
					
						
							|  |  |  | #include "qemu/module.h"
 | 
					
						
							|  |  |  | #include "qemu/timer.h"
 | 
					
						
							|  |  |  | #include "qemu/dbus.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-06 15:56:40 +04:00
										 |  |  | #ifdef G_OS_UNIX
 | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  | #include <gio/gunixfdlist.h>
 | 
					
						
							| 
									
										
										
										
											2023-06-06 15:56:40 +04:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-06 15:56:42 +04:00
										 |  |  | #include "ui/dbus.h"
 | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  | #include "ui/dbus-display1.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define AUDIO_CAP "dbus"
 | 
					
						
							|  |  |  | #include "audio.h"
 | 
					
						
							|  |  |  | #include "audio_int.h"
 | 
					
						
							|  |  |  | #include "trace.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define DBUS_DISPLAY1_AUDIO_PATH DBUS_DISPLAY1_ROOT "/Audio"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define DBUS_AUDIO_NSAMPLES 1024 /* could be configured? */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct DBusAudio { | 
					
						
							|  |  |  |     GDBusObjectManagerServer *server; | 
					
						
							| 
									
										
										
										
											2022-11-15 16:19:43 +04:00
										 |  |  |     bool p2p; | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  |     GDBusObjectSkeleton *audio; | 
					
						
							|  |  |  |     QemuDBusDisplay1Audio *iface; | 
					
						
							|  |  |  |     GHashTable *out_listeners; | 
					
						
							|  |  |  |     GHashTable *in_listeners; | 
					
						
							|  |  |  | } DBusAudio; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct DBusVoiceOut { | 
					
						
							|  |  |  |     HWVoiceOut hw; | 
					
						
							|  |  |  |     bool enabled; | 
					
						
							|  |  |  |     RateCtl rate; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void *buf; | 
					
						
							|  |  |  |     size_t buf_pos; | 
					
						
							|  |  |  |     size_t buf_size; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bool has_volume; | 
					
						
							|  |  |  |     Volume volume; | 
					
						
							|  |  |  | } DBusVoiceOut; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | typedef struct DBusVoiceIn { | 
					
						
							|  |  |  |     HWVoiceIn hw; | 
					
						
							|  |  |  |     bool enabled; | 
					
						
							|  |  |  |     RateCtl rate; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bool has_volume; | 
					
						
							|  |  |  |     Volume volume; | 
					
						
							|  |  |  | } DBusVoiceIn; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void *dbus_get_buffer_out(HWVoiceOut *hw, size_t *size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!vo->buf) { | 
					
						
							|  |  |  |         vo->buf_size = hw->samples * hw->info.bytes_per_frame; | 
					
						
							|  |  |  |         vo->buf = g_malloc(vo->buf_size); | 
					
						
							|  |  |  |         vo->buf_pos = 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     *size = MIN(vo->buf_size - vo->buf_pos, *size); | 
					
						
							| 
									
										
										
										
											2022-09-23 20:36:36 +02:00
										 |  |  |     *size = audio_rate_get_bytes(&vo->rate, &hw->info, *size); | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return vo->buf + vo->buf_pos; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static size_t dbus_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DBusAudio *da = (DBusAudio *)hw->s->drv_opaque; | 
					
						
							|  |  |  |     DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw); | 
					
						
							|  |  |  |     GHashTableIter iter; | 
					
						
							|  |  |  |     QemuDBusDisplay1AudioOutListener *listener = NULL; | 
					
						
							|  |  |  |     g_autoptr(GBytes) bytes = NULL; | 
					
						
							|  |  |  |     g_autoptr(GVariant) v_data = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(buf == vo->buf + vo->buf_pos && vo->buf_pos + size <= vo->buf_size); | 
					
						
							|  |  |  |     vo->buf_pos += size; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     trace_dbus_audio_put_buffer_out(size); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (vo->buf_pos < vo->buf_size) { | 
					
						
							|  |  |  |         return size; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bytes = g_bytes_new_take(g_steal_pointer(&vo->buf), vo->buf_size); | 
					
						
							|  |  |  |     v_data = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE); | 
					
						
							|  |  |  |     g_variant_ref_sink(v_data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     g_hash_table_iter_init(&iter, da->out_listeners); | 
					
						
							|  |  |  |     while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) { | 
					
						
							|  |  |  |         qemu_dbus_display1_audio_out_listener_call_write( | 
					
						
							|  |  |  |             listener, | 
					
						
							|  |  |  |             (uintptr_t)hw, | 
					
						
							|  |  |  |             v_data, | 
					
						
							|  |  |  |             G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return size; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-23 19:57:17 +04:00
										 |  |  | #if HOST_BIG_ENDIAN
 | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  | #define AUDIO_HOST_BE TRUE
 | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | #define AUDIO_HOST_BE FALSE
 | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | dbus_init_out_listener(QemuDBusDisplay1AudioOutListener *listener, | 
					
						
							|  |  |  |                        HWVoiceOut *hw) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     qemu_dbus_display1_audio_out_listener_call_init( | 
					
						
							|  |  |  |         listener, | 
					
						
							|  |  |  |         (uintptr_t)hw, | 
					
						
							|  |  |  |         hw->info.bits, | 
					
						
							|  |  |  |         hw->info.is_signed, | 
					
						
							|  |  |  |         hw->info.is_float, | 
					
						
							|  |  |  |         hw->info.freq, | 
					
						
							|  |  |  |         hw->info.nchannels, | 
					
						
							|  |  |  |         hw->info.bytes_per_frame, | 
					
						
							|  |  |  |         hw->info.bytes_per_second, | 
					
						
							|  |  |  |         hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE, | 
					
						
							|  |  |  |         G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | dbus_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DBusAudio *da = (DBusAudio *)hw->s->drv_opaque; | 
					
						
							|  |  |  |     DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw); | 
					
						
							|  |  |  |     GHashTableIter iter; | 
					
						
							|  |  |  |     QemuDBusDisplay1AudioOutListener *listener = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     audio_pcm_init_info(&hw->info, as); | 
					
						
							|  |  |  |     hw->samples = DBUS_AUDIO_NSAMPLES; | 
					
						
							|  |  |  |     audio_rate_start(&vo->rate); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     g_hash_table_iter_init(&iter, da->out_listeners); | 
					
						
							|  |  |  |     while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) { | 
					
						
							|  |  |  |         dbus_init_out_listener(listener, hw); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | dbus_fini_out(HWVoiceOut *hw) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DBusAudio *da = (DBusAudio *)hw->s->drv_opaque; | 
					
						
							|  |  |  |     DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw); | 
					
						
							|  |  |  |     GHashTableIter iter; | 
					
						
							|  |  |  |     QemuDBusDisplay1AudioOutListener *listener = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     g_hash_table_iter_init(&iter, da->out_listeners); | 
					
						
							|  |  |  |     while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) { | 
					
						
							|  |  |  |         qemu_dbus_display1_audio_out_listener_call_fini( | 
					
						
							|  |  |  |             listener, | 
					
						
							|  |  |  |             (uintptr_t)hw, | 
					
						
							|  |  |  |             G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     g_clear_pointer(&vo->buf, g_free); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | dbus_enable_out(HWVoiceOut *hw, bool enable) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DBusAudio *da = (DBusAudio *)hw->s->drv_opaque; | 
					
						
							|  |  |  |     DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw); | 
					
						
							|  |  |  |     GHashTableIter iter; | 
					
						
							|  |  |  |     QemuDBusDisplay1AudioOutListener *listener = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     vo->enabled = enable; | 
					
						
							|  |  |  |     if (enable) { | 
					
						
							|  |  |  |         audio_rate_start(&vo->rate); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     g_hash_table_iter_init(&iter, da->out_listeners); | 
					
						
							|  |  |  |     while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) { | 
					
						
							|  |  |  |         qemu_dbus_display1_audio_out_listener_call_set_enabled( | 
					
						
							|  |  |  |             listener, (uintptr_t)hw, enable, | 
					
						
							|  |  |  |             G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | dbus_volume_out_listener(HWVoiceOut *hw, | 
					
						
							|  |  |  |                          QemuDBusDisplay1AudioOutListener *listener) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw); | 
					
						
							|  |  |  |     Volume *vol = &vo->volume; | 
					
						
							|  |  |  |     g_autoptr(GBytes) bytes = NULL; | 
					
						
							|  |  |  |     GVariant *v_vol = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!vo->has_volume) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(vol->channels < sizeof(vol->vol)); | 
					
						
							|  |  |  |     bytes = g_bytes_new(vol->vol, vol->channels); | 
					
						
							|  |  |  |     v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE); | 
					
						
							|  |  |  |     qemu_dbus_display1_audio_out_listener_call_set_volume( | 
					
						
							|  |  |  |         listener, (uintptr_t)hw, vol->mute, v_vol, | 
					
						
							|  |  |  |         G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | dbus_volume_out(HWVoiceOut *hw, Volume *vol) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DBusAudio *da = (DBusAudio *)hw->s->drv_opaque; | 
					
						
							|  |  |  |     DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw); | 
					
						
							|  |  |  |     GHashTableIter iter; | 
					
						
							|  |  |  |     QemuDBusDisplay1AudioOutListener *listener = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     vo->has_volume = true; | 
					
						
							|  |  |  |     vo->volume = *vol; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     g_hash_table_iter_init(&iter, da->out_listeners); | 
					
						
							|  |  |  |     while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) { | 
					
						
							|  |  |  |         dbus_volume_out_listener(hw, listener); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | dbus_init_in_listener(QemuDBusDisplay1AudioInListener *listener, HWVoiceIn *hw) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     qemu_dbus_display1_audio_in_listener_call_init( | 
					
						
							|  |  |  |         listener, | 
					
						
							|  |  |  |         (uintptr_t)hw, | 
					
						
							|  |  |  |         hw->info.bits, | 
					
						
							|  |  |  |         hw->info.is_signed, | 
					
						
							|  |  |  |         hw->info.is_float, | 
					
						
							|  |  |  |         hw->info.freq, | 
					
						
							|  |  |  |         hw->info.nchannels, | 
					
						
							|  |  |  |         hw->info.bytes_per_frame, | 
					
						
							|  |  |  |         hw->info.bytes_per_second, | 
					
						
							|  |  |  |         hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE, | 
					
						
							|  |  |  |         G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | dbus_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DBusAudio *da = (DBusAudio *)hw->s->drv_opaque; | 
					
						
							|  |  |  |     DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw); | 
					
						
							|  |  |  |     GHashTableIter iter; | 
					
						
							|  |  |  |     QemuDBusDisplay1AudioInListener *listener = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     audio_pcm_init_info(&hw->info, as); | 
					
						
							|  |  |  |     hw->samples = DBUS_AUDIO_NSAMPLES; | 
					
						
							|  |  |  |     audio_rate_start(&vo->rate); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     g_hash_table_iter_init(&iter, da->in_listeners); | 
					
						
							|  |  |  |     while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) { | 
					
						
							|  |  |  |         dbus_init_in_listener(listener, hw); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | dbus_fini_in(HWVoiceIn *hw) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DBusAudio *da = (DBusAudio *)hw->s->drv_opaque; | 
					
						
							|  |  |  |     GHashTableIter iter; | 
					
						
							|  |  |  |     QemuDBusDisplay1AudioInListener *listener = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     g_hash_table_iter_init(&iter, da->in_listeners); | 
					
						
							|  |  |  |     while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) { | 
					
						
							|  |  |  |         qemu_dbus_display1_audio_in_listener_call_fini( | 
					
						
							|  |  |  |             listener, | 
					
						
							|  |  |  |             (uintptr_t)hw, | 
					
						
							|  |  |  |             G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | dbus_volume_in_listener(HWVoiceIn *hw, | 
					
						
							|  |  |  |                          QemuDBusDisplay1AudioInListener *listener) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw); | 
					
						
							|  |  |  |     Volume *vol = &vo->volume; | 
					
						
							|  |  |  |     g_autoptr(GBytes) bytes = NULL; | 
					
						
							|  |  |  |     GVariant *v_vol = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!vo->has_volume) { | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     assert(vol->channels < sizeof(vol->vol)); | 
					
						
							|  |  |  |     bytes = g_bytes_new(vol->vol, vol->channels); | 
					
						
							|  |  |  |     v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE); | 
					
						
							|  |  |  |     qemu_dbus_display1_audio_in_listener_call_set_volume( | 
					
						
							|  |  |  |         listener, (uintptr_t)hw, vol->mute, v_vol, | 
					
						
							|  |  |  |         G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | dbus_volume_in(HWVoiceIn *hw, Volume *vol) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DBusAudio *da = (DBusAudio *)hw->s->drv_opaque; | 
					
						
							|  |  |  |     DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw); | 
					
						
							|  |  |  |     GHashTableIter iter; | 
					
						
							|  |  |  |     QemuDBusDisplay1AudioInListener *listener = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     vo->has_volume = true; | 
					
						
							|  |  |  |     vo->volume = *vol; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     g_hash_table_iter_init(&iter, da->in_listeners); | 
					
						
							|  |  |  |     while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) { | 
					
						
							|  |  |  |         dbus_volume_in_listener(hw, listener); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static size_t | 
					
						
							|  |  |  | dbus_read(HWVoiceIn *hw, void *buf, size_t size) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DBusAudio *da = (DBusAudio *)hw->s->drv_opaque; | 
					
						
							|  |  |  |     /* DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw); */ | 
					
						
							|  |  |  |     GHashTableIter iter; | 
					
						
							|  |  |  |     QemuDBusDisplay1AudioInListener *listener = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     trace_dbus_audio_read(size); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-23 20:36:36 +02:00
										 |  |  |     /* size = audio_rate_get_bytes(&vo->rate, &hw->info, size); */ | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     g_hash_table_iter_init(&iter, da->in_listeners); | 
					
						
							|  |  |  |     while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) { | 
					
						
							|  |  |  |         g_autoptr(GVariant) v_data = NULL; | 
					
						
							|  |  |  |         const char *data; | 
					
						
							|  |  |  |         gsize n = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (qemu_dbus_display1_audio_in_listener_call_read_sync( | 
					
						
							|  |  |  |                 listener, | 
					
						
							|  |  |  |                 (uintptr_t)hw, | 
					
						
							|  |  |  |                 size, | 
					
						
							|  |  |  |                 G_DBUS_CALL_FLAGS_NONE, -1, | 
					
						
							|  |  |  |                 &v_data, NULL, NULL)) { | 
					
						
							|  |  |  |             data = g_variant_get_fixed_array(v_data, &n, 1); | 
					
						
							|  |  |  |             g_warn_if_fail(n <= size); | 
					
						
							|  |  |  |             size = MIN(n, size); | 
					
						
							|  |  |  |             memcpy(buf, data, size); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return size; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | dbus_enable_in(HWVoiceIn *hw, bool enable) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DBusAudio *da = (DBusAudio *)hw->s->drv_opaque; | 
					
						
							|  |  |  |     DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw); | 
					
						
							|  |  |  |     GHashTableIter iter; | 
					
						
							|  |  |  |     QemuDBusDisplay1AudioInListener *listener = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     vo->enabled = enable; | 
					
						
							|  |  |  |     if (enable) { | 
					
						
							|  |  |  |         audio_rate_start(&vo->rate); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     g_hash_table_iter_init(&iter, da->in_listeners); | 
					
						
							|  |  |  |     while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) { | 
					
						
							|  |  |  |         qemu_dbus_display1_audio_in_listener_call_set_enabled( | 
					
						
							|  |  |  |             listener, (uintptr_t)hw, enable, | 
					
						
							|  |  |  |             G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void * | 
					
						
							|  |  |  | dbus_audio_init(Audiodev *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DBusAudio *da = g_new0(DBusAudio, 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     da->out_listeners = g_hash_table_new_full(g_str_hash, g_str_equal, | 
					
						
							|  |  |  |                                                 g_free, g_object_unref); | 
					
						
							|  |  |  |     da->in_listeners = g_hash_table_new_full(g_str_hash, g_str_equal, | 
					
						
							|  |  |  |                                                g_free, g_object_unref); | 
					
						
							|  |  |  |     return da; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | dbus_audio_fini(void *opaque) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DBusAudio *da = opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (da->server) { | 
					
						
							|  |  |  |         g_dbus_object_manager_server_unexport(da->server, | 
					
						
							|  |  |  |                                               DBUS_DISPLAY1_AUDIO_PATH); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     g_clear_object(&da->audio); | 
					
						
							|  |  |  |     g_clear_object(&da->iface); | 
					
						
							|  |  |  |     g_clear_pointer(&da->in_listeners, g_hash_table_unref); | 
					
						
							|  |  |  |     g_clear_pointer(&da->out_listeners, g_hash_table_unref); | 
					
						
							|  |  |  |     g_clear_object(&da->server); | 
					
						
							|  |  |  |     g_free(da); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | listener_out_vanished_cb(GDBusConnection *connection, | 
					
						
							|  |  |  |                          gboolean remote_peer_vanished, | 
					
						
							|  |  |  |                          GError *error, | 
					
						
							|  |  |  |                          DBusAudio *da) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     char *name = g_object_get_data(G_OBJECT(connection), "name"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     g_hash_table_remove(da->out_listeners, name); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | listener_in_vanished_cb(GDBusConnection *connection, | 
					
						
							|  |  |  |                         gboolean remote_peer_vanished, | 
					
						
							|  |  |  |                         GError *error, | 
					
						
							|  |  |  |                         DBusAudio *da) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     char *name = g_object_get_data(G_OBJECT(connection), "name"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     g_hash_table_remove(da->in_listeners, name); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static gboolean | 
					
						
							|  |  |  | dbus_audio_register_listener(AudioState *s, | 
					
						
							|  |  |  |                              GDBusMethodInvocation *invocation, | 
					
						
							| 
									
										
										
										
											2023-06-06 15:56:42 +04:00
										 |  |  | #ifdef G_OS_UNIX
 | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  |                              GUnixFDList *fd_list, | 
					
						
							| 
									
										
										
										
											2023-06-06 15:56:42 +04:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  |                              GVariant *arg_listener, | 
					
						
							|  |  |  |                              bool out) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     DBusAudio *da = s->drv_opaque; | 
					
						
							| 
									
										
										
										
											2022-11-15 16:19:43 +04:00
										 |  |  |     const char *sender = | 
					
						
							|  |  |  |         da->p2p ? "p2p" : g_dbus_method_invocation_get_sender(invocation); | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  |     g_autoptr(GDBusConnection) listener_conn = NULL; | 
					
						
							|  |  |  |     g_autoptr(GError) err = NULL; | 
					
						
							|  |  |  |     g_autoptr(GSocket) socket = NULL; | 
					
						
							|  |  |  |     g_autoptr(GSocketConnection) socket_conn = NULL; | 
					
						
							|  |  |  |     g_autofree char *guid = g_dbus_generate_guid(); | 
					
						
							|  |  |  |     GHashTable *listeners = out ? da->out_listeners : da->in_listeners; | 
					
						
							|  |  |  |     GObject *listener; | 
					
						
							|  |  |  |     int fd; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     trace_dbus_audio_register(sender, out ? "out" : "in"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (g_hash_table_contains(listeners, sender)) { | 
					
						
							|  |  |  |         g_dbus_method_invocation_return_error(invocation, | 
					
						
							|  |  |  |                                               DBUS_DISPLAY_ERROR, | 
					
						
							|  |  |  |                                               DBUS_DISPLAY_ERROR_INVALID, | 
					
						
							|  |  |  |                                               "`%s` is already registered!", | 
					
						
							|  |  |  |                                               sender); | 
					
						
							|  |  |  |         return DBUS_METHOD_INVOCATION_HANDLED; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-06 15:56:42 +04:00
										 |  |  | #ifdef G_OS_WIN32
 | 
					
						
							|  |  |  |     if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) { | 
					
						
							|  |  |  |         return DBUS_METHOD_INVOCATION_HANDLED; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  |     fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         g_dbus_method_invocation_return_error(invocation, | 
					
						
							|  |  |  |                                               DBUS_DISPLAY_ERROR, | 
					
						
							|  |  |  |                                               DBUS_DISPLAY_ERROR_FAILED, | 
					
						
							|  |  |  |                                               "Couldn't get peer fd: %s", | 
					
						
							|  |  |  |                                               err->message); | 
					
						
							|  |  |  |         return DBUS_METHOD_INVOCATION_HANDLED; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-06-06 15:56:42 +04:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     socket = g_socket_new_from_fd(fd, &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         g_dbus_method_invocation_return_error(invocation, | 
					
						
							|  |  |  |                                               DBUS_DISPLAY_ERROR, | 
					
						
							|  |  |  |                                               DBUS_DISPLAY_ERROR_FAILED, | 
					
						
							|  |  |  |                                               "Couldn't make a socket: %s", | 
					
						
							|  |  |  |                                               err->message); | 
					
						
							| 
									
										
										
										
											2023-06-06 15:56:42 +04:00
										 |  |  | #ifdef G_OS_WIN32
 | 
					
						
							|  |  |  |         closesocket(fd); | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |         close(fd); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  |         return DBUS_METHOD_INVOCATION_HANDLED; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     socket_conn = g_socket_connection_factory_create_connection(socket); | 
					
						
							|  |  |  |     if (out) { | 
					
						
							|  |  |  |         qemu_dbus_display1_audio_complete_register_out_listener( | 
					
						
							| 
									
										
										
										
											2023-06-06 15:56:42 +04:00
										 |  |  |             da->iface, invocation | 
					
						
							|  |  |  | #ifdef G_OS_UNIX
 | 
					
						
							|  |  |  |             , NULL | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |             ); | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  |     } else { | 
					
						
							|  |  |  |         qemu_dbus_display1_audio_complete_register_in_listener( | 
					
						
							| 
									
										
										
										
											2023-06-06 15:56:42 +04:00
										 |  |  |             da->iface, invocation | 
					
						
							|  |  |  | #ifdef G_OS_UNIX
 | 
					
						
							|  |  |  |             , NULL | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |             ); | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     listener_conn = | 
					
						
							|  |  |  |         g_dbus_connection_new_sync( | 
					
						
							|  |  |  |             G_IO_STREAM(socket_conn), | 
					
						
							|  |  |  |             guid, | 
					
						
							|  |  |  |             G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER, | 
					
						
							|  |  |  |             NULL, NULL, &err); | 
					
						
							|  |  |  |     if (err) { | 
					
						
							|  |  |  |         error_report("Failed to setup peer connection: %s", err->message); | 
					
						
							|  |  |  |         return DBUS_METHOD_INVOCATION_HANDLED; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     listener = out ? | 
					
						
							|  |  |  |         G_OBJECT(qemu_dbus_display1_audio_out_listener_proxy_new_sync( | 
					
						
							|  |  |  |             listener_conn, | 
					
						
							|  |  |  |             G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, | 
					
						
							|  |  |  |             NULL, | 
					
						
							|  |  |  |             "/org/qemu/Display1/AudioOutListener", | 
					
						
							|  |  |  |             NULL, | 
					
						
							|  |  |  |             &err)) : | 
					
						
							|  |  |  |         G_OBJECT(qemu_dbus_display1_audio_in_listener_proxy_new_sync( | 
					
						
							|  |  |  |             listener_conn, | 
					
						
							|  |  |  |             G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, | 
					
						
							|  |  |  |             NULL, | 
					
						
							|  |  |  |             "/org/qemu/Display1/AudioInListener", | 
					
						
							|  |  |  |             NULL, | 
					
						
							|  |  |  |             &err)); | 
					
						
							|  |  |  |     if (!listener) { | 
					
						
							|  |  |  |         error_report("Failed to setup proxy: %s", err->message); | 
					
						
							|  |  |  |         return DBUS_METHOD_INVOCATION_HANDLED; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (out) { | 
					
						
							|  |  |  |         HWVoiceOut *hw; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         QLIST_FOREACH(hw, &s->hw_head_out, entries) { | 
					
						
							|  |  |  |             DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw); | 
					
						
							|  |  |  |             QemuDBusDisplay1AudioOutListener *l = | 
					
						
							|  |  |  |                 QEMU_DBUS_DISPLAY1_AUDIO_OUT_LISTENER(listener); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             dbus_init_out_listener(l, hw); | 
					
						
							|  |  |  |             qemu_dbus_display1_audio_out_listener_call_set_enabled( | 
					
						
							|  |  |  |                 l, (uintptr_t)hw, vo->enabled, | 
					
						
							|  |  |  |                 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         HWVoiceIn *hw; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         QLIST_FOREACH(hw, &s->hw_head_in, entries) { | 
					
						
							|  |  |  |             DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw); | 
					
						
							|  |  |  |             QemuDBusDisplay1AudioInListener *l = | 
					
						
							|  |  |  |                 QEMU_DBUS_DISPLAY1_AUDIO_IN_LISTENER(listener); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             dbus_init_in_listener( | 
					
						
							|  |  |  |                 QEMU_DBUS_DISPLAY1_AUDIO_IN_LISTENER(listener), hw); | 
					
						
							|  |  |  |             qemu_dbus_display1_audio_in_listener_call_set_enabled( | 
					
						
							|  |  |  |                 l, (uintptr_t)hw, vo->enabled, | 
					
						
							|  |  |  |                 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     g_object_set_data_full(G_OBJECT(listener_conn), "name", | 
					
						
							|  |  |  |                            g_strdup(sender), g_free); | 
					
						
							|  |  |  |     g_hash_table_insert(listeners, g_strdup(sender), listener); | 
					
						
							|  |  |  |     g_object_connect(listener_conn, | 
					
						
							|  |  |  |                      "signal::closed", | 
					
						
							|  |  |  |                      out ? listener_out_vanished_cb : listener_in_vanished_cb, | 
					
						
							|  |  |  |                      da, | 
					
						
							|  |  |  |                      NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return DBUS_METHOD_INVOCATION_HANDLED; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static gboolean | 
					
						
							|  |  |  | dbus_audio_register_out_listener(AudioState *s, | 
					
						
							|  |  |  |                                  GDBusMethodInvocation *invocation, | 
					
						
							| 
									
										
										
										
											2023-06-06 15:56:42 +04:00
										 |  |  | #ifdef G_OS_UNIX
 | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  |                                  GUnixFDList *fd_list, | 
					
						
							| 
									
										
										
										
											2023-06-06 15:56:42 +04:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  |                                  GVariant *arg_listener) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return dbus_audio_register_listener(s, invocation, | 
					
						
							| 
									
										
										
										
											2023-06-06 15:56:42 +04:00
										 |  |  | #ifdef G_OS_UNIX
 | 
					
						
							|  |  |  |                                         fd_list, | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |                                         arg_listener, true); | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static gboolean | 
					
						
							|  |  |  | dbus_audio_register_in_listener(AudioState *s, | 
					
						
							|  |  |  |                                 GDBusMethodInvocation *invocation, | 
					
						
							| 
									
										
										
										
											2023-06-06 15:56:42 +04:00
										 |  |  | #ifdef G_OS_UNIX
 | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  |                                 GUnixFDList *fd_list, | 
					
						
							| 
									
										
										
										
											2023-06-06 15:56:42 +04:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  |                                 GVariant *arg_listener) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return dbus_audio_register_listener(s, invocation, | 
					
						
							| 
									
										
										
										
											2023-06-06 15:56:42 +04:00
										 |  |  | #ifdef G_OS_UNIX
 | 
					
						
							|  |  |  |                                         fd_list, | 
					
						
							| 
									
										
										
										
											2023-06-06 15:56:40 +04:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2023-06-06 15:56:42 +04:00
										 |  |  |                                         arg_listener, false); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2022-11-15 16:19:43 +04:00
										 |  |  | dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server, bool p2p) | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  | { | 
					
						
							|  |  |  |     DBusAudio *da = s->drv_opaque; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     g_assert(da); | 
					
						
							|  |  |  |     g_assert(!da->server); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     da->server = g_object_ref(server); | 
					
						
							| 
									
										
										
										
											2022-11-15 16:19:43 +04:00
										 |  |  |     da->p2p = p2p; | 
					
						
							| 
									
										
										
										
											2021-03-09 17:15:28 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     da->audio = g_dbus_object_skeleton_new(DBUS_DISPLAY1_AUDIO_PATH); | 
					
						
							|  |  |  |     da->iface = qemu_dbus_display1_audio_skeleton_new(); | 
					
						
							|  |  |  |     g_object_connect(da->iface, | 
					
						
							|  |  |  |                      "swapped-signal::handle-register-in-listener", | 
					
						
							|  |  |  |                      dbus_audio_register_in_listener, s, | 
					
						
							|  |  |  |                      "swapped-signal::handle-register-out-listener", | 
					
						
							|  |  |  |                      dbus_audio_register_out_listener, s, | 
					
						
							|  |  |  |                      NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(da->audio), | 
					
						
							|  |  |  |                                          G_DBUS_INTERFACE_SKELETON(da->iface)); | 
					
						
							|  |  |  |     g_dbus_object_manager_server_export(da->server, da->audio); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct audio_pcm_ops dbus_pcm_ops = { | 
					
						
							|  |  |  |     .init_out = dbus_init_out, | 
					
						
							|  |  |  |     .fini_out = dbus_fini_out, | 
					
						
							|  |  |  |     .write    = audio_generic_write, | 
					
						
							|  |  |  |     .get_buffer_out = dbus_get_buffer_out, | 
					
						
							|  |  |  |     .put_buffer_out = dbus_put_buffer_out, | 
					
						
							|  |  |  |     .enable_out = dbus_enable_out, | 
					
						
							|  |  |  |     .volume_out = dbus_volume_out, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     .init_in  = dbus_init_in, | 
					
						
							|  |  |  |     .fini_in  = dbus_fini_in, | 
					
						
							|  |  |  |     .read     = dbus_read, | 
					
						
							|  |  |  |     .run_buffer_in = audio_generic_run_buffer_in, | 
					
						
							|  |  |  |     .enable_in = dbus_enable_in, | 
					
						
							|  |  |  |     .volume_in = dbus_volume_in, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct audio_driver dbus_audio_driver = { | 
					
						
							|  |  |  |     .name            = "dbus", | 
					
						
							|  |  |  |     .descr           = "Timer based audio exposed with DBus interface", | 
					
						
							|  |  |  |     .init            = dbus_audio_init, | 
					
						
							|  |  |  |     .fini            = dbus_audio_fini, | 
					
						
							|  |  |  |     .set_dbus_server = dbus_audio_set_server, | 
					
						
							|  |  |  |     .pcm_ops         = &dbus_pcm_ops, | 
					
						
							|  |  |  |     .can_be_default  = 1, | 
					
						
							|  |  |  |     .max_voices_out  = INT_MAX, | 
					
						
							|  |  |  |     .max_voices_in   = INT_MAX, | 
					
						
							|  |  |  |     .voice_size_out  = sizeof(DBusVoiceOut), | 
					
						
							|  |  |  |     .voice_size_in   = sizeof(DBusVoiceIn) | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void register_audio_dbus(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     audio_driver_register(&dbus_audio_driver); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | type_init(register_audio_dbus); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module_dep("ui-dbus") |