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 pa_device_port
* find_best_port(pa_hashmap
*ports
) {
41 pa_device_port
* port
, *result
= NULL
;
43 PA_HASHMAP_FOREACH(port
, ports
, state
) {
45 result
->available
== PA_AVAILABLE_NO
||
46 (port
->available
!= PA_AVAILABLE_NO
&& port
->priority
> result
->priority
)) {
54 static bool profile_good_for_output(pa_card_profile
*profile
) {
60 if (profile
->card
->active_profile
->n_sources
!= profile
->n_sources
)
63 if (profile
->card
->active_profile
->max_source_channels
!= profile
->max_source_channels
)
66 /* Try not to switch to HDMI sinks from analog when HDMI is becoming available */
67 PA_IDXSET_FOREACH(sink
, profile
->card
->sinks
, idx
) {
68 if (!sink
->active_port
)
71 if (sink
->active_port
->available
!= PA_AVAILABLE_NO
)
78 static bool profile_good_for_input(pa_card_profile
*profile
) {
81 if (profile
->card
->active_profile
->n_sinks
!= profile
->n_sinks
)
84 if (profile
->card
->active_profile
->max_sink_channels
!= profile
->max_sink_channels
)
90 static int try_to_switch_profile(pa_device_port
*port
) {
91 pa_card_profile
*best_profile
= NULL
, *profile
;
94 pa_log_debug("Finding best profile");
96 PA_HASHMAP_FOREACH(profile
, port
->profiles
, state
) {
99 if (best_profile
&& best_profile
->priority
>= profile
->priority
)
102 /* We make a best effort to keep other direction unchanged */
103 switch (port
->direction
) {
104 case PA_DIRECTION_OUTPUT
:
105 good
= profile_good_for_output(profile
);
108 case PA_DIRECTION_INPUT
:
109 good
= profile_good_for_input(profile
);
116 best_profile
= profile
;
120 pa_log_debug("No suitable profile found");
124 if (pa_card_set_profile(port
->card
, best_profile
, false) != 0) {
125 pa_log_debug("Could not set profile %s", best_profile
->name
);
132 static void find_sink_and_source(pa_card
*card
, pa_device_port
*port
, pa_sink
**si
, pa_source
**so
) {
133 pa_sink
*sink
= NULL
;
134 pa_source
*source
= NULL
;
137 switch (port
->direction
) {
138 case PA_DIRECTION_OUTPUT
:
139 PA_IDXSET_FOREACH(sink
, card
->sinks
, state
)
140 if (port
== pa_hashmap_get(sink
->ports
, port
->name
))
144 case PA_DIRECTION_INPUT
:
145 PA_IDXSET_FOREACH(source
, card
->sources
, state
)
146 if (port
== pa_hashmap_get(source
->ports
, port
->name
))
155 static pa_hook_result_t
port_available_hook_callback(pa_core
*c
, pa_device_port
*port
, void* userdata
) {
159 bool is_active_profile
, is_active_port
;
161 if (port
->available
== PA_AVAILABLE_UNKNOWN
)
167 pa_log_warn("Port %s does not have a card", port
->name
);
171 if (pa_idxset_size(card
->sinks
) == 0 && pa_idxset_size(card
->sources
) == 0)
172 /* This card is not initialized yet. We'll handle it in
173 sink_new / source_new callbacks later. */
176 find_sink_and_source(card
, port
, &sink
, &source
);
178 is_active_profile
= card
->active_profile
== pa_hashmap_get(port
->profiles
, card
->active_profile
->name
);
179 is_active_port
= (sink
&& sink
->active_port
== port
) || (source
&& source
->active_port
== port
);
181 if (port
->available
== PA_AVAILABLE_NO
&& !is_active_port
)
184 if (port
->available
== PA_AVAILABLE_YES
) {
188 if (!is_active_profile
) {
189 if (try_to_switch_profile(port
) < 0)
192 pa_assert(card
->active_profile
== pa_hashmap_get(port
->profiles
, card
->active_profile
->name
));
194 /* Now that profile has changed, our sink and source pointers must be updated */
195 find_sink_and_source(card
, port
, &sink
, &source
);
199 pa_source_set_port(source
, port
->name
, false);
201 pa_sink_set_port(sink
, port
->name
, false);
204 if (port
->available
== PA_AVAILABLE_NO
) {
206 pa_device_port
*p2
= find_best_port(sink
->ports
);
208 if (p2
&& p2
->available
!= PA_AVAILABLE_NO
)
209 pa_sink_set_port(sink
, p2
->name
, false);
211 /* Maybe try to switch to another profile? */
216 pa_device_port
*p2
= find_best_port(source
->ports
);
218 if (p2
&& p2
->available
!= PA_AVAILABLE_NO
)
219 pa_source_set_port(source
, p2
->name
, false);
221 /* Maybe try to switch to another profile? */
229 static void handle_all_unavailable(pa_core
*core
) {
233 PA_IDXSET_FOREACH(card
, core
->cards
, state
) {
234 pa_device_port
*port
;
237 PA_HASHMAP_FOREACH(port
, card
->ports
, state2
) {
238 if (port
->available
== PA_AVAILABLE_NO
)
239 port_available_hook_callback(core
, port
, NULL
);
244 static pa_device_port
*new_sink_source(pa_hashmap
*ports
, const char *name
) {
247 pa_device_port
*i
, *p
= NULL
;
252 p
= pa_hashmap_get(ports
, name
);
254 PA_HASHMAP_FOREACH(i
, ports
, state
)
255 if (!p
|| i
->priority
> p
->priority
)
259 if (p
->available
!= PA_AVAILABLE_NO
)
262 pa_assert_se(p
= find_best_port(ports
));
266 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
268 pa_device_port
*p
= new_sink_source(new_data
->ports
, new_data
->active_port
);
271 pa_log_debug("Switching initial port for sink '%s' to '%s'", new_data
->name
, p
->name
);
272 pa_sink_new_data_set_port(new_data
, p
->name
);
277 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
279 pa_device_port
*p
= new_sink_source(new_data
->ports
, new_data
->active_port
);
282 pa_log_debug("Switching initial port for source '%s' to '%s'", new_data
->name
,
283 new_data
->active_port
);
284 pa_source_new_data_set_port(new_data
, p
->name
);
289 int pa__init(pa_module
*m
) {
294 m
->userdata
= u
= pa_xnew(struct userdata
, 1);
296 /* Make sure we are after module-device-restore, so we can overwrite that suggestion if necessary */
297 u
->sink_new_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SINK_NEW
],
298 PA_HOOK_NORMAL
, (pa_hook_cb_t
) sink_new_hook_callback
, u
);
299 u
->source_new_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SOURCE_NEW
],
300 PA_HOOK_NORMAL
, (pa_hook_cb_t
) source_new_hook_callback
, u
);
301 u
->available_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED
],
302 PA_HOOK_LATE
, (pa_hook_cb_t
) port_available_hook_callback
, u
);
304 handle_all_unavailable(m
->core
);
309 void pa__done(pa_module
*m
) {
314 if (!(u
= m
->userdata
))
317 if (u
->available_slot
)
318 pa_hook_slot_free(u
->available_slot
);
319 if (u
->sink_new_slot
)
320 pa_hook_slot_free(u
->sink_new_slot
);
321 if (u
->source_new_slot
)
322 pa_hook_slot_free(u
->source_new_slot
);