xref: /openbmc/qemu/audio/dbusaudio.c (revision 3cce8bd4d737f2ca688bbdcb92cd5cc683245bbd)
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