]> code.delx.au - pulseaudio/blob - src/modules/module-switch-on-port-available.c
switch-on-port-available: Silence gcc warning.
[pulseaudio] / src / modules / module-switch-on-port-available.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2006 Lennart Poettering
5 Copyright 2011 Canonical Ltd
6
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.
11
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.
16
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 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <pulsecore/core.h>
28 #include <pulsecore/device-port.h>
29 #include <pulsecore/hashmap.h>
30
31 #include "module-switch-on-port-available-symdef.h"
32
33 struct userdata {
34 pa_hook_slot *available_slot;
35 pa_hook_slot *sink_new_slot;
36 pa_hook_slot *source_new_slot;
37 };
38
39 static pa_device_port* find_best_port(pa_hashmap *ports) {
40 void *state;
41 pa_device_port* port, *result = NULL;
42
43 PA_HASHMAP_FOREACH(port, ports, state) {
44 if (result == NULL ||
45 result->available == PA_AVAILABLE_NO ||
46 (port->available != PA_AVAILABLE_NO && port->priority > result->priority)) {
47 result = port;
48 }
49 }
50
51 return result;
52 }
53
54 static bool profile_good_for_output(pa_card_profile *profile) {
55 pa_sink *sink;
56 uint32_t idx;
57
58 pa_assert(profile);
59
60 if (profile->card->active_profile->n_sources != profile->n_sources)
61 return false;
62
63 if (profile->card->active_profile->max_source_channels != profile->max_source_channels)
64 return false;
65
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)
69 continue;
70
71 if (sink->active_port->available != PA_AVAILABLE_NO)
72 return false;
73 }
74
75 return true;
76 }
77
78 static bool profile_good_for_input(pa_card_profile *profile) {
79 pa_assert(profile);
80
81 if (profile->card->active_profile->n_sinks != profile->n_sinks)
82 return false;
83
84 if (profile->card->active_profile->max_sink_channels != profile->max_sink_channels)
85 return false;
86
87 return true;
88 }
89
90 static int try_to_switch_profile(pa_device_port *port) {
91 pa_card_profile *best_profile = NULL, *profile;
92 void *state;
93
94 pa_log_debug("Finding best profile");
95
96 PA_HASHMAP_FOREACH(profile, port->profiles, state) {
97 bool good = false;
98
99 if (best_profile && best_profile->priority >= profile->priority)
100 continue;
101
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);
106 break;
107
108 case PA_DIRECTION_INPUT:
109 good = profile_good_for_input(profile);
110 break;
111 }
112
113 if (!good)
114 continue;
115
116 best_profile = profile;
117 }
118
119 if (!best_profile) {
120 pa_log_debug("No suitable profile found");
121 return -1;
122 }
123
124 if (pa_card_set_profile(port->card, best_profile->name, FALSE) != 0) {
125 pa_log_debug("Could not set profile %s", best_profile->name);
126 return -1;
127 }
128
129 return 0;
130 }
131
132 static void find_sink_and_source(pa_card *card, pa_device_port *port, pa_sink **si, pa_source **so)
133 {
134 pa_sink *sink = NULL;
135 pa_source *source = NULL;
136 uint32_t state;
137
138 switch (port->direction) {
139 case PA_DIRECTION_OUTPUT:
140 PA_IDXSET_FOREACH(sink, card->sinks, state)
141 if (port == pa_hashmap_get(sink->ports, port->name))
142 break;
143 break;
144
145 case PA_DIRECTION_INPUT:
146 PA_IDXSET_FOREACH(source, card->sources, state)
147 if (port == pa_hashmap_get(source->ports, port->name))
148 break;
149 break;
150 }
151
152 *si = sink;
153 *so = source;
154 }
155
156 static pa_hook_result_t port_available_hook_callback(pa_core *c, pa_device_port *port, void* userdata) {
157 uint32_t state;
158 pa_card* card;
159 pa_sink *sink;
160 pa_source *source;
161 pa_bool_t is_active_profile, is_active_port;
162
163 if (port->available == PA_AVAILABLE_UNKNOWN)
164 return PA_HOOK_OK;
165
166 pa_log_debug("finding port %s", port->name);
167
168 PA_IDXSET_FOREACH(card, c->cards, state)
169 if (port == pa_hashmap_get(card->ports, port->name))
170 break;
171
172 if (!card) {
173 pa_log_warn("Did not find port %s in array of cards", port->name);
174 return PA_HOOK_OK;
175 }
176
177 find_sink_and_source(card, port, &sink, &source);
178
179 is_active_profile = card->active_profile == pa_hashmap_get(port->profiles, card->active_profile->name);
180 is_active_port = (sink && sink->active_port == port) || (source && source->active_port == port);
181
182 if (port->available == PA_AVAILABLE_NO && !is_active_port)
183 return PA_HOOK_OK;
184
185 if (port->available == PA_AVAILABLE_YES) {
186 if (is_active_port)
187 return PA_HOOK_OK;
188
189 if (!is_active_profile) {
190 if (try_to_switch_profile(port) < 0)
191 return PA_HOOK_OK;
192
193 pa_assert(card->active_profile == pa_hashmap_get(port->profiles, card->active_profile->name));
194
195 /* Now that profile has changed, our sink and source pointers must be updated */
196 find_sink_and_source(card, port, &sink, &source);
197 }
198
199 if (source)
200 pa_source_set_port(source, port->name, FALSE);
201 if (sink)
202 pa_sink_set_port(sink, port->name, FALSE);
203 }
204
205 if (port->available == PA_AVAILABLE_NO) {
206 if (sink) {
207 pa_device_port *p2 = find_best_port(sink->ports);
208
209 if (p2 && p2->available != PA_AVAILABLE_NO)
210 pa_sink_set_port(sink, p2->name, FALSE);
211 else {
212 /* Maybe try to switch to another profile? */
213 }
214 }
215
216 if (source) {
217 pa_device_port *p2 = find_best_port(source->ports);
218
219 if (p2 && p2->available != PA_AVAILABLE_NO)
220 pa_source_set_port(source, p2->name, FALSE);
221 else {
222 /* Maybe try to switch to another profile? */
223 }
224 }
225 }
226
227 return PA_HOOK_OK;
228 }
229
230 static void handle_all_unavailable(pa_core *core) {
231 pa_card *card;
232 uint32_t state;
233
234 PA_IDXSET_FOREACH(card, core->cards, state) {
235 pa_device_port *port;
236 void *state2;
237
238 PA_HASHMAP_FOREACH(port, card->ports, state2) {
239 if (port->available == PA_AVAILABLE_NO)
240 port_available_hook_callback(core, port, NULL);
241 }
242 }
243 }
244
245 static pa_device_port *new_sink_source(pa_hashmap *ports, const char *name) {
246
247 void *state;
248 pa_device_port *i, *p = NULL;
249
250 if (!ports)
251 return NULL;
252 if (name)
253 p = pa_hashmap_get(ports, name);
254 if (!p)
255 PA_HASHMAP_FOREACH(i, ports, state)
256 if (!p || i->priority > p->priority)
257 p = i;
258 if (!p)
259 return NULL;
260 if (p->available != PA_AVAILABLE_NO)
261 return NULL;
262
263 pa_assert_se(p = find_best_port(ports));
264 return p;
265 }
266
267 static pa_hook_result_t sink_new_hook_callback(pa_core *c, pa_sink_new_data *new_data, struct userdata *u) {
268
269 pa_device_port *p = new_sink_source(new_data->ports, new_data->active_port);
270
271 if (p) {
272 pa_log_debug("Switching initial port for sink '%s' to '%s'", new_data->name, p->name);
273 pa_sink_new_data_set_port(new_data, p->name);
274 }
275 return PA_HOOK_OK;
276 }
277
278 static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data *new_data, struct userdata *u) {
279
280 pa_device_port *p = new_sink_source(new_data->ports, new_data->active_port);
281
282 if (p) {
283 pa_log_debug("Switching initial port for source '%s' to '%s'", new_data->name,
284 new_data->active_port);
285 pa_source_new_data_set_port(new_data, p->name);
286 }
287 return PA_HOOK_OK;
288 }
289
290
291 int pa__init(pa_module*m) {
292 struct userdata *u;
293
294 pa_assert(m);
295
296 m->userdata = u = pa_xnew(struct userdata, 1);
297
298 /* Make sure we are after module-device-restore, so we can overwrite that suggestion if necessary */
299 u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_NEW],
300 PA_HOOK_NORMAL, (pa_hook_cb_t) sink_new_hook_callback, u);
301 u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_NEW],
302 PA_HOOK_NORMAL, (pa_hook_cb_t) source_new_hook_callback, u);
303 u->available_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED],
304 PA_HOOK_LATE, (pa_hook_cb_t) port_available_hook_callback, u);
305
306 handle_all_unavailable(m->core);
307
308 return 0;
309 }
310
311 void pa__done(pa_module*m) {
312 struct userdata *u;
313
314 pa_assert(m);
315
316 if (!(u = m->userdata))
317 return;
318
319 if (u->available_slot)
320 pa_hook_slot_free(u->available_slot);
321 if (u->sink_new_slot)
322 pa_hook_slot_free(u->sink_new_slot);
323 if (u->source_new_slot)
324 pa_hook_slot_free(u->source_new_slot);
325
326 pa_xfree(u);
327 }