2 This file is part of PulseAudio.
4 Copyright 2006 Lennart Poettering
5 Copyright 2011 Canonical Ltd
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 #include <pulsecore/core.h>
28 #include <pulsecore/device-port.h>
29 #include <pulsecore/hashmap.h>
31 #include "module-switch-on-port-available-symdef.h"
34 pa_hook_slot
*available_slot
;
35 pa_hook_slot
*sink_new_slot
;
36 pa_hook_slot
*source_new_slot
;
39 static bool profile_good_for_output(pa_card_profile
*profile
) {
45 if (profile
->card
->active_profile
->n_sources
!= profile
->n_sources
)
48 if (profile
->card
->active_profile
->max_source_channels
!= profile
->max_source_channels
)
51 /* Try not to switch to HDMI sinks from analog when HDMI is becoming available */
52 PA_IDXSET_FOREACH(sink
, profile
->card
->sinks
, idx
) {
53 if (!sink
->active_port
)
56 if (sink
->active_port
->available
!= PA_AVAILABLE_NO
)
63 static bool profile_good_for_input(pa_card_profile
*profile
) {
66 if (profile
->card
->active_profile
->n_sinks
!= profile
->n_sinks
)
69 if (profile
->card
->active_profile
->max_sink_channels
!= profile
->max_sink_channels
)
75 static int try_to_switch_profile(pa_device_port
*port
) {
76 pa_card_profile
*best_profile
= NULL
, *profile
;
79 pa_log_debug("Finding best profile");
81 PA_HASHMAP_FOREACH(profile
, port
->profiles
, state
) {
84 if (best_profile
&& best_profile
->priority
>= profile
->priority
)
87 /* We make a best effort to keep other direction unchanged */
88 switch (port
->direction
) {
89 case PA_DIRECTION_OUTPUT
:
90 good
= profile_good_for_output(profile
);
93 case PA_DIRECTION_INPUT
:
94 good
= profile_good_for_input(profile
);
101 best_profile
= profile
;
105 pa_log_debug("No suitable profile found");
109 if (pa_card_set_profile(port
->card
, best_profile
, false) != 0) {
110 pa_log_debug("Could not set profile %s", best_profile
->name
);
117 static void find_sink_and_source(pa_card
*card
, pa_device_port
*port
, pa_sink
**si
, pa_source
**so
) {
118 pa_sink
*sink
= NULL
;
119 pa_source
*source
= NULL
;
122 switch (port
->direction
) {
123 case PA_DIRECTION_OUTPUT
:
124 PA_IDXSET_FOREACH(sink
, card
->sinks
, state
)
125 if (port
== pa_hashmap_get(sink
->ports
, port
->name
))
129 case PA_DIRECTION_INPUT
:
130 PA_IDXSET_FOREACH(source
, card
->sources
, state
)
131 if (port
== pa_hashmap_get(source
->ports
, port
->name
))
140 static pa_hook_result_t
port_available_hook_callback(pa_core
*c
, pa_device_port
*port
, void* userdata
) {
144 bool is_active_profile
, is_active_port
;
146 if (port
->available
== PA_AVAILABLE_UNKNOWN
)
152 pa_log_warn("Port %s does not have a card", port
->name
);
156 if (pa_idxset_size(card
->sinks
) == 0 && pa_idxset_size(card
->sources
) == 0)
157 /* This card is not initialized yet. We'll handle it in
158 sink_new / source_new callbacks later. */
161 find_sink_and_source(card
, port
, &sink
, &source
);
163 is_active_profile
= card
->active_profile
== pa_hashmap_get(port
->profiles
, card
->active_profile
->name
);
164 is_active_port
= (sink
&& sink
->active_port
== port
) || (source
&& source
->active_port
== port
);
166 if (port
->available
== PA_AVAILABLE_NO
&& !is_active_port
)
169 if (port
->available
== PA_AVAILABLE_YES
) {
173 if (!is_active_profile
) {
174 if (try_to_switch_profile(port
) < 0)
177 pa_assert(card
->active_profile
== pa_hashmap_get(port
->profiles
, card
->active_profile
->name
));
179 /* Now that profile has changed, our sink and source pointers must be updated */
180 find_sink_and_source(card
, port
, &sink
, &source
);
184 pa_source_set_port(source
, port
->name
, false);
186 pa_sink_set_port(sink
, port
->name
, false);
189 if (port
->available
== PA_AVAILABLE_NO
) {
191 pa_device_port
*p2
= pa_device_port_find_best(sink
->ports
);
193 if (p2
&& p2
->available
!= PA_AVAILABLE_NO
)
194 pa_sink_set_port(sink
, p2
->name
, false);
196 /* Maybe try to switch to another profile? */
201 pa_device_port
*p2
= pa_device_port_find_best(source
->ports
);
203 if (p2
&& p2
->available
!= PA_AVAILABLE_NO
)
204 pa_source_set_port(source
, p2
->name
, false);
206 /* Maybe try to switch to another profile? */
214 static void handle_all_unavailable(pa_core
*core
) {
218 PA_IDXSET_FOREACH(card
, core
->cards
, state
) {
219 pa_device_port
*port
;
222 PA_HASHMAP_FOREACH(port
, card
->ports
, state2
) {
223 if (port
->available
== PA_AVAILABLE_NO
)
224 port_available_hook_callback(core
, port
, NULL
);
229 static pa_device_port
*new_sink_source(pa_hashmap
*ports
, const char *name
) {
232 pa_device_port
*i
, *p
= NULL
;
237 p
= pa_hashmap_get(ports
, name
);
239 PA_HASHMAP_FOREACH(i
, ports
, state
)
240 if (!p
|| i
->priority
> p
->priority
)
244 if (p
->available
!= PA_AVAILABLE_NO
)
247 pa_assert_se(p
= pa_device_port_find_best(ports
));
251 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
253 pa_device_port
*p
= new_sink_source(new_data
->ports
, new_data
->active_port
);
256 pa_log_debug("Switching initial port for sink '%s' to '%s'", new_data
->name
, p
->name
);
257 pa_sink_new_data_set_port(new_data
, p
->name
);
262 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
264 pa_device_port
*p
= new_sink_source(new_data
->ports
, new_data
->active_port
);
267 pa_log_debug("Switching initial port for source '%s' to '%s'", new_data
->name
,
268 new_data
->active_port
);
269 pa_source_new_data_set_port(new_data
, p
->name
);
274 int pa__init(pa_module
*m
) {
279 m
->userdata
= u
= pa_xnew(struct userdata
, 1);
281 /* Make sure we are after module-device-restore, so we can overwrite that suggestion if necessary */
282 u
->sink_new_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SINK_NEW
],
283 PA_HOOK_NORMAL
, (pa_hook_cb_t
) sink_new_hook_callback
, u
);
284 u
->source_new_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SOURCE_NEW
],
285 PA_HOOK_NORMAL
, (pa_hook_cb_t
) source_new_hook_callback
, u
);
286 u
->available_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED
],
287 PA_HOOK_LATE
, (pa_hook_cb_t
) port_available_hook_callback
, u
);
289 handle_all_unavailable(m
->core
);
294 void pa__done(pa_module
*m
) {
299 if (!(u
= m
->userdata
))
302 if (u
->available_slot
)
303 pa_hook_slot_free(u
->available_slot
);
304 if (u
->sink_new_slot
)
305 pa_hook_slot_free(u
->sink_new_slot
);
306 if (u
->source_new_slot
)
307 pa_hook_slot_free(u
->source_new_slot
);