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>
33 #include "constrain.h"
40 static void rtgroup_destroy(struct userdata *, mir_rtgroup *);
41 static int rtgroup_print(mir_rtgroup *, char *, int);
42 static void rtgroup_update_module_property(struct userdata *, mir_direction,
45 static void add_rtentry(struct userdata *, mir_direction, mir_rtgroup *,
47 static void remove_rtentry(struct userdata *, mir_rtentry *);
49 static void make_explicit_routes(struct userdata *, uint32_t);
50 static mir_node *find_default_route(struct userdata *, mir_node *, uint32_t);
51 static void implement_preroute(struct userdata *, mir_node *, mir_node *,
53 static void implement_default_route(struct userdata *, mir_node *, mir_node *,
56 static int uint32_cmp(uint32_t, uint32_t);
58 static int node_priority(struct userdata *, mir_node *);
60 static int print_routing_table(pa_hashmap *, const char *, char *, int);
63 static void pa_hashmap_rtgroup_free(void *rtg, void *u)
65 rtgroup_destroy(u, rtg);
69 pa_router *pa_router_init(struct userdata *u)
71 size_t num_classes = mir_application_class_end;
72 pa_router *router = pa_xnew0(pa_router, 1);
74 router->rtgroups.input = pa_hashmap_new(pa_idxset_string_hash_func,
75 pa_idxset_string_compare_func);
76 router->rtgroups.output = pa_hashmap_new(pa_idxset_string_hash_func,
77 pa_idxset_string_compare_func);
79 router->maplen = num_classes;
81 router->classmap.input = pa_xnew0(mir_rtgroup *, num_classes);
82 router->classmap.output = pa_xnew0(mir_rtgroup *, 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;
98 if (u && (router = u->router)) {
99 MIR_DLIST_FOR_EACH_SAFE(mir_node, rtentries, e,n, &router->nodlist) {
100 MIR_DLIST_UNLINK(mir_node, rtentries, e);
103 MIR_DLIST_FOR_EACH_SAFE(mir_connection,link, conn,c,&router->connlist){
104 MIR_DLIST_UNLINK(mir_connection, link, conn);
108 pa_hashmap_free(router->rtgroups.input , pa_hashmap_rtgroup_free,u);
109 pa_hashmap_free(router->rtgroups.output, pa_hashmap_rtgroup_free,u);
111 pa_xfree(router->classmap.input);
112 pa_xfree(router->classmap.output);
114 pa_xfree(router->priormap);
122 void mir_router_assign_class_priority(struct userdata *u,
130 pa_assert_se((router = u->router));
131 pa_assert_se((priormap = router->priormap));
133 if (class >= 0 && class < router->maplen) {
134 pa_log_debug("assigning priority %d to class '%s'",
135 pri, mir_node_type_str(class));
136 priormap[class] = pri;
141 pa_bool_t mir_router_create_rtgroup(struct userdata *u,
144 mir_rtgroup_accept_t accept,
145 mir_rtgroup_compare_t compare)
152 pa_assert(type == mir_input || type == mir_output);
156 pa_assert_se((router = u->router));
158 if (type == mir_input)
159 table = router->rtgroups.input;
161 table = router->rtgroups.output;
165 rtg = pa_xnew0(mir_rtgroup, 1);
166 rtg->name = pa_xstrdup(name);
167 rtg->accept = accept;
168 rtg->compare = compare;
169 MIR_DLIST_INIT(rtg->entries);
171 if (pa_hashmap_put(table, rtg->name, rtg) < 0) {
177 pa_log_debug("%s routing group '%s' created",
178 mir_direction_str(type), name);
183 void mir_router_destroy_rtgroup(struct userdata *u,
193 pa_assert_se((router = u->router));
195 if (type == mir_input)
196 table = router->rtgroups.input;
198 table = router->rtgroups.output;
202 if (!(rtg = pa_hashmap_remove(table, name))) {
203 pa_log_debug("can't destroy %s routing group '%s': group not found",
204 mir_direction_str(type), name);
207 rtgroup_destroy(u, rtg);
208 pa_log_debug("routing group '%s' destroyed", name);
213 pa_bool_t mir_router_assign_class_to_rtgroup(struct userdata *u,
216 const char *rtgrpnam)
220 mir_rtgroup **classmap;
223 const char *direction;
226 pa_assert(type == mir_input || type == mir_output);
228 pa_assert_se((router = u->router));
230 if (type == mir_input) {
231 rtable = router->rtgroups.input;
232 classmap = router->classmap.input;
235 rtable = router->rtgroups.output;
236 classmap = router->classmap.output;
239 if (class < 0 || class >= router->maplen) {
240 pa_log_debug("can't assign class (%d) to routing group '%s': "
241 "class id is out of range (0 - %d)",
242 class, rtgrpnam, router->maplen);
246 clnam = mir_node_type_str(class);
247 direction = mir_direction_str(type);
249 if (!(rtg = pa_hashmap_get(rtable, rtgrpnam))) {
250 pa_log_debug("can't assign class '%s' to %s routing group '%s': "
251 "router group not found", clnam, direction, rtgrpnam);
254 classmap[class] = rtg;
256 pa_log_debug("class '%s' assigned to %s routing group '%s'",
257 clnam, direction, rtgrpnam);
264 void mir_router_register_node(struct userdata *u, mir_node *node)
274 pa_assert_se((router = u->router));
276 if (node->direction == mir_output) {
277 if (node->implement == mir_device) {
278 PA_HASHMAP_FOREACH(rtg, router->rtgroups.output, state) {
279 add_rtentry(u, mir_output, rtg, node);
286 if (node->direction == mir_input) {
288 if (node->implement == mir_device) &&
289 !pa_classify_loopback_stream(node))
293 if (node->implement == mir_device) {
294 PA_HASHMAP_FOREACH(rtg, router->rtgroups.input, state) {
295 add_rtentry(u, mir_input, rtg, node);
298 if (!pa_classify_loopback_stream(node))
302 priority = node_priority(u, node);
304 MIR_DLIST_FOR_EACH(mir_node, rtentries, before, &router->nodlist) {
305 if (priority < node_priority(u, before)) {
306 MIR_DLIST_INSERT_BEFORE(mir_node, rtentries, node,
312 MIR_DLIST_APPEND(mir_node, rtentries, node, &router->nodlist);
318 void mir_router_unregister_node(struct userdata *u, mir_node *node)
321 mir_rtentry *rte, *n;
325 pa_assert_se((router = u->router));
327 if (node->direction == mir_output && node->implement == mir_device) {
328 MIR_DLIST_FOR_EACH_SAFE(mir_rtentry,nodchain, rte,n, &node->rtentries){
329 remove_rtentry(u, rte);
334 if (node->direction == mir_input) {
335 MIR_DLIST_UNLINK(mir_node, rtentries, node);
340 mir_connection *mir_router_add_explicit_route(struct userdata *u,
346 mir_connection *conn;
351 pa_assert_se((router = u->router));
353 conn = pa_xnew0(mir_connection, 1);
354 MIR_DLIST_INIT(conn->link);
356 conn->from = from->index;
357 conn->to = to->index;
359 MIR_DLIST_APPEND(mir_connection, link, conn, &router->connlist);
361 mir_router_make_routing(u);
366 void mir_router_remove_explicit_route(struct userdata *u, mir_connection *conn)
375 pa_assert_se((core = u->core));
376 pa_assert_se((router = u->router));
378 MIR_DLIST_UNLINK(mir_connection, link, conn);
380 if (!(from = mir_node_find_by_index(u, conn->from)) ||
381 !(to = mir_node_find_by_index(u, conn->to)) )
383 pa_log_debug("can't remove explicit route: some node was not found");
386 pa_log_debug("tear down link '%s' => '%s'", from->amname, to->amname);
388 if (!mir_switch_teardown_link(u, from, to)) {
389 pa_log_debug("can't remove explicit route: "
390 "failed to teardown link");
394 mir_router_make_routing(u);
402 int mir_router_print_rtgroups(struct userdata *u, char *buf, int len)
410 pa_assert_se((router = u->router));
411 pa_assert(router->rtgroups.input);
412 pa_assert(router->rtgroups.output);
417 p += print_routing_table(router->rtgroups.input, "input", p, e-p);
420 p += print_routing_table(router->rtgroups.output, "output", p, e-p);
426 mir_node *mir_router_make_prerouting(struct userdata *u, mir_node *data)
437 pa_assert_se((router = u->router));
438 pa_assert_se((data->implement == mir_stream));
440 priority = node_priority(u, data);
443 stamp = pa_utils_new_stamp();
445 make_explicit_routes(u, stamp);
447 MIR_DLIST_FOR_EACH_BACKWARD(mir_node, rtentries, start, &router->nodlist) {
448 if (start->implement == mir_device) {
450 if (start->direction == mir_output)
451 continue; /* we should never get here */
452 if (!start->mux && !start->loop)
453 continue; /* skip not looped back input nodes */
456 continue; /* only looped back devices routed here */
459 if (priority >= node_priority(u, start)) {
460 if ((target = find_default_route(u, data, stamp)))
461 implement_preroute(u, data, target, stamp);
465 if (start->stamp >= stamp)
468 if ((end = find_default_route(u, start, stamp)))
469 implement_default_route(u, start, end, stamp);
472 if (!done && (target = find_default_route(u, data, stamp)))
473 implement_preroute(u, data, target, stamp);
479 void mir_router_make_routing(struct userdata *u)
481 static pa_bool_t ongoing_routing;
489 pa_assert_se((router = u->router));
494 ongoing_routing = TRUE;
495 stamp = pa_utils_new_stamp();
497 make_explicit_routes(u, stamp);
499 MIR_DLIST_FOR_EACH_BACKWARD(mir_node,rtentries, start, &router->nodlist) {
500 if (start->implement == mir_device) {
502 if (start->direction == mir_output)
503 continue; /* we should never get here */
504 if (!start->mux && !start->loop)
505 continue; /* skip not looped back input nodes */
508 continue; /* only looped back devices routed here */
511 if (start->stamp >= stamp)
514 if ((end = find_default_route(u, start, stamp)))
515 implement_default_route(u, start, end, stamp);
518 pa_fader_apply_volume_limits(u, stamp);
520 ongoing_routing = FALSE;
525 pa_bool_t mir_router_default_accept(struct userdata *u, mir_rtgroup *rtg,
534 if (node->type == mir_bluetooth_carkit)
537 accept = (node->type >= mir_device_class_begin &&
538 node->type < mir_device_class_end);
544 pa_bool_t mir_router_phone_accept(struct userdata *u, mir_rtgroup *rtg,
555 if (class >= mir_device_class_begin && class < mir_device_class_end) {
556 if (class != mir_bluetooth_a2dp &&
557 class != mir_usb_headphone &&
558 class != mir_wired_headphone &&
561 class != mir_spdif &&
562 class != mir_bluetooth_source &&
563 class != mir_bluetooth_sink &&
564 class != mir_bluetooth_carkit )
574 int mir_router_default_compare(struct userdata *u, mir_node *n1, mir_node *n2)
583 if (n1->type == mir_null)
585 if (n2->type == mir_null)
588 p1 = ((((n1->channels & 31) << 5) + n1->privacy) << 2) + n1->location;
589 p2 = ((((n2->channels & 31) << 5) + n2->privacy) << 2) + n2->location;
591 p1 = (p1 << 8) + ((n1->type - mir_device_class_begin) & 0xff);
592 p2 = (p2 << 8) + ((n2->type - mir_device_class_begin) & 0xff);
594 return uint32_cmp(p1,p2);
598 int mir_router_phone_compare(struct userdata *u, mir_node *n1, mir_node *n2)
607 if (n1->type == mir_null)
609 if (n2->type == mir_null)
612 p1 = (n1->privacy << 8) + ((n1->type - mir_device_class_begin) & 0xff);
613 p2 = (n2->privacy << 8) + ((n2->type - mir_device_class_begin) & 0xff);
615 return uint32_cmp(p1,p2);
619 static void rtgroup_destroy(struct userdata *u, mir_rtgroup *rtg)
621 mir_rtentry *rte, *n;
626 MIR_DLIST_FOR_EACH_SAFE(mir_rtentry, link, rte,n, &rtg->entries) {
627 remove_rtentry(u, rte);
634 static int rtgroup_print(mir_rtgroup *rtg, char *buf, int len)
642 MIR_DLIST_FOR_EACH_BACKWARD(mir_rtentry, link, rte, &rtg->entries) {
646 p += snprintf(p, e-p, " '%s'", node->amname);
652 static void rtgroup_update_module_property(struct userdata *u,
662 pa_assert_se((module = u->module));
664 snprintf(key, sizeof(key), PA_PROP_ROUTING_TABLE ".%s.%s",
665 mir_direction_str(type), rtg->name);
666 rtgroup_print(rtg, value, sizeof(value));
668 pa_proplist_sets(module->proplist, key, value+1); /* skip ' '@beginning */
671 static void add_rtentry(struct userdata *u,
677 mir_rtentry *rte, *before;
682 pa_assert_se((router = u->router));
684 if (!rtg->accept(u, rtg, node)) {
685 pa_log_debug("refuse node '%s' registration to routing group '%s'",
686 node->amname, rtg->name);
690 rte = pa_xnew0(mir_rtentry, 1);
692 MIR_DLIST_APPEND(mir_rtentry, nodchain, rte, &node->rtentries);
696 MIR_DLIST_FOR_EACH(mir_rtentry, link, before, &rtg->entries) {
697 if (rtg->compare(u, node, before->node) < 0) {
698 MIR_DLIST_INSERT_BEFORE(mir_rtentry, link, rte, &before->link);
703 MIR_DLIST_APPEND(mir_rtentry, link, rte, &rtg->entries);
706 rtgroup_update_module_property(u, type, rtg);
707 pa_log_debug("node '%s' added to routing group '%s'",
708 node->amname, rtg->name);
711 static void remove_rtentry(struct userdata *u, mir_rtentry *rte)
716 MIR_DLIST_UNLINK(mir_rtentry, link, rte);
717 MIR_DLIST_UNLINK(mir_rtentry, nodchain, rte);
722 static void make_explicit_routes(struct userdata *u, uint32_t stamp)
725 mir_connection *conn;
730 pa_assert_se((router = u->router));
732 MIR_DLIST_FOR_EACH_BACKWARD(mir_connection,link, conn, &router->connlist) {
736 if (!(from = mir_node_find_by_index(u, conn->from)) ||
737 !(to = mir_node_find_by_index(u, conn->to)) )
739 pa_log_debug("ignoring explicit route %u: some of the nodes "
740 "not found", conn->amid);
744 if (!mir_switch_setup_link(u, from, to, TRUE))
747 if (from->implement == mir_stream)
750 if (to->implement == mir_device)
751 mir_volume_add_limiting_class(u, to, from->type, stamp);
756 static mir_node *find_default_route(struct userdata *u,
760 pa_router *router = u->router;
761 mir_node_type class = pa_classify_guess_application_class(start);
762 mir_rtgroup **classmap;
767 if (class < 0 || class > router->maplen) {
768 pa_log_debug("can't route '%s': class %d is out of range (0 - %d)",
769 start->amname, class, router->maplen);
773 switch (start->direction) {
774 case mir_input: classmap = router->classmap.output; break;
775 case mir_output: classmap = router->classmap.input; break;
776 default: classmap = NULL; break;
779 if (!classmap || !(rtg = classmap[class])) {
780 pa_log_debug("node '%s' won't be routed beacuse its class '%s' "
781 "is not assigned to any router group",
782 start->amname, mir_node_type_str(class));
786 pa_log_debug("using '%s' router group when routing '%s'",
787 rtg->name, start->amname);
790 MIR_DLIST_FOR_EACH_BACKWARD(mir_rtentry, link, rte, &rtg->entries) {
791 if (!(end = rte->node)) {
792 pa_log(" node was null in mir_rtentry");
797 pa_log_debug(" '%s' ignored. Skipping...",end->amname);
801 if (!end->available) {
802 pa_log_debug(" '%s' not available. Skipping...", end->amname);
806 if (end->paidx == PA_IDXSET_INVALID && !end->paport) {
807 /* requires profile change. We do it only for BT headsets */
808 if (end->type != mir_bluetooth_a2dp &&
809 end->type != mir_bluetooth_sco )
811 pa_log_debug(" '%s' has no sink. Skipping...", end->amname);
816 if (rte->stamp < stamp)
817 mir_constrain_apply(u, end, stamp);
820 pa_log_debug(" '%s' is blocked by constraints. Skipping...",
826 pa_log_debug("routing '%s' => '%s'", start->amname, end->amname);
831 pa_log_debug("could not find route for '%s'", start->amname);
836 static void implement_preroute(struct userdata *u,
841 if (data->direction == mir_output)
842 mir_switch_setup_link(u, target, NULL, FALSE);
844 mir_switch_setup_link(u, NULL, target, FALSE);
845 mir_volume_add_limiting_class(u, target, data->type, stamp);
849 static void implement_default_route(struct userdata *u,
854 if (start->direction == mir_output)
855 mir_switch_setup_link(u, end, start, FALSE);
857 mir_switch_setup_link(u, start, end, FALSE);
858 mir_volume_add_limiting_class(u, end, start->type, stamp);
863 static int uint32_cmp(uint32_t v1, uint32_t v2)
873 static int node_priority(struct userdata *u, mir_node *node)
880 pa_assert_se((router = u->router));
881 pa_assert(router->priormap);
883 class = pa_classify_guess_application_class(node);
885 if (class < 0 || class >= router->maplen)
888 return router->priormap[class];
891 static int print_routing_table(pa_hashmap *table,
909 p += snprintf(p, e-p, "%s routing table:\n", type);
912 PA_HASHMAP_FOREACH(rtg, table, state) {
916 p += snprintf(p, e-p, " %s:", rtg->name);
919 p += rtgroup_print(rtg, p, e-p);
922 p += snprintf(p, e-p, "\n");
926 p += snprintf(p, e-p, " <empty>\n");
937 * indent-tabs-mode: nil