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