Revert "virtio-pci: add support for configure interrupt"
[qemu.git] / audio / dbusaudio.c
blobf178b47deec11777796a6ba35573d8ba533f7fc0
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 #include <gio/gunixfdlist.h>
33 #include "ui/dbus-display1.h"
35 #define AUDIO_CAP "dbus"
36 #include "audio.h"
37 #include "audio_int.h"
38 #include "trace.h"
40 #define DBUS_DISPLAY1_AUDIO_PATH DBUS_DISPLAY1_ROOT "/Audio"
42 #define DBUS_AUDIO_NSAMPLES 1024 /* could be configured? */
44 typedef struct DBusAudio {
45 GDBusObjectManagerServer *server;
46 GDBusObjectSkeleton *audio;
47 QemuDBusDisplay1Audio *iface;
48 GHashTable *out_listeners;
49 GHashTable *in_listeners;
50 } DBusAudio;
52 typedef struct DBusVoiceOut {
53 HWVoiceOut hw;
54 bool enabled;
55 RateCtl rate;
57 void *buf;
58 size_t buf_pos;
59 size_t buf_size;
61 bool has_volume;
62 Volume volume;
63 } DBusVoiceOut;
65 typedef struct DBusVoiceIn {
66 HWVoiceIn hw;
67 bool enabled;
68 RateCtl rate;
70 bool has_volume;
71 Volume volume;
72 } DBusVoiceIn;
74 static void *dbus_get_buffer_out(HWVoiceOut *hw, size_t *size)
76 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
78 if (!vo->buf) {
79 vo->buf_size = hw->samples * hw->info.bytes_per_frame;
80 vo->buf = g_malloc(vo->buf_size);
81 vo->buf_pos = 0;
84 *size = MIN(vo->buf_size - vo->buf_pos, *size);
85 *size = audio_rate_get_bytes(&hw->info, &vo->rate, *size);
87 return vo->buf + vo->buf_pos;
91 static size_t dbus_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
93 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
94 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
95 GHashTableIter iter;
96 QemuDBusDisplay1AudioOutListener *listener = NULL;
97 g_autoptr(GBytes) bytes = NULL;
98 g_autoptr(GVariant) v_data = NULL;
100 assert(buf == vo->buf + vo->buf_pos && vo->buf_pos + size <= vo->buf_size);
101 vo->buf_pos += size;
103 trace_dbus_audio_put_buffer_out(size);
105 if (vo->buf_pos < vo->buf_size) {
106 return size;
109 bytes = g_bytes_new_take(g_steal_pointer(&vo->buf), vo->buf_size);
110 v_data = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
111 g_variant_ref_sink(v_data);
113 g_hash_table_iter_init(&iter, da->out_listeners);
114 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
115 qemu_dbus_display1_audio_out_listener_call_write(
116 listener,
117 (uintptr_t)hw,
118 v_data,
119 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
122 return size;
125 #ifdef HOST_WORDS_BIGENDIAN
126 #define AUDIO_HOST_BE TRUE
127 #else
128 #define AUDIO_HOST_BE FALSE
129 #endif
131 static void
132 dbus_init_out_listener(QemuDBusDisplay1AudioOutListener *listener,
133 HWVoiceOut *hw)
135 qemu_dbus_display1_audio_out_listener_call_init(
136 listener,
137 (uintptr_t)hw,
138 hw->info.bits,
139 hw->info.is_signed,
140 hw->info.is_float,
141 hw->info.freq,
142 hw->info.nchannels,
143 hw->info.bytes_per_frame,
144 hw->info.bytes_per_second,
145 hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE,
146 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
149 static int
150 dbus_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
152 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
153 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
154 GHashTableIter iter;
155 QemuDBusDisplay1AudioOutListener *listener = NULL;
157 audio_pcm_init_info(&hw->info, as);
158 hw->samples = DBUS_AUDIO_NSAMPLES;
159 audio_rate_start(&vo->rate);
161 g_hash_table_iter_init(&iter, da->out_listeners);
162 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
163 dbus_init_out_listener(listener, hw);
165 return 0;
168 static void
169 dbus_fini_out(HWVoiceOut *hw)
171 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
172 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
173 GHashTableIter iter;
174 QemuDBusDisplay1AudioOutListener *listener = NULL;
176 g_hash_table_iter_init(&iter, da->out_listeners);
177 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
178 qemu_dbus_display1_audio_out_listener_call_fini(
179 listener,
180 (uintptr_t)hw,
181 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
184 g_clear_pointer(&vo->buf, g_free);
187 static void
188 dbus_enable_out(HWVoiceOut *hw, bool enable)
190 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
191 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
192 GHashTableIter iter;
193 QemuDBusDisplay1AudioOutListener *listener = NULL;
195 vo->enabled = enable;
196 if (enable) {
197 audio_rate_start(&vo->rate);
200 g_hash_table_iter_init(&iter, da->out_listeners);
201 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
202 qemu_dbus_display1_audio_out_listener_call_set_enabled(
203 listener, (uintptr_t)hw, enable,
204 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
208 static void
209 dbus_volume_out_listener(HWVoiceOut *hw,
210 QemuDBusDisplay1AudioOutListener *listener)
212 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
213 Volume *vol = &vo->volume;
214 g_autoptr(GBytes) bytes = NULL;
215 GVariant *v_vol = NULL;
217 if (!vo->has_volume) {
218 return;
221 assert(vol->channels < sizeof(vol->vol));
222 bytes = g_bytes_new(vol->vol, vol->channels);
223 v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
224 qemu_dbus_display1_audio_out_listener_call_set_volume(
225 listener, (uintptr_t)hw, vol->mute, v_vol,
226 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
229 static void
230 dbus_volume_out(HWVoiceOut *hw, Volume *vol)
232 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
233 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
234 GHashTableIter iter;
235 QemuDBusDisplay1AudioOutListener *listener = NULL;
237 vo->has_volume = true;
238 vo->volume = *vol;
240 g_hash_table_iter_init(&iter, da->out_listeners);
241 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
242 dbus_volume_out_listener(hw, listener);
246 static void
247 dbus_init_in_listener(QemuDBusDisplay1AudioInListener *listener, HWVoiceIn *hw)
249 qemu_dbus_display1_audio_in_listener_call_init(
250 listener,
251 (uintptr_t)hw,
252 hw->info.bits,
253 hw->info.is_signed,
254 hw->info.is_float,
255 hw->info.freq,
256 hw->info.nchannels,
257 hw->info.bytes_per_frame,
258 hw->info.bytes_per_second,
259 hw->info.swap_endianness ? !AUDIO_HOST_BE : AUDIO_HOST_BE,
260 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
263 static int
264 dbus_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
266 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
267 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
268 GHashTableIter iter;
269 QemuDBusDisplay1AudioInListener *listener = NULL;
271 audio_pcm_init_info(&hw->info, as);
272 hw->samples = DBUS_AUDIO_NSAMPLES;
273 audio_rate_start(&vo->rate);
275 g_hash_table_iter_init(&iter, da->in_listeners);
276 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
277 dbus_init_in_listener(listener, hw);
279 return 0;
282 static void
283 dbus_fini_in(HWVoiceIn *hw)
285 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
286 GHashTableIter iter;
287 QemuDBusDisplay1AudioInListener *listener = NULL;
289 g_hash_table_iter_init(&iter, da->in_listeners);
290 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
291 qemu_dbus_display1_audio_in_listener_call_fini(
292 listener,
293 (uintptr_t)hw,
294 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
298 static void
299 dbus_volume_in_listener(HWVoiceIn *hw,
300 QemuDBusDisplay1AudioInListener *listener)
302 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
303 Volume *vol = &vo->volume;
304 g_autoptr(GBytes) bytes = NULL;
305 GVariant *v_vol = NULL;
307 if (!vo->has_volume) {
308 return;
311 assert(vol->channels < sizeof(vol->vol));
312 bytes = g_bytes_new(vol->vol, vol->channels);
313 v_vol = g_variant_new_from_bytes(G_VARIANT_TYPE("ay"), bytes, TRUE);
314 qemu_dbus_display1_audio_in_listener_call_set_volume(
315 listener, (uintptr_t)hw, vol->mute, v_vol,
316 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
319 static void
320 dbus_volume_in(HWVoiceIn *hw, Volume *vol)
322 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
323 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
324 GHashTableIter iter;
325 QemuDBusDisplay1AudioInListener *listener = NULL;
327 vo->has_volume = true;
328 vo->volume = *vol;
330 g_hash_table_iter_init(&iter, da->in_listeners);
331 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
332 dbus_volume_in_listener(hw, listener);
336 static size_t
337 dbus_read(HWVoiceIn *hw, void *buf, size_t size)
339 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
340 /* DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw); */
341 GHashTableIter iter;
342 QemuDBusDisplay1AudioInListener *listener = NULL;
344 trace_dbus_audio_read(size);
346 /* size = audio_rate_get_bytes(&hw->info, &vo->rate, size); */
348 g_hash_table_iter_init(&iter, da->in_listeners);
349 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
350 g_autoptr(GVariant) v_data = NULL;
351 const char *data;
352 gsize n = 0;
354 if (qemu_dbus_display1_audio_in_listener_call_read_sync(
355 listener,
356 (uintptr_t)hw,
357 size,
358 G_DBUS_CALL_FLAGS_NONE, -1,
359 &v_data, NULL, NULL)) {
360 data = g_variant_get_fixed_array(v_data, &n, 1);
361 g_warn_if_fail(n <= size);
362 size = MIN(n, size);
363 memcpy(buf, data, size);
364 break;
368 return size;
371 static void
372 dbus_enable_in(HWVoiceIn *hw, bool enable)
374 DBusAudio *da = (DBusAudio *)hw->s->drv_opaque;
375 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
376 GHashTableIter iter;
377 QemuDBusDisplay1AudioInListener *listener = NULL;
379 vo->enabled = enable;
380 if (enable) {
381 audio_rate_start(&vo->rate);
384 g_hash_table_iter_init(&iter, da->in_listeners);
385 while (g_hash_table_iter_next(&iter, NULL, (void **)&listener)) {
386 qemu_dbus_display1_audio_in_listener_call_set_enabled(
387 listener, (uintptr_t)hw, enable,
388 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
392 static void *
393 dbus_audio_init(Audiodev *dev)
395 DBusAudio *da = g_new0(DBusAudio, 1);
397 da->out_listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
398 g_free, g_object_unref);
399 da->in_listeners = g_hash_table_new_full(g_str_hash, g_str_equal,
400 g_free, g_object_unref);
401 return da;
404 static void
405 dbus_audio_fini(void *opaque)
407 DBusAudio *da = opaque;
409 if (da->server) {
410 g_dbus_object_manager_server_unexport(da->server,
411 DBUS_DISPLAY1_AUDIO_PATH);
413 g_clear_object(&da->audio);
414 g_clear_object(&da->iface);
415 g_clear_pointer(&da->in_listeners, g_hash_table_unref);
416 g_clear_pointer(&da->out_listeners, g_hash_table_unref);
417 g_clear_object(&da->server);
418 g_free(da);
421 static void
422 listener_out_vanished_cb(GDBusConnection *connection,
423 gboolean remote_peer_vanished,
424 GError *error,
425 DBusAudio *da)
427 char *name = g_object_get_data(G_OBJECT(connection), "name");
429 g_hash_table_remove(da->out_listeners, name);
432 static void
433 listener_in_vanished_cb(GDBusConnection *connection,
434 gboolean remote_peer_vanished,
435 GError *error,
436 DBusAudio *da)
438 char *name = g_object_get_data(G_OBJECT(connection), "name");
440 g_hash_table_remove(da->in_listeners, name);
443 static gboolean
444 dbus_audio_register_listener(AudioState *s,
445 GDBusMethodInvocation *invocation,
446 GUnixFDList *fd_list,
447 GVariant *arg_listener,
448 bool out)
450 DBusAudio *da = s->drv_opaque;
451 const char *sender = g_dbus_method_invocation_get_sender(invocation);
452 g_autoptr(GDBusConnection) listener_conn = NULL;
453 g_autoptr(GError) err = NULL;
454 g_autoptr(GSocket) socket = NULL;
455 g_autoptr(GSocketConnection) socket_conn = NULL;
456 g_autofree char *guid = g_dbus_generate_guid();
457 GHashTable *listeners = out ? da->out_listeners : da->in_listeners;
458 GObject *listener;
459 int fd;
461 trace_dbus_audio_register(sender, out ? "out" : "in");
463 if (g_hash_table_contains(listeners, sender)) {
464 g_dbus_method_invocation_return_error(invocation,
465 DBUS_DISPLAY_ERROR,
466 DBUS_DISPLAY_ERROR_INVALID,
467 "`%s` is already registered!",
468 sender);
469 return DBUS_METHOD_INVOCATION_HANDLED;
472 fd = g_unix_fd_list_get(fd_list, g_variant_get_handle(arg_listener), &err);
473 if (err) {
474 g_dbus_method_invocation_return_error(invocation,
475 DBUS_DISPLAY_ERROR,
476 DBUS_DISPLAY_ERROR_FAILED,
477 "Couldn't get peer fd: %s",
478 err->message);
479 return DBUS_METHOD_INVOCATION_HANDLED;
482 socket = g_socket_new_from_fd(fd, &err);
483 if (err) {
484 g_dbus_method_invocation_return_error(invocation,
485 DBUS_DISPLAY_ERROR,
486 DBUS_DISPLAY_ERROR_FAILED,
487 "Couldn't make a socket: %s",
488 err->message);
489 return DBUS_METHOD_INVOCATION_HANDLED;
491 socket_conn = g_socket_connection_factory_create_connection(socket);
492 if (out) {
493 qemu_dbus_display1_audio_complete_register_out_listener(
494 da->iface, invocation, NULL);
495 } else {
496 qemu_dbus_display1_audio_complete_register_in_listener(
497 da->iface, invocation, NULL);
500 listener_conn =
501 g_dbus_connection_new_sync(
502 G_IO_STREAM(socket_conn),
503 guid,
504 G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_SERVER,
505 NULL, NULL, &err);
506 if (err) {
507 error_report("Failed to setup peer connection: %s", err->message);
508 return DBUS_METHOD_INVOCATION_HANDLED;
511 listener = out ?
512 G_OBJECT(qemu_dbus_display1_audio_out_listener_proxy_new_sync(
513 listener_conn,
514 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
515 NULL,
516 "/org/qemu/Display1/AudioOutListener",
517 NULL,
518 &err)) :
519 G_OBJECT(qemu_dbus_display1_audio_in_listener_proxy_new_sync(
520 listener_conn,
521 G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
522 NULL,
523 "/org/qemu/Display1/AudioInListener",
524 NULL,
525 &err));
526 if (!listener) {
527 error_report("Failed to setup proxy: %s", err->message);
528 return DBUS_METHOD_INVOCATION_HANDLED;
531 if (out) {
532 HWVoiceOut *hw;
534 QLIST_FOREACH(hw, &s->hw_head_out, entries) {
535 DBusVoiceOut *vo = container_of(hw, DBusVoiceOut, hw);
536 QemuDBusDisplay1AudioOutListener *l =
537 QEMU_DBUS_DISPLAY1_AUDIO_OUT_LISTENER(listener);
539 dbus_init_out_listener(l, hw);
540 qemu_dbus_display1_audio_out_listener_call_set_enabled(
541 l, (uintptr_t)hw, vo->enabled,
542 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
544 } else {
545 HWVoiceIn *hw;
547 QLIST_FOREACH(hw, &s->hw_head_in, entries) {
548 DBusVoiceIn *vo = container_of(hw, DBusVoiceIn, hw);
549 QemuDBusDisplay1AudioInListener *l =
550 QEMU_DBUS_DISPLAY1_AUDIO_IN_LISTENER(listener);
552 dbus_init_in_listener(
553 QEMU_DBUS_DISPLAY1_AUDIO_IN_LISTENER(listener), hw);
554 qemu_dbus_display1_audio_in_listener_call_set_enabled(
555 l, (uintptr_t)hw, vo->enabled,
556 G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL);
560 g_object_set_data_full(G_OBJECT(listener_conn), "name",
561 g_strdup(sender), g_free);
562 g_hash_table_insert(listeners, g_strdup(sender), listener);
563 g_object_connect(listener_conn,
564 "signal::closed",
565 out ? listener_out_vanished_cb : listener_in_vanished_cb,
567 NULL);
569 return DBUS_METHOD_INVOCATION_HANDLED;
572 static gboolean
573 dbus_audio_register_out_listener(AudioState *s,
574 GDBusMethodInvocation *invocation,
575 GUnixFDList *fd_list,
576 GVariant *arg_listener)
578 return dbus_audio_register_listener(s, invocation,
579 fd_list, arg_listener, true);
583 static gboolean
584 dbus_audio_register_in_listener(AudioState *s,
585 GDBusMethodInvocation *invocation,
586 GUnixFDList *fd_list,
587 GVariant *arg_listener)
589 return dbus_audio_register_listener(s, invocation,
590 fd_list, arg_listener, false);
593 static void
594 dbus_audio_set_server(AudioState *s, GDBusObjectManagerServer *server)
596 DBusAudio *da = s->drv_opaque;
598 g_assert(da);
599 g_assert(!da->server);
601 da->server = g_object_ref(server);
603 da->audio = g_dbus_object_skeleton_new(DBUS_DISPLAY1_AUDIO_PATH);
604 da->iface = qemu_dbus_display1_audio_skeleton_new();
605 g_object_connect(da->iface,
606 "swapped-signal::handle-register-in-listener",
607 dbus_audio_register_in_listener, s,
608 "swapped-signal::handle-register-out-listener",
609 dbus_audio_register_out_listener, s,
610 NULL);
612 g_dbus_object_skeleton_add_interface(G_DBUS_OBJECT_SKELETON(da->audio),
613 G_DBUS_INTERFACE_SKELETON(da->iface));
614 g_dbus_object_manager_server_export(da->server, da->audio);
617 static struct audio_pcm_ops dbus_pcm_ops = {
618 .init_out = dbus_init_out,
619 .fini_out = dbus_fini_out,
620 .write = audio_generic_write,
621 .get_buffer_out = dbus_get_buffer_out,
622 .put_buffer_out = dbus_put_buffer_out,
623 .enable_out = dbus_enable_out,
624 .volume_out = dbus_volume_out,
626 .init_in = dbus_init_in,
627 .fini_in = dbus_fini_in,
628 .read = dbus_read,
629 .run_buffer_in = audio_generic_run_buffer_in,
630 .enable_in = dbus_enable_in,
631 .volume_in = dbus_volume_in,
634 static struct audio_driver dbus_audio_driver = {
635 .name = "dbus",
636 .descr = "Timer based audio exposed with DBus interface",
637 .init = dbus_audio_init,
638 .fini = dbus_audio_fini,
639 .set_dbus_server = dbus_audio_set_server,
640 .pcm_ops = &dbus_pcm_ops,
641 .can_be_default = 1,
642 .max_voices_out = INT_MAX,
643 .max_voices_in = INT_MAX,
644 .voice_size_out = sizeof(DBusVoiceOut),
645 .voice_size_in = sizeof(DBusVoiceIn)
648 static void register_audio_dbus(void)
650 audio_driver_register(&dbus_audio_driver);
652 type_init(register_audio_dbus);
654 module_dep("ui-dbus")