hw/core: expand on the alignment of CPUState
[qemu/armbru.git] / audio / dbusaudio.c
blob60fcf643ecf85af8b0818dfe51c01694dd708b6d
1 /*
2 * QEMU DBus audio
4 * Copyright (c) 2021 Red Hat, Inc.
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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.
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"
32 #ifdef G_OS_UNIX
33 #include <gio/gunixfdlist.h>
34 #endif
36 #include "ui/dbus.h"
37 #include "ui/dbus-display1.h"
39 #define AUDIO_CAP "dbus"
40 #include "audio.h"
41 #include "audio_int.h"
42 #include "trace.h"
44 #define DBUS_DISPLAY1_AUDIO_PATH DBUS_DISPLAY1_ROOT "/Audio"
46 #define DBUS_AUDIO_NSAMPLES 1024 /* could be configured? */
48 typedef struct DBusAudio {
49 GDBusObjectManagerServer *server;
50 bool p2p;
51 GDBusObjectSkeleton *audio;
52 QemuDBusDisplay1Audio *iface;
53 GHashTable *out_listeners;
54 GHashTable *in_listeners;
55 } DBusAudio;
57 typedef struct DBusVoiceOut {
58 HWVoiceOut hw;
59 bool enabled;
60 RateCtl rate;
62 void *buf;
63 size_t buf_pos;
64 size_t buf_size;
66 bool has_volume;
67 Volume volume;
68 } DBusVoiceOut;
70 typedef struct DBusVoiceIn {
71 HWVoiceIn hw;
72 bool enabled;
73 RateCtl rate;
75 bool has_volume;
76 Volume volume;
77 } DBusVoiceIn;
79 static void *dbus_get_buffer_out(HWVoiceOut *hw, size_t *size)
81 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
83 if (!vo->buf) {
84 vo->buf_size = hw->samples * hw->info.bytes_per_frame;
85 vo->buf = g_malloc(vo->buf_size);
86 vo->buf_pos = 0;
89 *size = MIN(vo->buf_size - vo->buf_pos, *size);
90 *size = audio_rate_get_bytes(&vo->rate, &hw->info, *size);
92 return vo->buf + vo->buf_pos;
96 static size_t dbus_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
98 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
99 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
100 GHashTableIter iter;
101 QemuDBusDisplay1AudioOutListener *listener = NULL;
102 g_autoptr(GBytes) bytes = NULL;
103 g_autoptr(GVariant) v_data = NULL;
105 assert(buf == vo->buf + vo->buf_pos && vo->buf_pos + size <= vo->buf_size);
106 vo->buf_pos += size;
108 trace_dbus_audio_put_buffer_out(size);
110 if (vo->buf_pos < vo->buf_size) {
111 return size;
114 bytes = g_bytes_new_take(g_steal_pointer(&vo->buf), vo->buf_size);
115 v_data = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
116 g_variant_ref_sink(v_data);
118 g_hash_table_iter_init(&iter, da->out_listeners);
119 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
120 qemu_dbus_display1_audio_out_listener_call_write(
121 listener,
122 (uintptr_t)hw,
123 v_data,
124 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
127 return size;
130 #if HOST_BIG_ENDIAN
131 #define AUDIO_HOST_BE TRUE
132 #else
133 #define AUDIO_HOST_BE FALSE
134 #endif
136 static void
137 dbus_init_out_listener(QemuDBusDisplay1AudioOutListener *listener,
138 HWVoiceOut *hw)
140 qemu_dbus_display1_audio_out_listener_call_init(
141 listener,
142 (uintptr_t)hw,
143 hw->info.bits,
144 hw->info.is_signed,
145 hw->info.is_float,
146 hw->info.freq,
147 hw->info.nchannels,
148 hw->info.bytes_per_frame,
149 hw->info.bytes_per_second,
150 hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE,
151 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
154 static int
155 dbus_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
157 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
158 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
159 GHashTableIter iter;
160 QemuDBusDisplay1AudioOutListener *listener = NULL;
162 audio_pcm_init_info(&hw->info, as);
163 hw->samples = DBUS_AUDIO_NSAMPLES;
164 audio_rate_start(&vo->rate);
166 g_hash_table_iter_init(&iter, da->out_listeners);
167 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
168 dbus_init_out_listener(listener, hw);
170 return 0;
173 static void
174 dbus_fini_out(HWVoiceOut *hw)
176 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
177 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
178 GHashTableIter iter;
179 QemuDBusDisplay1AudioOutListener *listener = NULL;
181 g_hash_table_iter_init(&iter, da->out_listeners);
182 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
183 qemu_dbus_display1_audio_out_listener_call_fini(
184 listener,
185 (uintptr_t)hw,
186 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
189 g_clear_pointer(&vo->buf, g_free);
192 static void
193 dbus_enable_out(HWVoiceOut *hw, bool enable)
195 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
196 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
197 GHashTableIter iter;
198 QemuDBusDisplay1AudioOutListener *listener = NULL;
200 vo->enabled = enable;
201 if (enable) {
202 audio_rate_start(&vo->rate);
205 g_hash_table_iter_init(&iter, da->out_listeners);
206 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
207 qemu_dbus_display1_audio_out_listener_call_set_enabled(
208 listener, (uintptr_t)hw, enable,
209 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
213 static void
214 dbus_volume_out_listener(HWVoiceOut *hw,
215 QemuDBusDisplay1AudioOutListener *listener)
217 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
218 Volume *vol = &vo->volume;
219 g_autoptr(GBytes) bytes = NULL;
220 GVariant *v_vol = NULL;
222 if (!vo->has_volume) {
223 return;
226 assert(vol->channels < sizeof(vol->vol));
227 bytes = g_bytes_new(vol->vol, vol->channels);
228 v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
229 qemu_dbus_display1_audio_out_listener_call_set_volume(
230 listener, (uintptr_t)hw, vol->mute, v_vol,
231 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
234 static void
235 dbus_volume_out(HWVoiceOut *hw, Volume *vol)
237 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
238 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
239 GHashTableIter iter;
240 QemuDBusDisplay1AudioOutListener *listener = NULL;
242 vo->has_volume = true;
243 vo->volume = *vol;
245 g_hash_table_iter_init(&iter, da->out_listeners);
246 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
247 dbus_volume_out_listener(hw, listener);
251 static void
252 dbus_init_in_listener(QemuDBusDisplay1AudioInListener *listener, HWVoiceIn *hw)
254 qemu_dbus_display1_audio_in_listener_call_init(
255 listener,
256 (uintptr_t)hw,
257 hw->info.bits,
258 hw->info.is_signed,
259 hw->info.is_float,
260 hw->info.freq,
261 hw->info.nchannels,
262 hw->info.bytes_per_frame,
263 hw->info.bytes_per_second,
264 hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE,
265 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
268 static int
269 dbus_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
271 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
272 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
273 GHashTableIter iter;
274 QemuDBusDisplay1AudioInListener *listener = NULL;
276 audio_pcm_init_info(&hw->info, as);
277 hw->samples = DBUS_AUDIO_NSAMPLES;
278 audio_rate_start(&vo->rate);
280 g_hash_table_iter_init(&iter, da->in_listeners);
281 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
282 dbus_init_in_listener(listener, hw);
284 return 0;
287 static void
288 dbus_fini_in(HWVoiceIn *hw)
290 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
291 GHashTableIter iter;
292 QemuDBusDisplay1AudioInListener *listener = NULL;
294 g_hash_table_iter_init(&iter, da->in_listeners);
295 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
296 qemu_dbus_display1_audio_in_listener_call_fini(
297 listener,
298 (uintptr_t)hw,
299 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
303 static void
304 dbus_volume_in_listener(HWVoiceIn *hw,
305 QemuDBusDisplay1AudioInListener *listener)
307 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
308 Volume *vol = &vo->volume;
309 g_autoptr(GBytes) bytes = NULL;
310 GVariant *v_vol = NULL;
312 if (!vo->has_volume) {
313 return;
316 assert(vol->channels < sizeof(vol->vol));
317 bytes = g_bytes_new(vol->vol, vol->channels);
318 v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
319 qemu_dbus_display1_audio_in_listener_call_set_volume(
320 listener, (uintptr_t)hw, vol->mute, v_vol,
321 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
324 static void
325 dbus_volume_in(HWVoiceIn *hw, Volume *vol)
327 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
328 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
329 GHashTableIter iter;
330 QemuDBusDisplay1AudioInListener *listener = NULL;
332 vo->has_volume = true;
333 vo->volume = *vol;
335 g_hash_table_iter_init(&iter, da->in_listeners);
336 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
337 dbus_volume_in_listener(hw, listener);
341 static size_t
342 dbus_read(HWVoiceIn *hw, void *buf, size_t size)
344 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
345 /* DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw); */
346 GHashTableIter iter;
347 QemuDBusDisplay1AudioInListener *listener = NULL;
349 trace_dbus_audio_read(size);
351 /* size = audio_rate_get_bytes(&vo->rate, &hw->info, size); */
353 g_hash_table_iter_init(&iter, da->in_listeners);
354 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
355 g_autoptr(GVariant) v_data = NULL;
356 const char *data;
357 gsize n = 0;
359 if (qemu_dbus_display1_audio_in_listener_call_read_sync(
360 listener,
361 (uintptr_t)hw,
362 size,
363 G_DBUS_CALL_FLAGS_NONE, -1,
364 &v_data, NULL, NULL)) {
365 data = g_variant_get_fixed_array(v_data, &n, 1);
366 g_warn_if_fail(n <= size);
367 size = MIN(n, size);
368 memcpy(buf, data, size);
369 break;
373 return size;
376 static void
377 dbus_enable_in(HWVoiceIn *hw, bool enable)
379 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
380 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
381 GHashTableIter iter;
382 QemuDBusDisplay1AudioInListener *listener = NULL;
384 vo->enabled = enable;
385 if (enable) {
386 audio_rate_start(&vo->rate);
389 g_hash_table_iter_init(&iter, da->in_listeners);
390 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
391 qemu_dbus_display1_audio_in_listener_call_set_enabled(
392 listener, (uintptr_t)hw, enable,
393 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
397 static void *
398 dbus_audio_init(Audiodev *dev, Error **errp)
400 DBusAudio *da = g_new0(DBusAudio, 1);
402 da->out_listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
403 g_free, g_object_unref);
404 da->in_listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
405 g_free, g_object_unref);
406 return da;
409 static void
410 dbus_audio_fini(void *opaque)
412 DBusAudio *da = opaque;
414 if (da->server) {
415 g_dbus_object_manager_server_unexport(da->server,
416 DBUS_DISPLAY1_AUDIO_PATH);
418 g_clear_object(&da->audio);
419 g_clear_object(&da->iface);
420 g_clear_pointer(&da->in_listeners, g_hash_table_unref);
421 g_clear_pointer(&da->out_listeners, g_hash_table_unref);
422 g_clear_object(&da->server);
423 g_free(da);
426 static void
427 listener_out_vanished_cb(GDBusConnection *connection,
428 gboolean remote_peer_vanished,
429 GError *error,
430 DBusAudio *da)
432 char *name = g_object_get_data(G_OBJECT(connection), "name");
434 g_hash_table_remove(da->out_listeners, name);
437 static void
438 listener_in_vanished_cb(GDBusConnection *connection,
439 gboolean remote_peer_vanished,
440 GError *error,
441 DBusAudio *da)
443 char *name = g_object_get_data(G_OBJECT(connection), "name");
445 g_hash_table_remove(da->in_listeners, name);
448 static gboolean
449 dbus_audio_register_listener(AudioState *s,
450 GDBusMethodInvocation *invocation,
451 #ifdef G_OS_UNIX
452 GUnixFDList *fd_list,
453 #endif
454 GVariant *arg_listener,
455 bool out)
457 DBusAudio *da = s->drv_opaque;
458 const char *sender =
459 da->p2p ? "p2p" : g_dbus_method_invocation_get_sender(invocation);
460 g_autoptr(GDBusConnection) listener_conn = NULL;
461 g_autoptr(GError) err = NULL;
462 g_autoptr(GSocket) socket = NULL;
463 g_autoptr(GSocketConnection) socket_conn = NULL;
464 g_autofree char *guid = g_dbus_generate_guid();
465 GHashTable *listeners = out ? da->out_listeners : da->in_listeners;
466 GObject *listener;
467 int fd;
469 trace_dbus_audio_register(sender, out ? "out" : "in");
471 if (g_hash_table_contains(listeners, sender)) {
472 g_dbus_method_invocation_return_error(invocation,
473 DBUS_DISPLAY_ERROR,
474 DBUS_DISPLAY_ERROR_INVALID,
475 "`%s` is already registered!",
476 sender);
477 return DBUS_METHOD_INVOCATION_HANDLED;
480 #ifdef G_OS_WIN32
481 if (!dbus_win32_import_socket(invocation, arg_listener, &fd)) {
482 return DBUS_METHOD_INVOCATION_HANDLED;
484 #else
485 fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err);
486 if (err) {
487 g_dbus_method_invocation_return_error(invocation,
488 DBUS_DISPLAY_ERROR,
489 DBUS_DISPLAY_ERROR_FAILED,
490 "Couldn't get peer fd: %s",
491 err->message);
492 return DBUS_METHOD_INVOCATION_HANDLED;
494 #endif
496 socket = g_socket_new_from_fd(fd, &err);
497 if (err) {
498 g_dbus_method_invocation_return_error(invocation,
499 DBUS_DISPLAY_ERROR,
500 DBUS_DISPLAY_ERROR_FAILED,
501 "Couldn't make a socket: %s",
502 err->message);
503 #ifdef G_OS_WIN32
504 closesocket(fd);
505 #else
506 close(fd);
507 #endif
508 return DBUS_METHOD_INVOCATION_HANDLED;
510 socket_conn = g_socket_connection_factory_create_connection(socket);
511 if (out) {
512 qemu_dbus_display1_audio_complete_register_out_listener(
513 da->iface, invocation
514 #ifdef G_OS_UNIX
515 , NULL
516 #endif
518 } else {
519 qemu_dbus_display1_audio_complete_register_in_listener(
520 da->iface, invocation
521 #ifdef G_OS_UNIX
522 , NULL
523 #endif
527 listener_conn =
528 g_dbus_connection_new_sync(
529 G_IO_STREAM(socket_conn),
530 guid,
531 G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER,
532 NULL, NULL, &err);
533 if (err) {
534 error_report("Failed to setup peer connection: %s", err->message);
535 return DBUS_METHOD_INVOCATION_HANDLED;
538 listener = out ?
539 G_OBJECT(qemu_dbus_display1_audio_out_listener_proxy_new_sync(
540 listener_conn,
541 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
542 NULL,
543 "/org/qemu/Display1/AudioOutListener",
544 NULL,
545 &err)) :
546 G_OBJECT(qemu_dbus_display1_audio_in_listener_proxy_new_sync(
547 listener_conn,
548 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
549 NULL,
550 "/org/qemu/Display1/AudioInListener",
551 NULL,
552 &err));
553 if (!listener) {
554 error_report("Failed to setup proxy: %s", err->message);
555 return DBUS_METHOD_INVOCATION_HANDLED;
558 if (out) {
559 HWVoiceOut *hw;
561 QLIST_FOREACH(hw, &s->hw_head_out, entries) {
562 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
563 QemuDBusDisplay1AudioOutListener *l =
564 QEMU_DBUS_DISPLAY1_AUDIO_OUT_LISTENER(listener);
566 dbus_init_out_listener(l, hw);
567 qemu_dbus_display1_audio_out_listener_call_set_enabled(
568 l, (uintptr_t)hw, vo->enabled,
569 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
571 } else {
572 HWVoiceIn *hw;
574 QLIST_FOREACH(hw, &s->hw_head_in, entries) {
575 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
576 QemuDBusDisplay1AudioInListener *l =
577 QEMU_DBUS_DISPLAY1_AUDIO_IN_LISTENER(listener);
579 dbus_init_in_listener(
580 QEMU_DBUS_DISPLAY1_AUDIO_IN_LISTENER(listener), hw);
581 qemu_dbus_display1_audio_in_listener_call_set_enabled(
582 l, (uintptr_t)hw, vo->enabled,
583 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
587 g_object_set_data_full(G_OBJECT(listener_conn), "name",
588 g_strdup(sender), g_free);
589 g_hash_table_insert(listeners, g_strdup(sender), listener);
590 g_object_connect(listener_conn,
591 "signal::closed",
592 out ? listener_out_vanished_cb : listener_in_vanished_cb,
594 NULL);
596 return DBUS_METHOD_INVOCATION_HANDLED;
599 static gboolean
600 dbus_audio_register_out_listener(AudioState *s,
601 GDBusMethodInvocation *invocation,
602 #ifdef G_OS_UNIX
603 GUnixFDList *fd_list,
604 #endif
605 GVariant *arg_listener)
607 return dbus_audio_register_listener(s, invocation,
608 #ifdef G_OS_UNIX
609 fd_list,
610 #endif
611 arg_listener, true);
615 static gboolean
616 dbus_audio_register_in_listener(AudioState *s,
617 GDBusMethodInvocation *invocation,
618 #ifdef G_OS_UNIX
619 GUnixFDList *fd_list,
620 #endif
621 GVariant *arg_listener)
623 return dbus_audio_register_listener(s, invocation,
624 #ifdef G_OS_UNIX
625 fd_list,
626 #endif
627 arg_listener, false);
630 static void
631 dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server, bool p2p)
633 DBusAudio *da = s->drv_opaque;
635 g_assert(da);
636 g_assert(!da->server);
638 da->server = g_object_ref(server);
639 da->p2p = p2p;
641 da->audio = g_dbus_object_skeleton_new(DBUS_DISPLAY1_AUDIO_PATH);
642 da->iface = qemu_dbus_display1_audio_skeleton_new();
643 g_object_connect(da->iface,
644 "swapped-signal::handle-register-in-listener",
645 dbus_audio_register_in_listener, s,
646 "swapped-signal::handle-register-out-listener",
647 dbus_audio_register_out_listener, s,
648 NULL);
650 g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(da->audio),
651 G_DBUS_INTERFACE_SKELETON(da->iface));
652 g_dbus_object_manager_server_export(da->server, da->audio);
655 static struct audio_pcm_ops dbus_pcm_ops = {
656 .init_out = dbus_init_out,
657 .fini_out = dbus_fini_out,
658 .write = audio_generic_write,
659 .get_buffer_out = dbus_get_buffer_out,
660 .put_buffer_out = dbus_put_buffer_out,
661 .enable_out = dbus_enable_out,
662 .volume_out = dbus_volume_out,
664 .init_in = dbus_init_in,
665 .fini_in = dbus_fini_in,
666 .read = dbus_read,
667 .run_buffer_in = audio_generic_run_buffer_in,
668 .enable_in = dbus_enable_in,
669 .volume_in = dbus_volume_in,
672 static struct audio_driver dbus_audio_driver = {
673 .name = "dbus",
674 .descr = "Timer based audio exposed with DBus interface",
675 .init = dbus_audio_init,
676 .fini = dbus_audio_fini,
677 .set_dbus_server = dbus_audio_set_server,
678 .pcm_ops = &dbus_pcm_ops,
679 .max_voices_out = INT_MAX,
680 .max_voices_in = INT_MAX,
681 .voice_size_out = sizeof(DBusVoiceOut),
682 .voice_size_in = sizeof(DBusVoiceIn)
685 static void register_audio_dbus(void)
687 audio_driver_register(&dbus_audio_driver);
689 type_init(register_audio_dbus);
691 module_dep("ui-dbus")