routing: null sink support added to make sure there is always a routing target
[profile/ivi/pulseaudio-module-murphy-ivi.git] / src / 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 "router.h"
9 #include "node.h"
10 #include "switch.h"
11
12
13 static void rtgroup_destroy(struct userdata *, mir_rtgroup *);
14 static int rtgroup_print(mir_rtgroup *, char *, int);
15
16 static void add_rtentry(struct userdata *, mir_rtgroup *, mir_node *);
17 static void remove_rtentry(struct userdata *, mir_rtentry *);
18
19 static mir_node *route_stream(struct userdata *, mir_node *);
20
21
22 static int uint32_cmp(uint32_t, uint32_t);
23
24 static int node_priority(struct userdata *, mir_node *);
25
26
27 static void pa_hashmap_rtgroup_free(void *rtg, void *u)
28 {
29     rtgroup_destroy(u, rtg);
30 }
31
32
33 pa_router *pa_router_init(struct userdata *u)
34 {
35     size_t     num_classes = mir_application_class_end;
36     pa_router *router = pa_xnew0(pa_router, 1);
37     
38     router->rtgroups = pa_hashmap_new(pa_idxset_string_hash_func,
39                                       pa_idxset_string_compare_func);
40     router->maplen = num_classes;
41     router->classmap = pa_xnew0(mir_rtgroup *, num_classes);
42     router->priormap = pa_xnew0(int, num_classes);
43     MIR_DLIST_INIT(router->nodlist);
44     
45     return router;
46 }
47
48 void pa_router_done(struct userdata *u)
49 {
50     pa_router  *router;
51     mir_node   *e,*n;
52
53     if (u && (router = u->router)) {
54         MIR_DLIST_FOR_EACH_SAFE(mir_node, rtentries, e,n, &router->nodlist) {
55             MIR_DLIST_UNLINK(mir_node, rtentries, e);
56         }
57
58         pa_hashmap_free(router->rtgroups, pa_hashmap_rtgroup_free,u);
59
60         pa_xfree(router->classmap);
61         pa_xfree(router->priormap);
62         pa_xfree(router);
63
64         u->router = NULL;
65     }
66 }
67
68
69 pa_bool_t mir_router_create_rtgroup(struct userdata      *u,
70                                     const char           *name,
71                                     mir_rtgroup_accept_t  accept,
72                                     mir_rtgroup_compare_t compare)
73 {
74     pa_router   *router;
75     mir_rtgroup *rtg;
76
77     pa_assert(u);
78     pa_assert(name);
79     pa_assert(accept);
80     pa_assert(compare);
81     pa_assert_se((router = u->router));
82
83     rtg = pa_xnew0(mir_rtgroup, 1);
84     rtg->name    = pa_xstrdup(name);
85     rtg->accept  = accept;
86     rtg->compare = compare;
87     MIR_DLIST_INIT(rtg->entries);
88
89     if (pa_hashmap_put(router->rtgroups, rtg->name, rtg) < 0) {
90         pa_xfree(rtg->name);
91         pa_xfree(rtg);
92         return FALSE;
93     }
94
95     pa_log_debug("routing group '%s' created", name);
96
97     return TRUE;
98 }
99
100 void mir_router_destroy_rtgroup(struct userdata *u, const char *name)
101 {
102     pa_router *router;
103     mir_rtgroup *rtg;
104
105     pa_assert(u);
106     pa_assert(name);
107     pa_assert_se((router = u->router));
108
109     if (!(rtg = pa_hashmap_remove(router->rtgroups, name)))
110         pa_log_debug("can't destroy routing group '%s': group not found",name);
111     else {
112         rtgroup_destroy(u, rtg);
113         pa_log_debug("routing group '%s' destroyed", name);
114     }
115 }
116
117
118 pa_bool_t mir_router_assign_class_to_rtgroup(struct userdata *u,
119                                              mir_node_type    class,
120                                              const char      *rtgrpnam)
121 {
122     pa_router *router;
123     mir_rtgroup *rtg;
124     const char * clnam;
125
126     pa_assert(u);
127     pa_assert(rtgrpnam);
128     pa_assert_se((router = u->router));
129
130     if (class < 0 || class >= router->maplen) {
131         pa_log_debug("can't assign class (%d) to  routing group '%s': "
132                      "class id is out of range (0 - %d)",
133                      class, rtgrpnam, router->maplen);
134         return FALSE;
135     }
136
137     clnam = mir_node_type_str(class);
138
139     if (!(rtg = pa_hashmap_get(router->rtgroups, rtgrpnam))) {
140         pa_log_debug("can't assign class '%s' to routing group '%s': "
141                      "router group not found", clnam, rtgrpnam);
142     }
143
144     router->classmap[class] = rtg;
145
146     pa_log_debug("class '%s' assigned to routing group '%s'", clnam, rtgrpnam);
147
148     return TRUE;
149 }
150
151
152 void mir_router_register_node(struct userdata *u, mir_node *node)
153 {
154     pa_router   *router;
155     mir_rtgroup *rtg;
156     void        *state;
157     int          priority;
158     mir_node    *before;
159
160     pa_assert(u);
161     pa_assert(node);
162     pa_assert_se((router = u->router));
163     
164     if (node->implement == mir_device) {
165         if (node->direction == mir_output) {
166             PA_HASHMAP_FOREACH(rtg, router->rtgroups, state) {
167                 add_rtentry(u, rtg, node);
168             }
169         }
170         return;
171     }
172     
173     if (node->implement == mir_stream) {
174         if (node->direction == mir_input) {
175             priority = node_priority(u, node);
176             
177             MIR_DLIST_FOR_EACH(mir_node, rtentries, before, &router->nodlist) {
178                 if (priority < node_priority(u, before)) {
179                     MIR_DLIST_INSERT_BEFORE(mir_node, rtentries, node,
180                                             &before->rtentries);
181                     return;
182                 }
183             }
184             
185             MIR_DLIST_APPEND(mir_node, rtentries, node, &router->nodlist);
186         }
187         return;
188     }
189 }
190
191 void mir_router_unregister_node(struct userdata *u, mir_node *node)
192 {
193     pa_router *router;
194     mir_rtentry *rte, *n;
195     
196     pa_assert(u);
197     pa_assert(node);
198     pa_assert_se((router = u->router));
199
200     if (node->implement == mir_device && node->direction == mir_output) {
201         MIR_DLIST_FOR_EACH_SAFE(mir_rtentry,nodchain, rte,n, &node->rtentries){
202             remove_rtentry(u, rte);
203         }
204         return;
205     }
206
207     if (node->implement == mir_stream && node->direction == mir_input) {
208         MIR_DLIST_UNLINK(mir_node, rtentries, node);
209         return;
210     }
211 }
212
213 int mir_router_print_rtgroups(struct userdata *u, char *buf, int len)
214 {
215     pa_router *router;
216     mir_rtgroup *rtg;
217     void *state;
218     char *p, *e;
219
220     pa_assert(u);
221     pa_assert(buf);
222     pa_assert(len > 0);
223     pa_assert_se((router = u->router));
224     pa_assert(router->rtgroups);
225
226     e = (p = buf) + len;
227
228     if (len > 0) {
229         p += snprintf(p, e-p, "routing table:\n");
230
231         if (p < e) {
232             PA_HASHMAP_FOREACH(rtg, router->rtgroups, state) {
233                 if (p >= e) break;
234                 p += snprintf(p, e-p, "   %s:", rtg->name);
235                 
236                 if (p >= e) break;
237                 p += rtgroup_print(rtg, p, e-p);
238                 
239                 if (p >= e) break;
240                 p += snprintf(p, e-p, "\n");
241             }
242         }
243     }
244
245     return p - buf;
246 }
247
248
249 mir_node *mir_router_make_prerouting(struct userdata *u, mir_node *data)
250 {
251     pa_router     *router;
252     mir_node      *source;
253     mir_node      *sink;
254     int            priority;
255     pa_bool_t      done;
256     mir_node      *target;
257
258     pa_assert(u);
259     pa_assert_se((router = u->router));
260
261     priority = node_priority(u, data);
262     done = FALSE;
263     target = NULL;
264
265     MIR_DLIST_FOR_EACH_BACKWARD(mir_node,rtentries, source, &router->nodlist) {
266         if (priority >= node_priority(u, source)) {
267             if ((target = route_stream(u, data)))
268                 mir_switch_setup_link(u, NULL, target, TRUE);
269             done = TRUE;
270         }
271
272         if ((sink = route_stream(u, source)))
273             mir_switch_setup_link(u, source, sink, FALSE);
274     }    
275
276     if (!done && (target = route_stream(u, data)))
277         mir_switch_setup_link(u, NULL, target, TRUE);
278
279     return target;
280 }
281
282
283 void mir_router_make_routing(struct userdata *u)
284 {
285     static pa_bool_t ongoing_routing;
286
287     pa_router     *router;
288     mir_node      *source;
289     mir_node      *sink;
290
291     pa_assert(u);
292     pa_assert_se((router = u->router));
293
294     if (ongoing_routing)
295         return;
296
297     ongoing_routing = TRUE;
298
299     MIR_DLIST_FOR_EACH_BACKWARD(mir_node,rtentries, source, &router->nodlist) {
300         if ((sink = route_stream(u, source)))
301             mir_switch_setup_link(u, source, sink, FALSE);
302     }    
303
304     ongoing_routing = FALSE;
305 }
306
307
308
309 pa_bool_t mir_router_default_accept(struct userdata *u, mir_rtgroup *rtg,
310                                     mir_node *node)
311 {
312     pa_bool_t accept;
313
314     pa_assert(u);
315     pa_assert(rtg);
316     pa_assert(node);
317
318     accept = (node->type >= mir_device_class_begin &&
319               node->type < mir_device_class_end);
320         
321     return accept;
322 }
323
324 int mir_router_default_compare(struct userdata *u, mir_node *n1, mir_node *n2)
325 {
326     uint32_t p1, p2;
327
328     (void)u;
329
330     pa_assert(n1);
331     pa_assert(n2);
332
333     if (n1->type == mir_null)
334         return -1;
335     if (n2->type == mir_null)
336         return 1;
337
338     p1 = ((((n1->channels & 31) << 5) + n1->privacy) << 2) + n1->location;
339     p2 = ((((n2->channels & 31) << 5) + n2->privacy) << 2) + n2->location;
340
341     p1 = (p1 << 8) + ((n1->type - mir_device_class_begin) & 0xff);
342     p2 = (p2 << 8) + ((n2->type - mir_device_class_begin) & 0xff);
343
344     return uint32_cmp(p1,p2);
345 }
346
347
348 static void rtgroup_destroy(struct userdata *u, mir_rtgroup *rtg)
349 {
350     mir_rtentry *rte, *n;
351
352     pa_assert(u);
353     pa_assert(rtg);
354
355     MIR_DLIST_FOR_EACH_SAFE(mir_rtentry, link, rte,n, &rtg->entries) {
356         remove_rtentry(u, rte);
357     }
358
359     pa_xfree(rtg->name);
360     pa_xfree(rtg);
361 }
362
363 static int rtgroup_print(mir_rtgroup *rtg, char *buf, int len)
364 {
365     mir_rtentry *rte;
366     mir_node *node;
367     char *p, *e;
368
369     e = (p = buf) + len;
370
371     MIR_DLIST_FOR_EACH_BACKWARD(mir_rtentry, link, rte, &rtg->entries) {
372         node = rte->node;
373         if (p >= e)
374             break;
375         p += snprintf(p, e-p, " '%s'", node->amname);
376     }
377
378     return p - buf;
379 }
380
381 static void add_rtentry(struct userdata *u, mir_rtgroup *rtg, mir_node *node)
382 {
383     pa_router *router;
384     mir_rtentry *rte, *before;
385
386     pa_assert(u);
387     pa_assert(rtg);
388     pa_assert(node);
389     pa_assert_se((router = u->router));
390
391     if (!rtg->accept(u, rtg, node)) {
392         pa_log_debug("refuse node '%s' registration to routing group '%s'",
393                      node->amname, rtg->name);
394         return;
395     }
396
397     rte = pa_xnew0(mir_rtentry, 1);
398
399     MIR_DLIST_APPEND(mir_rtentry, nodchain, rte, &node->rtentries);
400     rte->node = node;
401
402     MIR_DLIST_FOR_EACH(mir_rtentry, link, before, &rtg->entries) {
403         if (rtg->compare(u, node, before->node) < 0) {
404             MIR_DLIST_INSERT_BEFORE(mir_rtentry, link, rte, &before->link);
405             goto added;
406         }
407     }
408
409     MIR_DLIST_APPEND(mir_rtentry, link, rte, &rtg->entries);
410
411  added:
412     pa_log_debug("node '%s' added to routing group '%s'",
413                  node->amname, rtg->name);
414 }
415
416 static void remove_rtentry(struct userdata *u, mir_rtentry *rte)
417 {
418     pa_assert(u);
419     pa_assert(rte);
420
421     MIR_DLIST_UNLINK(mir_rtentry, link, rte);
422     MIR_DLIST_UNLINK(mir_rtentry, nodchain, rte);
423
424     pa_xfree(rte);
425 }
426
427
428 static mir_node *route_stream(struct userdata *u, mir_node *source)
429 {
430     pa_router     *router = u->router;
431     mir_node_type  class  = source->type;
432     mir_node      *sink;
433     mir_rtgroup   *rtg;
434     mir_rtentry   *rte;
435
436
437     if (class < 0 || class > router->maplen) {
438         pa_log_debug("can't route '%s': class %d is out of range (0 - %d)",
439                      source->amname, class, router->maplen);
440         return NULL;
441     }
442     
443     if (!(rtg = router->classmap[class])) {
444         pa_log_debug("node '%s' won't be routed beacuse its class '%s' "
445                      "is not assigned to any router group",
446                      source->amname, mir_node_type_str(class));
447         return NULL;
448     }
449     
450     pa_log_debug("using '%s' router group when routing '%s'",
451                  rtg->name, source->amname);
452
453         
454     MIR_DLIST_FOR_EACH_BACKWARD(mir_rtentry, link, rte, &rtg->entries) {
455         if (!(sink = rte->node)) {
456             pa_log("   node was null in mir_rtentry");
457             continue;
458         }
459         
460         if (sink->ignore) {
461             pa_log_debug("   '%s' should be ignored", sink->amname);
462             continue;
463         }
464
465         if (!sink->available) {
466             pa_log_debug("   '%s' not available", sink->amname);
467             continue;
468         }
469
470         if (sink->paidx == PA_IDXSET_INVALID) {
471             if (sink->type != mir_bluetooth_a2dp &&
472                 sink->type != mir_bluetooth_sco)
473             {
474                 pa_log_debug("   '%s' has no sink", sink->amname); 
475                 continue;
476             }
477         }
478         
479         pa_log_debug("routing '%s' => '%s'", source->amname, sink->amname);
480
481         return sink;
482     }
483     
484     pa_log_debug("could not find route for '%s'", source->amname);
485
486     return NULL;
487 }
488
489
490
491 static int uint32_cmp(uint32_t v1, uint32_t v2)
492 {
493     if (v1 > v2)
494         return 1;
495     if (v1 < v2)
496         return -1;
497     return 0;
498 }
499
500
501 static int node_priority(struct userdata *u, mir_node *node)
502 {
503     pa_router *router;
504     mir_node_type type;
505
506     pa_assert(u);
507     pa_assert(node);
508     pa_assert_se((router = u->router));
509     pa_assert(router->priormap);
510
511     type = node->type;
512
513     if (type < 0 || type >= router->maplen)
514         return 0;
515
516     return router->priormap[type];
517 }
518
519
520 /*
521  * Local Variables:
522  * c-basic-offset: 4
523  * indent-tabs-mode: nil
524  * End:
525  *
526  */