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"
44 static bool setup_explicit_stream2dev_link(struct userdata *,
47 static bool teardown_explicit_stream2dev_link(struct userdata*, mir_node*,
50 static bool setup_default_dev2stream_link(struct userdata *, mir_node *,
52 static bool setup_default_stream2dev_link(struct userdata *, mir_node *,
54 static bool setup_default_dev2dev_link(struct userdata *,
58 static pa_source *setup_device_input(struct userdata *, mir_node *);
59 static pa_sink *setup_device_output(struct userdata *, mir_node *);
61 static bool set_profile(struct userdata *, mir_node *);
62 static bool set_port(struct userdata *, mir_node *);
65 bool mir_switch_setup_link(struct userdata *u,
73 pa_assert_se((core = u->core));
74 pa_assert(!from || from->direction == mir_input);
75 pa_assert(!to || to->direction == mir_output);
79 * links for explic routes
84 switch (from->implement) {
87 switch (to->implement) {
90 pa_log_debug("routing to streams is not implemented yet");
94 if (!setup_explicit_stream2dev_link(u, from, to))
99 pa_log("%s: can't setup link: invalid sink node "
100 "implement", __FILE__);
106 pa_log_debug("input device routing is not implemented yet");
110 pa_log("%s: can't setup link: invalid source node "
111 "implement", __FILE__);
117 * links for default routes
119 pa_assert(from || to);
122 /* default input route */
123 switch (to->implement) {
128 switch (from->implement) {
130 pa_log_debug("routing between streams is not implemented");
134 if (!setup_default_dev2stream_link(u, from, to))
139 pa_log("%s: can't setup link: invalid source node "
140 "implement", __FILE__);
147 /* default output preroute */
148 return setup_device_output(u, to) != NULL;
150 switch (from->implement) {
153 if (!setup_default_stream2dev_link(u, from, to))
158 if (!setup_default_dev2dev_link(u, from, to))
163 pa_log("%s: can't setup link: invalid source node "
164 "implement", __FILE__);
171 pa_log("%s: can't setup link: invalid sink node "
172 "implement", __FILE__);
177 /* default input preroute */
178 pa_assert(from && from->implement == mir_device);
180 return setup_device_input(u, from) != NULL;
184 pa_log_debug("link %s => %s is established", from->amname, to->amname);
189 bool mir_switch_teardown_link(struct userdata *u,
198 pa_assert_se((core = u->core));
199 pa_assert(from->direction == mir_input);
200 pa_assert(to->direction == mir_output);
203 switch (from->implement) {
206 switch (to->implement) {
208 case mir_stream: /* stream -> stream */
209 pa_log_debug("routing to streams is not implemented yet");
212 case mir_device: /* stream -> device */
213 if (!teardown_explicit_stream2dev_link(u, from, to))
218 pa_log("%s: can't teardown link: invalid sink node "
219 "implement", __FILE__);
224 case mir_device: /* device -> stream | device->device */
225 pa_log_debug("input device routing is not implemented yet");
229 pa_log("%s: can't teardown link: invalid source node "
230 "implement", __FILE__);
234 pa_log_debug("link %s => %s is torn down", from->amname, to->amname);
239 static bool setup_explicit_stream2dev_link(struct userdata *u,
251 pa_assert((core = u->core));
253 if (!(sink = setup_device_output(u, to)))
256 if (!set_profile(u, from) || !set_port(u, from)) {
257 pa_log("can't route from '%s'", from->amname);
261 if ((mux = from->mux)) {
262 sinp = pa_idxset_get_by_index(core->sink_inputs, mux->defstream_index);
264 if (sinp && sinp->sink == sink) {
265 if (!pa_multiplex_remove_default_route(core, mux, true))
268 else if (pa_multiplex_duplicate_route(core, mux, NULL, sink)) {
269 pa_log_debug("multiplex route %s => %s already exists",
270 from->amname, to->amname);
274 if (!pa_multiplex_add_explicit_route(core, mux, sink, from->type))
279 if ((sinp = pa_idxset_get_by_index(core->sink_inputs, from->paidx))) {
280 if (sinp->sink == sink)
281 pa_log_debug("direct route already exists. nothing to do");
283 pa_log_debug("direct route: sink-input.%u -> sink.%u",
284 sinp->index, sink->index);
286 if (pa_sink_input_move_to(sinp, sink, false) < 0)
292 pa_log_debug("link %s => %s is established", from->amname, to->amname);
298 static bool teardown_explicit_stream2dev_link(struct userdata *u,
310 pa_assert((core = u->core));
312 if ((mux = from->mux)) {
313 if (!(sink = pa_idxset_get_by_index(core->sinks, to->paidx))) {
314 pa_log_debug("can't find sink.%u", to->paidx);
318 if (!pa_multiplex_remove_explicit_route(core, mux, sink)) {
319 pa_log_debug("can't remove multiplex route on mux %u", mux->module_index);
324 if (!(sinp = pa_idxset_get_by_index(core->sink_inputs, from->paidx))) {
325 pa_log_debug("can't find source.%u", from->paidx);
329 if (!(sink = pa_utils_get_null_sink(u))) {
330 pa_log_debug("can't remove direct route: no null sink");
334 if (pa_sink_input_move_to(sinp, sink, false) < 0)
338 pa_log_debug("link %s => %s is torn down", from->amname, to->amname);
344 static bool setup_default_dev2stream_link(struct userdata *u,
350 pa_source_output *sout;
355 pa_assert((core = u->core));
357 if (!(source = setup_device_input(u, from))) {
358 pa_log_debug("can't route '%s': no source", from->amname);
362 if (to->paidx == PA_IDXSET_INVALID) {
363 pa_log_debug("can't route '%s': no source-output", to->amname);
367 if (!(sout = pa_idxset_get_by_index(core->source_outputs, to->paidx))) {
368 pa_log_debug("can't find sink input for '%s'", to->amname);
372 pa_log_debug("direct route: source.%d -> source->output.%d",
373 source->index, sout->index);
375 if (pa_source_output_move_to(sout, source, false) < 0)
381 static bool setup_default_stream2dev_link(struct userdata *u,
394 pa_assert((core = u->core));
396 if (!(sink = setup_device_output(u, to)))
399 if (!set_profile(u, from) || !set_port(u, from)) {
400 pa_log("can't route from '%s'", from->amname);
404 if ((mux = from->mux)) {
405 if (mux->defstream_index == PA_IDXSET_INVALID) {
406 if ((n = pa_multiplex_no_of_routes(core, mux)) < 0)
409 pa_log_debug("currently mux %u has no default route",
416 sinp = pa_idxset_get_by_index(core->sink_inputs,
417 mux->defstream_index);
422 * we supposed to have a default stream but the sink-input
423 * on the combine side is not existing any more. This can
424 * happen, for instance, if the sink, where it was connected,
425 * died for some reason.
427 pa_log_debug("supposed to have a default stream on multiplex "
428 "%u but non was found. Trying to make one",
430 if (pa_multiplex_duplicate_route(core, mux, sinp, sink)) {
431 pa_log_debug("the default stream on mux %u would be a "
432 "duplicate to an explicit route. "
433 "Removing it ...", mux->module_index);
434 mux->defstream_index = PA_IDXSET_INVALID;
435 return true; /* the routing is a success */
438 if (!pa_multiplex_add_default_route(core, mux,sink, from->type)) {
439 pa_log_debug("failed to add default route on mux %d",
441 mux->defstream_index = PA_IDXSET_INVALID;
445 else if (pa_multiplex_duplicate_route(core, mux, sinp, sink)) {
446 pa_log_debug("the default stream on mux %u would be a duplicate "
447 "to an explicit route. Removing it ...",
449 return true; /* the routing is a success */
453 pa_log_debug("multiplex route: sink-input.%d -> (sink.%d - "
454 "sink-input.%d) -> sink.%d", from->paidx,
455 mux->sink_index, sinp->index, sink->index);
458 pa_log_debug("multiplex route: sink-input.%d -> (sink.%d - "
459 "sink-input) -> sink.%d", from->paidx,
460 mux->sink_index, sink->index);
463 if (!pa_multiplex_change_default_route(core, mux, sink))
467 if (from->paidx == PA_IDXSET_INVALID) {
468 pa_log_debug("can't route '%s': no sink-input", to->amname);
472 if (!(sinp = pa_idxset_get_by_index(core->sink_inputs, from->paidx))) {
473 pa_log_debug("can't find sink input for '%s'", from->amname);
477 pa_log_debug("direct route: sink-input.%d -> sink.%d",
478 sinp->index, sink->index);
480 if (pa_sink_input_move_to(sinp, sink, false) < 0)
487 static bool setup_default_dev2dev_link(struct userdata *u,
502 pa_assert((core = u->core));
504 if (!(loop = from->loop)) {
505 pa_log_debug("source is not looped back");
509 if (!(sink = setup_device_output(u, to)))
512 if ((mux = from->mux)) {
513 if (mux->defstream_index == PA_IDXSET_INVALID) {
514 if ((n = pa_multiplex_no_of_routes(core, mux)) < 0)
517 pa_log_debug("currently mux %u has no default route",
524 sinp = pa_idxset_get_by_index(core->sink_inputs,
525 mux->defstream_index);
530 * we supposed to have a default stream but the sink-input
531 * on the combine side is not existing any more. This can
532 * happen, for instance, if the sink, where it was connected,
533 * died for some reason.
535 pa_log_debug("supposed to have a default stream on multiplex "
536 "%u but non was found. Trying to make one",
538 if (pa_multiplex_duplicate_route(core, mux, sinp, sink)) {
539 pa_log_debug("the default stream on mux %u would be a "
540 "duplicate to an explicit route. "
541 "Removing it ...", mux->module_index);
542 mux->defstream_index = PA_IDXSET_INVALID;
543 return true; /* the routing is a success */
546 type = pa_classify_guess_application_class(from);
548 if (!pa_multiplex_add_default_route(core, mux,sink, type)) {
549 pa_log_debug("failed to add default route on mux %d",
551 mux->defstream_index = PA_IDXSET_INVALID;
555 else if (pa_multiplex_duplicate_route(core, mux, sinp, sink)) {
556 pa_log_debug("the default stream on mux %u would be a duplicate "
557 "to an explicit route. Removing it ...",
559 return true; /* the routing is a success */
563 pa_log_debug("multiplex route: source.%d -> "
564 "(source-output - sink-input %d) -> (sink.%d - "
565 "sink-input.%d) -> sink.%d", from->paidx,
566 loop->sink_input_index,
567 mux->sink_index, sinp->index, sink->index);
570 pa_log_debug("multiplex route: source.%d -> "
571 "(source-output - sink-input.%d) -> (sink.%d - "
572 "sink-input) -> sink.%d", from->paidx,
573 loop->sink_input_index,
574 mux->sink_index, sink->index);
577 if (!pa_multiplex_change_default_route(core, mux, sink))
581 sinp = pa_idxset_get_by_index(core->sink_inputs,
582 loop->sink_input_index);
585 pa_log_debug("can't find looped back sink input for '%s'",
590 pa_log_debug("loopback route: source.%d -> (source-output - "
591 "sink-input.%d) -> sink.%d",
592 from->paidx, sinp->index, sink->index);
594 if (pa_sink_input_move_to(sinp, sink, false) < 0)
601 static pa_source *setup_device_input(struct userdata *u, mir_node *node)
608 pa_assert_se((core = u->core));
610 if (!set_profile(u, node) || !set_port(u, node)) {
611 pa_log("can't route to '%s'", node->amname);
615 if (node->paidx == PA_IDXSET_INVALID) {
616 pa_log_debug("can't route to '%s': no source", node->amname);
620 if (!(source = pa_idxset_get_by_index(core->sources, node->paidx))) {
621 pa_log_debug("can't route to '%s': cant find source", node->amname);
629 static pa_sink *setup_device_output(struct userdata *u, mir_node *node)
636 pa_assert_se((core = u->core));
638 if (!set_profile(u, node) || !set_port(u, node)) {
639 pa_log("can't route to '%s'", node->amname);
643 if (node->paidx == PA_IDXSET_INVALID) {
644 pa_log_debug("can't route to '%s': no sink", node->amname);
648 if (!(sink = pa_idxset_get_by_index(core->sinks, node->paidx))) {
649 pa_log_debug("can't route to '%s': cant find sink", node->amname);
657 static bool set_profile(struct userdata *u, mir_node *node)
661 pa_card_profile *prof;
665 pa_assert_se((core = u->core));
667 if (node->implement != mir_device)
670 if (node->type == mir_bluetooth_a2dp ||
671 node->type == mir_bluetooth_sco)
673 card = pa_idxset_get_by_index(core->cards, node->pacard.index);
676 pa_log("can't find card for '%s'", node->amname);
680 pa_assert_se(prof = card->active_profile);
682 if (!pa_streq(node->pacard.profile, prof->name)) {
683 pa_log_debug("changing profile '%s' => '%s'",
684 prof->name, node->pacard.profile);
686 if (u->state.profile) {
687 pa_log("nested profile setting is not allowed. won't change "
688 "'%s' => '%s'", prof->name, node->pacard.profile);
692 u->state.profile = node->pacard.profile;
694 if ((prof = pa_hashmap_get(card->profiles, node->pacard.profile)) != NULL)
695 pa_card_set_profile(card, prof, false);
697 u->state.profile = NULL;
706 static bool set_port(struct userdata *u, mir_node *node)
711 pa_device_port *port;
714 uint32_t paidx = PA_IDXSET_INVALID;
718 pa_assert(node->paname);
719 pa_assert_se((core = u->core));
721 if (node->direction != mir_input && node->direction != mir_output)
724 if (node->implement != mir_device)
730 if (node->direction == mir_input) {
731 source = pa_namereg_get(core, node->paname, PA_NAMEREG_SOURCE);
733 if (!(data = source)) {
734 pa_log("can't set port for '%s': source not found",
739 if ((port = source->active_port) && pa_streq(node->paport, port->name))
742 if (pa_source_set_port(source, node->paport, false) < 0)
745 paidx = source->index;
748 if (node->direction == mir_output) {
749 sink = pa_namereg_get(core, node->paname, PA_NAMEREG_SINK);
751 if (!(data = sink)) {
752 pa_log("can't set port for '%s': sink not found",
757 if ((port = sink->active_port) && pa_streq(node->paport, port->name))
760 if (pa_sink_set_port(sink, node->paport, false) < 0)
766 if ((oldnode = pa_discover_remove_node_from_ptr_hash(u, data)))
767 oldnode->paidx = PA_IDXSET_INVALID;
770 pa_discover_add_node_to_ptr_hash(u, data, node);
781 * indent-tabs-mode: nil