2 * module-murphy-ivi -- PulseAudio module for providing audio routing support
3 * Copyright (c) 2012, Intel Corporation.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.
12 * See the GNU Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston,
25 #include <pulsecore/pulsecore-config.h>
26 #include <pulsecore/core-util.h>
27 #include <pulsecore/namereg.h>
29 #include <pulsecore/card.h>
30 #include <pulsecore/sink.h>
31 #include <pulsecore/device-port.h>
32 #include <pulsecore/source.h>
33 #include <pulsecore/sink-input.h>
34 #include <pulsecore/source-output.h>
38 #include "multiplex.h"
42 static pa_bool_t setup_explicit_link_from_stream_to_device(struct userdata *,
43 mir_node *, mir_node *);
44 static pa_bool_t teardown_explicit_link_from_stream_to_device(struct userdata *,
45 mir_node *, mir_node *);
47 static pa_bool_t setup_default_link_from_stream_to_device(struct userdata *,
48 mir_node *, mir_node *);
51 static pa_sink *setup_device_output(struct userdata *, mir_node *);
53 static pa_bool_t set_profile(struct userdata *, mir_node *);
54 static pa_bool_t set_port(struct userdata *, mir_node *);
57 pa_bool_t mir_switch_setup_link(struct userdata *u,
66 pa_assert_se((core = u->core));
67 pa_assert(!from || from->direction == mir_input);
68 pa_assert(to->direction == mir_output);
72 * links for explic routes
76 switch (from->implement) {
79 switch (to->implement) {
82 pa_log_debug("routing to streams is not implemented yet");
86 if (!setup_explicit_link_from_stream_to_device(u, from, to))
91 pa_log("%s: can't setup link: invalid sink node "
92 "implement", __FILE__);
98 pa_log_debug("input device routing is not implemented yet");
102 pa_log("%s: can't setup link: invalid source node "
103 "implement", __FILE__);
109 * links for default routes
111 switch (to->implement) {
114 pa_log_debug("routing to a stream is not implemented yet");
118 if (!from) /* prerouting */
119 return (!explicit && setup_device_output(u, to) != NULL);
121 switch (from->implement) {
124 if (!setup_default_link_from_stream_to_device(u, from, to))
129 pa_log("%s: default device -> device route is "
130 "not supported", __FILE__);
134 pa_log("%s: can't setup link: invalid source node "
135 "implement", __FILE__);
142 pa_log("%s: can't setup link: invalid sink node "
143 "implement", __FILE__);
148 pa_log_debug("link %s => %s is established", from->amname, to->amname);
153 pa_bool_t mir_switch_teardown_link(struct userdata *u,
162 pa_assert_se((core = u->core));
163 pa_assert(from->direction == mir_input);
164 pa_assert(to->direction == mir_output);
167 switch (from->implement) {
170 switch (to->implement) {
172 case mir_stream: /* stream -> stream */
173 pa_log_debug("routing to streams is not implemented yet");
176 case mir_device: /* stream -> device */
177 if (!teardown_explicit_link_from_stream_to_device(u, from, to))
182 pa_log("%s: can't teardown link: invalid sink node "
183 "implement", __FILE__);
188 case mir_device: /* device -> stream | device->device */
189 pa_log_debug("input device routing is not implemented yet");
193 pa_log("%s: can't teardown link: invalid source node "
194 "implement", __FILE__);
198 pa_log_debug("link %s => %s is torn down", from->amname, to->amname);
203 static pa_bool_t setup_explicit_link_from_stream_to_device(struct userdata *u,
215 pa_assert((core = u->core));
217 if (!(sink = setup_device_output(u, to)))
220 if (!set_profile(u, from) || !set_port(u, from)) {
221 pa_log("can't route from '%s'", from->amname);
225 if ((mux = from->mux)) {
226 sinp = pa_idxset_get_by_index(core->sink_inputs, mux->defstream_index);
228 if (sinp && sinp->sink == sink) {
229 if (!pa_multiplex_remove_default_route(core, mux, TRUE))
232 else if (pa_multiplex_duplicate_route(core, mux, NULL, sink)) {
233 pa_log_debug("multiplex route %s => %s already exists",
234 from->amname, to->amname);
238 if (!pa_multiplex_add_explicit_route(core, mux, sink, from->type))
243 if ((sinp = pa_idxset_get_by_index(core->sink_inputs, from->paidx))) {
244 if (sinp->sink == sink)
245 pa_log_debug("direct route already exists. nothing to do");
247 pa_log_debug("direct route: sink-input.%u -> sink.%u",
248 sinp->index, sink->index);
250 if (pa_sink_input_move_to(sinp, sink, FALSE) < 0)
256 pa_log_debug("link %s => %s is established", from->amname, to->amname);
262 static pa_bool_t teardown_explicit_link_from_stream_to_device(struct userdata *u,
274 pa_assert((core = u->core));
276 if ((mux = from->mux)) {
277 if (!(sink = pa_idxset_get_by_index(core->sinks, to->paidx))) {
278 pa_log_debug("can't find sink.%u", to->paidx);
282 if (!pa_multiplex_remove_explicit_route(core, mux, sink)) {
283 pa_log_debug("can't remove multiplex route on mux %u", mux->module_index);
288 if (!(sinp = pa_idxset_get_by_index(core->sink_inputs, from->paidx))) {
289 pa_log_debug("can't find source.%u", from->paidx);
293 if (!(sink = pa_utils_get_null_sink(u))) {
294 pa_log_debug("can't remove direct route: no null sink");
298 if (pa_sink_input_move_to(sinp, sink, FALSE) < 0)
302 pa_log_debug("link %s => %s is torn down", from->amname, to->amname);
308 static pa_bool_t setup_default_link_from_stream_to_device(struct userdata *u,
321 pa_assert((core = u->core));
323 if (!(sink = setup_device_output(u, to)))
326 if (!set_profile(u, from) || !set_port(u, from)) {
327 pa_log("can't route from '%s'", from->amname);
331 if ((mux = from->mux)) {
332 if (mux->defstream_index == PA_IDXSET_INVALID) {
333 if ((n = pa_multiplex_no_of_routes(core, mux)) < 0)
336 pa_log_debug("currently mux %u has no default route",
343 sinp = pa_idxset_get_by_index(core->sink_inputs,
344 mux->defstream_index);
349 * we supposed to have a default stream but the sink-input
350 * on the combine side is not existing any more. This can
351 * happen, for instance, if the sink, where it was connected,
352 * died for some reason.
354 pa_log_debug("supposed to have a default stream on multiplex "
355 "%u but non was found. Trying to make one",
357 if (pa_multiplex_duplicate_route(core, mux, sinp, sink)) {
358 pa_log_debug("the default stream on mux %u would be a "
359 "duplicate to an explicit route. "
360 "Removing it ...", mux->module_index);
361 mux->defstream_index = PA_IDXSET_INVALID;
362 return TRUE; /* the routing is a success */
365 if (!pa_multiplex_add_default_route(core, mux,sink, from->type)) {
366 pa_log_debug("failed to add default route on mux %d",
368 mux->defstream_index = PA_IDXSET_INVALID;
372 else if (pa_multiplex_duplicate_route(core, mux, sinp, sink)) {
373 pa_log_debug("the default stream on mux %u would be a duplicate "
374 "to an explicit route. Removing it ...",
376 return TRUE; /* the routing is a success */
380 pa_log_debug("multiplex route: sink-input.%d -> (sink.%d - "
381 "sink-input.%d) -> sink.%d", from->paidx,
382 mux->sink_index, sinp->index, sink->index);
385 pa_log_debug("multiplex route: sink-input.%d -> (sink.%d - "
386 "sink-input) -> sink.%d", from->paidx,
387 mux->sink_index, sink->index);
390 if (!pa_multiplex_change_default_route(core, mux, sink))
394 if (from->paidx == PA_IDXSET_INVALID) {
395 pa_log_debug("can't route '%s': no sink-input", to->amname);
399 if (!(sinp = pa_idxset_get_by_index(core->sink_inputs, from->paidx))) {
400 pa_log_debug("can't find sink input for '%s'", from->amname);
404 pa_log_debug("direct route: sink-input.%d -> sink.%d",
405 sinp->index, sink->index);
407 if (pa_sink_input_move_to(sinp, sink, FALSE) < 0)
414 static pa_sink *setup_device_output(struct userdata *u, mir_node *node)
421 pa_assert_se((core = u->core));
423 if (!set_profile(u, node) || !set_port(u, node)) {
424 pa_log("can't route to '%s'", node->amname);
428 if (node->paidx == PA_IDXSET_INVALID) {
429 pa_log_debug("can't route to '%s': no sink", node->amname);
433 if (!(sink = pa_idxset_get_by_index(core->sinks, node->paidx))) {
434 pa_log_debug("can't route to '%s': cant find sink", node->amname);
442 static pa_bool_t set_profile(struct userdata *u, mir_node *node)
446 pa_card_profile *prof;
450 pa_assert_se((core = u->core));
452 if (node->implement != mir_device)
455 if (node->type == mir_bluetooth_a2dp ||
456 node->type == mir_bluetooth_sco)
458 card = pa_idxset_get_by_index(core->cards, node->pacard.index);
461 pa_log("can't find card for '%s'", node->amname);
465 pa_assert_se(prof = card->active_profile);
467 if (!pa_streq(node->pacard.profile, prof->name)) {
468 pa_log_debug("changing profile '%s' => '%s'",
469 prof->name, node->pacard.profile);
471 if (u->state.profile) {
472 pa_log("nested profile setting is not allowed. won't change "
473 "'%s' => '%s'", prof->name, node->pacard.profile);
477 u->state.profile = node->pacard.profile;
479 pa_card_set_profile(card, node->pacard.profile, FALSE);
481 u->state.profile = NULL;
490 static pa_bool_t set_port(struct userdata *u, mir_node *node)
495 pa_device_port *port;
498 uint32_t paidx = PA_IDXSET_INVALID;
502 pa_assert(node->paname);
503 pa_assert_se((core = u->core));
505 if (node->direction != mir_input && node->direction != mir_output)
508 if (node->implement != mir_device)
514 if (node->direction == mir_input) {
515 source = pa_namereg_get(core, node->paname, PA_NAMEREG_SOURCE);
517 if (!(data = source)) {
518 pa_log("can't set port for '%s': source not found",
523 if ((port = source->active_port) && pa_streq(node->paport, port->name))
526 if (pa_source_set_port(source, node->paport, FALSE) < 0)
529 paidx = source->index;
532 if (node->direction == mir_output) {
533 sink = pa_namereg_get(core, node->paname, PA_NAMEREG_SINK);
535 if (!(data = sink)) {
536 pa_log("can't set port for '%s': sink not found",
541 if ((port = sink->active_port) && pa_streq(node->paport, port->name))
544 if (pa_sink_set_port(sink, node->paport, FALSE) < 0)
550 if ((oldnode = pa_discover_remove_node_from_ptr_hash(u, data)))
551 oldnode->paidx = PA_IDXSET_INVALID;
554 pa_discover_add_node_to_ptr_hash(u, data, node);
565 * indent-tabs-mode: nil