6 #include <pulsecore/pulsecore-config.h>
8 #include <pulse/proplist.h>
9 #include <pulsecore/module.h>
16 static void rtgroup_destroy(struct userdata *, mir_rtgroup *);
17 static int rtgroup_print(mir_rtgroup *, char *, int);
18 static void rtgroup_update_module_property(struct userdata *, mir_rtgroup *);
21 static void add_rtentry(struct userdata *, mir_rtgroup *, mir_node *);
22 static void remove_rtentry(struct userdata *, mir_rtentry *);
24 static mir_node *route_stream(struct userdata *, mir_node *);
27 static int uint32_cmp(uint32_t, uint32_t);
29 static int node_priority(struct userdata *, mir_node *);
32 static void pa_hashmap_rtgroup_free(void *rtg, void *u)
34 rtgroup_destroy(u, rtg);
38 pa_router *pa_router_init(struct userdata *u)
40 size_t num_classes = mir_application_class_end;
41 pa_router *router = pa_xnew0(pa_router, 1);
43 router->rtgroups = pa_hashmap_new(pa_idxset_string_hash_func,
44 pa_idxset_string_compare_func);
45 router->maplen = num_classes;
46 router->classmap = pa_xnew0(mir_rtgroup *, num_classes);
47 router->priormap = pa_xnew0(int, num_classes);
48 MIR_DLIST_INIT(router->nodlist);
53 void pa_router_done(struct userdata *u)
58 if (u && (router = u->router)) {
59 MIR_DLIST_FOR_EACH_SAFE(mir_node, rtentries, e,n, &router->nodlist) {
60 MIR_DLIST_UNLINK(mir_node, rtentries, e);
63 pa_hashmap_free(router->rtgroups, pa_hashmap_rtgroup_free,u);
65 pa_xfree(router->classmap);
66 pa_xfree(router->priormap);
74 pa_bool_t mir_router_create_rtgroup(struct userdata *u,
76 mir_rtgroup_accept_t accept,
77 mir_rtgroup_compare_t compare)
86 pa_assert_se((router = u->router));
88 rtg = pa_xnew0(mir_rtgroup, 1);
89 rtg->name = pa_xstrdup(name);
91 rtg->compare = compare;
92 MIR_DLIST_INIT(rtg->entries);
94 if (pa_hashmap_put(router->rtgroups, rtg->name, rtg) < 0) {
100 pa_log_debug("routing group '%s' created", name);
105 void mir_router_destroy_rtgroup(struct userdata *u, const char *name)
112 pa_assert_se((router = u->router));
114 if (!(rtg = pa_hashmap_remove(router->rtgroups, name)))
115 pa_log_debug("can't destroy routing group '%s': group not found",name);
117 rtgroup_destroy(u, rtg);
118 pa_log_debug("routing group '%s' destroyed", name);
123 pa_bool_t mir_router_assign_class_to_rtgroup(struct userdata *u,
125 const char *rtgrpnam)
133 pa_assert_se((router = u->router));
135 if (class < 0 || class >= router->maplen) {
136 pa_log_debug("can't assign class (%d) to routing group '%s': "
137 "class id is out of range (0 - %d)",
138 class, rtgrpnam, router->maplen);
142 clnam = mir_node_type_str(class);
144 if (!(rtg = pa_hashmap_get(router->rtgroups, rtgrpnam))) {
145 pa_log_debug("can't assign class '%s' to routing group '%s': "
146 "router group not found", clnam, rtgrpnam);
149 router->classmap[class] = rtg;
151 pa_log_debug("class '%s' assigned to routing group '%s'", clnam, rtgrpnam);
157 void mir_router_register_node(struct userdata *u, mir_node *node)
167 pa_assert_se((router = u->router));
169 if (node->implement == mir_device) {
170 if (node->direction == mir_output) {
171 PA_HASHMAP_FOREACH(rtg, router->rtgroups, state) {
172 add_rtentry(u, rtg, node);
178 if (node->implement == mir_stream) {
179 if (node->direction == mir_input) {
180 priority = node_priority(u, node);
182 MIR_DLIST_FOR_EACH(mir_node, rtentries, before, &router->nodlist) {
183 if (priority < node_priority(u, before)) {
184 MIR_DLIST_INSERT_BEFORE(mir_node, rtentries, node,
190 MIR_DLIST_APPEND(mir_node, rtentries, node, &router->nodlist);
196 void mir_router_unregister_node(struct userdata *u, mir_node *node)
199 mir_rtentry *rte, *n;
203 pa_assert_se((router = u->router));
205 if (node->implement == mir_device && node->direction == mir_output) {
206 MIR_DLIST_FOR_EACH_SAFE(mir_rtentry,nodchain, rte,n, &node->rtentries){
207 remove_rtentry(u, rte);
212 if (node->implement == mir_stream && node->direction == mir_input) {
213 MIR_DLIST_UNLINK(mir_node, rtentries, node);
218 int mir_router_print_rtgroups(struct userdata *u, char *buf, int len)
228 pa_assert_se((router = u->router));
229 pa_assert(router->rtgroups);
234 p += snprintf(p, e-p, "routing table:\n");
237 PA_HASHMAP_FOREACH(rtg, router->rtgroups, state) {
239 p += snprintf(p, e-p, " %s:", rtg->name);
242 p += rtgroup_print(rtg, p, e-p);
245 p += snprintf(p, e-p, "\n");
254 mir_node *mir_router_make_prerouting(struct userdata *u, mir_node *data)
264 pa_assert_se((router = u->router));
266 priority = node_priority(u, data);
270 MIR_DLIST_FOR_EACH_BACKWARD(mir_node,rtentries, source, &router->nodlist) {
271 if (priority >= node_priority(u, source)) {
272 if ((target = route_stream(u, data)))
273 mir_switch_setup_link(u, NULL, target, TRUE);
277 if ((sink = route_stream(u, source)))
278 mir_switch_setup_link(u, source, sink, FALSE);
281 if (!done && (target = route_stream(u, data)))
282 mir_switch_setup_link(u, NULL, target, TRUE);
288 void mir_router_make_routing(struct userdata *u)
290 static pa_bool_t ongoing_routing;
297 pa_assert_se((router = u->router));
302 ongoing_routing = TRUE;
304 MIR_DLIST_FOR_EACH_BACKWARD(mir_node,rtentries, source, &router->nodlist) {
305 if ((sink = route_stream(u, source)))
306 mir_switch_setup_link(u, source, sink, FALSE);
309 ongoing_routing = FALSE;
314 pa_bool_t mir_router_default_accept(struct userdata *u, mir_rtgroup *rtg,
323 accept = (node->type >= mir_device_class_begin &&
324 node->type < mir_device_class_end);
329 int mir_router_default_compare(struct userdata *u, mir_node *n1, mir_node *n2)
338 if (n1->type == mir_null)
340 if (n2->type == mir_null)
343 p1 = ((((n1->channels & 31) << 5) + n1->privacy) << 2) + n1->location;
344 p2 = ((((n2->channels & 31) << 5) + n2->privacy) << 2) + n2->location;
346 p1 = (p1 << 8) + ((n1->type - mir_device_class_begin) & 0xff);
347 p2 = (p2 << 8) + ((n2->type - mir_device_class_begin) & 0xff);
349 return uint32_cmp(p1,p2);
353 static void rtgroup_destroy(struct userdata *u, mir_rtgroup *rtg)
355 mir_rtentry *rte, *n;
360 MIR_DLIST_FOR_EACH_SAFE(mir_rtentry, link, rte,n, &rtg->entries) {
361 remove_rtentry(u, rte);
368 static int rtgroup_print(mir_rtgroup *rtg, char *buf, int len)
376 MIR_DLIST_FOR_EACH_BACKWARD(mir_rtentry, link, rte, &rtg->entries) {
380 p += snprintf(p, e-p, " '%s'", node->amname);
386 static void rtgroup_update_module_property(struct userdata *u,mir_rtgroup *rtg)
394 pa_assert_se((module = u->module));
396 snprintf(key, sizeof(key), PA_PROP_ROUTING_TABLE ".%s", rtg->name);
397 rtgroup_print(rtg, value, sizeof(value));
399 pa_proplist_sets(module->proplist, key, value+1); /* skip ' '@beginning */
402 static void add_rtentry(struct userdata *u, mir_rtgroup *rtg, mir_node *node)
405 mir_rtentry *rte, *before;
410 pa_assert_se((router = u->router));
412 if (!rtg->accept(u, rtg, node)) {
413 pa_log_debug("refuse node '%s' registration to routing group '%s'",
414 node->amname, rtg->name);
418 rte = pa_xnew0(mir_rtentry, 1);
420 MIR_DLIST_APPEND(mir_rtentry, nodchain, rte, &node->rtentries);
423 MIR_DLIST_FOR_EACH(mir_rtentry, link, before, &rtg->entries) {
424 if (rtg->compare(u, node, before->node) < 0) {
425 MIR_DLIST_INSERT_BEFORE(mir_rtentry, link, rte, &before->link);
430 MIR_DLIST_APPEND(mir_rtentry, link, rte, &rtg->entries);
433 rtgroup_update_module_property(u, rtg);
434 pa_log_debug("node '%s' added to routing group '%s'",
435 node->amname, rtg->name);
438 static void remove_rtentry(struct userdata *u, mir_rtentry *rte)
443 MIR_DLIST_UNLINK(mir_rtentry, link, rte);
444 MIR_DLIST_UNLINK(mir_rtentry, nodchain, rte);
450 static mir_node *route_stream(struct userdata *u, mir_node *source)
452 pa_router *router = u->router;
453 mir_node_type class = source->type;
459 if (class < 0 || class > router->maplen) {
460 pa_log_debug("can't route '%s': class %d is out of range (0 - %d)",
461 source->amname, class, router->maplen);
465 if (!(rtg = router->classmap[class])) {
466 pa_log_debug("node '%s' won't be routed beacuse its class '%s' "
467 "is not assigned to any router group",
468 source->amname, mir_node_type_str(class));
472 pa_log_debug("using '%s' router group when routing '%s'",
473 rtg->name, source->amname);
476 MIR_DLIST_FOR_EACH_BACKWARD(mir_rtentry, link, rte, &rtg->entries) {
477 if (!(sink = rte->node)) {
478 pa_log(" node was null in mir_rtentry");
483 pa_log_debug(" '%s' should be ignored", sink->amname);
487 if (!sink->available) {
488 pa_log_debug(" '%s' not available", sink->amname);
492 if (sink->paidx == PA_IDXSET_INVALID) {
493 if (sink->type != mir_bluetooth_a2dp &&
494 sink->type != mir_bluetooth_sco)
496 pa_log_debug(" '%s' has no sink", sink->amname);
501 pa_log_debug("routing '%s' => '%s'", source->amname, sink->amname);
506 pa_log_debug("could not find route for '%s'", source->amname);
513 static int uint32_cmp(uint32_t v1, uint32_t v2)
523 static int node_priority(struct userdata *u, mir_node *node)
530 pa_assert_se((router = u->router));
531 pa_assert(router->priormap);
535 if (type < 0 || type >= router->maplen)
538 return router->priormap[type];
545 * indent-tabs-mode: nil