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
*callback_slot
;
37 static pa_device_port
* find_best_port(pa_hashmap
*ports
) {
39 pa_device_port
* port
, *result
= NULL
;
41 PA_HASHMAP_FOREACH(port
, ports
, state
) {
43 result
->available
== PA_PORT_AVAILABLE_NO
||
44 (port
->available
!= PA_PORT_AVAILABLE_NO
&& port
->priority
> result
->priority
)) {
52 static pa_bool_t
try_to_switch_profile(pa_card
*card
, pa_device_port
*port
) {
53 pa_card_profile
*best_profile
= NULL
, *profile
;
56 pa_log_debug("Finding best profile");
58 PA_HASHMAP_FOREACH(profile
, port
->profiles
, state
) {
59 if (best_profile
&& best_profile
->priority
>= profile
->priority
)
62 /* We make a best effort to keep other direction unchanged */
63 if (!port
->is_input
) {
64 if (card
->active_profile
->n_sources
!= profile
->n_sources
)
67 if (card
->active_profile
->max_source_channels
!= profile
->max_source_channels
)
71 if (!port
->is_output
) {
72 if (card
->active_profile
->n_sinks
!= profile
->n_sinks
)
75 if (card
->active_profile
->max_sink_channels
!= profile
->max_sink_channels
)
79 if (port
->is_output
) {
80 /* Try not to switch to HDMI sinks from analog when HDMI is becoming available */
83 pa_bool_t found_active_port
= FALSE
;
85 PA_IDXSET_FOREACH(sink
, card
->sinks
, state2
) {
86 if (!sink
->active_port
)
88 if (sink
->active_port
->available
!= PA_PORT_AVAILABLE_NO
)
89 found_active_port
= TRUE
;
92 if (found_active_port
)
96 best_profile
= profile
;
100 pa_log_debug("No suitable profile found");
104 if (pa_card_set_profile(card
, best_profile
->name
, FALSE
) != 0) {
105 pa_log_debug("Could not set profile %s", best_profile
->name
);
112 static void find_sink_and_source(pa_card
*card
, pa_device_port
*port
, pa_sink
**si
, pa_source
**so
)
114 pa_sink
*sink
= NULL
;
115 pa_source
*source
= NULL
;
119 PA_IDXSET_FOREACH(sink
, card
->sinks
, state
)
120 if (port
== pa_hashmap_get(sink
->ports
, port
->name
))
124 PA_IDXSET_FOREACH(source
, card
->sources
, state
)
125 if (port
== pa_hashmap_get(source
->ports
, port
->name
))
132 static pa_hook_result_t
port_available_hook_callback(pa_core
*c
, pa_device_port
*port
, void* userdata
) {
137 pa_bool_t is_active_profile
, is_active_port
;
139 if (port
->available
== PA_PORT_AVAILABLE_UNKNOWN
)
142 pa_log_debug("finding port %s", port
->name
);
144 PA_IDXSET_FOREACH(card
, c
->cards
, state
)
145 if (card
->ports
&& port
== pa_hashmap_get(card
->ports
, port
->name
))
149 pa_log_warn("Did not find port %s in array of cards", port
->name
);
153 find_sink_and_source(card
, port
, &sink
, &source
);
155 is_active_profile
= card
->active_profile
== pa_hashmap_get(port
->profiles
, card
->active_profile
->name
);
156 is_active_port
= (sink
&& sink
->active_port
== port
) || (source
&& source
->active_port
== port
);
158 if (port
->available
== PA_PORT_AVAILABLE_NO
&& !is_active_port
)
161 if (port
->available
== PA_PORT_AVAILABLE_YES
) {
165 if (!is_active_profile
) {
166 if (!try_to_switch_profile(card
, port
))
169 pa_assert(card
->active_profile
== pa_hashmap_get(port
->profiles
, card
->active_profile
->name
));
171 /* Now that profile has changed, our sink and source pointers must be updated */
172 find_sink_and_source(card
, port
, &sink
, &source
);
176 pa_source_set_port(source
, port
->name
, FALSE
);
178 pa_sink_set_port(sink
, port
->name
, FALSE
);
181 if (port
->available
== PA_PORT_AVAILABLE_NO
) {
183 pa_device_port
*p2
= find_best_port(sink
->ports
);
185 if (p2
&& p2
->available
!= PA_PORT_AVAILABLE_NO
)
186 pa_sink_set_port(sink
, p2
->name
, FALSE
);
188 /* Maybe try to switch to another profile? */
193 pa_device_port
*p2
= find_best_port(source
->ports
);
195 if (p2
&& p2
->available
!= PA_PORT_AVAILABLE_NO
)
196 pa_source_set_port(source
, p2
->name
, FALSE
);
198 /* Maybe try to switch to another profile? */
206 static void handle_all_unavailable(pa_core
*core
) {
210 PA_IDXSET_FOREACH(card
, core
->cards
, state
) {
211 pa_device_port
*port
;
217 PA_HASHMAP_FOREACH(port
, card
->ports
, state2
) {
218 if (port
->available
== PA_PORT_AVAILABLE_NO
)
219 port_available_hook_callback(core
, port
, NULL
);
224 int pa__init(pa_module
*m
) {
229 m
->userdata
= u
= pa_xnew(struct userdata
, 1);
231 u
->callback_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED
],
232 PA_HOOK_LATE
, (pa_hook_cb_t
) port_available_hook_callback
, u
);
234 handle_all_unavailable(m
->core
);
239 void pa__done(pa_module
*m
) {
244 if (!(u
= m
->userdata
))
247 if (u
->callback_slot
)
248 pa_hook_slot_free(u
->callback_slot
);