2 This file is part of PulseAudio.
4 Copyright 2013-2016 Seungbae Shin, Sangchul Lee, Jeongho Mok
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of the License,
9 or (at your option) any later version.
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
28 #include <vconf.h> // for mono
29 #include <iniparser.h>
33 #include <pulse/proplist.h>
34 #include <pulse/timeval.h>
35 #include <pulse/util.h>
36 #include <pulse/rtclock.h>
38 #include <pulsecore/core.h>
39 #include <pulsecore/module.h>
40 #include <pulsecore/modargs.h>
41 #include <pulsecore/core-error.h>
42 #include <pulsecore/core-rtclock.h>
43 #include <pulsecore/core-scache.h>
44 #include <pulsecore/core-subscribe.h>
45 #include <pulsecore/core-util.h>
46 #include <pulsecore/mutex.h>
47 #include <pulsecore/log.h>
48 #include <pulsecore/namereg.h>
49 #include <pulsecore/sink-input.h>
50 #include <pulsecore/source-output.h>
51 #include <pulsecore/pstream-util.h>
52 #include <pulsecore/strbuf.h>
53 #include <pulsecore/sink-input.h>
54 #include <pulsecore/sound-file.h>
55 #include <pulsecore/play-memblockq.h>
56 #include <pulsecore/shared.h>
58 #include "module-tizenaudio-policy-symdef.h"
59 #include "communicator.h"
60 #include "hal-interface.h"
61 #include "stream-manager.h"
62 #include "device-manager.h"
64 PA_MODULE_AUTHOR("Seungbae Shin, Sangchul Lee, Jeongho Mok");
65 PA_MODULE_DESCRIPTION("Tizen Audio Policy module");
66 PA_MODULE_VERSION(PACKAGE_VERSION);
67 PA_MODULE_LOAD_ONCE(true);
70 static const char* const valid_modargs[] = {
76 /* Modules for dynamic loading */
77 #define MODULE_COMBINE_SINK "module-combine-sink"
78 #define MODULE_NULL_SINK "module-null-sink"
79 #define MODULE_NULL_SOURCE "module-null-source"
80 #define MODULE_LOOPBACK "module-loopback"
82 /* Name of combine sink for external route type */
83 #define SINK_NAME_COMBINED_EX SINK_NAME_COMBINED"_ex"
86 #define LOOPBACK_DEFAULT_LATENCY_MSEC 40
87 #define LOOPBACK_DEFAULT_ADJUST_SEC 3
89 #define TIMED_BT_SCO_CLOSE_USEC 3000000
92 #define CONVERT_TO_HAL_DIRECTION(stream_type) \
93 ((stream_type == STREAM_SINK_INPUT) ? DIRECTION_OUT : DIRECTION_IN)
94 #define CONVERT_TO_DEVICE_DIRECTION(stream_type) \
95 ((stream_type == STREAM_SINK_INPUT) ? DM_DEVICE_DIRECTION_OUT : DM_DEVICE_DIRECTION_IN)
96 #define IS_AVAILABLE_DIRECTION(stream_type, device_direction) \
97 ((stream_type == STREAM_SINK_INPUT) ? (device_direction & DM_DEVICE_DIRECTION_OUT) : (device_direction & DM_DEVICE_DIRECTION_IN))
100 #define PA_DUMP_INI_DEFAULT_PATH SYSCONFDIR"/multimedia/mmfw_audio_pcm_dump.ini" /* SYSCONFDIR is defined at .spec */
101 #define PA_DUMP_INI_TEMP_PATH "/home/owner/media/mmfw_audio_pcm_dump.ini"
102 #define PA_DUMP_VCONF_KEY "memory/private/sound/pcm_dump"
104 typedef enum _device_type {
105 DEVICE_BUILTIN_SPEAKER,
106 DEVICE_BUILTIN_RECEIVER,
119 CACHED_DEVICE_DIRECTION_IN,
120 CACHED_DEVICE_DIRECTION_OUT,
121 CACHED_DEVICE_DIRECTION_MAX,
124 int cached_connected_devices[DEVICE_MAX][CACHED_DEVICE_DIRECTION_MAX];
126 static device_type_t convert_device_type_str(const char *device)
128 if (pa_streq(device, DEVICE_TYPE_SPEAKER))
129 return DEVICE_BUILTIN_SPEAKER;
130 else if (pa_streq(device, DEVICE_TYPE_RECEIVER))
131 return DEVICE_BUILTIN_RECEIVER;
132 else if (pa_streq(device, DEVICE_TYPE_MIC))
133 return DEVICE_BUILTIN_MIC;
134 else if (pa_streq(device, DEVICE_TYPE_AUDIO_JACK))
135 return DEVICE_AUDIO_JACK;
136 else if (pa_streq(device, DEVICE_TYPE_BT_A2DP))
137 return DEVICE_BT_A2DP;
138 else if (pa_streq(device, DEVICE_TYPE_BT_SCO))
139 return DEVICE_BT_SCO;
140 else if (pa_streq(device, DEVICE_TYPE_HDMI))
142 else if (pa_streq(device, DEVICE_TYPE_FORWARDING))
143 return DEVICE_FORWARDING;
144 else if (pa_streq(device, DEVICE_TYPE_USB_AUDIO))
145 return DEVICE_USB_AUDIO;
147 pa_log_warn("unknown device (%s)", device);
148 return DEVICE_UNKNOWN;
156 pa_communicator *comm;
157 pa_hook_slot *comm_hook_select_proper_sink_or_source_slot;
158 pa_hook_slot *comm_hook_change_route_slot;
159 pa_hook_slot *comm_hook_device_connection_changed_slot;
160 pa_hook_slot *comm_hook_update_info_slot;
163 pa_hal_interface *hal_interface;
164 pa_stream_manager *stream_manager;
165 pa_device_manager *device_manager;
167 pa_module *module_combine_sink;
168 pa_module *module_combine_sink_for_ex;
169 pa_module *module_null_sink;
170 pa_module *module_null_source;
171 pa_module *module_loopback;
173 int32_t latency_msec;
178 pa_time_event *time_event_bt_sco_close;
181 static void __load_dump_config(struct userdata *u)
183 dictionary * dict = NULL;
186 dict = iniparser_load(PA_DUMP_INI_DEFAULT_PATH);
188 pa_log_debug("%s load failed. Use temporary file", PA_DUMP_INI_DEFAULT_PATH);
189 dict = iniparser_load(PA_DUMP_INI_TEMP_PATH);
191 pa_log_warn("%s load failed", PA_DUMP_INI_TEMP_PATH);
196 vconf_dump |= iniparser_getboolean(dict, "pcm_dump:decoder_out", 0) ? PA_PCM_DUMP_GST_DECODER_OUT : 0;
197 vconf_dump |= iniparser_getboolean(dict, "pcm_dump:resampler_in", 0) ? PA_PCM_DUMP_GST_RESAMPLER_IN : 0;
198 vconf_dump |= iniparser_getboolean(dict, "pcm_dump:resampler_out", 0) ? PA_PCM_DUMP_GST_RESAMPLER_OUT : 0;
199 vconf_dump |= iniparser_getboolean(dict, "pcm_dump:gst_audio_sink", 0) ? PA_PCM_DUMP_GST_AUDIO_SINK_IN : 0;
200 vconf_dump |= iniparser_getboolean(dict, "pcm_dump:pa_stream_write", 0) ? PA_PCM_DUMP_PA_STREAM_WRITE : 0;
201 u->core->pcm_dump |= (bool)iniparser_getboolean(dict, "pcm_dump:pa_sink_input", 0) ? PA_PCM_DUMP_PA_SINK_INPUT : 0;
202 u->core->pcm_dump |= (bool)iniparser_getboolean(dict, "pcm_dump:pa_sink", 0) ? PA_PCM_DUMP_PA_SINK : 0;
203 u->core->pcm_dump |= (bool)iniparser_getboolean(dict, "pcm_dump:pa_source", 0) ? PA_PCM_DUMP_PA_SOURCE : 0;
204 u->core->pcm_dump |= (bool)iniparser_getboolean(dict, "pcm_dump:pa_source_output", 0) ? PA_PCM_DUMP_PA_SOURCE_OUTPUT : 0;
205 vconf_dump |= iniparser_getboolean(dict, "pcm_dump:pa_stream_read", 0) ? PA_PCM_DUMP_PA_STREAM_READ : 0;
206 vconf_dump |= iniparser_getboolean(dict, "pcm_dump:gst_audio_src", 0) ? PA_PCM_DUMP_GST_AUDIO_SRC_OUT : 0;
207 vconf_dump |= iniparser_getboolean(dict, "pcm_dump:encoder_in", 0) ? PA_PCM_DUMP_GST_ENCODER_IN : 0;
209 iniparser_freedict(dict);
211 if (vconf_set_int(PA_PCM_DUMP_VCONF_KEY, vconf_dump)) {
212 pa_log_warn("vconf_set_int %s=%x failed", PA_PCM_DUMP_VCONF_KEY, vconf_dump);
216 /* threre is only one sco connected device */
217 static pa_tz_device* _get_sco_connected_device(pa_device_manager *dm) {
218 pa_idxset *device_list;
219 pa_tz_device *device;
224 device_list = pa_device_manager_get_device_list(dm);
226 PA_IDXSET_FOREACH(device, device_list, device_idx) {
227 if (pa_streq(device->type, DEVICE_TYPE_BT_SCO)) {
234 static bool is_bt_sco_opened(pa_device_manager *dm) {
235 dm_device_bt_sco_status_t sco_status;
236 pa_tz_device *bt_device;
240 bt_device = _get_sco_connected_device(dm);
241 if (bt_device == NULL) {
242 pa_log_debug("No SCO connected bt device");
245 if (pa_tz_device_sco_get_status(bt_device, &sco_status) < 0) {
246 pa_log_error("get BT SCO status failed");
249 if (sco_status != DM_DEVICE_BT_SCO_STATUS_OPENED) {
250 pa_log_error("SCO is not opened");
253 pa_log_info("SCO is opened");
258 static void timed_bt_sco_close_cb(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) {
259 struct userdata *u = userdata;
260 pa_tz_device *bt_device;
261 dm_device_bt_sco_status_t sco_status;
264 pa_assert(u->time_event_bt_sco_close == e);
266 pa_log_info("timed_bt_sco_close_cb is called");
268 u->core->mainloop->time_free(u->time_event_bt_sco_close);
269 u->time_event_bt_sco_close = NULL;
271 bt_device = _get_sco_connected_device(u->device_manager);
272 if (bt_device == NULL) {
273 pa_log_debug("No SCO connected bt device");
276 if (pa_tz_device_sco_get_status(bt_device, &sco_status) < 0) {
277 pa_log_error("get BT SCO status failed");
280 if (pa_tz_device_sco_close(bt_device) < 0) {
281 pa_log_error("BT SCO was opened, but failed to close SCO");
284 pa_log_info("BT SCO is now closed in timed callback");
288 static int bt_sco_open(struct userdata *u) {
289 dm_device_bt_sco_status_t sco_status;
290 pa_tz_device *bt_device;
294 if (u->time_event_bt_sco_close) {
295 u->core->mainloop->time_free(u->time_event_bt_sco_close);
296 u->time_event_bt_sco_close = NULL;
299 bt_device = _get_sco_connected_device(u->device_manager);
300 if (bt_device == NULL) {
301 pa_log_debug("No SCO connected bt device");
304 pa_log_info("Got BT SCO connected device(%u)", bt_device->id);
306 if (pa_tz_device_sco_get_status(bt_device, &sco_status) < 0) {
307 pa_log_error("get BT SCO status failed");
310 if (sco_status == DM_DEVICE_BT_SCO_STATUS_OPENED) {
311 pa_log_warn("BT SCO is already opened for this BT device");
314 if (pa_tz_device_sco_open(bt_device) < 0) {
315 pa_log_error("failed to open BT SCO");
318 pa_log_debug("BT SCO is now opened");
323 static int bt_sco_close(struct userdata *u, bool delayed_close) {
324 dm_device_bt_sco_status_t sco_status;
325 pa_tz_device *bt_device;
329 bt_device = _get_sco_connected_device(u->device_manager);
330 if (bt_device == NULL) {
331 pa_log_debug("No SCO connected bt device");
334 pa_log_info("Got BT SCO connected device(%u)", bt_device->id);
336 if (pa_tz_device_sco_get_status(bt_device, &sco_status) < 0) {
337 pa_log_error("get BT SCO status failed");
340 if (sco_status == DM_DEVICE_BT_SCO_STATUS_CONNECTED) {
341 pa_log_warn("BT SCO is already closed for this BT device");
345 /* request to close SCO after 3 sec. */
346 if (!u->time_event_bt_sco_close) {
347 u->time_event_bt_sco_close = pa_core_rttime_new(u->core, pa_rtclock_now() + TIMED_BT_SCO_CLOSE_USEC, timed_bt_sco_close_cb, u);
348 pa_log_debug("Append time event to close BT SCO");
351 if (u->time_event_bt_sco_close) {
352 u->core->mainloop->time_free(u->time_event_bt_sco_close);
353 u->time_event_bt_sco_close = NULL;
355 if (pa_tz_device_sco_close(bt_device) < 0) {
356 pa_log_error("BT SCO was opened, but failed to close SCO");
359 pa_log_debug("BT SCO is now closed");
365 /* Open/Close BT SCO if it is possible */
366 static int update_bt_sco_state(struct userdata *u, bool open, bool delayed_close) {
370 return bt_sco_open(u);
372 return bt_sco_close(u, delayed_close);
375 static int get_bt_property(pa_device_manager *dm, bool *is_wb, bool *is_nrec) {
376 dm_device_bt_sco_status_t sco_status;
377 pa_tz_device *bt_device;
383 bt_device = _get_sco_connected_device(dm);
384 if (bt_device == NULL) {
385 pa_log_error("No SCO connected bt device");
388 if (pa_tz_device_sco_get_status(bt_device, &sco_status) < 0) {
389 pa_log_error("get BT SCO status failed");
393 pa_log_info("Got BT SCO connected device(%u), status(%d)", bt_device->id, sco_status);
394 if (sco_status == DM_DEVICE_BT_SCO_STATUS_CONNECTED)
397 return pa_tz_device_sco_get_property(bt_device, is_wb, is_nrec);
400 /* Load/Unload module-loopback */
401 static void update_loopback_module(struct userdata *u, bool load) {
406 if (load && u->loopback_args.sink && u->loopback_args.source) {
407 if (!u->loopback_args.latency_msec)
408 u->loopback_args.latency_msec = LOOPBACK_DEFAULT_LATENCY_MSEC;
409 if (!u->loopback_args.adjust_sec)
410 u->loopback_args.adjust_sec = LOOPBACK_DEFAULT_ADJUST_SEC;
412 args = pa_sprintf_malloc("sink=%s source=%s latency_msec=%d adjust_time=%d",
413 u->loopback_args.sink->name, u->loopback_args.source->name,
414 u->loopback_args.latency_msec, u->loopback_args.adjust_sec);
415 if (u->module_loopback)
416 pa_module_unload(u->core, u->module_loopback, true);
418 u->module_loopback = pa_module_load(u->core, MODULE_LOOPBACK, args);
420 pa_log_info(" -- load module-loopback with (%s)", args);
424 if (u->module_loopback) {
425 pa_module_unload(u->core, u->module_loopback, true);
426 u->module_loopback = NULL;
427 u->loopback_args.sink = NULL;
428 u->loopback_args.source = NULL;
429 pa_log_info(" -- unload module-loopback");
432 pa_log_error(" -- failed to update loopback module");
436 static void unload_combine_sink_module(struct userdata *u, const char *combine_sink_name, pa_sink *dst_sink)
438 pa_module **combine_sink_module = NULL;
439 pa_sink *combine_sink = NULL;
440 pa_sink_input *s = NULL;
444 pa_assert(combine_sink_name);
447 if (pa_streq(combine_sink_name, SINK_NAME_COMBINED)) {
448 combine_sink_module = &u->module_combine_sink;
449 } else if (pa_streq(combine_sink_name, SINK_NAME_COMBINED_EX)) {
450 combine_sink_module = &u->module_combine_sink_for_ex;
452 pa_log_error("unknown combine_sink_name(%s)", combine_sink_name);
456 if (*combine_sink_module) {
457 combine_sink = (pa_sink*)pa_namereg_get(u->core, combine_sink_name, PA_NAMEREG_SINK);
459 pa_log_error("could not get combine_sink(%s)", combine_sink_name);
461 PA_IDXSET_FOREACH(s, combine_sink->inputs, idx) {
462 pa_sink_input_move_to(s, dst_sink, false);
463 pa_log_info("[UNLOAD COMBINE SINK MODULE] *** sink-input(%p,%u) of (%s) moves to another sink(%p,%s)",
464 s, ((pa_sink_input*)s)->index, combine_sink_name, dst_sink, dst_sink->name);
466 pa_sink_suspend(combine_sink, true, PA_SUSPEND_USER);
468 pa_log_info("unload combine sink module[%s]", combine_sink_name);
469 pa_module_unload(u->core, *combine_sink_module, true);
470 *combine_sink_module = NULL;
472 pa_log_warn("module combine sink(%s) has been already unloaded", combine_sink_name);
475 static bool skip_device(const char *stream_role, const char *device_type)
479 if (pa_streq(device_type, DEVICE_TYPE_FORWARDING))
482 /* get sound profile */
483 if (vconf_get_bool(VCONFKEY_SETAPPL_SOUND_STATUS_BOOL, &sound_on) < 0) {
484 pa_log_error("failed to get vconf - sound status");
488 if (!sound_on && IS_ROLE_RINGTONE(stream_role) && pa_streq(device_type, DEVICE_TYPE_SPEAKER)) {
489 pa_log_info("sound status is 0 with ringtone-call stream, skip built-in speaker");
496 static bool skip_bt_sco_device(struct userdata *u, const char *stream_role, const char *device_type) {
498 pa_assert(stream_role);
499 pa_assert(device_type);
501 if (pa_streq(stream_role, STREAM_ROLE_VOICE_INFORMATION) && pa_streq(device_type, DEVICE_TYPE_BT_SCO)) {
502 if (is_bt_sco_opened(u->device_manager)) {
503 /* remove the request of closing SCO */
504 if (u->time_event_bt_sco_close) {
505 u->core->mainloop->time_free(u->time_event_bt_sco_close);
506 u->time_event_bt_sco_close = NULL;
508 pa_log_info("It is VOICE_INFORMATION, BT SCO is already opened, do not skip this device");
511 pa_log_info("It is VOICE_INFORMATION, BT SCO is not opened, skip this device");
519 static inline bool is_cached_device_connected(const char* device_type, stream_type_t stream_type) {
520 if (cached_connected_devices[convert_device_type_str(device_type)][CONVERT_TO_DEVICE_DIRECTION(stream_type)-1] > 0)
525 /* Set the proper sink(source) according to the data of the parameter.
526 * - ROUTE_TYPE_AUTO(_ALL)
527 * 1. Find the proper sink/source comparing between avail_devices
528 * and current connected devices.
529 * 2. If not found, set it to null sink/source.
530 * - ROUTE_TYPE_MANUAL(_EXT)
531 * 1. Find the proper sink/source comparing between avail_devices
532 * and manual_devices that have been set by user.
533 * 2. If not found, set it to null sink/source. */
534 static pa_hook_result_t select_proper_sink_or_source_hook_cb(pa_core *c, pa_stream_manager_hook_data_for_select *data, struct userdata *u) {
537 uint32_t conn_idx = 0;
538 uint32_t *device_id = NULL;
539 uint32_t dm_device_id = 0;
540 const char *device_type = NULL;
541 const char *dm_device_type = NULL;
542 dm_device_direction_t dm_device_direction = DM_DEVICE_DIRECTION_NONE;
543 pa_tz_device *device = NULL;
544 pa_idxset *conn_devices = NULL;
545 pa_sink *sink = NULL;
546 pa_sink *null_sink = NULL;
547 pa_sink *combine_sink_arg1 = NULL;
548 pa_sink *combine_sink_arg2 = NULL;
549 pa_source *source = NULL;
550 pa_source *null_source = NULL;
557 pa_log_info("[SELECT] select_proper_sink_or_source_hook_cb is called. (%p), stream_type(%d), stream_role(%s), device_role(%s), route_type(%d)",
558 data, data->stream_type, data->stream_role, data->device_role, data->route_type);
560 null_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_NULL, PA_NAMEREG_SINK);
561 null_source = (pa_source*)pa_namereg_get(u->core, SOURCE_NAME_NULL, PA_NAMEREG_SOURCE);
562 if (!null_sink || !null_source) {
563 pa_log_error("[SELECT] could not get null_sink(%p) or null_source(%p)", null_sink, null_source);
567 /* check if the current occupying role is related to call.
568 * some targets use several pcm card as per their purpose.
569 * e.g) using a specific pcm card during voice call. */
570 if (data->occupying_role) {
571 if (IS_ROLE_COMMUNICATION(data->occupying_role)) {
572 CONVERT_TO_DEVICE_ROLE(data->occupying_role, data->device_role);
573 pa_log_info("[SELECT] current occupying stream role is [%s], set device role to [%s]", data->occupying_role, data->device_role);
577 if (!data->idx_avail_devices) {
578 pa_log_error("[SELECT] available devices is NULL, set it to null sink/source");
582 if (IS_MANUAL_ROUTE_TYPE_SERIES(data->route_type) && !data->idx_manual_devices) {
583 pa_log_error("[SELECT] manual devices is NULL, set it to null sink/source");
587 if (IS_AUTO_ROUTE_TYPE_SERIES(data->route_type)) {
588 /* get current connected devices */
589 conn_devices = pa_device_manager_get_device_list(u->device_manager);
590 if (data->route_type == STREAM_ROUTE_TYPE_AUTO || data->route_type == STREAM_ROUTE_TYPE_AUTO_ALL) {
591 PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
592 pa_log_debug("[SELECT][AUTO(_ALL)] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
593 if (!is_cached_device_connected(device_type, data->stream_type))
595 PA_IDXSET_FOREACH(device, conn_devices, conn_idx) {
596 dm_device_type = pa_tz_device_get_type(device);
597 dm_device_direction = pa_tz_device_get_direction(device);
598 dm_device_id = pa_tz_device_get_id(device);
599 pa_log_debug(" -- type[%-16s], direction[0x%x], id[%u]",
600 dm_device_type, dm_device_direction, dm_device_id);
601 if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
602 pa_log_info(" ** found a matched device: type[%-16s], direction[0x%x]", dm_device_type, dm_device_direction);
603 if (skip_bt_sco_device(u, data->stream_role, dm_device_type))
606 if (data->stream_type == STREAM_SINK_INPUT) {
607 if (data->route_type == STREAM_ROUTE_TYPE_AUTO_ALL && u->module_combine_sink) {
608 *(data->proper_sink) = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK);
609 pa_log_info(" -- found the combine-sink, set it to the sink");
611 *(data->proper_sink) = pa_tz_device_get_sink(device, data->device_role);
613 *(data->proper_source) = pa_tz_device_get_source(device, data->device_role);
615 if (data->route_type == STREAM_ROUTE_TYPE_AUTO) {
616 pa_proplist_sets(GET_STREAM_NEW_PROPLIST(data->stream, data->stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
622 } else if (data->route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED) {
623 pa_tz_device *latest_device = NULL;
624 pa_usec_t creation_time = 0;
625 pa_usec_t latest_creation_time = 0;
627 PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
628 pa_log_debug("[SELECT][AUTO_LAST_CONN] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
629 if (!is_cached_device_connected(device_type, data->stream_type))
631 PA_IDXSET_FOREACH(device, conn_devices, conn_idx) {
632 dm_device_type = pa_tz_device_get_type(device);
633 dm_device_direction = pa_tz_device_get_direction(device);
634 dm_device_id = pa_tz_device_get_id(device);
635 creation_time = pa_tz_device_get_creation_time(device);
636 pa_log_debug(" -- type[%-16s], direction[0x%x], id[%u], creation_time[%llu]",
637 dm_device_type, dm_device_direction, dm_device_id, creation_time);
638 if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
639 if (skip_bt_sco_device(u, data->stream_role, dm_device_type))
642 if (!latest_device || (latest_creation_time <= creation_time)) {
643 latest_device = device;
644 latest_creation_time = creation_time;
645 pa_log_info(" ** updated the last connected device: type[%-16s], direction[0x%x]", dm_device_type, dm_device_direction);
650 /* update active device info. */
652 if (data->stream_type == STREAM_SINK_INPUT)
653 *(data->proper_sink) = pa_tz_device_get_sink(latest_device, data->device_role);
655 *(data->proper_source) = pa_tz_device_get_source(latest_device, data->device_role);
657 pa_proplist_sets(GET_STREAM_NEW_PROPLIST(data->stream, data->stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, pa_tz_device_get_type(latest_device));
661 } else if (data->route_type == STREAM_ROUTE_TYPE_MANUAL) {
662 PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
663 pa_log_info("[SELECT][MANUAL] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
664 if (!is_cached_device_connected(device_type, data->stream_type))
666 PA_IDXSET_FOREACH(device_id, data->idx_manual_devices, m_idx) {
667 if (!(device = pa_device_manager_get_device_by_id(u->device_manager, *device_id)))
669 dm_device_type = pa_tz_device_get_type(device);
670 dm_device_direction = pa_tz_device_get_direction(device);
671 pa_log_debug(" -- type[%-16s], direction[0x%x], device id[%u]",
672 dm_device_type, dm_device_direction, *device_id);
673 if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
674 pa_log_info(" ** found a matched device: type[%-16s], direction[0x%x]", device_type, dm_device_direction);
675 if (data->stream_type == STREAM_SINK_INPUT) {
676 if ((*(data->proper_sink)) == null_sink)
677 pa_sink_input_move_to((pa_sink_input*)(data->stream), pa_tz_device_get_sink(device, data->device_role), false);
679 *(data->proper_sink) = pa_tz_device_get_sink(device, data->device_role);
681 if ((*(data->proper_source)) == null_source)
682 pa_source_output_move_to((pa_source_output*)(data->stream), pa_tz_device_get_source(device, data->device_role), false);
684 *(data->proper_source) = pa_tz_device_get_source(device, data->device_role);
690 } else if (data->route_type == STREAM_ROUTE_TYPE_MANUAL_EXT) {
691 PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
692 pa_log_info("[SELECT][MANUAL_EXT] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
693 if (!is_cached_device_connected(device_type, data->stream_type))
695 PA_IDXSET_FOREACH(device_id, data->idx_manual_devices, m_idx) {
696 if (!(device = pa_device_manager_get_device_by_id(u->device_manager, *device_id)))
698 dm_device_type = pa_tz_device_get_type(device);
699 dm_device_direction = pa_tz_device_get_direction(device);
700 pa_log_debug(" -- type[%-16s], direction[0x%x], device id[%u]",
701 dm_device_type, dm_device_direction, *device_id);
702 if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
703 pa_log_info(" ** found a matched device: type[%-16s], direction[0x%x]", device_type, dm_device_direction);
704 /* currently, we support two sinks for combining */
705 if (data->stream_type == STREAM_SINK_INPUT) {
706 if (!combine_sink_arg1) {
707 if ((sink = combine_sink_arg1 = pa_tz_device_get_sink(device, DEVICE_ROLE_NORMAL)))
708 pa_log_info(" -- combine_sink_arg1[%s], combine_sink_arg2[%p]", sink->name, combine_sink_arg2);
710 pa_log_warn(" -- could not get combine_sink_arg1");
711 } else if (!combine_sink_arg2) {
712 sink = combine_sink_arg2 = pa_tz_device_get_sink(device, DEVICE_ROLE_NORMAL);
713 if (sink && !pa_streq(sink->name, combine_sink_arg1->name)) {
716 pa_log_info(" -- combine_sink_arg2[%s]", sink->name);
717 /* load combine sink */
718 if (!u->module_combine_sink_for_ex) {
719 char *args = pa_sprintf_malloc("sink_name=%s slaves=\"%s,%s\"", SINK_NAME_COMBINED_EX, combine_sink_arg1->name, combine_sink_arg2->name);
720 pa_log_info(" -- combined sink is not prepared, now load module[%s]", args);
721 u->module_combine_sink_for_ex = pa_module_load(u->core, MODULE_COMBINE_SINK, args);
724 sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED_EX, PA_NAMEREG_SINK);
725 PA_IDXSET_FOREACH(s, combine_sink_arg1->inputs, s_idx) {
726 if (sink && s == data->stream) {
727 pa_sink_input_move_to(s, sink, false);
728 pa_log_info(" -- *** sink-input(%p,%u) moves to sink(%p,%s)", s, ((pa_sink_input*)s)->index, sink, sink->name);
733 pa_log_warn(" -- could not get combine_sink_arg2");
736 if (data->origins_from_new_data)
737 *(data->proper_sink) = sink;
739 if (((pa_sink_input*)(data->stream))->sink != sink)
740 pa_sink_input_move_to(data->stream, sink, false);
743 } else if (data->stream_type == STREAM_SOURCE_OUTPUT) {
744 if ((source = pa_tz_device_get_source(device, DEVICE_ROLE_NORMAL))) {
745 if (data->origins_from_new_data)
746 *(data->proper_source) = source;
748 if (((pa_source_output*)(data->stream))->source != source)
749 pa_source_output_move_to(data->stream, source, false);
752 pa_log_warn(" -- could not get source");
760 if ((data->stream_type == STREAM_SINK_INPUT) ? !(*(data->proper_sink)) : !(*(data->proper_source))) {
761 pa_log_warn("[SELECT] could not find a proper sink/source, set it to null sink/source");
762 if (data->stream_type == STREAM_SINK_INPUT)
763 *(data->proper_sink) = null_sink;
765 *(data->proper_source) = null_source;
771 static void update_bt_route_option(pa_hal_interface *hal_intf, const char *role, const char* name, int value)
773 hal_route_option route_option;
775 memset(&route_option, 0, sizeof(hal_route_option));
776 route_option.role = role;
777 route_option.name = name;
778 route_option.value = value;
780 pa_hal_interface_update_route_option(hal_intf, &route_option);
783 static void reset_route(struct userdata *u, stream_type_t stream_type) {
784 hal_route_info route_info = {NULL, NULL, 0};
785 pa_sink *null_sink = NULL;
789 if (u->module_loopback) {
790 if (stream_type == STREAM_SINK_INPUT && u->loopback_args.sink->use_internal_codec)
791 update_loopback_module(u, false);
792 else if (stream_type == STREAM_SOURCE_OUTPUT && u->loopback_args.source->use_internal_codec)
793 update_loopback_module(u, false);
796 /* update BT SCO: close */
797 update_bt_sco_state(u, false, true);
799 /* unload combine sink */
800 if (stream_type == STREAM_SINK_INPUT) {
801 null_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_NULL, PA_NAMEREG_SINK);
802 unload_combine_sink_module(u, SINK_NAME_COMBINED, null_sink);
805 route_info.role = "reset";
806 route_info.num_of_devices = 1;
807 route_info.device_infos = pa_xmalloc0(sizeof(hal_device_info)*route_info.num_of_devices);
808 route_info.device_infos[0].direction = CONVERT_TO_HAL_DIRECTION(stream_type);
810 /* send information to HAL to update route */
811 if (pa_hal_interface_update_route(u->hal_interface, &route_info))
812 pa_log_error("[ROUTE] Failed to pa_hal_interface_update_route()");
814 pa_xfree(route_info.device_infos);
817 /* Some H/W can have several alsa cards to serve various purposes. Therefore,
818 * a particular stream may have to use a specific card, e.g) during voice call.
819 * Here are codes to move streams to the specific device according to device role.
820 * It has to be set only when the new_data is true - start routing of the new
821 * highest priority stream. */
822 static void route_change_move_streams(struct userdata *u, pa_stream_manager_hook_data_for_route *data, pa_tz_device *device) {
823 const char *device_role;
826 pa_idxset *streams = NULL;
827 pa_sink *sink = NULL;
828 pa_sink *dst_sink = NULL;
829 pa_source *source = NULL;
830 pa_source *dst_source = NULL;
835 /* if it's NOT from new_data, return here */
836 if (!device || !data->origins_from_new_data)
841 if (!IS_ROLE_COMMUNICATION(data->stream_role))
844 CONVERT_TO_DEVICE_ROLE(data->stream_role, device_role);
845 pa_log_info("[ROUTE][MOVE] stream_role[%s], device role[%s]", data->stream_role, device_role);
846 if (data->stream_type == STREAM_SINK_INPUT) {
847 if (!(sink = pa_tz_device_get_sink(device, DEVICE_ROLE_NORMAL)) ||
848 !(dst_sink = pa_tz_device_get_sink(device, device_role)) ||
850 pa_log_info("[ROUTE][MOVE][%s] no need to move streams, sink(%p), dst_sink(%p)", data->stream_role, sink, dst_sink);
852 streams = sink->inputs;
853 } else if (data->stream_type == STREAM_SOURCE_OUTPUT) {
854 if (!(source = pa_tz_device_get_source(device, DEVICE_ROLE_NORMAL)) ||
855 !(dst_source = pa_tz_device_get_source(device, device_role)) ||
856 source == dst_source)
857 pa_log_info("[ROUTE][MOVE][%s] no need to move streams, source(%p), dst_source(%p)", data->stream_role, source, dst_source);
859 streams = source->outputs;
865 /* move other streams that belong to the device of NORMAL role */
866 PA_IDXSET_FOREACH(s, streams, idx) {
867 if (data->stream_type == STREAM_SINK_INPUT) {
868 pa_sink_input_move_to(s, dst_sink, false);
869 pa_log_info("[ROUTE][MOVE][%s] *** sink-input(%p,%u) moves to sink(%p,%s)",
870 data->stream_role, s, ((pa_sink_input*)s)->index, dst_sink, dst_sink->name);
871 } else if (data->stream_type == STREAM_SOURCE_OUTPUT) {
872 pa_source_output_move_to(s, dst_source, false);
873 pa_log_info("[ROUTE][MOVE][%s] *** source-output(%p,%u) moves to source(%p,%s)",
874 data->stream_role, s, ((pa_source_output*)s)->index, dst_source, dst_source->name);
877 /* make sure the previous device is closed */
878 if (data->stream_type == STREAM_SINK_INPUT)
879 pa_sink_suspend(sink, true, PA_SUSPEND_INTERNAL);
880 else if (data->stream_type == STREAM_SOURCE_OUTPUT)
881 pa_source_suspend(source, true, PA_SUSPEND_INTERNAL);
884 /* Some H/W can have several alsa cards to serve various purposes. Therefore,
885 * a particular stream may have to use a specific card, e.g) during voice call.
886 * Here are codes to depart from the above situation and move streams to the device
887 * for rollback if needed only when the new_data is false - end of stream or
888 * start routing of the new highest priority stream. */
889 static void route_change_rollback_streams(struct userdata *u, pa_stream_manager_hook_data_for_route *data, pa_tz_device *device) {
890 pa_sink *sink = NULL;
891 pa_source *source = NULL;
894 pa_sink *combine_sink = NULL;
899 /* if it's from new_data, return here */
900 if (!device || data->origins_from_new_data)
902 if (!data->stream || !data->idx_streams)
904 if (!IS_AUTO_ROUTE_TYPE_SERIES(data->route_type))
907 if (data->stream_type == STREAM_SINK_INPUT) {
908 if (!(sink = pa_tz_device_get_sink(device, data->device_role)))
910 } else if (data->stream_type == STREAM_SOURCE_OUTPUT) {
911 if (!(source = pa_tz_device_get_source(device, data->device_role)))
915 PA_IDXSET_FOREACH(s, data->idx_streams, idx) {
916 if (sink && sink != ((pa_sink_input*)s)->sink) {
917 if ((pa_stream_manager_check_name_is_vstream(s, STREAM_SINK_INPUT, false)))
919 if (((combine_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK)) &&
920 ((pa_sink_input*)s)->sink == combine_sink))
922 if ((pa_stream_manager_check_filter_apply_stream(s, STREAM_SINK_INPUT)))
924 pa_sink_input_move_to(s, sink, false);
925 pa_log_info("[ROUTE][ROLLBACK] *** sink-input(%p,%u) moves to sink(%p,%s)",
926 s, ((pa_sink_input*)s)->index, sink, sink->name);
927 } else if (source && source != ((pa_source_output*)s)->source) {
928 pa_source_output_move_to(s, source, false);
929 pa_log_info("[ROUTE][ROLLBACK] *** source-output(%p,%u) moves to source(%p,%s)",
930 s, ((pa_source_output*)s)->index, source, source->name);
935 static void fill_device_info(hal_route_info *route_info, const char *type, uint32_t direction, uint32_t id) {
936 pa_assert(route_info);
939 route_info->num_of_devices++;
940 route_info->device_infos = pa_xrealloc(route_info->device_infos, sizeof(hal_device_info)*route_info->num_of_devices);
941 route_info->device_infos[route_info->num_of_devices-1].type = type;
942 route_info->device_infos[route_info->num_of_devices-1].direction = direction;
943 route_info->device_infos[route_info->num_of_devices-1].id = id;
944 pa_log_info(" ** filled a matched device to device_infos: type[%-16s], direction[0x%x], id[%u]", type, direction, id);
947 /* Change the route setting according to the data from argument.
948 * This function is called only when it needs to change routing path via HAL.
950 * 1. It will be received when it is needed to terminate playback
951 * or capture routing path.
952 * 2. Update the state of the device to be deactivated.
953 * 3. Call HAL API to "reset" routing.
955 * 1. Find the proper sink/source comparing between avail_devices
956 * and current connected devices.
957 * : Need to check the priority of the device list by order of receipt.
958 * 2. Update the state of devices.
959 * 3. Call HAL API to apply the routing setting
960 * - ROUTE_TYPE_AUTO_ALL
961 * 1. Find the proper sink/source comparing between avail_devices
962 * and current connected devices.
963 * : Might use combine-sink according to the conditions.
964 * 2. Update the state of devices.
965 * 3. Call HAL API to apply the routing setting
966 * - ROUTE_TYPE_MANUAL
967 * 1. Find the proper sink/source comparing between avail_devices
968 * and manual_devices that have been set by user.
969 * 2. Update the state of devices.
970 * 3. Call HAL API to apply the routing setting. */
971 static pa_hook_result_t route_change_hook_cb(pa_core *c, pa_stream_manager_hook_data_for_route *data, struct userdata *u) {
973 hal_route_info route_info = {NULL, NULL, 0};
974 uint32_t *device_id = NULL;
975 uint32_t dm_device_id = 0;
976 stream_route_type_t route_type;
977 const char *device_type = NULL;
978 pa_tz_device *device = NULL;
979 pa_tz_device *latest_device = NULL;
980 const char *dm_device_type = NULL;
981 dm_device_direction_t dm_device_direction = DM_DEVICE_DIRECTION_NONE;
982 pa_sink *sink = NULL;
983 pa_source *source = NULL;
984 pa_sink *combine_sink_arg1 = NULL;
985 pa_sink *combine_sink_arg2 = NULL;
986 bool use_internal_codec = false;
992 pa_log_info("[ROUTE] route_change_hook_cb is called. (%p), stream_type(%d), stream_role(%s), route_type(%d)",
993 data, data->stream_type, data->stream_role, data->route_type);
995 if (data->stream == NULL) {
996 reset_route(u, data->stream_type);
1000 if (!data->idx_avail_devices) {
1001 pa_log_error("[ROUTE] available devices is NULL, do nothing...");
1004 route_info.role = data->stream_role;
1006 if (IS_AUTO_ROUTE_TYPE_SERIES(data->route_type)) {
1007 uint32_t conn_idx = 0;
1008 pa_idxset *conn_devices = NULL;
1010 /* unload module-loopback */
1011 if (u->module_loopback) {
1012 if (data->stream_type == STREAM_SINK_INPUT && u->loopback_args.sink->use_internal_codec)
1013 update_loopback_module(u, false);
1014 else if (data->stream_type == STREAM_SOURCE_OUTPUT && u->loopback_args.source->use_internal_codec)
1015 update_loopback_module(u, false);
1018 /* get current connected devices */
1019 conn_devices = pa_device_manager_get_device_list(u->device_manager);
1020 if (data->route_type == STREAM_ROUTE_TYPE_AUTO || data->route_type == STREAM_ROUTE_TYPE_AUTO_ALL) {
1021 PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
1022 pa_log_debug("[ROUTE][AUTO(_ALL)] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, route_info.role, device_type);
1023 if (!is_cached_device_connected(device_type, data->stream_type))
1025 PA_IDXSET_FOREACH(device, conn_devices, conn_idx) {
1026 dm_device_type = pa_tz_device_get_type(device);
1027 dm_device_direction = pa_tz_device_get_direction(device);
1028 dm_device_id = pa_tz_device_get_id(device);
1029 pa_log_debug(" -- type[%-16s], direction[0x%x], id[%u]",
1030 dm_device_type, dm_device_direction, dm_device_id);
1031 if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
1032 pa_log_debug(" ** found a matched device: type[%-16s], direction[0x%x]", dm_device_type, dm_device_direction);
1033 use_internal_codec = pa_tz_device_is_use_internal_codec(device);
1034 if (use_internal_codec) {
1035 /* if it needs to skip it, keep going to next device for proper UCM setting */
1036 if (skip_device(data->stream_role, dm_device_type))
1038 if (skip_bt_sco_device(u, data->stream_role, dm_device_type))
1040 fill_device_info(&route_info, dm_device_type, CONVERT_TO_HAL_DIRECTION(data->stream_type), dm_device_id);
1042 pa_log_debug(" -- it does not use internal audio codec, skip it");
1049 if (data->route_type == STREAM_ROUTE_TYPE_AUTO) {
1050 if (data->origins_from_new_data)
1051 pa_proplist_sets(GET_STREAM_NEW_PROPLIST(data->stream, data->stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
1053 pa_proplist_sets(GET_STREAM_PROPLIST(data->stream, data->stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
1055 /* unload combine sink */
1056 if (data->stream_type == STREAM_SINK_INPUT) {
1057 if ((sink = pa_tz_device_get_sink(device, data->device_role)))
1058 unload_combine_sink_module(u, SINK_NAME_COMBINED, sink);
1060 pa_log_error("[ROUTE][AUTO] could not get sink");
1063 if (pa_streq(dm_device_type, DEVICE_TYPE_BT_SCO)) {
1064 if (IS_ROLE_AVAILABLE_BT_SCO_OPEN(route_info.role))
1065 update_bt_sco_state(u, true, false);
1067 update_bt_sco_state(u, false, false);
1071 } else if (data->route_type == STREAM_ROUTE_TYPE_AUTO_ALL) {
1075 update_bt_sco_state(u, false, false);
1077 /* find the proper sink/source */
1078 /* currently, we support two sinks for combining */
1079 if (data->stream_type == STREAM_SINK_INPUT && u->module_combine_sink) {
1080 sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK);
1081 pa_log_info("[ROUTE][AUTO_ALL] found the combine_sink already existed");
1082 } else if (data->stream_type == STREAM_SINK_INPUT && !combine_sink_arg1) {
1083 sink = combine_sink_arg1 = pa_tz_device_get_sink(device, data->device_role);
1085 pa_log_info("[ROUTE][AUTO_ALL] combine_sink_arg1[%s], combine_sink_arg2[%p]", sink->name, combine_sink_arg2);
1087 pa_log_error("[ROUTE][AUTO_ALL] could not get sink from pa_device_manager_get_sink");
1088 } else if (data->stream_type == STREAM_SINK_INPUT && !combine_sink_arg2) {
1089 sink = combine_sink_arg2 = pa_tz_device_get_sink(device, data->device_role);
1090 if (sink && !pa_streq(sink->name, combine_sink_arg1->name)) {
1091 pa_log_info("[ROUTE][AUTO_ALL] combine_sink_arg2[%s]", sink->name);
1092 /* load combine sink */
1093 if (!u->module_combine_sink) {
1094 char *args = pa_sprintf_malloc("sink_name=%s slaves=\"%s,%s\"", SINK_NAME_COMBINED, combine_sink_arg1->name, combine_sink_arg2->name);
1095 pa_log_info("[ROUTE][AUTO_ALL] combined sink is not prepared, now load module[%s]", args);
1096 u->module_combine_sink = pa_module_load(u->core, MODULE_COMBINE_SINK, args);
1099 if ((sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_COMBINED, PA_NAMEREG_SINK))) {
1100 PA_IDXSET_FOREACH(s, combine_sink_arg1->inputs, s_idx) {
1101 if (s == data->stream) {
1102 pa_sink_input_move_to(s, sink, false);
1103 pa_log_info("[ROUTE][AUTO_ALL] *** sink-nput(%p,%u) moves to sink(%p,%s)",
1104 s, ((pa_sink_input*)s)->index, sink, sink->name);
1108 pa_log_error("[ROUTE][AUTO_ALL] could not get combine_sink");
1110 } else if (data->stream_type == STREAM_SOURCE_OUTPUT)
1111 source = pa_tz_device_get_source(device, data->device_role);
1113 if (data->origins_from_new_data) {
1114 if (data->stream_type == STREAM_SINK_INPUT)
1115 *(data->proper_sink) = sink;
1117 *(data->proper_source) = source;
1119 /* move sink-inputs/source-outputs if needed */
1120 if (!data->idx_streams)
1122 PA_IDXSET_FOREACH(s, data->idx_streams, s_idx) { /* data->idx_streams: null_sink */
1123 if (pa_stream_manager_get_route_type(s, data->stream_type, false, &route_type))
1125 if (route_type != STREAM_ROUTE_TYPE_AUTO_ALL)
1127 if ((data->stream_type == STREAM_SINK_INPUT) && (sink && (sink != ((pa_sink_input*)s)->sink))) {
1128 pa_sink_input_move_to(s, sink, false);
1129 pa_log_info("[ROUTE][AUTO_ALL] *** sink-input(%p,%u) moves to sink(%p,%s)",
1130 s, ((pa_sink_input*)s)->index, sink, sink->name);
1131 } else if ((data->stream_type == STREAM_SOURCE_OUTPUT) && (source && (source != ((pa_source_output*)s)->source))) {
1132 pa_source_output_move_to(s, source, false);
1133 pa_log_info("[ROUTE][AUTO_ALL] *** source-output(%p,%u) moves to source(%p,%s)",
1134 s, ((pa_source_output*)s)->index, source, source->name);
1140 } else if (data->route_type == STREAM_ROUTE_TYPE_AUTO_LAST_CONNECTED) {
1141 pa_usec_t creation_time = 0;
1142 pa_usec_t latest_creation_time = 0;
1144 PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
1145 pa_log_debug("[ROUTE][AUTO_LAST_CONN] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
1146 if (!is_cached_device_connected(device_type, data->stream_type))
1148 PA_IDXSET_FOREACH(device, conn_devices, conn_idx) {
1149 dm_device_type = pa_tz_device_get_type(device);
1150 dm_device_direction = pa_tz_device_get_direction(device);
1151 dm_device_id = pa_tz_device_get_id(device);
1152 creation_time = pa_tz_device_get_creation_time(device);
1153 pa_log_debug(" -- type[%-16s], direction[0x%x], id[%u], creation_time[%llu]",
1154 dm_device_type, dm_device_direction, dm_device_id, creation_time);
1155 if (pa_streq(device_type, dm_device_type) && IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
1156 use_internal_codec = pa_tz_device_is_use_internal_codec(device);
1157 if (use_internal_codec) {
1158 /* if it needs to skip it, keep going to next device for proper UCM setting */
1159 if (skip_device(data->stream_role, dm_device_type))
1161 if (skip_bt_sco_device(u, data->stream_role, dm_device_type))
1164 if (!latest_device || (latest_creation_time <= creation_time)) {
1165 latest_device = device;
1166 latest_creation_time = creation_time;
1167 pa_log_info(" ** updated the last connected device: type[%-16s], direction[0x%x]", dm_device_type, dm_device_direction);
1172 /* update activated device if it is found */
1173 if (latest_device) {
1174 dm_device_type = pa_tz_device_get_type(latest_device);
1175 dm_device_id = pa_tz_device_get_id(latest_device);
1176 use_internal_codec = pa_tz_device_is_use_internal_codec(latest_device);
1177 if (use_internal_codec)
1178 fill_device_info(&route_info, dm_device_type, CONVERT_TO_HAL_DIRECTION(data->stream_type), dm_device_id);
1180 pa_log_debug(" -- it does not use internal audio codec, skip it");
1182 if (data->origins_from_new_data)
1183 pa_proplist_sets(GET_STREAM_NEW_PROPLIST(data->stream, data->stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
1185 pa_proplist_sets(GET_STREAM_PROPLIST(data->stream, data->stream_type), PA_PROP_MEDIA_ROUTE_AUTO_ACTIVE_DEV, dm_device_type);
1187 /* unload combine sink */
1188 if (data->stream_type == STREAM_SINK_INPUT) {
1189 if ((sink = pa_tz_device_get_sink(latest_device, data->device_role)))
1190 unload_combine_sink_module(u, SINK_NAME_COMBINED, sink);
1192 pa_log_error("[ROUTE][AUTO_LAST_CONN] could not get sink");
1195 if (pa_streq(dm_device_type, DEVICE_TYPE_BT_SCO)) {
1196 if (IS_ROLE_AVAILABLE_BT_SCO_OPEN(route_info.role))
1197 update_bt_sco_state(u, true, false);
1199 update_bt_sco_state(u, false, false);
1202 /* Update device with latest_device to use be used later in this function */
1203 device = latest_device;
1207 } else if (data->route_type == STREAM_ROUTE_TYPE_MANUAL) {
1210 PA_IDXSET_FOREACH(device_type, data->idx_avail_devices, idx) {
1211 pa_log_info("[ROUTE][MANUAL] avail_device[%u] for this role[%-16s]: type[%-16s]", idx, data->stream_role, device_type);
1212 if (!is_cached_device_connected(device_type, data->stream_type))
1214 PA_IDXSET_FOREACH(device_id, data->idx_manual_devices, d_idx) {
1215 pa_log_debug(" -- manual_device[%u] for this role[%-16s]: device_id(%u)", idx, data->stream_role, *device_id);
1216 if (!(device = pa_device_manager_get_device_by_id(u->device_manager, *device_id)))
1218 dm_device_type = pa_tz_device_get_type(device);
1219 if (!pa_streq(device_type, dm_device_type))
1221 dm_device_direction = pa_tz_device_get_direction(device);
1222 pa_log_debug(" ** found a matched device: type[%-16s], direction[0x%x]",
1223 dm_device_type, dm_device_direction);
1224 /* Check for availability for opening Bluetooth SCO */
1225 if (pa_streq(dm_device_type, DEVICE_TYPE_BT_SCO) && IS_ROLE_AVAILABLE_BT_SCO_OPEN(route_info.role)) {
1227 bool is_nrec = false;
1229 /* update BT SCO: open */
1230 if (update_bt_sco_state(u, true, false)) {
1231 pa_log_error(" ** could not open BT SCO");
1232 return PA_HOOK_CANCEL;
1235 if (get_bt_property(u->device_manager, &is_wb, &is_nrec) == 0) {
1236 pa_log_info("bt property : wideband(%d), nrec(%d)", is_wb, is_nrec);
1237 update_bt_route_option(u->hal_interface, route_info.role, "bt-wideband", (int)is_wb);
1238 update_bt_route_option(u->hal_interface, route_info.role, "bt-nrec", (int)is_nrec);
1240 pa_log_warn("Failed to get property for wideband / nrec....");
1242 /* update BT SCO: close */
1243 update_bt_sco_state(u, false, false);
1245 /* Check for in/out devices in case of loopback */
1246 if (pa_streq(data->stream_role, STREAM_ROLE_LOOPBACK)) {
1247 if ((data->stream_type == STREAM_SINK_INPUT) && (dm_device_direction & DM_DEVICE_DIRECTION_OUT))
1248 u->loopback_args.sink = pa_tz_device_get_sink(device, DEVICE_ROLE_NORMAL);
1249 else if ((data->stream_type == STREAM_SOURCE_OUTPUT) && (dm_device_direction & DM_DEVICE_DIRECTION_IN))
1250 u->loopback_args.source = pa_tz_device_get_source(device, DEVICE_ROLE_NORMAL);
1253 if (IS_AVAILABLE_DIRECTION(data->stream_type, dm_device_direction)) {
1254 use_internal_codec = pa_tz_device_is_use_internal_codec(device);
1255 if (use_internal_codec)
1256 fill_device_info(&route_info, dm_device_type, CONVERT_TO_HAL_DIRECTION(data->stream_type), *device_id);
1258 pa_log_debug(" -- it does not use internal audio codec, skip it");
1262 if (pa_streq(data->stream_role, STREAM_ROLE_LOOPBACK)) {
1263 /* load module-loopback */
1264 if (u->loopback_args.sink && u->loopback_args.source)
1265 update_loopback_module(u, true);
1269 route_change_move_streams(u, data, device);
1271 if (route_info.device_infos) {
1272 /* send information to HAL to update route */
1273 if (pa_hal_interface_update_route(u->hal_interface, &route_info))
1274 pa_log_error("[ROUTE] Failed to pa_hal_interface_update_route()");
1275 pa_xfree(route_info.device_infos);
1278 route_change_rollback_streams(u, data, device);
1283 static pa_hook_result_t update_info_hook_cb(pa_core *c, pa_stream_manager_hook_data_for_update_info *data, struct userdata *u) {
1288 if (pa_streq(data->stream_role, STREAM_ROLE_LOOPBACK)) {
1289 pa_log_info("[UPDATE] update_info_hook_cb is called. stream_role(%s) [name(%s)/value(%d)]",
1290 data->stream_role, data->name, data->value);
1291 if (pa_streq(data->name, MSG_FOR_LOOPBACK_ARG_LATENCY))
1292 u->loopback_args.latency_msec = data->value;
1293 else if (pa_streq(data->name, MSG_FOR_LOOPBACK_ARG_ADJUST_TIME))
1294 u->loopback_args.adjust_sec = data->value;
1300 /* Update ref. count of each connected device */
1301 static void update_cached_connected_devices(const char *device_type, dm_device_direction_t direction, bool is_connected) {
1304 int* ptr_out = NULL;
1306 ptr_in = &cached_connected_devices[convert_device_type_str(device_type)][CACHED_DEVICE_DIRECTION_IN];
1307 ptr_out = &cached_connected_devices[convert_device_type_str(device_type)][CACHED_DEVICE_DIRECTION_OUT];
1308 val = (is_connected) ? 1 : -1;
1310 if (direction & DM_DEVICE_DIRECTION_IN)
1312 if (direction & DM_DEVICE_DIRECTION_OUT)
1321 static void dump_connected_devices()
1326 pa_log_debug("== dump cached current device ==");
1327 for (i = 0; i < DEVICE_MAX-1; i++)
1328 pa_log_debug("in: %d, out: %d", cached_connected_devices[i][CACHED_DEVICE_DIRECTION_IN], cached_connected_devices[i][CACHED_DEVICE_DIRECTION_OUT]);
1329 pa_log_debug("================================");
1333 /* Reorganize routing when a device has been connected or disconnected */
1334 static pa_hook_result_t device_connection_changed_hook_cb(pa_core *c, pa_tz_device_hook_data_for_conn_changed *conn, struct userdata *u) {
1336 dm_device_direction_t device_direction = DM_DEVICE_DIRECTION_OUT;
1337 pa_sink *sink = NULL;
1338 pa_sink *null_sink = NULL;
1339 bool use_internal_codec = false;
1340 pa_idxset* conn_devices = NULL;
1341 pa_tz_device *device = NULL;
1347 device_direction = pa_tz_device_get_direction(conn->device);
1349 pa_log_info("[CONN] device_connection_changed_hook_cb is called. conn(%p), is_connected(%d), device(%p), direction(0x%x)",
1350 conn, conn->is_connected, conn->device, device_direction);
1352 update_cached_connected_devices(pa_tz_device_get_type(conn->device), device_direction, conn->is_connected);
1353 dump_connected_devices();
1355 sink = null_sink = (pa_sink*)pa_namereg_get(u->core, SINK_NAME_NULL, PA_NAMEREG_SINK);
1357 pa_log_error("[CONN] could not get null_sink(%p)", null_sink);
1361 use_internal_codec = pa_tz_device_is_use_internal_codec(conn->device);
1362 /* update for unloading modules when external device is disconnected */
1363 if (!use_internal_codec && !conn->is_connected) {
1364 if (device_direction & DM_DEVICE_DIRECTION_OUT) {
1365 /* unload combine sink */
1366 conn_devices = pa_device_manager_get_device_list(u->device_manager);
1367 PA_IDXSET_FOREACH(device, conn_devices, idx) {
1368 device_direction = pa_tz_device_get_direction(device);
1369 if (device_direction == DM_DEVICE_DIRECTION_OUT) {
1370 if ((use_internal_codec = pa_tz_device_is_use_internal_codec(device))) {
1371 sink = pa_tz_device_get_sink(device, DEVICE_ROLE_NORMAL);
1376 unload_combine_sink_module(u, SINK_NAME_COMBINED, sink);
1378 /* unload combine sink for external devices */
1379 unload_combine_sink_module(u, SINK_NAME_COMBINED_EX, null_sink);
1381 /* unload loopback module */
1382 if (u->module_loopback)
1383 if (u->loopback_args.sink == pa_tz_device_get_sink(conn->device, DEVICE_ROLE_NORMAL))
1384 update_loopback_module(u, false);
1386 if (device_direction & DM_DEVICE_DIRECTION_IN) {
1387 /* unload loopback module */
1388 if (u->module_loopback)
1389 if (u->loopback_args.source == pa_tz_device_get_source(conn->device, DEVICE_ROLE_NORMAL))
1390 update_loopback_module(u, false);
1397 int pa__init(pa_module *m)
1399 pa_modargs *ma = NULL;
1405 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1406 pa_log("Failed to parse module arguments");
1410 m->userdata = u = pa_xnew0(struct userdata, 1);
1414 u->hal_interface = pa_hal_interface_get(u->core);
1416 if ((u->communicator.comm = pa_communicator_get(u->core))) {
1417 u->communicator.comm_hook_select_proper_sink_or_source_slot = pa_hook_connect(
1418 pa_communicator_hook(u->communicator.comm, PA_COMMUNICATOR_HOOK_SELECT_INIT_SINK_OR_SOURCE),
1419 PA_HOOK_EARLY, (pa_hook_cb_t)select_proper_sink_or_source_hook_cb, u);
1420 u->communicator.comm_hook_change_route_slot = pa_hook_connect(
1421 pa_communicator_hook(u->communicator.comm, PA_COMMUNICATOR_HOOK_CHANGE_ROUTE),
1422 PA_HOOK_EARLY, (pa_hook_cb_t)route_change_hook_cb, u);
1423 u->communicator.comm_hook_device_connection_changed_slot = pa_hook_connect(
1424 pa_communicator_hook(u->communicator.comm, PA_COMMUNICATOR_HOOK_DEVICE_CONNECTION_CHANGED),
1425 PA_HOOK_EARLY, (pa_hook_cb_t)device_connection_changed_hook_cb, u);
1426 u->communicator.comm_hook_update_info_slot = pa_hook_connect(
1427 pa_communicator_hook(u->communicator.comm, PA_COMMUNICATOR_HOOK_UPDATE_INFORMATION),
1428 PA_HOOK_EARLY, (pa_hook_cb_t)update_info_hook_cb, u);
1430 u->device_manager = pa_device_manager_get(u->core);
1432 u->stream_manager = pa_stream_manager_init(u->core);
1434 /* load null sink/source */
1435 args = pa_sprintf_malloc("sink_name=%s", SINK_NAME_NULL);
1436 u->module_null_sink = pa_module_load(u->core, MODULE_NULL_SINK, args);
1438 args = pa_sprintf_malloc("source_name=%s", SOURCE_NAME_NULL);
1439 u->module_null_source = pa_module_load(u->core, MODULE_NULL_SOURCE, args);
1442 __load_dump_config(u);
1444 pa_log_info("Tizen Audio Policy module is loaded\n");
1447 pa_modargs_free(ma);
1457 void pa__done(pa_module *m)
1463 if (!(u = m->userdata))
1466 pa_module_unload(u->core, u->module_null_sink, true);
1467 u->module_null_sink = NULL;
1468 pa_module_unload(u->core, u->module_null_source, true);
1469 u->module_null_source = NULL;
1471 if (u->device_manager)
1472 pa_device_manager_unref(u->device_manager);
1474 if (u->stream_manager)
1475 pa_stream_manager_done(u->stream_manager);
1477 if (u->communicator.comm) {
1478 if (u->communicator.comm_hook_select_proper_sink_or_source_slot)
1479 pa_hook_slot_free(u->communicator.comm_hook_select_proper_sink_or_source_slot);
1480 if (u->communicator.comm_hook_change_route_slot)
1481 pa_hook_slot_free(u->communicator.comm_hook_change_route_slot);
1482 if (u->communicator.comm_hook_device_connection_changed_slot)
1483 pa_hook_slot_free(u->communicator.comm_hook_device_connection_changed_slot);
1484 if (u->communicator.comm_hook_update_info_slot)
1485 pa_hook_slot_free(u->communicator.comm_hook_update_info_slot);
1486 pa_communicator_unref(u->communicator.comm);
1489 if (u->hal_interface)
1490 pa_hal_interface_unref(u->hal_interface);
1492 bt_sco_close(u, false);
1496 pa_log_info("Tizen Audio Policy module is unloaded\n");