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");
59 PA_HASHMAP_FOREACH(profile
, port
->profiles
, state
) {
60 if (best_profile
&& best_profile
->priority
>= profile
->priority
)
63 /* We make a best effort to keep other direction unchanged */
64 if (card
->active_profile
&& !port
->is_input
) {
65 if (card
->active_profile
->n_sources
!= profile
->n_sources
)
68 if (card
->active_profile
->max_source_channels
!= profile
->max_source_channels
)
72 if (card
->active_profile
&& !port
->is_output
) {
73 if (card
->active_profile
->n_sinks
!= profile
->n_sinks
)
76 if (card
->active_profile
->max_sink_channels
!= profile
->max_sink_channels
)
80 best_profile
= profile
;
84 pa_log_debug("No suitable profile found");
88 if (pa_card_set_profile(card
, best_profile
->name
, FALSE
) != 0) {
89 pa_log_debug("Could not set profile %s", best_profile
->name
);
96 static void find_sink_and_source(pa_card
*card
, pa_device_port
*port
, pa_sink
**si
, pa_source
**so
)
99 pa_source
*source
= NULL
;
103 PA_IDXSET_FOREACH(sink
, card
->sinks
, state
)
104 if (sink
->ports
&& port
== pa_hashmap_get(sink
->ports
, port
->name
))
108 PA_IDXSET_FOREACH(source
, card
->sources
, state
)
109 if (source
->ports
&& port
== pa_hashmap_get(source
->ports
, port
->name
))
116 static pa_hook_result_t
port_available_hook_callback(pa_core
*c
, pa_device_port
*port
, void* userdata
) {
121 pa_bool_t is_active_profile
, is_active_port
;
123 if (port
->available
== PA_PORT_AVAILABLE_UNKNOWN
)
126 pa_log_debug("finding port %s", port
->name
);
128 PA_IDXSET_FOREACH(card
, c
->cards
, state
)
129 if (card
->ports
&& port
== pa_hashmap_get(card
->ports
, port
->name
))
133 pa_log_warn("Did not find port %s in array of cards", port
->name
);
137 find_sink_and_source(card
, port
, &sink
, &source
);
139 is_active_profile
= port
->profiles
&& card
->active_profile
&&
140 card
->active_profile
== pa_hashmap_get(port
->profiles
, card
->active_profile
->name
);
141 is_active_port
= (sink
&& sink
->active_port
== port
) || (source
&& source
->active_port
== port
);
143 if (port
->available
== PA_PORT_AVAILABLE_NO
&& !is_active_port
)
146 if (port
->available
== PA_PORT_AVAILABLE_YES
) {
150 if (!is_active_profile
) {
151 if (!try_to_switch_profile(card
, port
))
154 pa_assert(card
->active_profile
== pa_hashmap_get(port
->profiles
, card
->active_profile
->name
));
156 /* Now that profile has changed, our sink and source pointers must be updated */
157 find_sink_and_source(card
, port
, &sink
, &source
);
161 pa_source_set_port(source
, port
->name
, FALSE
);
163 pa_sink_set_port(sink
, port
->name
, FALSE
);
166 if (port
->available
== PA_PORT_AVAILABLE_NO
) {
168 pa_device_port
*p2
= find_best_port(sink
->ports
);
170 if (p2
&& p2
->available
!= PA_PORT_AVAILABLE_NO
)
171 pa_sink_set_port(sink
, p2
->name
, FALSE
);
173 /* Maybe try to switch to another profile? */
178 pa_device_port
*p2
= find_best_port(source
->ports
);
180 if (p2
&& p2
->available
!= PA_PORT_AVAILABLE_NO
)
181 pa_source_set_port(source
, p2
->name
, FALSE
);
183 /* Maybe try to switch to another profile? */
191 static void handle_all_unavailable(pa_core
*core
) {
195 PA_IDXSET_FOREACH(card
, core
->cards
, state
) {
196 pa_device_port
*port
;
202 PA_HASHMAP_FOREACH(port
, card
->ports
, state2
) {
203 if (port
->available
== PA_PORT_AVAILABLE_NO
)
204 port_available_hook_callback(core
, port
, NULL
);
209 int pa__init(pa_module
*m
) {
214 m
->userdata
= u
= pa_xnew(struct userdata
, 1);
216 u
->callback_slot
= pa_hook_connect(&m
->core
->hooks
[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED
],
217 PA_HOOK_LATE
, (pa_hook_cb_t
) port_available_hook_callback
, u
);
219 handle_all_unavailable(m
->core
);
224 void pa__done(pa_module
*m
) {
229 if (!(u
= m
->userdata
))
232 if (u
->callback_slot
)
233 pa_hook_slot_free(u
->callback_slot
);