router: fix for bluetooth disappearing causing assert in combine
[profile/ivi/pulseaudio-module-murphy-ivi.git] / murphy / router.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5
6 #include <pulsecore/pulsecore-config.h>
7
8 #include <pulse/proplist.h>
9 #include <pulsecore/module.h>
10
11 #include "router.h"
12 #include "node.h"
13 #include "switch.h"
14
15
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 *);
19
20 static void add_rtentry(struct userdata *, mir_rtgroup *, mir_node *);
21 static void remove_rtentry(struct userdata *, mir_rtentry *);
22
23 static void make_explicit_routes(struct userdata *, uint32_t);
24 static mir_node *find_default_route(struct userdata *, mir_node *);
25
26
27 static int uint32_cmp(uint32_t, uint32_t);
28
29 static int node_priority(struct userdata *, mir_node *);
30
31
32 static void pa_hashmap_rtgroup_free(void *rtg, void *u)
33 {
34     rtgroup_destroy(u, rtg);
35 }
36
37
38 pa_router *pa_router_init(struct userdata *u)
39 {
40     size_t     num_classes = mir_application_class_end;
41     pa_router *router = pa_xnew0(pa_router, 1);
42     
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);
49     MIR_DLIST_INIT(router->connlist);
50     
51     return router;
52 }
53
54 void pa_router_done(struct userdata *u)
55 {
56     pa_router      *router;
57     mir_connection *conn, *c;
58     mir_node       *e,*n;
59
60     if (u && (router = u->router)) {
61         MIR_DLIST_FOR_EACH_SAFE(mir_node, rtentries, e,n, &router->nodlist) {
62             MIR_DLIST_UNLINK(mir_node, rtentries, e);
63         }
64
65         MIR_DLIST_FOR_EACH_SAFE(mir_connection,link, conn,c,&router->connlist){
66             MIR_DLIST_UNLINK(mir_connection, link, conn);
67             pa_xfree(conn);
68         }
69
70         pa_hashmap_free(router->rtgroups, pa_hashmap_rtgroup_free,u);
71
72         pa_xfree(router->classmap);
73         pa_xfree(router->priormap);
74         pa_xfree(router);
75
76         u->router = NULL;
77     }
78 }
79
80
81 pa_bool_t mir_router_create_rtgroup(struct userdata      *u,
82                                     const char           *name,
83                                     mir_rtgroup_accept_t  accept,
84                                     mir_rtgroup_compare_t compare)
85 {
86     pa_router   *router;
87     mir_rtgroup *rtg;
88
89     pa_assert(u);
90     pa_assert(name);
91     pa_assert(accept);
92     pa_assert(compare);
93     pa_assert_se((router = u->router));
94
95     rtg = pa_xnew0(mir_rtgroup, 1);
96     rtg->name    = pa_xstrdup(name);
97     rtg->accept  = accept;
98     rtg->compare = compare;
99     MIR_DLIST_INIT(rtg->entries);
100
101     if (pa_hashmap_put(router->rtgroups, rtg->name, rtg) < 0) {
102         pa_xfree(rtg->name);
103         pa_xfree(rtg);
104         return FALSE;
105     }
106
107     pa_log_debug("routing group '%s' created", name);
108
109     return TRUE;
110 }
111
112 void mir_router_destroy_rtgroup(struct userdata *u, const char *name)
113 {
114     pa_router *router;
115     mir_rtgroup *rtg;
116
117     pa_assert(u);
118     pa_assert(name);
119     pa_assert_se((router = u->router));
120
121     if (!(rtg = pa_hashmap_remove(router->rtgroups, name)))
122         pa_log_debug("can't destroy routing group '%s': group not found",name);
123     else {
124         rtgroup_destroy(u, rtg);
125         pa_log_debug("routing group '%s' destroyed", name);
126     }
127 }
128
129
130 pa_bool_t mir_router_assign_class_to_rtgroup(struct userdata *u,
131                                              mir_node_type    class,
132                                              const char      *rtgrpnam)
133 {
134     pa_router *router;
135     mir_rtgroup *rtg;
136     const char * clnam;
137
138     pa_assert(u);
139     pa_assert(rtgrpnam);
140     pa_assert_se((router = u->router));
141
142     if (class < 0 || class >= router->maplen) {
143         pa_log_debug("can't assign class (%d) to  routing group '%s': "
144                      "class id is out of range (0 - %d)",
145                      class, rtgrpnam, router->maplen);
146         return FALSE;
147     }
148
149     clnam = mir_node_type_str(class);
150
151     if (!(rtg = pa_hashmap_get(router->rtgroups, rtgrpnam))) {
152         pa_log_debug("can't assign class '%s' to routing group '%s': "
153                      "router group not found", clnam, rtgrpnam);
154     }
155
156     router->classmap[class] = rtg;
157
158     pa_log_debug("class '%s' assigned to routing group '%s'", clnam, rtgrpnam);
159
160     return TRUE;
161 }
162
163
164 void mir_router_register_node(struct userdata *u, mir_node *node)
165 {
166     pa_router   *router;
167     mir_rtgroup *rtg;
168     void        *state;
169     int          priority;
170     mir_node    *before;
171
172     pa_assert(u);
173     pa_assert(node);
174     pa_assert_se((router = u->router));
175     
176     if (node->implement == mir_device) {
177         if (node->direction == mir_output) {
178             PA_HASHMAP_FOREACH(rtg, router->rtgroups, state) {
179                 add_rtentry(u, rtg, node);
180             }
181         }
182         return;
183     }
184     
185     if (node->implement == mir_stream) {
186         if (node->direction == mir_input) {
187             priority = node_priority(u, node);
188             
189             MIR_DLIST_FOR_EACH(mir_node, rtentries, before, &router->nodlist) {
190                 if (priority < node_priority(u, before)) {
191                     MIR_DLIST_INSERT_BEFORE(mir_node, rtentries, node,
192                                             &before->rtentries);
193                     return;
194                 }
195             }
196             
197             MIR_DLIST_APPEND(mir_node, rtentries, node, &router->nodlist);
198         }
199         return;
200     }
201 }
202
203 void mir_router_unregister_node(struct userdata *u, mir_node *node)
204 {
205     pa_router *router;
206     mir_rtentry *rte, *n;
207     
208     pa_assert(u);
209     pa_assert(node);
210     pa_assert_se((router = u->router));
211
212     if (node->implement == mir_device && node->direction == mir_output) {
213         MIR_DLIST_FOR_EACH_SAFE(mir_rtentry,nodchain, rte,n, &node->rtentries){
214             remove_rtentry(u, rte);
215         }
216         return;
217     }
218
219     if (node->implement == mir_stream && node->direction == mir_input) {
220         MIR_DLIST_UNLINK(mir_node, rtentries, node);
221         return;
222     }
223 }
224
225 mir_connection *mir_router_add_explicit_route(struct userdata *u,
226                                               uint16_t   amid,
227                                               mir_node  *from,
228                                               mir_node  *to)
229 {
230     pa_router *router;
231     mir_connection *conn;
232
233     pa_assert(u);
234     pa_assert(from);
235     pa_assert(to);
236     pa_assert_se((router = u->router));
237
238     conn = pa_xnew0(mir_connection, 1);
239     MIR_DLIST_INIT(conn->link);
240     conn->amid = amid;
241     conn->from = from->index;
242     conn->to = to->index;
243     
244     MIR_DLIST_APPEND(mir_connection, link, conn, &router->connlist);
245
246     mir_router_make_routing(u);
247
248     return conn;
249 }
250
251 void mir_router_remove_explicit_route(struct userdata *u, mir_connection *conn)
252 {
253     pa_core   *core;
254     pa_router *router;
255     mir_node  *from;
256     mir_node  *to;
257
258     pa_assert(u);
259     pa_assert(conn);
260     pa_assert_se((core = u->core));
261     pa_assert_se((router = u->router));
262
263     MIR_DLIST_UNLINK(mir_connection, link, conn);
264
265     if (!(from = mir_node_find_by_index(u, conn->from)) ||
266         !(to   = mir_node_find_by_index(u, conn->to))     )
267     {
268         pa_log_debug("can't remove explicit route: some node was not found");
269     }
270     else {
271         pa_log_debug("tear down link '%s' => '%s'", from->amname, to->amname);
272
273         if (!mir_switch_teardown_link(u, from, to)) {
274             pa_log_debug("can't remove explicit route: "
275                          "failed to teardown link");
276         }
277         else {
278             if (!conn->blocked)
279                 mir_router_make_routing(u);
280         }
281     }
282
283     pa_xfree(conn);
284 }
285
286 int mir_router_print_rtgroups(struct userdata *u, char *buf, int len)
287 {
288     pa_router *router;
289     mir_rtgroup *rtg;
290     void *state;
291     char *p, *e;
292
293     pa_assert(u);
294     pa_assert(buf);
295     pa_assert(len > 0);
296     pa_assert_se((router = u->router));
297     pa_assert(router->rtgroups);
298
299     e = (p = buf) + len;
300
301     if (len > 0) {
302         p += snprintf(p, e-p, "routing table:\n");
303
304         if (p < e) {
305             PA_HASHMAP_FOREACH(rtg, router->rtgroups, state) {
306                 if (p >= e) break;
307                 p += snprintf(p, e-p, "   %s:", rtg->name);
308                 
309                 if (p >= e) break;
310                 p += rtgroup_print(rtg, p, e-p);
311                 
312                 if (p >= e) break;
313                 p += snprintf(p, e-p, "\n");
314             }
315         }
316     }
317
318     return p - buf;
319 }
320
321
322 mir_node *mir_router_make_prerouting(struct userdata *u, mir_node *data)
323 {
324     pa_router     *router;
325     mir_node      *from;
326     mir_node      *to;
327     int            priority;
328     pa_bool_t      done;
329     mir_node      *target;
330     uint32_t       stamp;
331
332     pa_assert(u);
333     pa_assert_se((router = u->router));
334     pa_assert_se((data->implement == mir_stream));
335     pa_assert_se((data->direction == mir_input));
336
337     priority = node_priority(u, data);
338     done = FALSE;
339     target = NULL;
340     stamp = pa_utils_new_stamp();
341
342     make_explicit_routes(u, stamp);
343
344     MIR_DLIST_FOR_EACH_BACKWARD(mir_node,rtentries, from, &router->nodlist) {
345         if (priority >= node_priority(u, from)) {
346             if ((target = find_default_route(u, data)))
347                 mir_switch_setup_link(u, NULL, target, FALSE);
348             done = TRUE;
349         }
350
351         if (from->stamp >= stamp)
352             continue;
353
354         if ((to = find_default_route(u, from)))
355             mir_switch_setup_link(u, from, to, FALSE);
356     }    
357
358     if (!done && (target = find_default_route(u, data)))
359         mir_switch_setup_link(u, NULL, target, FALSE);
360
361     return target;
362 }
363
364
365 void mir_router_make_routing(struct userdata *u)
366 {
367     static pa_bool_t ongoing_routing;
368
369     pa_router  *router;
370     mir_node   *from;
371     mir_node   *to;
372     uint32_t    stamp;
373
374     pa_assert(u);
375     pa_assert_se((router = u->router));
376
377     if (ongoing_routing)
378         return;
379
380     ongoing_routing = TRUE;
381     stamp = pa_utils_new_stamp();
382
383     make_explicit_routes(u, stamp);
384
385     MIR_DLIST_FOR_EACH_BACKWARD(mir_node,rtentries, from, &router->nodlist) {
386         if (from->stamp >= stamp)
387             continue;
388
389         if ((to = find_default_route(u, from)))
390             mir_switch_setup_link(u, from, to, FALSE);
391     }    
392
393     ongoing_routing = FALSE;
394 }
395
396
397
398 pa_bool_t mir_router_default_accept(struct userdata *u, mir_rtgroup *rtg,
399                                     mir_node *node)
400 {
401     pa_bool_t accept;
402
403     pa_assert(u);
404     pa_assert(rtg);
405     pa_assert(node);
406
407     accept = (node->type >= mir_device_class_begin &&
408               node->type < mir_device_class_end);
409         
410     return accept;
411 }
412
413
414 pa_bool_t mir_router_phone_accept(struct userdata *u, mir_rtgroup *rtg,
415                                   mir_node *node)
416 {
417     mir_node_type class;
418
419     pa_assert(u);
420     pa_assert(rtg);
421     pa_assert(node);
422
423     class = node->type;
424
425     if (class >= mir_device_class_begin &&  class < mir_device_class_end) {
426         if (class != mir_bluetooth_a2dp  &&
427             class != mir_usb_headphone   &&
428             class != mir_wired_headphone &&
429             class != mir_hdmi            &&
430             class != mir_spdif             )
431         {
432             return TRUE;
433         }
434     }
435
436     return FALSE;
437 }
438
439
440 int mir_router_default_compare(struct userdata *u, mir_node *n1, mir_node *n2)
441 {
442     uint32_t p1, p2;
443
444     (void)u;
445
446     pa_assert(n1);
447     pa_assert(n2);
448
449     if (n1->type == mir_null)
450         return -1;
451     if (n2->type == mir_null)
452         return 1;
453
454     p1 = ((((n1->channels & 31) << 5) + n1->privacy) << 2) + n1->location;
455     p2 = ((((n2->channels & 31) << 5) + n2->privacy) << 2) + n2->location;
456
457     p1 = (p1 << 8) + ((n1->type - mir_device_class_begin) & 0xff);
458     p2 = (p2 << 8) + ((n2->type - mir_device_class_begin) & 0xff);
459
460     return uint32_cmp(p1,p2);
461 }
462
463
464 int mir_router_phone_compare(struct userdata *u, mir_node *n1, mir_node *n2)
465 {
466     uint32_t p1, p2;
467
468     (void)u;
469
470     pa_assert(n1);
471     pa_assert(n2);
472
473     if (n1->type == mir_null)
474         return -1;
475     if (n2->type == mir_null)
476         return 1;
477
478     p1 = (n1->privacy << 8) + ((n1->type - mir_device_class_begin) & 0xff);
479     p2 = (n2->privacy << 8) + ((n2->type - mir_device_class_begin) & 0xff);
480
481     return uint32_cmp(p1,p2);
482 }
483
484
485 static void rtgroup_destroy(struct userdata *u, mir_rtgroup *rtg)
486 {
487     mir_rtentry *rte, *n;
488
489     pa_assert(u);
490     pa_assert(rtg);
491
492     MIR_DLIST_FOR_EACH_SAFE(mir_rtentry, link, rte,n, &rtg->entries) {
493         remove_rtentry(u, rte);
494     }
495
496     pa_xfree(rtg->name);
497     pa_xfree(rtg);
498 }
499
500 static int rtgroup_print(mir_rtgroup *rtg, char *buf, int len)
501 {
502     mir_rtentry *rte;
503     mir_node *node;
504     char *p, *e;
505
506     e = (p = buf) + len;
507
508     MIR_DLIST_FOR_EACH_BACKWARD(mir_rtentry, link, rte, &rtg->entries) {
509         node = rte->node;
510         if (p >= e)
511             break;
512         p += snprintf(p, e-p, " '%s'", node->amname);
513     }
514
515     return p - buf;
516 }
517
518 static void rtgroup_update_module_property(struct userdata *u,mir_rtgroup *rtg)
519 {
520     pa_module *module;
521     char       key[64];
522     char       value[512];
523
524     pa_assert(u);
525     pa_assert(rtg);
526     pa_assert_se((module = u->module));
527
528     snprintf(key, sizeof(key), PA_PROP_ROUTING_TABLE ".%s", rtg->name);
529     rtgroup_print(rtg, value, sizeof(value));
530
531     pa_proplist_sets(module->proplist, key, value+1); /* skip ' '@beginning */
532 }
533
534 static void add_rtentry(struct userdata *u, mir_rtgroup *rtg, mir_node *node)
535 {
536     pa_router *router;
537     mir_rtentry *rte, *before;
538
539     pa_assert(u);
540     pa_assert(rtg);
541     pa_assert(node);
542     pa_assert_se((router = u->router));
543
544     if (!rtg->accept(u, rtg, node)) {
545         pa_log_debug("refuse node '%s' registration to routing group '%s'",
546                      node->amname, rtg->name);
547         return;
548     }
549
550     rte = pa_xnew0(mir_rtentry, 1);
551
552     MIR_DLIST_APPEND(mir_rtentry, nodchain, rte, &node->rtentries);
553     rte->node = node;
554
555     MIR_DLIST_FOR_EACH(mir_rtentry, link, before, &rtg->entries) {
556         if (rtg->compare(u, node, before->node) < 0) {
557             MIR_DLIST_INSERT_BEFORE(mir_rtentry, link, rte, &before->link);
558             goto added;
559         }
560     }
561
562     MIR_DLIST_APPEND(mir_rtentry, link, rte, &rtg->entries);
563
564  added:
565     rtgroup_update_module_property(u, rtg);
566     pa_log_debug("node '%s' added to routing group '%s'",
567                  node->amname, rtg->name);
568 }
569
570 static void remove_rtentry(struct userdata *u, mir_rtentry *rte)
571 {
572     pa_assert(u);
573     pa_assert(rte);
574
575     MIR_DLIST_UNLINK(mir_rtentry, link, rte);
576     MIR_DLIST_UNLINK(mir_rtentry, nodchain, rte);
577
578     pa_xfree(rte);
579 }
580
581 static void make_explicit_routes(struct userdata *u, uint32_t stamp)
582 {
583     pa_router *router;
584     mir_connection *conn;
585     mir_node *from;
586     mir_node *to;
587
588     pa_assert(u);
589     pa_assert_se((router = u->router));
590
591     MIR_DLIST_FOR_EACH_BACKWARD(mir_connection,link, conn, &router->connlist) {
592         if (conn->blocked)
593             continue;
594         
595         if (!(from = mir_node_find_by_index(u, conn->from)) ||
596             !(to   = mir_node_find_by_index(u, conn->to))     )
597         {
598             pa_log_debug("ignoring explicit route %u: some of the nodes "
599                          "not found", conn->amid);
600             continue;
601         }
602
603         if (!mir_switch_setup_link(u, from, to, TRUE))
604             continue;
605
606         if (from->implement == mir_stream)
607             from->stamp = stamp;
608     }
609 }
610
611
612 static mir_node *find_default_route(struct userdata *u, mir_node *from)
613 {
614     pa_router     *router = u->router;
615     mir_node_type  class  = from->type;
616     mir_node      *to;
617     mir_rtgroup   *rtg;
618     mir_rtentry   *rte;
619
620
621     if (class < 0 || class > router->maplen) {
622         pa_log_debug("can't route '%s': class %d is out of range (0 - %d)",
623                      from->amname, class, router->maplen);
624         return NULL;
625     }
626     
627     if (!(rtg = router->classmap[class])) {
628         pa_log_debug("node '%s' won't be routed beacuse its class '%s' "
629                      "is not assigned to any router group",
630                      from->amname, mir_node_type_str(class));
631         return NULL;
632     }
633     
634     pa_log_debug("using '%s' router group when routing '%s'",
635                  rtg->name, from->amname);
636
637         
638     MIR_DLIST_FOR_EACH_BACKWARD(mir_rtentry, link, rte, &rtg->entries) {
639         if (!(to = rte->node)) {
640             pa_log("   node was null in mir_rtentry");
641             continue;
642         }
643         
644         if (to->ignore) {
645             pa_log_debug("   '%s' ignored. Skipping...",to->amname);
646             continue;
647         }
648
649         if (!to->available) {
650             pa_log_debug("   '%s' not available. Skipping...", to->amname);
651             continue;
652         }
653
654         if (to->paidx == PA_IDXSET_INVALID) {
655             if (to->type != mir_bluetooth_a2dp &&
656                 to->type != mir_bluetooth_sco)
657             {
658                 pa_log_debug("   '%s' has no to. Skipping...", to->amname);
659                 continue;
660             }
661         }
662         
663         pa_log_debug("routing '%s' => '%s'", from->amname, to->amname);
664
665         return to;
666     }
667     
668     pa_log_debug("could not find route for '%s'", from->amname);
669
670     return NULL;
671 }
672
673
674
675 static int uint32_cmp(uint32_t v1, uint32_t v2)
676 {
677     if (v1 > v2)
678         return 1;
679     if (v1 < v2)
680         return -1;
681     return 0;
682 }
683
684
685 static int node_priority(struct userdata *u, mir_node *node)
686 {
687     pa_router *router;
688     mir_node_type type;
689
690     pa_assert(u);
691     pa_assert(node);
692     pa_assert_se((router = u->router));
693     pa_assert(router->priormap);
694
695     type = node->type;
696
697     if (type < 0 || type >= router->maplen)
698         return 0;
699
700     return router->priormap[type];
701 }
702
703
704 /*
705  * Local Variables:
706  * c-basic-offset: 4
707  * indent-tabs-mode: nil
708  * End:
709  *
710  */