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>
27 #include <pulse/proplist.h>
28 #include <pulsecore/module.h>
34 #include "constrain.h"
41 static void rtgroup_destroy(struct userdata *, mir_rtgroup *);
42 static int rtgroup_print(mir_rtgroup *, char *, int);
43 static void rtgroup_update_module_property(struct userdata *, mir_direction,
46 static void add_rtentry(struct userdata *, mir_direction, mir_rtgroup *,
48 static void remove_rtentry(struct userdata *, mir_rtentry *);
50 static void make_explicit_routes(struct userdata *, uint32_t);
51 static mir_node *find_default_route(struct userdata *, mir_node *, uint32_t);
52 static void implement_preroute(struct userdata *, mir_node *, mir_node *,
54 static void implement_default_route(struct userdata *, mir_node *, mir_node *,
57 static int uint32_cmp(uint32_t, uint32_t);
59 static int node_priority(struct userdata *, mir_node *);
61 static int volume_class(mir_node *);
63 static int print_routing_table(pa_hashmap *, const char *, char *, int);
66 static void pa_hashmap_rtgroup_free(void *rtg, void *u)
68 rtgroup_destroy(u, rtg);
72 pa_router *pa_router_init(struct userdata *u)
74 size_t num_classes = mir_application_class_end;
75 pa_router *router = pa_xnew0(pa_router, 1);
77 router->rtgroups.input = pa_hashmap_new(pa_idxset_string_hash_func,
78 pa_idxset_string_compare_func);
79 router->rtgroups.output = pa_hashmap_new(pa_idxset_string_hash_func,
80 pa_idxset_string_compare_func);
82 router->maplen = num_classes;
84 router->priormap = pa_xnew0(int, num_classes);
86 MIR_DLIST_INIT(router->nodlist);
87 MIR_DLIST_INIT(router->connlist);
92 void pa_router_done(struct userdata *u)
95 mir_connection *conn, *c;
102 if (u && (router = u->router)) {
103 MIR_DLIST_FOR_EACH_SAFE(mir_node, rtprilist, e,n, &router->nodlist) {
104 MIR_DLIST_UNLINK(mir_node, rtprilist, e);
107 MIR_DLIST_FOR_EACH_SAFE(mir_connection,link, conn,c,&router->connlist){
108 MIR_DLIST_UNLINK(mir_connection, link, conn);
112 PA_HASHMAP_FOREACH(rtg, router->rtgroups.input, state) {
113 rtgroup_destroy(u, rtg);
116 PA_HASHMAP_FOREACH(rtg, router->rtgroups.output, state) {
117 rtgroup_destroy(u, rtg);
120 pa_hashmap_free(router->rtgroups.input);
121 pa_hashmap_free(router->rtgroups.output);
123 for (i = 0; i < MRP_ZONE_MAX; i++) {
124 if ((map = router->classmap.input[i]))
127 if ((map = router->classmap.output[i]))
131 pa_xfree(router->priormap);
139 void mir_router_assign_class_priority(struct userdata *u,
147 pa_assert_se((router = u->router));
148 pa_assert_se((priormap = router->priormap));
150 if (class >= 0 && class < router->maplen) {
151 pa_log_debug("assigning priority %d to class '%s'",
152 pri, mir_node_type_str(class));
153 priormap[class] = pri;
158 mir_rtgroup *mir_router_create_rtgroup(struct userdata *u,
161 mir_rtgroup_accept_t accept,
162 mir_rtgroup_compare_t compare)
169 pa_assert(type == mir_input || type == mir_output);
173 pa_assert_se((router = u->router));
175 if (type == mir_input)
176 table = router->rtgroups.input;
178 table = router->rtgroups.output;
182 rtg = pa_xnew0(mir_rtgroup, 1);
183 rtg->name = pa_xstrdup(name);
184 rtg->accept = accept;
185 rtg->compare = compare;
186 MIR_DLIST_INIT(rtg->entries);
188 if (pa_hashmap_put(table, rtg->name, rtg) < 0) {
194 pa_log_debug("%s routing group '%s' created",
195 mir_direction_str(type), name);
200 void mir_router_destroy_rtgroup(struct userdata *u,
210 pa_assert_se((router = u->router));
212 if (type == mir_input)
213 table = router->rtgroups.input;
215 table = router->rtgroups.output;
219 if (!(rtg = pa_hashmap_remove(table, name))) {
220 pa_log_debug("can't destroy %s routing group '%s': group not found",
221 mir_direction_str(type), name);
224 rtgroup_destroy(u, rtg);
225 pa_log_debug("routing group '%s' destroyed", name);
230 bool mir_router_assign_class_to_rtgroup(struct userdata *u,
234 const char *rtgrpnam)
238 mir_rtgroup ***classmap;
239 mir_rtgroup **zonemap;
242 const char *direction;
246 pa_assert(zone < MRP_ZONE_MAX);
247 pa_assert(type == mir_input || type == mir_output);
249 pa_assert_se((router = u->router));
251 if (type == mir_input) {
252 rtable = router->rtgroups.input;
253 classmap = router->classmap.input;
256 rtable = router->rtgroups.output;
257 classmap = router->classmap.output;
260 if (class < 0 || class >= router->maplen) {
261 pa_log_debug("can't assign class (%d) to routing group '%s': "
262 "class id is out of range (0 - %d)",
263 class, rtgrpnam, router->maplen);
267 clnam = mir_node_type_str(class);
268 direction = mir_direction_str(type);
270 if (!(rtg = pa_hashmap_get(rtable, rtgrpnam))) {
271 pa_log_debug("can't assign class '%s' to %s routing group '%s': "
272 "router group not found", clnam, direction, rtgrpnam);
275 if (!(zonemap = classmap[zone])) {
276 zonemap = pa_xnew0(mir_rtgroup *, router->maplen);
277 classmap[zone] = zonemap;
280 zonemap[class] = rtg;
282 if ((z = pa_zoneset_get_zone_by_index(u, zone))) {
283 pa_log_debug("class '%s'@'%s' assigned to %s routing group '%s'",
284 clnam, z->name, direction, rtgrpnam);
287 pa_log_debug("class '%s'@zone%u assigned to %s routing group '%s'",
288 clnam, zone, direction, rtgrpnam);
296 void mir_router_register_node(struct userdata *u, mir_node *node)
306 pa_assert_se((router = u->router));
308 if (node->direction == mir_output) {
309 if (node->implement == mir_device) {
310 PA_HASHMAP_FOREACH(rtg, router->rtgroups.output, state) {
311 add_rtentry(u, mir_output, rtg, node);
318 if (node->direction == mir_input) {
320 if (node->implement == mir_device) &&
321 !pa_classify_loopback_stream(node))
325 if (node->implement == mir_device) {
326 PA_HASHMAP_FOREACH(rtg, router->rtgroups.input, state) {
327 add_rtentry(u, mir_input, rtg, node);
330 if (!pa_classify_loopback_stream(node))
334 priority = node_priority(u, node);
336 MIR_DLIST_FOR_EACH(mir_node, rtprilist, before, &router->nodlist) {
337 if (priority < node_priority(u, before)) {
338 MIR_DLIST_INSERT_BEFORE(mir_node, rtprilist, node,
344 MIR_DLIST_APPEND(mir_node, rtprilist, node, &router->nodlist);
350 void mir_router_unregister_node(struct userdata *u, mir_node *node)
353 mir_rtentry *rte, *n;
357 pa_assert_se((router = u->router));
359 MIR_DLIST_FOR_EACH_SAFE(mir_rtentry,nodchain, rte,n, &node->rtentries) {
360 remove_rtentry(u, rte);
363 MIR_DLIST_UNLINK(mir_node, rtprilist, node);
366 mir_connection *mir_router_add_explicit_route(struct userdata *u,
372 mir_connection *conn;
377 pa_assert_se((router = u->router));
379 conn = pa_xnew0(mir_connection, 1);
380 MIR_DLIST_INIT(conn->link);
382 conn->from = from->index;
383 conn->to = to->index;
385 MIR_DLIST_APPEND(mir_connection, link, conn, &router->connlist);
387 mir_router_make_routing(u);
392 void mir_router_remove_explicit_route(struct userdata *u, mir_connection *conn)
401 pa_assert_se((core = u->core));
402 pa_assert_se((router = u->router));
404 MIR_DLIST_UNLINK(mir_connection, link, conn);
406 if (!(from = mir_node_find_by_index(u, conn->from)) ||
407 !(to = mir_node_find_by_index(u, conn->to)) )
409 pa_log_debug("can't remove explicit route: some node was not found");
412 pa_log_debug("tear down link '%s' => '%s'", from->amname, to->amname);
414 if (!mir_switch_teardown_link(u, from, to)) {
415 pa_log_debug("can't remove explicit route: "
416 "failed to teardown link");
420 mir_router_make_routing(u);
428 int mir_router_print_rtgroups(struct userdata *u, char *buf, int len)
436 pa_assert_se((router = u->router));
437 pa_assert(router->rtgroups.input);
438 pa_assert(router->rtgroups.output);
443 p += print_routing_table(router->rtgroups.input, "input", p, e-p);
446 p += print_routing_table(router->rtgroups.output, "output", p, e-p);
452 mir_node *mir_router_make_prerouting(struct userdata *u, mir_node *data)
463 pa_assert_se((router = u->router));
464 pa_assert_se((data->implement == mir_stream));
466 priority = node_priority(u, data);
469 stamp = pa_utils_new_stamp();
471 make_explicit_routes(u, stamp);
473 pa_audiomgr_delete_default_routes(u);
475 MIR_DLIST_FOR_EACH_BACKWARD(mir_node, rtprilist, start, &router->nodlist) {
476 if (start->implement == mir_device) {
478 if (start->direction == mir_output)
479 continue; /* we should never get here */
480 if (!start->mux && !start->loop)
481 continue; /* skip not looped back input nodes */
484 continue; /* only looped back devices routed here */
487 if (priority >= node_priority(u, start)) {
488 if ((target = find_default_route(u, data, stamp)))
489 implement_preroute(u, data, target, stamp);
493 if (start->stamp >= stamp)
496 if ((end = find_default_route(u, start, stamp)))
497 implement_default_route(u, start, end, stamp);
500 if (!done && (target = find_default_route(u, data, stamp)))
501 implement_preroute(u, data, target, stamp);
507 void mir_router_make_routing(struct userdata *u)
509 static bool ongoing_routing;
517 pa_assert_se((router = u->router));
522 ongoing_routing = true;
523 stamp = pa_utils_new_stamp();
525 make_explicit_routes(u, stamp);
527 pa_audiomgr_delete_default_routes(u);
529 MIR_DLIST_FOR_EACH_BACKWARD(mir_node,rtprilist, start, &router->nodlist) {
530 if (start->implement == mir_device) {
532 if (start->direction == mir_output)
533 continue; /* we should never get here */
534 if (!start->mux && !start->loop)
535 continue; /* skip not looped back input nodes */
538 continue; /* only looped back devices routed here */
541 if (start->stamp >= stamp)
544 if ((end = find_default_route(u, start, stamp)))
545 implement_default_route(u, start, end, stamp);
548 pa_audiomgr_send_default_routes(u);
550 pa_fader_apply_volume_limits(u, stamp);
552 ongoing_routing = false;
557 bool mir_router_default_accept(struct userdata *u, mir_rtgroup *rtg,
566 const char *role, *excluded_role;
574 if (class == mir_bluetooth_carkit)
576 else if (class == mir_jack || class == mir_hdmi) {
577 pa_assert_se((core = u->core));
579 if (node->direction == mir_input) {
580 source = pa_idxset_get_by_index(core->sources,node->paidx);
581 pl = source ? source->proplist : NULL;
582 excluded_role = "hfp_uplink";
585 sink = pa_idxset_get_by_index(core->sinks, node->paidx);
586 pl = sink ? sink->proplist : NULL;
587 excluded_role = "hfp_downlink";
589 role = pl ? pa_proplist_gets(pl, PA_PROP_NODE_ROLE) : NULL;
590 accept = role ? strcmp(role, excluded_role) : true;
593 accept = (class >= mir_device_class_begin &&
594 class < mir_device_class_end);
601 bool mir_router_phone_accept(struct userdata *u, mir_rtgroup *rtg,
609 const char *role, *expected_role;
617 if (class >= mir_device_class_begin && class < mir_device_class_end) {
618 if (class != mir_bluetooth_a2dp &&
619 class != mir_spdif &&
621 class != mir_bluetooth_source &&
622 class != mir_bluetooth_sink &&
623 class != mir_bluetooth_carkit )
633 int mir_router_default_compare(struct userdata *u, mir_rtgroup *rtg,
634 mir_node *n1, mir_node *n2)
644 if (n1->type == mir_null)
646 if (n2->type == mir_null)
649 p1 = ((((n1->channels & 31) << 5) + n1->privacy) << 2) + n1->location;
650 p2 = ((((n2->channels & 31) << 5) + n2->privacy) << 2) + n2->location;
652 p1 = (p1 << 8) + ((n1->type - mir_device_class_begin) & 0xff);
653 p2 = (p2 << 8) + ((n2->type - mir_device_class_begin) & 0xff);
655 return uint32_cmp(p1,p2);
659 int mir_router_phone_compare(struct userdata *u, mir_rtgroup *rtg,
660 mir_node *n1, mir_node *n2)
670 if (n1->type == mir_null)
672 if (n2->type == mir_null)
675 p1 = (n1->privacy << 8) + ((n1->type - mir_device_class_begin) & 0xff);
676 p2 = (n2->privacy << 8) + ((n2->type - mir_device_class_begin) & 0xff);
678 return uint32_cmp(p1,p2);
682 static void rtgroup_destroy(struct userdata *u, mir_rtgroup *rtg)
684 mir_rtentry *rte, *n;
689 MIR_DLIST_FOR_EACH_SAFE(mir_rtentry, link, rte,n, &rtg->entries) {
690 remove_rtentry(u, rte);
697 static int rtgroup_print(mir_rtgroup *rtg, char *buf, int len)
707 MIR_DLIST_FOR_EACH_BACKWARD(mir_rtentry, link, rte, &rtg->entries) {
711 p += snprintf(p, e-p, " '%s'", node->amname);
717 static void rtgroup_update_module_property(struct userdata *u,
728 pa_assert(rtg->name);
729 pa_assert_se((module = u->module));
731 snprintf(key, sizeof(key), PA_PROP_ROUTING_TABLE ".%s.%s",
732 mir_direction_str(type), rtg->name);
733 ret = rtgroup_print(rtg, value, sizeof(value));
738 pa_proplist_sets(module->proplist, key, value+1); /* skip ' '@beginning */
741 static void add_rtentry(struct userdata *u,
747 mir_rtentry *rte, *before;
752 pa_assert_se((router = u->router));
754 if (!rtg->accept(u, rtg, node)) {
755 pa_log_debug("refuse node '%s' registration to routing group '%s'",
756 node->amname, rtg->name);
760 rte = pa_xnew0(mir_rtentry, 1);
762 MIR_DLIST_APPEND(mir_rtentry, nodchain, rte, &node->rtentries);
766 MIR_DLIST_FOR_EACH(mir_rtentry, link, before, &rtg->entries) {
767 if (rtg->compare(u, rtg, node, before->node) < 0) {
768 MIR_DLIST_INSERT_BEFORE(mir_rtentry, link, rte, &before->link);
773 MIR_DLIST_APPEND(mir_rtentry, link, rte, &rtg->entries);
776 rtgroup_update_module_property(u, type, rtg);
777 pa_log_debug("node '%s' added to routing group '%s'",
778 node->amname, rtg->name);
781 static void remove_rtentry(struct userdata *u, mir_rtentry *rte)
788 pa_assert_se((rtg = rte->group));
789 pa_assert_se((node = rte->node));
791 MIR_DLIST_UNLINK(mir_rtentry, link, rte);
792 MIR_DLIST_UNLINK(mir_rtentry, nodchain, rte);
796 rtgroup_update_module_property(u, node->direction, rtg);
799 static void make_explicit_routes(struct userdata *u, uint32_t stamp)
802 mir_connection *conn;
807 pa_assert_se((router = u->router));
809 MIR_DLIST_FOR_EACH_BACKWARD(mir_connection,link, conn, &router->connlist) {
813 if (!(from = mir_node_find_by_index(u, conn->from)) ||
814 !(to = mir_node_find_by_index(u, conn->to)) )
816 pa_log_debug("ignoring explicit route %u: some of the nodes "
817 "not found", conn->amid);
821 if (!mir_switch_setup_link(u, from, to, true))
824 if (from->implement == mir_stream)
827 if (to->implement == mir_device)
828 mir_volume_add_limiting_class(u, to, volume_class(from), stamp);
833 static mir_node *find_default_route(struct userdata *u,
837 pa_router *router = u->router;
838 mir_node_type class = pa_classify_guess_application_class(start);
839 mir_zone *zone = pa_zoneset_get_zone_by_name(u, start->zone);
846 if (class < 0 || class > router->maplen) {
847 pa_log_debug("can't route '%s': class %d is out of range (0 - %d)",
848 start->amname, class, router->maplen);
853 pa_log_debug("can't route '%s': zone '%s' is unknown",
854 start->amname, start->zone);
858 switch (start->direction) {
859 case mir_input: cmap = router->classmap.output; break;
860 case mir_output: cmap = router->classmap.input; break;
861 default: cmap = NULL; break;
864 if (!cmap || !(zmap = cmap[zone->index]) || !(rtg = zmap[class])) {
865 pa_log_debug("node '%s' won't be routed beacuse its class '%s' "
866 "is not assigned to any router group",
867 start->amname, mir_node_type_str(class));
871 pa_log_debug("using '%s' router group when routing '%s'",
872 rtg->name, start->amname);
875 MIR_DLIST_FOR_EACH_BACKWARD(mir_rtentry, link, rte, &rtg->entries) {
876 if (!(end = rte->node)) {
877 pa_log(" node was null in mir_rtentry");
882 pa_log_debug(" '%s' ignored. Skipping...",end->amname);
886 if (!end->available) {
887 pa_log_debug(" '%s' not available. Skipping...", end->amname);
891 if (end->paidx == PA_IDXSET_INVALID && !end->paport) {
892 /* requires profile change. We do it only for BT headsets */
893 if (end->type != mir_bluetooth_a2dp &&
894 end->type != mir_bluetooth_sco )
896 pa_log_debug(" '%s' has no sink. Skipping...", end->amname);
901 if (rte->stamp < stamp)
902 mir_constrain_apply(u, end, stamp);
905 pa_log_debug(" '%s' is blocked by constraints. Skipping...",
911 pa_log_debug("routing '%s' => '%s'", start->amname, end->amname);
913 pa_audiomgr_add_default_route(u, start, end);
918 pa_log_debug("could not find route for '%s'", start->amname);
923 static void implement_preroute(struct userdata *u,
928 if (data->direction == mir_output)
929 mir_switch_setup_link(u, target, NULL, false);
931 mir_switch_setup_link(u, NULL, target, false);
932 mir_volume_add_limiting_class(u, target, data->type, stamp);
936 static void implement_default_route(struct userdata *u,
941 if (start->direction == mir_output)
942 mir_switch_setup_link(u, end, start, false);
944 mir_switch_setup_link(u, start, end, false);
945 mir_volume_add_limiting_class(u, end, volume_class(start), stamp);
950 static int uint32_cmp(uint32_t v1, uint32_t v2)
959 static int node_priority(struct userdata *u, mir_node *node)
966 pa_assert_se((router = u->router));
967 pa_assert(router->priormap);
969 class = pa_classify_guess_application_class(node);
971 if (class < 0 || class >= router->maplen)
974 return router->priormap[class];
977 static int volume_class(mir_node *node)
979 int device_class[mir_device_class_end - mir_device_class_begin] = {
980 [ mir_bluetooth_carkit - mir_device_class_begin ] = mir_phone,
981 [ mir_bluetooth_source - mir_device_class_begin ] = mir_player,
990 if (t >= mir_application_class_begin && t < mir_application_class_end)
993 if (t >= mir_device_class_begin && t < mir_device_class_end)
994 return device_class[t - mir_device_class_begin];
996 return mir_node_type_unknown;
1000 static int print_routing_table(pa_hashmap *table,
1014 e = (p = buf) + len;
1018 p += snprintf(p, e-p, "%s routing table:\n", type);
1023 PA_HASHMAP_FOREACH(rtg, table, state) {
1027 p += snprintf(p, e-p, " %s:", rtg->name);
1030 p += rtgroup_print(rtg, p, e-p);
1033 p += snprintf(p, e-p, "\n");
1037 p += snprintf(p, e-p, " <empty>\n");
1048 * indent-tabs-mode: nil