1739362d4SMarc-André Lureau /*
2739362d4SMarc-André Lureau * QEMU DBus audio
3739362d4SMarc-André Lureau *
4739362d4SMarc-André Lureau * Copyright (c) 2021 Red Hat, Inc.
5739362d4SMarc-André Lureau *
6739362d4SMarc-André Lureau * Permission is hereby granted, free of charge, to any person obtaining a copy
7739362d4SMarc-André Lureau * of this software and associated documentation files (the "Software"), to deal
8739362d4SMarc-André Lureau * in the Software without restriction, including without limitation the rights
9739362d4SMarc-André Lureau * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10739362d4SMarc-André Lureau * copies of the Software, and to permit persons to whom the Software is
11739362d4SMarc-André Lureau * furnished to do so, subject to the following conditions:
12739362d4SMarc-André Lureau *
13739362d4SMarc-André Lureau * The above copyright notice and this permission notice shall be included in
14739362d4SMarc-André Lureau * all copies or substantial portions of the Software.
15739362d4SMarc-André Lureau *
16739362d4SMarc-André Lureau * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17739362d4SMarc-André Lureau * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18739362d4SMarc-André Lureau * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19739362d4SMarc-André Lureau * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20739362d4SMarc-André Lureau * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21739362d4SMarc-André Lureau * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22739362d4SMarc-André Lureau * THE SOFTWARE.
23739362d4SMarc-André Lureau */
24739362d4SMarc-André Lureau
25739362d4SMarc-André Lureau #include "qemu/osdep.h"
26739362d4SMarc-André Lureau #include "qemu/error-report.h"
27739362d4SMarc-André Lureau #include "qemu/host-utils.h"
28739362d4SMarc-André Lureau #include "qemu/module.h"
29739362d4SMarc-André Lureau #include "qemu/timer.h"
30739362d4SMarc-André Lureau #include "qemu/dbus.h"
31739362d4SMarc-André Lureau
3229c5c7e5SMarc-André Lureau #ifdef G_OS_UNIX
33739362d4SMarc-André Lureau #include <gio/gunixfdlist.h>
3429c5c7e5SMarc-André Lureau #endif
3529c5c7e5SMarc-André Lureau
366cc5a615SMarc-André Lureau #include "ui/dbus.h"
37739362d4SMarc-André Lureau #include "ui/dbus-display1.h"
38739362d4SMarc-André Lureau
39739362d4SMarc-André Lureau #define AUDIO_CAP "dbus"
40739362d4SMarc-André Lureau #include "audio.h"
41739362d4SMarc-André Lureau #include "audio_int.h"
42739362d4SMarc-André Lureau #include "trace.h"
43739362d4SMarc-André Lureau
44739362d4SMarc-André Lureau #define DBUS_DISPLAY1_AUDIO_PATH DBUS_DISPLAY1_ROOT "/Audio"
45739362d4SMarc-André Lureau
46739362d4SMarc-André Lureau #define DBUS_AUDIO_NSAMPLES 1024 /* could be configured? */
47739362d4SMarc-André Lureau
48739362d4SMarc-André Lureau typedef struct DBusAudio {
49739362d4SMarc-André Lureau GDBusObjectManagerServer *server;
50e74fec9aSMarc-André Lureau bool p2p;
51739362d4SMarc-André Lureau GDBusObjectSkeleton *audio;
52739362d4SMarc-André Lureau QemuDBusDisplay1Audio *iface;
53739362d4SMarc-André Lureau GHashTable *out_listeners;
54739362d4SMarc-André Lureau GHashTable *in_listeners;
55739362d4SMarc-André Lureau } DBusAudio;
56739362d4SMarc-André Lureau
57739362d4SMarc-André Lureau typedef struct DBusVoiceOut {
58739362d4SMarc-André Lureau HWVoiceOut hw;
59739362d4SMarc-André Lureau bool enabled;
60739362d4SMarc-André Lureau RateCtl rate;
61739362d4SMarc-André Lureau
62739362d4SMarc-André Lureau void *buf;
63739362d4SMarc-André Lureau size_t buf_pos;
64739362d4SMarc-André Lureau size_t buf_size;
65739362d4SMarc-André Lureau
66739362d4SMarc-André Lureau bool has_volume;
67739362d4SMarc-André Lureau Volume volume;
68739362d4SMarc-André Lureau } DBusVoiceOut;
69739362d4SMarc-André Lureau
70739362d4SMarc-André Lureau typedef struct DBusVoiceIn {
71739362d4SMarc-André Lureau HWVoiceIn hw;
72739362d4SMarc-André Lureau bool enabled;
73739362d4SMarc-André Lureau RateCtl rate;
74739362d4SMarc-André Lureau
75739362d4SMarc-André Lureau bool has_volume;
76739362d4SMarc-André Lureau Volume volume;
77739362d4SMarc-André Lureau } DBusVoiceIn;
78739362d4SMarc-André Lureau
dbus_get_buffer_out(HWVoiceOut * hw,size_t * size)79739362d4SMarc-André Lureau static void *dbus_get_buffer_out(HWVoiceOut *hw, size_t *size)
80739362d4SMarc-André Lureau {
81739362d4SMarc-André Lureau DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
82739362d4SMarc-André Lureau
83739362d4SMarc-André Lureau if (!vo->buf) {
84739362d4SMarc-André Lureau vo->buf_size = hw->samples * hw->info.bytes_per_frame;
85739362d4SMarc-André Lureau vo->buf = g_malloc(vo->buf_size);
86739362d4SMarc-André Lureau vo->buf_pos = 0;
87739362d4SMarc-André Lureau }
88739362d4SMarc-André Lureau
89739362d4SMarc-André Lureau *size = MIN(vo->buf_size - vo->buf_pos, *size);
90613fe02bSVolker Rümelin *size = audio_rate_get_bytes(&vo->rate, &hw->info, *size);
91739362d4SMarc-André Lureau
92739362d4SMarc-André Lureau return vo->buf + vo->buf_pos;
93739362d4SMarc-André Lureau
94739362d4SMarc-André Lureau }
95739362d4SMarc-André Lureau
dbus_put_buffer_out(HWVoiceOut * hw,void * buf,size_t size)96739362d4SMarc-André Lureau static size_t dbus_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
97739362d4SMarc-André Lureau {
98739362d4SMarc-André Lureau DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
99739362d4SMarc-André Lureau DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
100739362d4SMarc-André Lureau GHashTableIter iter;
101739362d4SMarc-André Lureau QemuDBusDisplay1AudioOutListener *listener = NULL;
102739362d4SMarc-André Lureau g_autoptr(GBytes) bytes = NULL;
103739362d4SMarc-André Lureau g_autoptr(GVariant) v_data = NULL;
104739362d4SMarc-André Lureau
105739362d4SMarc-André Lureau assert(buf == vo->buf + vo->buf_pos && vo->buf_pos + size <= vo->buf_size);
106739362d4SMarc-André Lureau vo->buf_pos += size;
107739362d4SMarc-André Lureau
108*2e35439fSMarc-André Lureau trace_dbus_audio_put_buffer_out(vo->buf_pos, vo->buf_size);
109739362d4SMarc-André Lureau
110739362d4SMarc-André Lureau if (vo->buf_pos < vo->buf_size) {
111739362d4SMarc-André Lureau return size;
112739362d4SMarc-André Lureau }
113739362d4SMarc-André Lureau
114739362d4SMarc-André Lureau bytes = g_bytes_new_take(g_steal_pointer(&vo->buf), vo->buf_size);
115739362d4SMarc-André Lureau v_data = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
116739362d4SMarc-André Lureau g_variant_ref_sink(v_data);
117739362d4SMarc-André Lureau
118739362d4SMarc-André Lureau g_hash_table_iter_init(&iter, da->out_listeners);
119739362d4SMarc-André Lureau while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
120739362d4SMarc-André Lureau qemu_dbus_display1_audio_out_listener_call_write(
121739362d4SMarc-André Lureau listener,
122739362d4SMarc-André Lureau (uintptr_t)hw,
123739362d4SMarc-André Lureau v_data,
124739362d4SMarc-André Lureau G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
125739362d4SMarc-André Lureau }
126739362d4SMarc-André Lureau
127739362d4SMarc-André Lureau return size;
128739362d4SMarc-André Lureau }
129739362d4SMarc-André Lureau
130e03b5686SMarc-André Lureau #if HOST_BIG_ENDIAN
131739362d4SMarc-André Lureau #define AUDIO_HOST_BE TRUE
132739362d4SMarc-André Lureau #else
133739362d4SMarc-André Lureau #define AUDIO_HOST_BE FALSE
134739362d4SMarc-André Lureau #endif
135739362d4SMarc-André Lureau
136739362d4SMarc-André Lureau static void
dbus_init_out_listener(QemuDBusDisplay1AudioOutListener * listener,HWVoiceOut * hw)137739362d4SMarc-André Lureau dbus_init_out_listener(QemuDBusDisplay1AudioOutListener *listener,
138739362d4SMarc-André Lureau HWVoiceOut *hw)
139739362d4SMarc-André Lureau {
140739362d4SMarc-André Lureau qemu_dbus_display1_audio_out_listener_call_init(
141739362d4SMarc-André Lureau listener,
142739362d4SMarc-André Lureau (uintptr_t)hw,
143739362d4SMarc-André Lureau hw->info.bits,
144739362d4SMarc-André Lureau hw->info.is_signed,
145739362d4SMarc-André Lureau hw->info.is_float,
146739362d4SMarc-André Lureau hw->info.freq,
147739362d4SMarc-André Lureau hw->info.nchannels,
148739362d4SMarc-André Lureau hw->info.bytes_per_frame,
149739362d4SMarc-André Lureau hw->info.bytes_per_second,
150739362d4SMarc-André Lureau hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE,
151739362d4SMarc-André Lureau G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
152739362d4SMarc-André Lureau }
153739362d4SMarc-André Lureau
154739362d4SMarc-André Lureau static int
dbus_init_out(HWVoiceOut * hw,struct audsettings * as,void * drv_opaque)155739362d4SMarc-André Lureau dbus_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
156739362d4SMarc-André Lureau {
157739362d4SMarc-André Lureau DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
158739362d4SMarc-André Lureau DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
159739362d4SMarc-André Lureau GHashTableIter iter;
160739362d4SMarc-André Lureau QemuDBusDisplay1AudioOutListener *listener = NULL;
161739362d4SMarc-André Lureau
162739362d4SMarc-André Lureau audio_pcm_init_info(&hw->info, as);
163739362d4SMarc-André Lureau hw->samples = DBUS_AUDIO_NSAMPLES;
164739362d4SMarc-André Lureau audio_rate_start(&vo->rate);
165739362d4SMarc-André Lureau
166739362d4SMarc-André Lureau g_hash_table_iter_init(&iter, da->out_listeners);
167739362d4SMarc-André Lureau while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
168739362d4SMarc-André Lureau dbus_init_out_listener(listener, hw);
169739362d4SMarc-André Lureau }
170739362d4SMarc-André Lureau return 0;
171739362d4SMarc-André Lureau }
172739362d4SMarc-André Lureau
173739362d4SMarc-André Lureau static void
dbus_fini_out(HWVoiceOut * hw)174739362d4SMarc-André Lureau dbus_fini_out(HWVoiceOut *hw)
175739362d4SMarc-André Lureau {
176739362d4SMarc-André Lureau DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
177739362d4SMarc-André Lureau DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
178739362d4SMarc-André Lureau GHashTableIter iter;
179739362d4SMarc-André Lureau QemuDBusDisplay1AudioOutListener *listener = NULL;
180739362d4SMarc-André Lureau
181739362d4SMarc-André Lureau g_hash_table_iter_init(&iter, da->out_listeners);
182739362d4SMarc-André Lureau while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
183739362d4SMarc-André Lureau qemu_dbus_display1_audio_out_listener_call_fini(
184739362d4SMarc-André Lureau listener,
185739362d4SMarc-André Lureau (uintptr_t)hw,
186739362d4SMarc-André Lureau G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
187739362d4SMarc-André Lureau }
188739362d4SMarc-André Lureau
189739362d4SMarc-André Lureau g_clear_pointer(&vo->buf, g_free);
190739362d4SMarc-André Lureau }
191739362d4SMarc-André Lureau
192739362d4SMarc-André Lureau static void
dbus_enable_out(HWVoiceOut * hw,bool enable)193739362d4SMarc-André Lureau dbus_enable_out(HWVoiceOut *hw, bool enable)
194739362d4SMarc-André Lureau {
195739362d4SMarc-André Lureau DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
196739362d4SMarc-André Lureau DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
197739362d4SMarc-André Lureau GHashTableIter iter;
198739362d4SMarc-André Lureau QemuDBusDisplay1AudioOutListener *listener = NULL;
199739362d4SMarc-André Lureau
200739362d4SMarc-André Lureau vo->enabled = enable;
201739362d4SMarc-André Lureau if (enable) {
202739362d4SMarc-André Lureau audio_rate_start(&vo->rate);
203739362d4SMarc-André Lureau }
204739362d4SMarc-André Lureau
205739362d4SMarc-André Lureau g_hash_table_iter_init(&iter, da->out_listeners);
206739362d4SMarc-André Lureau while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
207739362d4SMarc-André Lureau qemu_dbus_display1_audio_out_listener_call_set_enabled(
208739362d4SMarc-André Lureau listener, (uintptr_t)hw, enable,
209739362d4SMarc-André Lureau G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
210739362d4SMarc-André Lureau }
211739362d4SMarc-André Lureau }
212739362d4SMarc-André Lureau
213739362d4SMarc-André Lureau static void
dbus_volume_out_listener(HWVoiceOut * hw,QemuDBusDisplay1AudioOutListener * listener)214739362d4SMarc-André Lureau dbus_volume_out_listener(HWVoiceOut *hw,
215739362d4SMarc-André Lureau QemuDBusDisplay1AudioOutListener *listener)
216739362d4SMarc-André Lureau {
217739362d4SMarc-André Lureau DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
218739362d4SMarc-André Lureau Volume *vol = &vo->volume;
219739362d4SMarc-André Lureau g_autoptr(GBytes) bytes = NULL;
220739362d4SMarc-André Lureau GVariant *v_vol = NULL;
221739362d4SMarc-André Lureau
222739362d4SMarc-André Lureau if (!vo->has_volume) {
223739362d4SMarc-André Lureau return;
224739362d4SMarc-André Lureau }
225739362d4SMarc-André Lureau
226739362d4SMarc-André Lureau assert(vol->channels < sizeof(vol->vol));
227739362d4SMarc-André Lureau bytes = g_bytes_new(vol->vol, vol->channels);
228739362d4SMarc-André Lureau v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
229739362d4SMarc-André Lureau qemu_dbus_display1_audio_out_listener_call_set_volume(
230739362d4SMarc-André Lureau listener, (uintptr_t)hw, vol->mute, v_vol,
231739362d4SMarc-André Lureau G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
232739362d4SMarc-André Lureau }
233739362d4SMarc-André Lureau
234739362d4SMarc-André Lureau static void
dbus_volume_out(HWVoiceOut * hw,Volume * vol)235739362d4SMarc-André Lureau dbus_volume_out(HWVoiceOut *hw, Volume *vol)
236739362d4SMarc-André Lureau {
237739362d4SMarc-André Lureau DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
238739362d4SMarc-André Lureau DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
239739362d4SMarc-André Lureau GHashTableIter iter;
240739362d4SMarc-André Lureau QemuDBusDisplay1AudioOutListener *listener = NULL;
241739362d4SMarc-André Lureau
242739362d4SMarc-André Lureau vo->has_volume = true;
243739362d4SMarc-André Lureau vo->volume = *vol;
244739362d4SMarc-André Lureau
245739362d4SMarc-André Lureau g_hash_table_iter_init(&iter, da->out_listeners);
246739362d4SMarc-André Lureau while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
247739362d4SMarc-André Lureau dbus_volume_out_listener(hw, listener);
248739362d4SMarc-André Lureau }
249739362d4SMarc-André Lureau }
250739362d4SMarc-André Lureau
251739362d4SMarc-André Lureau static void
dbus_init_in_listener(QemuDBusDisplay1AudioInListener * listener,HWVoiceIn * hw)252739362d4SMarc-André Lureau dbus_init_in_listener(QemuDBusDisplay1AudioInListener *listener, HWVoiceIn *hw)
253739362d4SMarc-André Lureau {
254739362d4SMarc-André Lureau qemu_dbus_display1_audio_in_listener_call_init(
255739362d4SMarc-André Lureau listener,
256739362d4SMarc-André Lureau (uintptr_t)hw,
257739362d4SMarc-André Lureau hw->info.bits,
258739362d4SMarc-André Lureau hw->info.is_signed,
259739362d4SMarc-André Lureau hw->info.is_float,
260739362d4SMarc-André Lureau hw->info.freq,
261739362d4SMarc-André Lureau hw->info.nchannels,
262739362d4SMarc-André Lureau hw->info.bytes_per_frame,
263739362d4SMarc-André Lureau hw->info.bytes_per_second,
264739362d4SMarc-André Lureau hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE,
265739362d4SMarc-André Lureau G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
266739362d4SMarc-André Lureau }
267739362d4SMarc-André Lureau
268739362d4SMarc-André Lureau static int
dbus_init_in(HWVoiceIn * hw,struct audsettings * as,void * drv_opaque)269739362d4SMarc-André Lureau dbus_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
270739362d4SMarc-André Lureau {
271739362d4SMarc-André Lureau DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
272739362d4SMarc-André Lureau DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
273739362d4SMarc-André Lureau GHashTableIter iter;
274739362d4SMarc-André Lureau QemuDBusDisplay1AudioInListener *listener = NULL;
275739362d4SMarc-André Lureau
276739362d4SMarc-André Lureau audio_pcm_init_info(&hw->info, as);
277739362d4SMarc-André Lureau hw->samples = DBUS_AUDIO_NSAMPLES;
278739362d4SMarc-André Lureau audio_rate_start(&vo->rate);
279739362d4SMarc-André Lureau
280739362d4SMarc-André Lureau g_hash_table_iter_init(&iter, da->in_listeners);
281739362d4SMarc-André Lureau while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
282739362d4SMarc-André Lureau dbus_init_in_listener(listener, hw);
283739362d4SMarc-André Lureau }
284739362d4SMarc-André Lureau return 0;
285739362d4SMarc-André Lureau }
286739362d4SMarc-André Lureau
287739362d4SMarc-André Lureau static void
dbus_fini_in(HWVoiceIn * hw)288739362d4SMarc-André Lureau dbus_fini_in(HWVoiceIn *hw)
289739362d4SMarc-André Lureau {
290739362d4SMarc-André Lureau DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
291739362d4SMarc-André Lureau GHashTableIter iter;
292739362d4SMarc-André Lureau QemuDBusDisplay1AudioInListener *listener = NULL;
293739362d4SMarc-André Lureau
294739362d4SMarc-André Lureau g_hash_table_iter_init(&iter, da->in_listeners);
295739362d4SMarc-André Lureau while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
296739362d4SMarc-André Lureau qemu_dbus_display1_audio_in_listener_call_fini(
297739362d4SMarc-André Lureau listener,
298739362d4SMarc-André Lureau (uintptr_t)hw,
299739362d4SMarc-André Lureau G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
300739362d4SMarc-André Lureau }
301739362d4SMarc-André Lureau }
302739362d4SMarc-André Lureau
303739362d4SMarc-André Lureau static void
dbus_volume_in_listener(HWVoiceIn * hw,QemuDBusDisplay1AudioInListener * listener)304739362d4SMarc-André Lureau dbus_volume_in_listener(HWVoiceIn *hw,
305739362d4SMarc-André Lureau QemuDBusDisplay1AudioInListener *listener)
306739362d4SMarc-André Lureau {
307739362d4SMarc-André Lureau DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
308739362d4SMarc-André Lureau Volume *vol = &vo->volume;
309739362d4SMarc-André Lureau g_autoptr(GBytes) bytes = NULL;
310739362d4SMarc-André Lureau GVariant *v_vol = NULL;
311739362d4SMarc-André Lureau
312739362d4SMarc-André Lureau if (!vo->has_volume) {
313739362d4SMarc-André Lureau return;
314739362d4SMarc-André Lureau }
315739362d4SMarc-André Lureau
316739362d4SMarc-André Lureau assert(vol->channels < sizeof(vol->vol));
317739362d4SMarc-André Lureau bytes = g_bytes_new(vol->vol, vol->channels);
318739362d4SMarc-André Lureau v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
319739362d4SMarc-André Lureau qemu_dbus_display1_audio_in_listener_call_set_volume(
320739362d4SMarc-André Lureau listener, (uintptr_t)hw, vol->mute, v_vol,
321739362d4SMarc-André Lureau G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
322739362d4SMarc-André Lureau }
323739362d4SMarc-André Lureau
324739362d4SMarc-André Lureau static void
dbus_volume_in(HWVoiceIn * hw,Volume * vol)325739362d4SMarc-André Lureau dbus_volume_in(HWVoiceIn *hw, Volume *vol)
326739362d4SMarc-André Lureau {
327739362d4SMarc-André Lureau DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
328739362d4SMarc-André Lureau DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
329739362d4SMarc-André Lureau GHashTableIter iter;
330739362d4SMarc-André Lureau QemuDBusDisplay1AudioInListener *listener = NULL;
331739362d4SMarc-André Lureau
332739362d4SMarc-André Lureau vo->has_volume = true;
333739362d4SMarc-André Lureau vo->volume = *vol;
334739362d4SMarc-André Lureau
335739362d4SMarc-André Lureau g_hash_table_iter_init(&iter, da->in_listeners);
336739362d4SMarc-André Lureau while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
337739362d4SMarc-André Lureau dbus_volume_in_listener(hw, listener);
338739362d4SMarc-André Lureau }
339739362d4SMarc-André Lureau }
340739362d4SMarc-André Lureau
341739362d4SMarc-André Lureau static size_t
dbus_read(HWVoiceIn * hw,void * buf,size_t size)342739362d4SMarc-André Lureau dbus_read(HWVoiceIn *hw, void *buf, size_t size)
343739362d4SMarc-André Lureau {
344739362d4SMarc-André Lureau DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
345739362d4SMarc-André Lureau /* DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw); */
346739362d4SMarc-André Lureau GHashTableIter iter;
347739362d4SMarc-André Lureau QemuDBusDisplay1AudioInListener *listener = NULL;
348739362d4SMarc-André Lureau
349739362d4SMarc-André Lureau trace_dbus_audio_read(size);
350739362d4SMarc-André Lureau
351613fe02bSVolker Rümelin /* size = audio_rate_get_bytes(&vo->rate, &hw->info, size); */
352739362d4SMarc-André Lureau
353739362d4SMarc-André Lureau g_hash_table_iter_init(&iter, da->in_listeners);
354739362d4SMarc-André Lureau while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
355739362d4SMarc-André Lureau g_autoptr(GVariant) v_data = NULL;
356739362d4SMarc-André Lureau const char *data;
357739362d4SMarc-André Lureau gsize n = 0;
358739362d4SMarc-André Lureau
359739362d4SMarc-André Lureau if (qemu_dbus_display1_audio_in_listener_call_read_sync(
360739362d4SMarc-André Lureau listener,
361739362d4SMarc-André Lureau (uintptr_t)hw,
362739362d4SMarc-André Lureau size,
363739362d4SMarc-André Lureau G_DBUS_CALL_FLAGS_NONE, -1,
364739362d4SMarc-André Lureau &v_data, NULL, NULL)) {
365739362d4SMarc-André Lureau data = g_variant_get_fixed_array(v_data, &n, 1);
366739362d4SMarc-André Lureau g_warn_if_fail(n <= size);
367739362d4SMarc-André Lureau size = MIN(n, size);
368739362d4SMarc-André Lureau memcpy(buf, data, size);
369739362d4SMarc-André Lureau break;
370739362d4SMarc-André Lureau }
371739362d4SMarc-André Lureau }
372739362d4SMarc-André Lureau
373739362d4SMarc-André Lureau return size;
374739362d4SMarc-André Lureau }
375739362d4SMarc-André Lureau
376739362d4SMarc-André Lureau static void
dbus_enable_in(HWVoiceIn * hw,bool enable)377739362d4SMarc-André Lureau dbus_enable_in(HWVoiceIn *hw, bool enable)
378739362d4SMarc-André Lureau {
379739362d4SMarc-André Lureau DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
380739362d4SMarc-André Lureau DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
381739362d4SMarc-André Lureau GHashTableIter iter;
382739362d4SMarc-André Lureau QemuDBusDisplay1AudioInListener *listener = NULL;
383739362d4SMarc-André Lureau
384739362d4SMarc-André Lureau vo->enabled = enable;
385739362d4SMarc-André Lureau if (enable) {
386739362d4SMarc-André Lureau audio_rate_start(&vo->rate);
387739362d4SMarc-André Lureau }
388739362d4SMarc-André Lureau
389739362d4SMarc-André Lureau g_hash_table_iter_init(&iter, da->in_listeners);
390739362d4SMarc-André Lureau while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
391739362d4SMarc-André Lureau qemu_dbus_display1_audio_in_listener_call_set_enabled(
392739362d4SMarc-André Lureau listener, (uintptr_t)hw, enable,
393739362d4SMarc-André Lureau G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
394739362d4SMarc-André Lureau }
395739362d4SMarc-André Lureau }
396739362d4SMarc-André Lureau
397739362d4SMarc-André Lureau static void *
dbus_audio_init(Audiodev * dev,Error ** errp)398f6061733SPaolo Bonzini dbus_audio_init(Audiodev *dev, Error **errp)
399739362d4SMarc-André Lureau {
400739362d4SMarc-André Lureau DBusAudio *da = g_new0(DBusAudio, 1);
401739362d4SMarc-André Lureau
402739362d4SMarc-André Lureau da->out_listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
403739362d4SMarc-André Lureau g_free, g_object_unref);
404739362d4SMarc-André Lureau da->in_listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
405739362d4SMarc-André Lureau g_free, g_object_unref);
406739362d4SMarc-André Lureau return da;
407739362d4SMarc-André Lureau }
408739362d4SMarc-André Lureau
409739362d4SMarc-André Lureau static void
dbus_audio_fini(void * opaque)410739362d4SMarc-André Lureau dbus_audio_fini(void *opaque)
411739362d4SMarc-André Lureau {
412739362d4SMarc-André Lureau DBusAudio *da = opaque;
413739362d4SMarc-André Lureau
414739362d4SMarc-André Lureau if (da->server) {
415739362d4SMarc-André Lureau g_dbus_object_manager_server_unexport(da->server,
416739362d4SMarc-André Lureau DBUS_DISPLAY1_AUDIO_PATH);
417739362d4SMarc-André Lureau }
418739362d4SMarc-André Lureau g_clear_object(&da->audio);
419739362d4SMarc-André Lureau g_clear_object(&da->iface);
420739362d4SMarc-André Lureau g_clear_pointer(&da->in_listeners, g_hash_table_unref);
421739362d4SMarc-André Lureau g_clear_pointer(&da->out_listeners, g_hash_table_unref);
422739362d4SMarc-André Lureau g_clear_object(&da->server);
423739362d4SMarc-André Lureau g_free(da);
424739362d4SMarc-André Lureau }
425739362d4SMarc-André Lureau
426739362d4SMarc-André Lureau static void
listener_out_vanished_cb(GDBusConnection * connection,gboolean remote_peer_vanished,GError * error,DBusAudio * da)427739362d4SMarc-André Lureau listener_out_vanished_cb(GDBusConnection *connection,
428739362d4SMarc-André Lureau gboolean remote_peer_vanished,
429739362d4SMarc-André Lureau GError *error,
430739362d4SMarc-André Lureau DBusAudio *da)
431739362d4SMarc-André Lureau {
432739362d4SMarc-André Lureau char *name = g_object_get_data(G_OBJECT(connection), "name");
433739362d4SMarc-André Lureau
434739362d4SMarc-André Lureau g_hash_table_remove(da->out_listeners, name);
435739362d4SMarc-André Lureau }
436739362d4SMarc-André Lureau
437739362d4SMarc-André Lureau static void
listener_in_vanished_cb(GDBusConnection * connection,gboolean remote_peer_vanished,GError * error,DBusAudio * da)438739362d4SMarc-André Lureau listener_in_vanished_cb(GDBusConnection *connection,
439739362d4SMarc-André Lureau gboolean remote_peer_vanished,
440739362d4SMarc-André Lureau GError *error,
441739362d4SMarc-André Lureau DBusAudio *da)
442739362d4SMarc-André Lureau {
443739362d4SMarc-André Lureau char *name = g_object_get_data(G_OBJECT(connection), "name");
444739362d4SMarc-André Lureau
445739362d4SMarc-André Lureau g_hash_table_remove(da->in_listeners, name);
446739362d4SMarc-André Lureau }
447739362d4SMarc-André Lureau
448739362d4SMarc-André Lureau static gboolean
dbus_audio_register_listener(AudioState * s,GDBusMethodInvocation * invocation,GUnixFDList * fd_list,GVariant * arg_listener,bool out)449739362d4SMarc-André Lureau dbus_audio_register_listener(AudioState *s,
450739362d4SMarc-André Lureau GDBusMethodInvocation *invocation,
4516cc5a615SMarc-André Lureau #ifdef G_OS_UNIX
452739362d4SMarc-André Lureau GUnixFDList *fd_list,
4536cc5a615SMarc-André Lureau #endif
454739362d4SMarc-André Lureau GVariant *arg_listener,
455739362d4SMarc-André Lureau bool out)
456739362d4SMarc-André Lureau {
457739362d4SMarc-André Lureau DBusAudio *da = s->drv_opaque;
458e74fec9aSMarc-André Lureau const char *sender =
459e74fec9aSMarc-André Lureau da->p2p ? "p2p" : g_dbus_method_invocation_get_sender(invocation);
460739362d4SMarc-André Lureau g_autoptr(GDBusConnection) listener_conn = NULL;
461739362d4SMarc-André Lureau g_autoptr(GError) err = NULL;
462739362d4SMarc-André Lureau g_autoptr(GSocket) socket = NULL;
463739362d4SMarc-André Lureau g_autoptr(GSocketConnection) socket_conn = NULL;
464739362d4SMarc-André Lureau g_autofree char *guid = g_dbus_generate_guid();
465739362d4SMarc-André Lureau GHashTable *listeners = out ? da->out_listeners : da->in_listeners;
466739362d4SMarc-André Lureau GObject *listener;
467739362d4SMarc-André Lureau int fd;
468739362d4SMarc-André Lureau
469739362d4SMarc-André Lureau trace_dbus_audio_register(sender, out ? "out" : "in");
470739362d4SMarc-André Lureau
471739362d4SMarc-André Lureau if (g_hash_table_contains(listeners, sender)) {
472739362d4SMarc-André Lureau g_dbus_method_invocation_return_error(invocation,
473739362d4SMarc-André Lureau DBUS_DISPLAY_ERROR,
474739362d4SMarc-André Lureau DBUS_DISPLAY_ERROR_INVALID,
475739362d4SMarc-André Lureau "`%s` is already registered!",
476739362d4SMarc-André Lureau sender);
477739362d4SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED;
478739362d4SMarc-André Lureau }
479739362d4SMarc-André Lureau
4806cc5a615SMarc-André Lureau #ifdef G_OS_WIN32
4816cc5a615SMarc-André Lureau if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) {
4826cc5a615SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED;
4836cc5a615SMarc-André Lureau }
4846cc5a615SMarc-André Lureau #else
485739362d4SMarc-André Lureau fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err);
486739362d4SMarc-André Lureau if (err) {
487739362d4SMarc-André Lureau g_dbus_method_invocation_return_error(invocation,
488739362d4SMarc-André Lureau DBUS_DISPLAY_ERROR,
489739362d4SMarc-André Lureau DBUS_DISPLAY_ERROR_FAILED,
490739362d4SMarc-André Lureau "Couldn't get peer fd: %s",
491739362d4SMarc-André Lureau err->message);
492739362d4SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED;
493739362d4SMarc-André Lureau }
4946cc5a615SMarc-André Lureau #endif
495739362d4SMarc-André Lureau
496739362d4SMarc-André Lureau socket = g_socket_new_from_fd(fd, &err);
497739362d4SMarc-André Lureau if (err) {
498739362d4SMarc-André Lureau g_dbus_method_invocation_return_error(invocation,
499739362d4SMarc-André Lureau DBUS_DISPLAY_ERROR,
500739362d4SMarc-André Lureau DBUS_DISPLAY_ERROR_FAILED,
501739362d4SMarc-André Lureau "Couldn't make a socket: %s",
502739362d4SMarc-André Lureau err->message);
5036cc5a615SMarc-André Lureau #ifdef G_OS_WIN32
5046cc5a615SMarc-André Lureau closesocket(fd);
5056cc5a615SMarc-André Lureau #else
5066cc5a615SMarc-André Lureau close(fd);
5076cc5a615SMarc-André Lureau #endif
508739362d4SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED;
509739362d4SMarc-André Lureau }
510739362d4SMarc-André Lureau socket_conn = g_socket_connection_factory_create_connection(socket);
511739362d4SMarc-André Lureau if (out) {
512739362d4SMarc-André Lureau qemu_dbus_display1_audio_complete_register_out_listener(
5136cc5a615SMarc-André Lureau da->iface, invocation
5146cc5a615SMarc-André Lureau #ifdef G_OS_UNIX
5156cc5a615SMarc-André Lureau , NULL
5166cc5a615SMarc-André Lureau #endif
5176cc5a615SMarc-André Lureau );
518739362d4SMarc-André Lureau } else {
519739362d4SMarc-André Lureau qemu_dbus_display1_audio_complete_register_in_listener(
5206cc5a615SMarc-André Lureau da->iface, invocation
5216cc5a615SMarc-André Lureau #ifdef G_OS_UNIX
5226cc5a615SMarc-André Lureau , NULL
5236cc5a615SMarc-André Lureau #endif
5246cc5a615SMarc-André Lureau );
525739362d4SMarc-André Lureau }
526739362d4SMarc-André Lureau
527739362d4SMarc-André Lureau listener_conn =
528739362d4SMarc-André Lureau g_dbus_connection_new_sync(
529739362d4SMarc-André Lureau G_IO_STREAM(socket_conn),
530739362d4SMarc-André Lureau guid,
531739362d4SMarc-André Lureau G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER,
532739362d4SMarc-André Lureau NULL, NULL, &err);
533739362d4SMarc-André Lureau if (err) {
534739362d4SMarc-André Lureau error_report("Failed to setup peer connection: %s", err->message);
535739362d4SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED;
536739362d4SMarc-André Lureau }
537739362d4SMarc-André Lureau
538739362d4SMarc-André Lureau listener = out ?
539739362d4SMarc-André Lureau G_OBJECT(qemu_dbus_display1_audio_out_listener_proxy_new_sync(
540739362d4SMarc-André Lureau listener_conn,
541739362d4SMarc-André Lureau G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
542739362d4SMarc-André Lureau NULL,
543739362d4SMarc-André Lureau "/org/qemu/Display1/AudioOutListener",
544739362d4SMarc-André Lureau NULL,
545739362d4SMarc-André Lureau &err)) :
546739362d4SMarc-André Lureau G_OBJECT(qemu_dbus_display1_audio_in_listener_proxy_new_sync(
547739362d4SMarc-André Lureau listener_conn,
548739362d4SMarc-André Lureau G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
549739362d4SMarc-André Lureau NULL,
550739362d4SMarc-André Lureau "/org/qemu/Display1/AudioInListener",
551739362d4SMarc-André Lureau NULL,
552739362d4SMarc-André Lureau &err));
553739362d4SMarc-André Lureau if (!listener) {
554739362d4SMarc-André Lureau error_report("Failed to setup proxy: %s", err->message);
555739362d4SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED;
556739362d4SMarc-André Lureau }
557739362d4SMarc-André Lureau
558739362d4SMarc-André Lureau if (out) {
559739362d4SMarc-André Lureau HWVoiceOut *hw;
560739362d4SMarc-André Lureau
561739362d4SMarc-André Lureau QLIST_FOREACH(hw, &s->hw_head_out, entries) {
562739362d4SMarc-André Lureau DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
563739362d4SMarc-André Lureau QemuDBusDisplay1AudioOutListener *l =
564739362d4SMarc-André Lureau QEMU_DBUS_DISPLAY1_AUDIO_OUT_LISTENER(listener);
565739362d4SMarc-André Lureau
566739362d4SMarc-André Lureau dbus_init_out_listener(l, hw);
567739362d4SMarc-André Lureau qemu_dbus_display1_audio_out_listener_call_set_enabled(
568739362d4SMarc-André Lureau l, (uintptr_t)hw, vo->enabled,
569739362d4SMarc-André Lureau G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
570739362d4SMarc-André Lureau }
571739362d4SMarc-André Lureau } else {
572739362d4SMarc-André Lureau HWVoiceIn *hw;
573739362d4SMarc-André Lureau
574739362d4SMarc-André Lureau QLIST_FOREACH(hw, &s->hw_head_in, entries) {
575739362d4SMarc-André Lureau DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
576739362d4SMarc-André Lureau QemuDBusDisplay1AudioInListener *l =
577739362d4SMarc-André Lureau QEMU_DBUS_DISPLAY1_AUDIO_IN_LISTENER(listener);
578739362d4SMarc-André Lureau
579739362d4SMarc-André Lureau dbus_init_in_listener(
580739362d4SMarc-André Lureau QEMU_DBUS_DISPLAY1_AUDIO_IN_LISTENER(listener), hw);
581739362d4SMarc-André Lureau qemu_dbus_display1_audio_in_listener_call_set_enabled(
582739362d4SMarc-André Lureau l, (uintptr_t)hw, vo->enabled,
583739362d4SMarc-André Lureau G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
584739362d4SMarc-André Lureau }
585739362d4SMarc-André Lureau }
586739362d4SMarc-André Lureau
587739362d4SMarc-André Lureau g_object_set_data_full(G_OBJECT(listener_conn), "name",
588739362d4SMarc-André Lureau g_strdup(sender), g_free);
589739362d4SMarc-André Lureau g_hash_table_insert(listeners, g_strdup(sender), listener);
590739362d4SMarc-André Lureau g_object_connect(listener_conn,
591739362d4SMarc-André Lureau "signal::closed",
592739362d4SMarc-André Lureau out ? listener_out_vanished_cb : listener_in_vanished_cb,
593739362d4SMarc-André Lureau da,
594739362d4SMarc-André Lureau NULL);
595739362d4SMarc-André Lureau
596739362d4SMarc-André Lureau return DBUS_METHOD_INVOCATION_HANDLED;
597739362d4SMarc-André Lureau }
598739362d4SMarc-André Lureau
599739362d4SMarc-André Lureau static gboolean
dbus_audio_register_out_listener(AudioState * s,GDBusMethodInvocation * invocation,GUnixFDList * fd_list,GVariant * arg_listener)600739362d4SMarc-André Lureau dbus_audio_register_out_listener(AudioState *s,
601739362d4SMarc-André Lureau GDBusMethodInvocation *invocation,
6026cc5a615SMarc-André Lureau #ifdef G_OS_UNIX
603739362d4SMarc-André Lureau GUnixFDList *fd_list,
6046cc5a615SMarc-André Lureau #endif
605739362d4SMarc-André Lureau GVariant *arg_listener)
606739362d4SMarc-André Lureau {
607739362d4SMarc-André Lureau return dbus_audio_register_listener(s, invocation,
6086cc5a615SMarc-André Lureau #ifdef G_OS_UNIX
6096cc5a615SMarc-André Lureau fd_list,
6106cc5a615SMarc-André Lureau #endif
6116cc5a615SMarc-André Lureau arg_listener, true);
612739362d4SMarc-André Lureau
613739362d4SMarc-André Lureau }
614739362d4SMarc-André Lureau
615739362d4SMarc-André Lureau static gboolean
dbus_audio_register_in_listener(AudioState * s,GDBusMethodInvocation * invocation,GUnixFDList * fd_list,GVariant * arg_listener)616739362d4SMarc-André Lureau dbus_audio_register_in_listener(AudioState *s,
617739362d4SMarc-André Lureau GDBusMethodInvocation *invocation,
6186cc5a615SMarc-André Lureau #ifdef G_OS_UNIX
619739362d4SMarc-André Lureau GUnixFDList *fd_list,
6206cc5a615SMarc-André Lureau #endif
621739362d4SMarc-André Lureau GVariant *arg_listener)
622739362d4SMarc-André Lureau {
623739362d4SMarc-André Lureau return dbus_audio_register_listener(s, invocation,
6246cc5a615SMarc-André Lureau #ifdef G_OS_UNIX
6256cc5a615SMarc-André Lureau fd_list,
62629c5c7e5SMarc-André Lureau #endif
6276cc5a615SMarc-André Lureau arg_listener, false);
6286cc5a615SMarc-André Lureau }
629739362d4SMarc-André Lureau
630739362d4SMarc-André Lureau static void
dbus_audio_set_server(AudioState * s,GDBusObjectManagerServer * server,bool p2p)631e74fec9aSMarc-André Lureau dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server, bool p2p)
632739362d4SMarc-André Lureau {
633739362d4SMarc-André Lureau DBusAudio *da = s->drv_opaque;
634739362d4SMarc-André Lureau
635739362d4SMarc-André Lureau g_assert(da);
636739362d4SMarc-André Lureau g_assert(!da->server);
637739362d4SMarc-André Lureau
638739362d4SMarc-André Lureau da->server = g_object_ref(server);
639e74fec9aSMarc-André Lureau da->p2p = p2p;
640739362d4SMarc-André Lureau
641739362d4SMarc-André Lureau da->audio = g_dbus_object_skeleton_new(DBUS_DISPLAY1_AUDIO_PATH);
642739362d4SMarc-André Lureau da->iface = qemu_dbus_display1_audio_skeleton_new();
643739362d4SMarc-André Lureau g_object_connect(da->iface,
644739362d4SMarc-André Lureau "swapped-signal::handle-register-in-listener",
645739362d4SMarc-André Lureau dbus_audio_register_in_listener, s,
646739362d4SMarc-André Lureau "swapped-signal::handle-register-out-listener",
647739362d4SMarc-André Lureau dbus_audio_register_out_listener, s,
648739362d4SMarc-André Lureau NULL);
649739362d4SMarc-André Lureau
650739362d4SMarc-André Lureau g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(da->audio),
651739362d4SMarc-André Lureau G_DBUS_INTERFACE_SKELETON(da->iface));
652739362d4SMarc-André Lureau g_dbus_object_manager_server_export(da->server, da->audio);
653739362d4SMarc-André Lureau }
654739362d4SMarc-André Lureau
655739362d4SMarc-André Lureau static struct audio_pcm_ops dbus_pcm_ops = {
656739362d4SMarc-André Lureau .init_out = dbus_init_out,
657739362d4SMarc-André Lureau .fini_out = dbus_fini_out,
658739362d4SMarc-André Lureau .write = audio_generic_write,
659739362d4SMarc-André Lureau .get_buffer_out = dbus_get_buffer_out,
660739362d4SMarc-André Lureau .put_buffer_out = dbus_put_buffer_out,
661739362d4SMarc-André Lureau .enable_out = dbus_enable_out,
662739362d4SMarc-André Lureau .volume_out = dbus_volume_out,
663739362d4SMarc-André Lureau
664739362d4SMarc-André Lureau .init_in = dbus_init_in,
665739362d4SMarc-André Lureau .fini_in = dbus_fini_in,
666739362d4SMarc-André Lureau .read = dbus_read,
667739362d4SMarc-André Lureau .run_buffer_in = audio_generic_run_buffer_in,
668739362d4SMarc-André Lureau .enable_in = dbus_enable_in,
669739362d4SMarc-André Lureau .volume_in = dbus_volume_in,
670739362d4SMarc-André Lureau };
671739362d4SMarc-André Lureau
672739362d4SMarc-André Lureau static struct audio_driver dbus_audio_driver = {
673739362d4SMarc-André Lureau .name = "dbus",
674739362d4SMarc-André Lureau .descr = "Timer based audio exposed with DBus interface",
675739362d4SMarc-André Lureau .init = dbus_audio_init,
676739362d4SMarc-André Lureau .fini = dbus_audio_fini,
677739362d4SMarc-André Lureau .set_dbus_server = dbus_audio_set_server,
678739362d4SMarc-André Lureau .pcm_ops = &dbus_pcm_ops,
679739362d4SMarc-André Lureau .max_voices_out = INT_MAX,
680739362d4SMarc-André Lureau .max_voices_in = INT_MAX,
681739362d4SMarc-André Lureau .voice_size_out = sizeof(DBusVoiceOut),
682739362d4SMarc-André Lureau .voice_size_in = sizeof(DBusVoiceIn)
683739362d4SMarc-André Lureau };
684739362d4SMarc-André Lureau
register_audio_dbus(void)685739362d4SMarc-André Lureau static void register_audio_dbus(void)
686739362d4SMarc-André Lureau {
687739362d4SMarc-André Lureau audio_driver_register(&dbus_audio_driver);
688739362d4SMarc-André Lureau }
689739362d4SMarc-André Lureau type_init(register_audio_dbus);
690739362d4SMarc-André Lureau
691739362d4SMarc-André Lureau module_dep("ui-dbus")
692