dbusaudio.c 21 KB

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