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
) {
160 bool is_active_profile
, is_active_port
;
162 if (port
->available
== PA_AVAILABLE_UNKNOWN
)
165 pa_log_debug("finding port %s", port
->name
);
167 PA_IDXSET_FOREACH(card
, c
->cards
, state
)
168 if (port
== pa_hashmap_get(card
->ports
, port
->name
))
172 pa_log_warn("Did not find port %s in array of cards", port
->name
);
176 if (pa_idxset_size(card
->sinks
) == 0 && pa_idxset_size(card
->sources
) == 0)
177 /* This card is not initialized yet. We'll handle it in
178 sink_new / source_new callbacks later. */
181 find_sink_and_source(card
, port
, &sink
, &source
);
183 is_active_profile
= card
->active_profile
== pa_hashmap_get(port
->profiles
, card
->active_profile
->name
);
184 is_active_port
= (sink
&& sink
->active_port
== port
) || (source
&& source
->active_port
== port
);
186 if (port
->available
== PA_AVAILABLE_NO
&& !is_active_port
)
189 if (port
->available
== PA_AVAILABLE_YES
) {
193 if (!is_active_profile
) {
194 if (try_to_switch_profile(port
) < 0)
197 pa_assert(card
->active_profile
== pa_hashmap_get(port
->profiles
, card
->active_profile
->name
));
199 /* Now that profile has changed, our sink and source pointers must be updated */
200 find_sink_and_source(card
, port
, &sink
, &source
);
204 pa_source_set_port(source
, port
->name
, false);
206 pa_sink_set_port(sink
, port
->name
, false);
209 if (port
->available
== PA_AVAILABLE_NO
) {
211 pa_device_port
*p2
= find_best_port(sink
->ports
);
213 if (p2
&& p2
->available
!= PA_AVAILABLE_NO
)
214 pa_sink_set_port(sink
, p2
->name
, false);
216 /* Maybe try to switch to another profile? */
221 pa_device_port
*p2
= find_best_port(source
->ports
);
223 if (p2
&& p2
->available
!= PA_AVAILABLE_NO
)
224 pa_source_set_port(source
, p2
->name
, false);
226 /* Maybe try to switch to another profile? */
234 static void handle_all_unavailable(pa_core
*core
) {
238 PA_IDXSET_FOREACH(card
, core
->cards
, state
) {
239 pa_device_port
*port
;
242 PA_HASHMAP_FOREACH(port
, card
->ports
, state2
) {
243 if (port
->available
== PA_AVAILABLE_NO
)
244 port_available_hook_callback(core
, port
, NULL
);
249 static pa_device_port
*new_sink_source(pa_hashmap
*ports
, const char *name
) {
252 pa_device_port
*i
, *p
= NULL
;
257 p
= pa_hashmap_get(ports
, name
);
259 PA_HASHMAP_FOREACH(i
, ports
, state
)
260 if (!p
|| i
->priority
> p
->priority
)
264 if (p
->available
!= PA_AVAILABLE_NO
)
267 pa_assert_se(p
= find_best_port(ports
));
271 static pa_hook_result_t
sink_new_hook_callback(pa_core
*c
, pa_sink_new_data
*new_data
, struct userdata
*u
) {
273 pa_device_port
*p
= new_sink_source(new_data
->ports
, new_data
->active_port
);
276 pa_log_debug("Switching initial port for sink '%s' to '%s'", new_data
->name
, p
->name
);
277 pa_sink_new_data_set_port(new_data
, p
->name
);
282 static pa_hook_result_t
source_new_hook_callback(pa_core
*c
, pa_source_new_data
*new_data
, struct userdata
*u
) {
284 pa_device_port
*p
= new_sink_source(new_data
->ports
, new_data
->active_port
);
287 pa_log_debug("Switching initial port for source '%s' to '%s'", new_data
->name
,
288 new_data
->active_port
);
289 pa_source_new_data_set_port(new_data
, p
->name
);
294 int pa__init(pa_module
*m
) {
299 m
->userdata
= u
= pa_xnew(struct userdata
, 1);
301 /* Make sure we are after module-device-restore, so we can overwrite that suggestion if necessary */
302 u
->sink_new_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SINK_NEW
],
303 PA_HOOK_NORMAL
, (pa_hook_cb_t
) sink_new_hook_callback
, u
);
304 u
->source_new_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_SOURCE_NEW
],
305 PA_HOOK_NORMAL
, (pa_hook_cb_t
) source_new_hook_callback
, u
);
306 u
->available_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED
],
307 PA_HOOK_LATE
, (pa_hook_cb_t
) port_available_hook_callback
, u
);
309 handle_all_unavailable(m
->core
);
314 void pa__done(pa_module
*m
) {
319 if (!(u
= m
->userdata
))
322 if (u
->available_slot
)
323 pa_hook_slot_free(u
->available_slot
);
324 if (u
->sink_new_slot
)
325 pa_hook_slot_free(u
->sink_new_slot
);
326 if (u
->source_new_slot
)
327 pa_hook_slot_free(u
->source_new_slot
);