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 pa_log_debug("finding port %s", port->name);
125 PA_IDXSET_FOREACH(card, c->cards, state)
126 if (card->ports && port == pa_hashmap_get(card->ports, port->name))
130 pa_log_warn("Did not find port %s in array of cards", port->name);
134 find_sink_and_source(card, port, &sink, &source);
136 is_active_profile = port->profiles && card->active_profile &&
137 card->active_profile == pa_hashmap_get(port->profiles, card->active_profile->name);
138 is_active_port = (sink && sink->active_port == port) || (source && source->active_port == port);
140 if (port->available == PA_PORT_AVAILABLE_NO && !is_active_port)
143 if (port->available == PA_PORT_AVAILABLE_YES) {
147 if (!is_active_profile) {
148 if (!try_to_switch_profile(card, port))
151 pa_assert(card->active_profile == pa_hashmap_get(port->profiles, card->active_profile->name));
153 /* Now that profile has changed, our sink and source pointers must be updated */
154 find_sink_and_source(card, port, &sink, &source);
158 pa_source_set_port(source, port->name, FALSE);
160 pa_sink_set_port(sink, port->name, FALSE);
163 if (port->available == PA_PORT_AVAILABLE_NO) {
165 pa_device_port *p2 = find_best_port(sink->ports);
167 if (p2 && p2->available != PA_PORT_AVAILABLE_NO)
168 pa_sink_set_port(sink, p2->name, FALSE);
170 /* Maybe try to switch to another profile? */
175 pa_device_port *p2 = find_best_port(source->ports);
177 if (p2 && p2->available != PA_PORT_AVAILABLE_NO)
178 pa_source_set_port(source, p2->name, FALSE);
180 /* Maybe try to switch to another profile? */
188 static void handle_all_unavailable(pa_core *core) {
192 PA_IDXSET_FOREACH(card, core->cards, state) {
193 pa_device_port *port;
199 PA_HASHMAP_FOREACH(port, card->ports, state2) {
200 if (port->available == PA_PORT_AVAILABLE_NO)
201 port_available_hook_callback(core, port, NULL);
206 int pa__init(pa_module*m) {
211 m->userdata = u = pa_xnew(struct userdata, 1);
213 u->callback_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED],
214 PA_HOOK_LATE, (pa_hook_cb_t) port_available_hook_callback, u);
216 handle_all_unavailable(m->core);
221 void pa__done(pa_module*m) {
226 if (!(u = m->userdata))
229 if (u->callback_slot)
230 pa_hook_slot_free(u->callback_slot);