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