Assume that the profiles hashmap of ports is always non-NULL.
[pulseaudio-raopUDP/pulseaudio-raop-alac.git] / src / modules / module-switch-on-port-available.c
blob4c3d4afa75cb81b55f5318c6b86c167a4179bf16
1 /***
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
20 USA.
21 ***/
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
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"
33 struct userdata {
34 pa_hook_slot *callback_slot;
37 static pa_device_port* find_best_port(pa_hashmap *ports) {
38 void *state;
39 pa_device_port* port, *result = NULL;
41 PA_HASHMAP_FOREACH(port, ports, state) {
42 if (result == NULL ||
43 result->available == PA_PORT_AVAILABLE_NO ||
44 (port->available != PA_PORT_AVAILABLE_NO && port->priority > result->priority)) {
45 result = port;
49 return result;
52 static pa_bool_t try_to_switch_profile(pa_card *card, pa_device_port *port) {
53 pa_card_profile *best_profile = NULL, *profile;
54 void *state;
56 pa_log_debug("Finding best profile");
58 PA_HASHMAP_FOREACH(profile, port->profiles, state) {
59 if (best_profile && best_profile->priority >= profile->priority)
60 continue;
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)
65 continue;
67 if (card->active_profile->max_source_channels != profile->max_source_channels)
68 continue;
71 if (!port->is_output) {
72 if (card->active_profile->n_sinks != profile->n_sinks)
73 continue;
75 if (card->active_profile->max_sink_channels != profile->max_sink_channels)
76 continue;
79 if (port->is_output) {
80 /* Try not to switch to HDMI sinks from analog when HDMI is becoming available */
81 uint32_t state2;
82 pa_sink *sink;
83 pa_bool_t found_active_port = FALSE;
85 PA_IDXSET_FOREACH(sink, card->sinks, state2) {
86 if (!sink->active_port)
87 continue;
88 if (sink->active_port->available != PA_PORT_AVAILABLE_NO)
89 found_active_port = TRUE;
92 if (found_active_port)
93 continue;
96 best_profile = profile;
99 if (!best_profile) {
100 pa_log_debug("No suitable profile found");
101 return FALSE;
104 if (pa_card_set_profile(card, best_profile->name, FALSE) != 0) {
105 pa_log_debug("Could not set profile %s", best_profile->name);
106 return FALSE;
109 return TRUE;
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;
116 uint32_t state;
118 if (port->is_output)
119 PA_IDXSET_FOREACH(sink, card->sinks, state)
120 if (port == pa_hashmap_get(sink->ports, port->name))
121 break;
123 if (port->is_input)
124 PA_IDXSET_FOREACH(source, card->sources, state)
125 if (port == pa_hashmap_get(source->ports, port->name))
126 break;
128 *si = sink;
129 *so = source;
132 static pa_hook_result_t port_available_hook_callback(pa_core *c, pa_device_port *port, void* userdata) {
133 uint32_t state;
134 pa_card* card;
135 pa_sink *sink;
136 pa_source *source;
137 pa_bool_t is_active_profile, is_active_port;
139 if (port->available == PA_PORT_AVAILABLE_UNKNOWN)
140 return PA_HOOK_OK;
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))
146 break;
148 if (!card) {
149 pa_log_warn("Did not find port %s in array of cards", port->name);
150 return PA_HOOK_OK;
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)
159 return PA_HOOK_OK;
161 if (port->available == PA_PORT_AVAILABLE_YES) {
162 if (is_active_port)
163 return PA_HOOK_OK;
165 if (!is_active_profile) {
166 if (!try_to_switch_profile(card, port))
167 return PA_HOOK_OK;
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);
175 if (source)
176 pa_source_set_port(source, port->name, FALSE);
177 if (sink)
178 pa_sink_set_port(sink, port->name, FALSE);
181 if (port->available == PA_PORT_AVAILABLE_NO) {
182 if (sink) {
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);
187 else {
188 /* Maybe try to switch to another profile? */
192 if (source) {
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);
197 else {
198 /* Maybe try to switch to another profile? */
203 return PA_HOOK_OK;
206 static void handle_all_unavailable(pa_core *core) {
207 pa_card *card;
208 uint32_t state;
210 PA_IDXSET_FOREACH(card, core->cards, state) {
211 pa_device_port *port;
212 void *state2;
214 if (!card->ports)
215 continue;
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) {
225 struct userdata *u;
227 pa_assert(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);
236 return 0;
239 void pa__done(pa_module*m) {
240 struct userdata *u;
242 pa_assert(m);
244 if (!(u = m->userdata))
245 return;
247 if (u->callback_slot)
248 pa_hook_slot_free(u->callback_slot);
250 pa_xfree(u);