scripting: support for zone based routing in application classes
[profile/ivi/pulseaudio-module-murphy-ivi.git] / murphy / router.c
1 /*
2  * module-murphy-ivi -- PulseAudio module for providing audio routing support
3  * Copyright (c) 2012, Intel Corporation.
4  *
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.
8  *
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.
13  *
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,
17  * MA 02110-1301 USA.
18  *
19  */
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <errno.h>
24
25 #include <pulsecore/pulsecore-config.h>
26
27 #include <pulse/proplist.h>
28 #include <pulsecore/module.h>
29
30 #include "router.h"
31 #include "node.h"
32 #include "switch.h"
33 #include "constrain.h"
34 #include "volume.h"
35 #include "fader.h"
36 #include "utils.h"
37 #include "classify.h"
38
39
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,
43                                            mir_rtgroup *);
44
45 static void add_rtentry(struct userdata *, mir_direction, mir_rtgroup *,
46                         mir_node *);
47 static void remove_rtentry(struct userdata *, mir_rtentry *);
48
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 *,
52                                uint32_t);
53 static void implement_default_route(struct userdata *, mir_node *, mir_node *,
54                                     uint32_t);
55
56 static int uint32_cmp(uint32_t, uint32_t);
57
58 static int node_priority(struct userdata *, mir_node *);
59
60 static int volume_class(mir_node *);
61
62 static int print_routing_table(pa_hashmap *, const char *, char *, int);
63
64
65 static void pa_hashmap_rtgroup_free(void *rtg, void *u)
66 {
67     rtgroup_destroy(u, rtg);
68 }
69
70
71 pa_router *pa_router_init(struct userdata *u)
72 {
73     size_t     num_classes = mir_application_class_end;
74     pa_router *router = pa_xnew0(pa_router, 1);
75     
76     router->rtgroups.input  = pa_hashmap_new(pa_idxset_string_hash_func,
77                                             pa_idxset_string_compare_func);
78     router->rtgroups.output = pa_hashmap_new(pa_idxset_string_hash_func,
79                                              pa_idxset_string_compare_func);
80
81     router->maplen = num_classes;
82
83     router->classmap.input  = pa_xnew0(mir_rtgroup *, num_classes);
84     router->classmap.output = pa_xnew0(mir_rtgroup *, num_classes);
85
86     router->priormap = pa_xnew0(int, num_classes);
87
88     MIR_DLIST_INIT(router->nodlist);
89     MIR_DLIST_INIT(router->connlist);
90     
91     return router;
92 }
93
94 void pa_router_done(struct userdata *u)
95 {
96     pa_router      *router;
97     mir_connection *conn, *c;
98     mir_node       *e,*n;
99     void           *state;
100     mir_rtgroup    *rtg;
101
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);
105         }
106
107         MIR_DLIST_FOR_EACH_SAFE(mir_connection,link, conn,c,&router->connlist){
108             MIR_DLIST_UNLINK(mir_connection, link, conn);
109             pa_xfree(conn);
110         }
111
112         PA_HASHMAP_FOREACH(rtg, router->rtgroups.input, state) {
113             rtgroup_destroy(u, rtg);
114         }
115
116         PA_HASHMAP_FOREACH(rtg, router->rtgroups.output, state) {
117             rtgroup_destroy(u, rtg);
118         }
119
120         pa_hashmap_free(router->rtgroups.input, NULL);
121         pa_hashmap_free(router->rtgroups.output, NULL);
122
123         pa_xfree(router->classmap.input);
124         pa_xfree(router->classmap.output);
125
126         pa_xfree(router->priormap);
127         pa_xfree(router);
128
129         u->router = NULL;
130     }
131 }
132
133
134 void mir_router_assign_class_priority(struct userdata *u,
135                                       mir_node_type    class,
136                                       int              pri)
137 {
138     pa_router *router;
139     int *priormap;
140
141     pa_assert(u);
142     pa_assert_se((router = u->router));
143     pa_assert_se((priormap = router->priormap));
144
145     if (class >= 0 && class < router->maplen) {
146         pa_log_debug("assigning priority %d to class '%s'",
147                      pri, mir_node_type_str(class));
148         priormap[class] = pri;
149     }
150 }
151
152
153 mir_rtgroup *mir_router_create_rtgroup(struct userdata      *u,
154                                        mir_direction         type,
155                                        const char           *name,
156                                        mir_rtgroup_accept_t  accept,
157                                        mir_rtgroup_compare_t compare)
158 {
159     pa_router   *router;
160     pa_hashmap  *table;
161     mir_rtgroup *rtg;
162
163     pa_assert(u);
164     pa_assert(type == mir_input || type == mir_output);
165     pa_assert(name);
166     pa_assert(accept);
167     pa_assert(compare);
168     pa_assert_se((router = u->router));
169
170     if (type == mir_input)
171         table = router->rtgroups.input;
172     else
173         table = router->rtgroups.output;
174
175     pa_assert(table);
176
177     rtg = pa_xnew0(mir_rtgroup, 1);
178     rtg->name    = pa_xstrdup(name);
179     rtg->accept  = accept;
180     rtg->compare = compare;
181     MIR_DLIST_INIT(rtg->entries);
182
183     if (pa_hashmap_put(table, rtg->name, rtg) < 0) {
184         pa_xfree(rtg->name);
185         pa_xfree(rtg);
186         return NULL;
187     }
188
189     pa_log_debug("%s routing group '%s' created",
190                  mir_direction_str(type), name);
191
192     return rtg;
193 }
194
195 void mir_router_destroy_rtgroup(struct userdata *u,
196                                 mir_direction    type,
197                                 const char      *name)
198 {
199     pa_router   *router;
200     pa_hashmap  *table;
201     mir_rtgroup *rtg;
202
203     pa_assert(u);
204     pa_assert(name);
205     pa_assert_se((router = u->router));
206
207     if (type == mir_input)
208         table = router->rtgroups.input;
209     else
210         table = router->rtgroups.output;
211
212     pa_assert(table);
213
214     if (!(rtg = pa_hashmap_remove(table, name))) {
215         pa_log_debug("can't destroy %s routing group '%s': group not found",
216                      mir_direction_str(type), name);
217     }
218     else {
219         rtgroup_destroy(u, rtg);
220         pa_log_debug("routing group '%s' destroyed", name);
221     }
222 }
223
224
225 pa_bool_t mir_router_assign_class_to_rtgroup(struct userdata *u,
226                                              mir_node_type    class,
227                                              mir_direction    type,
228                                              const char      *rtgrpnam)
229 {
230     pa_router *router;
231     pa_hashmap *rtable;
232     mir_rtgroup **classmap;
233     mir_rtgroup *rtg;
234     const char *clnam;
235     const char *direction;
236
237     pa_assert(u);
238     pa_assert(type == mir_input || type == mir_output);
239     pa_assert(rtgrpnam);
240     pa_assert_se((router = u->router));
241
242     if (type == mir_input) {
243         rtable   = router->rtgroups.input;
244         classmap = router->classmap.input;
245     }
246     else {
247         rtable   = router->rtgroups.output;
248         classmap = router->classmap.output;
249     }
250
251     if (class < 0 || class >= router->maplen) {
252         pa_log_debug("can't assign class (%d) to  routing group '%s': "
253                      "class id is out of range (0 - %d)",
254                      class, rtgrpnam, router->maplen);
255         return FALSE;
256     }
257
258     clnam = mir_node_type_str(class);
259     direction = mir_direction_str(type);
260
261     if (!(rtg = pa_hashmap_get(rtable, rtgrpnam))) {
262         pa_log_debug("can't assign class '%s' to %s routing group '%s': "
263                      "router group not found", clnam, direction, rtgrpnam);
264     }
265
266     classmap[class] = rtg;
267
268     pa_log_debug("class '%s' assigned to %s routing group '%s'",
269                  clnam, direction, rtgrpnam);
270
271     return TRUE;
272 }
273
274
275
276 void mir_router_register_node(struct userdata *u, mir_node *node)
277 {
278     pa_router   *router;
279     mir_rtgroup *rtg;
280     void        *state;
281     int          priority;
282     mir_node    *before;
283
284     pa_assert(u);
285     pa_assert(node);
286     pa_assert_se((router = u->router));
287     
288     if (node->direction == mir_output) {
289         if (node->implement == mir_device) {
290             PA_HASHMAP_FOREACH(rtg, router->rtgroups.output, state) {
291                 add_rtentry(u, mir_output, rtg, node);
292             }
293         }
294         return;
295     }
296
297     
298     if (node->direction == mir_input) {
299 #if 0
300         if (node->implement == mir_device) &&
301             !pa_classify_loopback_stream(node))
302             return;
303 #endif
304
305         if (node->implement == mir_device) {
306             PA_HASHMAP_FOREACH(rtg, router->rtgroups.input, state) {
307                 add_rtentry(u, mir_input, rtg, node);
308             }
309
310             if (!pa_classify_loopback_stream(node))
311                 return;
312         }
313
314         priority = node_priority(u, node);
315             
316         MIR_DLIST_FOR_EACH(mir_node, rtprilist, before, &router->nodlist) {
317             if (priority < node_priority(u, before)) {
318                 MIR_DLIST_INSERT_BEFORE(mir_node, rtprilist, node,
319                                         &before->rtprilist);
320                 return;
321             }
322         }
323             
324         MIR_DLIST_APPEND(mir_node, rtprilist, node, &router->nodlist);
325
326         return;
327     }
328 }
329
330 void mir_router_unregister_node(struct userdata *u, mir_node *node)
331 {
332     pa_router *router;
333     mir_rtentry *rte, *n;
334     
335     pa_assert(u);
336     pa_assert(node);
337     pa_assert_se((router = u->router));
338
339     MIR_DLIST_FOR_EACH_SAFE(mir_rtentry,nodchain, rte,n, &node->rtentries) {
340         remove_rtentry(u, rte);
341     }
342
343     MIR_DLIST_UNLINK(mir_node, rtprilist, node);
344 }
345
346 mir_connection *mir_router_add_explicit_route(struct userdata *u,
347                                               uint16_t   amid,
348                                               mir_node  *from,
349                                               mir_node  *to)
350 {
351     pa_router *router;
352     mir_connection *conn;
353
354     pa_assert(u);
355     pa_assert(from);
356     pa_assert(to);
357     pa_assert_se((router = u->router));
358
359     conn = pa_xnew0(mir_connection, 1);
360     MIR_DLIST_INIT(conn->link);
361     conn->amid = amid;
362     conn->from = from->index;
363     conn->to = to->index;
364     
365     MIR_DLIST_APPEND(mir_connection, link, conn, &router->connlist);
366
367     mir_router_make_routing(u);
368
369     return conn;
370 }
371
372 void mir_router_remove_explicit_route(struct userdata *u, mir_connection *conn)
373 {
374     pa_core   *core;
375     pa_router *router;
376     mir_node  *from;
377     mir_node  *to;
378
379     pa_assert(u);
380     pa_assert(conn);
381     pa_assert_se((core = u->core));
382     pa_assert_se((router = u->router));
383
384     MIR_DLIST_UNLINK(mir_connection, link, conn);
385
386     if (!(from = mir_node_find_by_index(u, conn->from)) ||
387         !(to   = mir_node_find_by_index(u, conn->to))     )
388     {
389         pa_log_debug("can't remove explicit route: some node was not found");
390     }
391     else {
392         pa_log_debug("tear down link '%s' => '%s'", from->amname, to->amname);
393
394         if (!mir_switch_teardown_link(u, from, to)) {
395             pa_log_debug("can't remove explicit route: "
396                          "failed to teardown link");
397         }
398         else {
399             if (!conn->blocked)
400                 mir_router_make_routing(u);
401         }
402     }
403
404     pa_xfree(conn);
405 }
406
407
408 int mir_router_print_rtgroups(struct userdata *u, char *buf, int len)
409 {
410     pa_router *router;
411     char *p, *e;
412
413     pa_assert(u);
414     pa_assert(buf);
415     pa_assert(len > 0);
416     pa_assert_se((router = u->router));
417     pa_assert(router->rtgroups.input);
418     pa_assert(router->rtgroups.output);
419
420     e = (p = buf) + len;
421
422     if (p < e)
423         p += print_routing_table(router->rtgroups.input, "input", p, e-p);
424
425     if (p < e)
426         p += print_routing_table(router->rtgroups.output, "output", p, e-p);
427
428     return p - buf;
429 }
430
431
432 mir_node *mir_router_make_prerouting(struct userdata *u, mir_node *data)
433 {
434     pa_router     *router;
435     mir_node      *start;
436     mir_node      *end;
437     int            priority;
438     pa_bool_t      done;
439     mir_node      *target;
440     uint32_t       stamp;
441
442     pa_assert(u);
443     pa_assert_se((router = u->router));
444     pa_assert_se((data->implement == mir_stream));
445
446     priority = node_priority(u, data);
447     done = FALSE;
448     target = NULL;
449     stamp = pa_utils_new_stamp();
450
451     make_explicit_routes(u, stamp);
452
453     MIR_DLIST_FOR_EACH_BACKWARD(mir_node, rtprilist, start, &router->nodlist) {
454         if (start->implement == mir_device) {
455 #if 0
456             if (start->direction == mir_output)
457                 continue;       /* we should never get here */
458             if (!start->mux && !start->loop)
459                 continue;       /* skip not looped back input nodes */
460 #endif
461             if (!start->loop)
462                 continue;       /* only looped back devices routed here */
463         }
464
465         if (priority >= node_priority(u, start)) {
466             if ((target = find_default_route(u, data, stamp)))
467                 implement_preroute(u, data, target, stamp);
468             done = TRUE;
469         }
470
471         if (start->stamp >= stamp)
472             continue;
473
474         if ((end = find_default_route(u, start, stamp)))
475             implement_default_route(u, start, end, stamp);
476     }    
477
478     if (!done && (target = find_default_route(u, data, stamp)))
479         implement_preroute(u, data, target, stamp);
480
481     return target;
482 }
483
484
485 void mir_router_make_routing(struct userdata *u)
486 {
487     static pa_bool_t ongoing_routing;
488
489     pa_router  *router;
490     mir_node   *start;
491     mir_node   *end;
492     uint32_t    stamp;
493
494     pa_assert(u);
495     pa_assert_se((router = u->router));
496
497     if (ongoing_routing)
498         return;
499
500     ongoing_routing = TRUE;
501     stamp = pa_utils_new_stamp();
502
503     make_explicit_routes(u, stamp);
504
505     MIR_DLIST_FOR_EACH_BACKWARD(mir_node,rtprilist, start, &router->nodlist) {
506         if (start->implement == mir_device) {
507 #if 0
508             if (start->direction == mir_output)
509                 continue;       /* we should never get here */
510             if (!start->mux && !start->loop)
511                 continue;       /* skip not looped back input nodes */
512 #endif
513             if (!start->loop)
514                 continue;       /* only looped back devices routed here */
515         }
516
517         if (start->stamp >= stamp)
518             continue;
519
520         if ((end = find_default_route(u, start, stamp)))
521             implement_default_route(u, start, end, stamp);
522     }    
523
524     pa_fader_apply_volume_limits(u, stamp);
525
526     ongoing_routing = FALSE;
527 }
528
529
530
531 pa_bool_t mir_router_default_accept(struct userdata *u, mir_rtgroup *rtg,
532                                     mir_node *node)
533 {
534     pa_core *core;
535     pa_sink *sink;
536     pa_source *source;
537     pa_proplist *pl;
538     mir_node_type class;
539     pa_bool_t accept;
540     const char *role, *excluded_role;
541
542     pa_assert(u);
543     pa_assert(rtg);
544     pa_assert(node);
545
546     class = node->type;
547
548     if (class == mir_bluetooth_carkit)
549         accept = FALSE;
550     else if (class == mir_jack || class == mir_hdmi) {
551         pa_assert_se((core = u->core));
552             
553         if (node->direction == mir_input) {
554             source = pa_idxset_get_by_index(core->sources,node->paidx);
555             pl = source ? source->proplist : NULL;
556             excluded_role = "hfp_uplink";
557         }
558         else {
559             sink = pa_idxset_get_by_index(core->sinks, node->paidx);
560             pl = sink ? sink->proplist : NULL;
561             excluded_role = "hfp_downlink";
562         }
563         role = pl ? pa_proplist_gets(pl, PA_PROP_NODE_ROLE) : NULL;
564         accept = role ? strcmp(role, excluded_role) : TRUE;
565     }
566     else {
567         accept = (class >= mir_device_class_begin &&
568                   class < mir_device_class_end);
569     }
570         
571     return accept;
572 }
573
574
575 pa_bool_t mir_router_phone_accept(struct userdata *u, mir_rtgroup *rtg,
576                                   mir_node *node)
577 {
578     pa_core *core;
579     pa_sink *sink;
580     pa_source *source;
581     pa_proplist *pl;
582     mir_node_type class;
583     const char *role, *expected_role;
584
585     pa_assert(u);
586     pa_assert(rtg);
587     pa_assert(node);
588
589     class = node->type;
590
591     if (class >= mir_device_class_begin &&  class < mir_device_class_end) {
592         if (class != mir_bluetooth_a2dp   &&
593             class != mir_spdif            &&
594             class != mir_bluetooth_source &&
595             class != mir_bluetooth_sink   &&
596             class != mir_bluetooth_carkit   )
597         {
598             if (class == mir_usb_headphone || class == mir_wired_headphone) {
599                 return TRUE;
600             }
601             else if (class == mir_jack || class == mir_hdmi) {
602                 pa_assert_se((core = u->core));
603
604                 if (node->direction == mir_input) {
605                     source = pa_idxset_get_by_index(core->sources,node->paidx);
606                     pl = source ? source->proplist : NULL;
607                     expected_role = "hfp_uplink";
608                 }
609                 else {
610                     sink = pa_idxset_get_by_index(core->sinks, node->paidx);
611                     pl = sink ? sink->proplist : NULL;
612                     expected_role = "hfp_downlink";
613                 }
614                 if (!pl || !(role = pa_proplist_gets(pl, PA_PROP_NODE_ROLE)) ||
615                     strcmp(role, expected_role))
616                 {
617                     return FALSE;
618                 }
619             }
620
621             return TRUE;
622         }
623     }
624
625     return FALSE;
626 }
627
628
629 int mir_router_default_compare(struct userdata *u, mir_rtgroup *rtg,
630                                mir_node *n1, mir_node *n2)
631 {
632     uint32_t p1, p2;
633
634     (void)u;
635     (void)rtg;
636
637     pa_assert(n1);
638     pa_assert(n2);
639
640     if (n1->type == mir_null)
641         return -1;
642     if (n2->type == mir_null)
643         return 1;
644
645     p1 = ((((n1->channels & 31) << 5) + n1->privacy) << 2) + n1->location;
646     p2 = ((((n2->channels & 31) << 5) + n2->privacy) << 2) + n2->location;
647
648     p1 = (p1 << 8) + ((n1->type - mir_device_class_begin) & 0xff);
649     p2 = (p2 << 8) + ((n2->type - mir_device_class_begin) & 0xff);
650
651     return uint32_cmp(p1,p2);
652 }
653
654
655 int mir_router_phone_compare(struct userdata *u, mir_rtgroup *rtg,
656                              mir_node *n1, mir_node *n2)
657 {
658     uint32_t p1, p2;
659
660     (void)u;
661     (void)rtg;
662
663     pa_assert(n1);
664     pa_assert(n2);
665
666     if (n1->type == mir_null)
667         return -1;
668     if (n2->type == mir_null)
669         return 1;
670
671     p1 = (n1->privacy << 8) + ((n1->type - mir_device_class_begin) & 0xff);
672     p2 = (n2->privacy << 8) + ((n2->type - mir_device_class_begin) & 0xff);
673
674     return uint32_cmp(p1,p2);
675 }
676
677
678 static void rtgroup_destroy(struct userdata *u, mir_rtgroup *rtg)
679 {
680     mir_rtentry *rte, *n;
681
682     pa_assert(u);
683     pa_assert(rtg);
684
685     MIR_DLIST_FOR_EACH_SAFE(mir_rtentry, link, rte,n, &rtg->entries) {
686         remove_rtentry(u, rte);
687     }
688
689     pa_xfree(rtg->name);
690     pa_xfree(rtg);
691 }
692
693 static int rtgroup_print(mir_rtgroup *rtg, char *buf, int len)
694 {
695     mir_rtentry *rte;
696     mir_node *node;
697     char *p, *e;
698
699     e = (p = buf) + len;
700
701     *buf = 0;
702
703     MIR_DLIST_FOR_EACH_BACKWARD(mir_rtentry, link, rte, &rtg->entries) {
704         node = rte->node;
705         if (p >= e)
706             break;
707         p += snprintf(p, e-p, " '%s'", node->amname);
708     }
709
710     return p - buf;
711 }
712
713 static void rtgroup_update_module_property(struct userdata *u,
714                                            mir_direction    type,
715                                            mir_rtgroup     *rtg)
716 {
717     pa_module *module;
718     char       key[64];
719     char       value[512];
720     int        ret;
721
722     pa_assert(u);
723     pa_assert(rtg);
724     pa_assert(rtg->name);
725     pa_assert_se((module = u->module));
726
727     snprintf(key, sizeof(key), PA_PROP_ROUTING_TABLE ".%s.%s",
728              mir_direction_str(type), rtg->name);
729     ret = rtgroup_print(rtg, value, sizeof(value));
730
731     if (!ret)
732         value[1] = 0;
733     
734     pa_proplist_sets(module->proplist, key, value+1); /* skip ' '@beginning */
735 }
736
737 static void add_rtentry(struct userdata *u,
738                         mir_direction    type,
739                         mir_rtgroup     *rtg,
740                         mir_node        *node)
741 {
742     pa_router *router;
743     mir_rtentry *rte, *before;
744
745     pa_assert(u);
746     pa_assert(rtg);
747     pa_assert(node);
748     pa_assert_se((router = u->router));
749
750     if (!rtg->accept(u, rtg, node)) {
751         pa_log_debug("refuse node '%s' registration to routing group '%s'",
752                      node->amname, rtg->name);
753         return;
754     }
755
756     rte = pa_xnew0(mir_rtentry, 1);
757
758     MIR_DLIST_APPEND(mir_rtentry, nodchain, rte, &node->rtentries);
759     rte->group = rtg;
760     rte->node  = node;
761
762     MIR_DLIST_FOR_EACH(mir_rtentry, link, before, &rtg->entries) {
763         if (rtg->compare(u, rtg, node, before->node) < 0) {
764             MIR_DLIST_INSERT_BEFORE(mir_rtentry, link, rte, &before->link);
765             goto added;
766         }
767     }
768
769     MIR_DLIST_APPEND(mir_rtentry, link, rte, &rtg->entries);
770
771  added:
772     rtgroup_update_module_property(u, type, rtg);
773     pa_log_debug("node '%s' added to routing group '%s'",
774                  node->amname, rtg->name);
775 }
776
777 static void remove_rtentry(struct userdata *u, mir_rtentry *rte)
778 {
779     mir_rtgroup *rtg;
780     mir_node    *node;
781
782     pa_assert(u);
783     pa_assert(rte);
784     pa_assert_se((rtg = rte->group));
785     pa_assert_se((node = rte->node));
786
787     MIR_DLIST_UNLINK(mir_rtentry, link, rte);
788     MIR_DLIST_UNLINK(mir_rtentry, nodchain, rte);
789
790     pa_xfree(rte);
791
792     rtgroup_update_module_property(u, node->direction, rtg);
793 }
794
795 static void make_explicit_routes(struct userdata *u, uint32_t stamp)
796 {
797     pa_router *router;
798     mir_connection *conn;
799     mir_node *from;
800     mir_node *to;
801
802     pa_assert(u);
803     pa_assert_se((router = u->router));
804
805     MIR_DLIST_FOR_EACH_BACKWARD(mir_connection,link, conn, &router->connlist) {
806         if (conn->blocked)
807             continue;
808         
809         if (!(from = mir_node_find_by_index(u, conn->from)) ||
810             !(to   = mir_node_find_by_index(u, conn->to))     )
811         {
812             pa_log_debug("ignoring explicit route %u: some of the nodes "
813                          "not found", conn->amid);
814             continue;
815         }
816
817         if (!mir_switch_setup_link(u, from, to, TRUE))
818             continue;
819
820         if (from->implement == mir_stream)
821             from->stamp = stamp;
822
823         if (to->implement == mir_device)
824             mir_volume_add_limiting_class(u, to, volume_class(from), stamp);
825     }
826 }
827
828
829 static mir_node *find_default_route(struct userdata *u,
830                                     mir_node        *start,
831                                     uint32_t         stamp)
832 {
833     pa_router     *router = u->router;
834     mir_node_type  class  = pa_classify_guess_application_class(start);
835     mir_rtgroup  **classmap;
836     mir_node      *end;
837     mir_rtgroup   *rtg;
838     mir_rtentry   *rte;
839
840     if (class < 0 || class > router->maplen) {
841         pa_log_debug("can't route '%s': class %d is out of range (0 - %d)",
842                      start->amname, class, router->maplen);
843         return NULL;
844     }
845     
846     switch (start->direction) {
847     case mir_input:     classmap = router->classmap.output;     break;
848     case mir_output:    classmap = router->classmap.input;      break;
849     default:            classmap = NULL;                        break;
850     }
851
852     if (!classmap || !(rtg = classmap[class])) {
853         pa_log_debug("node '%s' won't be routed beacuse its class '%s' "
854                      "is not assigned to any router group",
855                      start->amname, mir_node_type_str(class));
856         return NULL;
857     }
858     
859     pa_log_debug("using '%s' router group when routing '%s'",
860                  rtg->name, start->amname);
861
862         
863     MIR_DLIST_FOR_EACH_BACKWARD(mir_rtentry, link, rte, &rtg->entries) {
864         if (!(end = rte->node)) {
865             pa_log("   node was null in mir_rtentry");
866             continue;
867         }
868         
869         if (end->ignore) {
870             pa_log_debug("   '%s' ignored. Skipping...",end->amname);
871             continue;
872         }
873
874         if (!end->available) {
875             pa_log_debug("   '%s' not available. Skipping...", end->amname);
876             continue;
877         }
878
879         if (end->paidx == PA_IDXSET_INVALID && !end->paport) {
880             /* requires profile change. We do it only for BT headsets */
881             if (end->type != mir_bluetooth_a2dp &&
882                 end->type != mir_bluetooth_sco    )
883             {
884                 pa_log_debug("   '%s' has no sink. Skipping...", end->amname);
885                 continue;
886             }
887         }
888
889         if (rte->stamp < stamp)
890             mir_constrain_apply(u, end, stamp);
891         else {
892             if (rte->blocked) {
893                 pa_log_debug("   '%s' is blocked by constraints. Skipping...",
894                              end->amname);
895                 continue;
896             }
897         }
898         
899         pa_log_debug("routing '%s' => '%s'", start->amname, end->amname);
900
901         return end;
902     }
903     
904     pa_log_debug("could not find route for '%s'", start->amname);
905
906     return NULL;
907 }
908
909 static void implement_preroute(struct userdata *u,
910                                mir_node        *data,
911                                mir_node        *target,
912                                uint32_t         stamp)
913 {
914     if (data->direction == mir_output)
915         mir_switch_setup_link(u, target, NULL, FALSE);
916     else {
917         mir_switch_setup_link(u, NULL, target, FALSE);
918         mir_volume_add_limiting_class(u, target, data->type, stamp);
919     }
920 }
921
922 static void implement_default_route(struct userdata *u,
923                                     mir_node        *start,
924                                     mir_node        *end,
925                                     uint32_t         stamp)
926 {
927     if (start->direction == mir_output)
928         mir_switch_setup_link(u, end, start, FALSE);
929     else {
930         mir_switch_setup_link(u, start, end, FALSE);
931         mir_volume_add_limiting_class(u, end, volume_class(start), stamp);
932     }
933 }
934
935
936 static int uint32_cmp(uint32_t v1, uint32_t v2)
937 {
938     if (v1 > v2)
939         return 1;
940     if (v1 < v2)
941         return -1;
942     return 0;
943 }
944
945 static int node_priority(struct userdata *u, mir_node *node)
946 {
947     pa_router *router;
948     int class;
949
950     pa_assert(u);
951     pa_assert(node);
952     pa_assert_se((router = u->router));
953     pa_assert(router->priormap);
954
955     class = pa_classify_guess_application_class(node);
956
957     if (class < 0 || class >= router->maplen)
958         return 0;
959
960     return router->priormap[class];
961 }
962
963 static int volume_class(mir_node *node)
964 {
965     int device_class[mir_device_class_end - mir_device_class_begin] = {
966         [ mir_bluetooth_carkit - mir_device_class_begin ] = mir_phone,
967         [ mir_bluetooth_source - mir_device_class_begin ] = mir_player,
968     };
969
970     int t;
971
972     pa_assert(node);
973
974     t = node->type;
975
976     if (t >= mir_application_class_begin && t < mir_application_class_end)
977         return t;
978
979     if (t >= mir_device_class_begin && t < mir_device_class_end)
980         return device_class[t - mir_device_class_begin];
981
982     return mir_node_type_unknown;
983 }
984
985
986 static int print_routing_table(pa_hashmap  *table,
987                                const char  *type,
988                                char        *buf,
989                                int          len)
990 {
991     mir_rtgroup *rtg;
992     void *state;
993     char *p, *e;
994     int n;
995
996     pa_assert(table);
997     pa_assert(type);
998     pa_assert(buf);
999
1000     e = (p = buf) + len;
1001     n = 0;
1002
1003     if (len > 0) {
1004         p += snprintf(p, e-p, "%s routing table:\n", type);
1005
1006         state = NULL;
1007
1008         if (p < e) {
1009             PA_HASHMAP_FOREACH(rtg, table, state) {
1010                 n++;
1011
1012                 if (p >= e) break;
1013                 p += snprintf(p, e-p, "   %s:", rtg->name);
1014                 
1015                 if (p >= e) break;
1016                 p += rtgroup_print(rtg, p, e-p);
1017                 
1018                 if (p >= e) break;
1019                 p += snprintf(p, e-p, "\n");
1020             }
1021
1022             if (!n && p < e)
1023                 p += snprintf(p, e-p, "   <empty>\n");
1024         }
1025     }
1026
1027     return p - buf;
1028 }
1029
1030
1031 /*
1032  * Local Variables:
1033  * c-basic-offset: 4
1034  * indent-tabs-mode: nil
1035  * End:
1036  *
1037  */