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_rtgroup *);
44 static void add_rtentry(struct userdata *, mir_rtgroup *, mir_node *);
45 static void remove_rtentry(struct userdata *, mir_rtentry *);
47 static void make_explicit_routes(struct userdata *, uint32_t);
48 static mir_node *find_default_route(struct userdata *, mir_node *, uint32_t);
51 static int uint32_cmp(uint32_t, uint32_t);
53 static int node_priority(struct userdata *, mir_node *);
56 static void pa_hashmap_rtgroup_free(void *rtg, void *u)
58 rtgroup_destroy(u, rtg);
62 pa_router *pa_router_init(struct userdata *u)
64 size_t num_classes = mir_application_class_end;
65 pa_router *router = pa_xnew0(pa_router, 1);
67 router->rtgroups = pa_hashmap_new(pa_idxset_string_hash_func,
68 pa_idxset_string_compare_func);
69 router->maplen = num_classes;
70 router->classmap = pa_xnew0(mir_rtgroup *, num_classes);
71 router->priormap = pa_xnew0(int, num_classes);
72 MIR_DLIST_INIT(router->nodlist);
73 MIR_DLIST_INIT(router->connlist);
78 void pa_router_done(struct userdata *u)
81 mir_connection *conn, *c;
84 if (u && (router = u->router)) {
85 MIR_DLIST_FOR_EACH_SAFE(mir_node, rtentries, e,n, &router->nodlist) {
86 MIR_DLIST_UNLINK(mir_node, rtentries, e);
89 MIR_DLIST_FOR_EACH_SAFE(mir_connection,link, conn,c,&router->connlist){
90 MIR_DLIST_UNLINK(mir_connection, link, conn);
94 pa_hashmap_free(router->rtgroups, pa_hashmap_rtgroup_free,u);
96 pa_xfree(router->classmap);
97 pa_xfree(router->priormap);
105 void mir_router_assign_class_priority(struct userdata *u,
113 pa_assert_se((router = u->router));
114 pa_assert_se((priormap = router->priormap));
116 if (class >= 0 && class < router->maplen) {
117 pa_log_debug("assigning priority %d to class '%s'",
118 pri, mir_node_type_str(class));
119 priormap[class] = pri;
124 pa_bool_t mir_router_create_rtgroup(struct userdata *u,
126 mir_rtgroup_accept_t accept,
127 mir_rtgroup_compare_t compare)
136 pa_assert_se((router = u->router));
138 rtg = pa_xnew0(mir_rtgroup, 1);
139 rtg->name = pa_xstrdup(name);
140 rtg->accept = accept;
141 rtg->compare = compare;
142 MIR_DLIST_INIT(rtg->entries);
144 if (pa_hashmap_put(router->rtgroups, rtg->name, rtg) < 0) {
150 pa_log_debug("routing group '%s' created", name);
155 void mir_router_destroy_rtgroup(struct userdata *u, const char *name)
162 pa_assert_se((router = u->router));
164 if (!(rtg = pa_hashmap_remove(router->rtgroups, name)))
165 pa_log_debug("can't destroy routing group '%s': group not found",name);
167 rtgroup_destroy(u, rtg);
168 pa_log_debug("routing group '%s' destroyed", name);
173 pa_bool_t mir_router_assign_class_to_rtgroup(struct userdata *u,
175 const char *rtgrpnam)
183 pa_assert_se((router = u->router));
185 if (class < 0 || class >= router->maplen) {
186 pa_log_debug("can't assign class (%d) to routing group '%s': "
187 "class id is out of range (0 - %d)",
188 class, rtgrpnam, router->maplen);
192 clnam = mir_node_type_str(class);
194 if (!(rtg = pa_hashmap_get(router->rtgroups, rtgrpnam))) {
195 pa_log_debug("can't assign class '%s' to routing group '%s': "
196 "router group not found", clnam, rtgrpnam);
199 router->classmap[class] = rtg;
201 pa_log_debug("class '%s' assigned to routing group '%s'", clnam, rtgrpnam);
208 void mir_router_register_node(struct userdata *u, mir_node *node)
218 pa_assert_se((router = u->router));
220 if (node->implement == mir_device) {
221 if (node->direction == mir_output) {
222 PA_HASHMAP_FOREACH(rtg, router->rtgroups, state) {
223 add_rtentry(u, rtg, node);
229 if (node->implement == mir_stream) {
230 if (node->direction == mir_input) {
231 priority = node_priority(u, node);
233 MIR_DLIST_FOR_EACH(mir_node, rtentries, before, &router->nodlist) {
234 if (priority < node_priority(u, before)) {
235 MIR_DLIST_INSERT_BEFORE(mir_node, rtentries, node,
241 MIR_DLIST_APPEND(mir_node, rtentries, node, &router->nodlist);
248 void mir_router_register_node(struct userdata *u, mir_node *node)
258 pa_assert_se((router = u->router));
260 if (node->direction == mir_output) {
261 if (node->implement == mir_device) {
262 PA_HASHMAP_FOREACH(rtg, router->rtgroups, state) {
263 add_rtentry(u, rtg, node);
270 if (node->direction == mir_input) {
271 if (node->implement == mir_device &&
272 !pa_classify_loopback_stream(node))
275 priority = node_priority(u, node);
277 MIR_DLIST_FOR_EACH(mir_node, rtentries, before, &router->nodlist) {
278 if (priority < node_priority(u, before)) {
279 MIR_DLIST_INSERT_BEFORE(mir_node, rtentries, node,
285 MIR_DLIST_APPEND(mir_node, rtentries, node, &router->nodlist);
291 void mir_router_unregister_node(struct userdata *u, mir_node *node)
294 mir_rtentry *rte, *n;
298 pa_assert_se((router = u->router));
300 if (node->direction == mir_output && node->implement == mir_device) {
301 MIR_DLIST_FOR_EACH_SAFE(mir_rtentry,nodchain, rte,n, &node->rtentries){
302 remove_rtentry(u, rte);
307 if (node->direction == mir_input) {
308 MIR_DLIST_UNLINK(mir_node, rtentries, node);
313 mir_connection *mir_router_add_explicit_route(struct userdata *u,
319 mir_connection *conn;
324 pa_assert_se((router = u->router));
326 conn = pa_xnew0(mir_connection, 1);
327 MIR_DLIST_INIT(conn->link);
329 conn->from = from->index;
330 conn->to = to->index;
332 MIR_DLIST_APPEND(mir_connection, link, conn, &router->connlist);
334 mir_router_make_routing(u);
339 void mir_router_remove_explicit_route(struct userdata *u, mir_connection *conn)
348 pa_assert_se((core = u->core));
349 pa_assert_se((router = u->router));
351 MIR_DLIST_UNLINK(mir_connection, link, conn);
353 if (!(from = mir_node_find_by_index(u, conn->from)) ||
354 !(to = mir_node_find_by_index(u, conn->to)) )
356 pa_log_debug("can't remove explicit route: some node was not found");
359 pa_log_debug("tear down link '%s' => '%s'", from->amname, to->amname);
361 if (!mir_switch_teardown_link(u, from, to)) {
362 pa_log_debug("can't remove explicit route: "
363 "failed to teardown link");
367 mir_router_make_routing(u);
374 int mir_router_print_rtgroups(struct userdata *u, char *buf, int len)
384 pa_assert_se((router = u->router));
385 pa_assert(router->rtgroups);
390 p += snprintf(p, e-p, "routing table:\n");
393 PA_HASHMAP_FOREACH(rtg, router->rtgroups, state) {
395 p += snprintf(p, e-p, " %s:", rtg->name);
398 p += rtgroup_print(rtg, p, e-p);
401 p += snprintf(p, e-p, "\n");
410 mir_node *mir_router_make_prerouting(struct userdata *u, mir_node *data)
421 pa_assert_se((router = u->router));
422 pa_assert_se((data->implement == mir_stream));
423 pa_assert_se((data->direction == mir_input));
425 priority = node_priority(u, data);
428 stamp = pa_utils_new_stamp();
430 make_explicit_routes(u, stamp);
432 MIR_DLIST_FOR_EACH_BACKWARD(mir_node, rtentries, from, &router->nodlist) {
433 if (from->implement == mir_device) {
434 if (from->direction == mir_output)
435 continue; /* we should never get here */
436 if (!from->mux && !from->loop)
437 continue; /* skip not looped back input nodes */
440 if (priority >= node_priority(u, from)) {
441 if ((target = find_default_route(u, data, stamp))) {
442 mir_switch_setup_link(u, NULL, target, FALSE);
443 mir_volume_add_limiting_class(u, target, data->type, stamp);
448 if (from->stamp >= stamp)
451 if ((to = find_default_route(u, from, stamp))) {
452 mir_switch_setup_link(u, from, to, FALSE);
453 mir_volume_add_limiting_class(u, to, from->type, stamp);
457 if (!done && (target = find_default_route(u, data, stamp))) {
458 mir_switch_setup_link(u, NULL, target, FALSE);
459 mir_volume_add_limiting_class(u, target, data->type, stamp);
466 void mir_router_make_routing(struct userdata *u)
468 static pa_bool_t ongoing_routing;
476 pa_assert_se((router = u->router));
481 ongoing_routing = TRUE;
482 stamp = pa_utils_new_stamp();
484 make_explicit_routes(u, stamp);
486 MIR_DLIST_FOR_EACH_BACKWARD(mir_node,rtentries, from, &router->nodlist) {
487 if (from->implement == mir_device) {
488 if (from->direction == mir_output)
489 continue; /* we should never get here */
490 if (!from->mux && !from->loop)
491 continue; /* skip not looped back input nodes */
494 if (from->stamp >= stamp)
497 if ((to = find_default_route(u, from, stamp))) {
498 mir_switch_setup_link(u, from, to, FALSE);
499 mir_volume_add_limiting_class(u, to, from->type, stamp);
503 pa_fader_apply_volume_limits(u, stamp);
505 ongoing_routing = FALSE;
510 pa_bool_t mir_router_default_accept(struct userdata *u, mir_rtgroup *rtg,
519 accept = (node->type >= mir_device_class_begin &&
520 node->type < mir_device_class_end);
526 pa_bool_t mir_router_phone_accept(struct userdata *u, mir_rtgroup *rtg,
537 if (class >= mir_device_class_begin && class < mir_device_class_end) {
538 if (class != mir_bluetooth_a2dp &&
539 class != mir_usb_headphone &&
540 class != mir_wired_headphone &&
552 int mir_router_default_compare(struct userdata *u, mir_node *n1, mir_node *n2)
561 if (n1->type == mir_null)
563 if (n2->type == mir_null)
566 p1 = ((((n1->channels & 31) << 5) + n1->privacy) << 2) + n1->location;
567 p2 = ((((n2->channels & 31) << 5) + n2->privacy) << 2) + n2->location;
569 p1 = (p1 << 8) + ((n1->type - mir_device_class_begin) & 0xff);
570 p2 = (p2 << 8) + ((n2->type - mir_device_class_begin) & 0xff);
572 return uint32_cmp(p1,p2);
576 int mir_router_phone_compare(struct userdata *u, mir_node *n1, mir_node *n2)
585 if (n1->type == mir_null)
587 if (n2->type == mir_null)
590 p1 = (n1->privacy << 8) + ((n1->type - mir_device_class_begin) & 0xff);
591 p2 = (n2->privacy << 8) + ((n2->type - mir_device_class_begin) & 0xff);
593 return uint32_cmp(p1,p2);
597 static void rtgroup_destroy(struct userdata *u, mir_rtgroup *rtg)
599 mir_rtentry *rte, *n;
604 MIR_DLIST_FOR_EACH_SAFE(mir_rtentry, link, rte,n, &rtg->entries) {
605 remove_rtentry(u, rte);
612 static int rtgroup_print(mir_rtgroup *rtg, char *buf, int len)
620 MIR_DLIST_FOR_EACH_BACKWARD(mir_rtentry, link, rte, &rtg->entries) {
624 p += snprintf(p, e-p, " '%s'", node->amname);
630 static void rtgroup_update_module_property(struct userdata *u,mir_rtgroup *rtg)
638 pa_assert_se((module = u->module));
640 snprintf(key, sizeof(key), PA_PROP_ROUTING_TABLE ".%s", rtg->name);
641 rtgroup_print(rtg, value, sizeof(value));
643 pa_proplist_sets(module->proplist, key, value+1); /* skip ' '@beginning */
646 static void add_rtentry(struct userdata *u, mir_rtgroup *rtg, mir_node *node)
649 mir_rtentry *rte, *before;
654 pa_assert_se((router = u->router));
656 if (!rtg->accept(u, rtg, node)) {
657 pa_log_debug("refuse node '%s' registration to routing group '%s'",
658 node->amname, rtg->name);
662 rte = pa_xnew0(mir_rtentry, 1);
664 MIR_DLIST_APPEND(mir_rtentry, nodchain, rte, &node->rtentries);
668 MIR_DLIST_FOR_EACH(mir_rtentry, link, before, &rtg->entries) {
669 if (rtg->compare(u, node, before->node) < 0) {
670 MIR_DLIST_INSERT_BEFORE(mir_rtentry, link, rte, &before->link);
675 MIR_DLIST_APPEND(mir_rtentry, link, rte, &rtg->entries);
678 rtgroup_update_module_property(u, rtg);
679 pa_log_debug("node '%s' added to routing group '%s'",
680 node->amname, rtg->name);
683 static void remove_rtentry(struct userdata *u, mir_rtentry *rte)
688 MIR_DLIST_UNLINK(mir_rtentry, link, rte);
689 MIR_DLIST_UNLINK(mir_rtentry, nodchain, rte);
694 static void make_explicit_routes(struct userdata *u, uint32_t stamp)
697 mir_connection *conn;
702 pa_assert_se((router = u->router));
704 MIR_DLIST_FOR_EACH_BACKWARD(mir_connection,link, conn, &router->connlist) {
708 if (!(from = mir_node_find_by_index(u, conn->from)) ||
709 !(to = mir_node_find_by_index(u, conn->to)) )
711 pa_log_debug("ignoring explicit route %u: some of the nodes "
712 "not found", conn->amid);
716 if (!mir_switch_setup_link(u, from, to, TRUE))
719 if (from->implement == mir_stream)
722 if (to->implement == mir_device)
723 mir_volume_add_limiting_class(u, to, from->type, stamp);
728 static mir_node *find_default_route(struct userdata *u,
732 pa_router *router = u->router;
733 mir_node_type class = pa_classify_guess_application_class(from);
739 if (class < 0 || class > router->maplen) {
740 pa_log_debug("can't route '%s': class %d is out of range (0 - %d)",
741 from->amname, class, router->maplen);
745 if (!(rtg = router->classmap[class])) {
746 pa_log_debug("node '%s' won't be routed beacuse its class '%s' "
747 "is not assigned to any router group",
748 from->amname, mir_node_type_str(class));
752 pa_log_debug("using '%s' router group when routing '%s'",
753 rtg->name, from->amname);
756 MIR_DLIST_FOR_EACH_BACKWARD(mir_rtentry, link, rte, &rtg->entries) {
757 if (!(to = rte->node)) {
758 pa_log(" node was null in mir_rtentry");
763 pa_log_debug(" '%s' ignored. Skipping...",to->amname);
767 if (!to->available) {
768 pa_log_debug(" '%s' not available. Skipping...", to->amname);
772 if (to->paidx == PA_IDXSET_INVALID) {
773 if (to->paport == NULL &&
774 to->type != mir_bluetooth_a2dp &&
775 to->type != mir_bluetooth_sco)
777 pa_log_debug(" '%s' has no sink. Skipping...", to->amname);
782 if (rte->stamp < stamp)
783 mir_constrain_apply(u, to, stamp);
786 pa_log_debug(" '%s' is blocked by constraints. Skipping...",
792 pa_log_debug("routing '%s' => '%s'", from->amname, to->amname);
797 pa_log_debug("could not find route for '%s'", from->amname);
804 static int uint32_cmp(uint32_t v1, uint32_t v2)
814 static int node_priority(struct userdata *u, mir_node *node)
822 pa_assert_se((router = u->router));
823 pa_assert(router->priormap);
825 class = pa_classify_guess_application_class(node);
827 if (class < 0 || class >= router->maplen)
830 return router->priormap[class];
837 * indent-tabs-mode: nil