router: add missing accessor
[staging/agl-audio-plugin.git] / router.c
1 /*
2  * module-agl-audio -- PulseAudio module for providing audio routing support
3  * (forked from "module-murphy-ivi" - https://github.com/otcshare )
4  * Copyright (c) 2012, Intel Corporation.
5  * Copyright (c) 2016, IoT.bzh
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms and conditions of the GNU Lesser General Public License,
9  * version 2.1, as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston,
19  * MA 02110-1301 USA.
20  *
21  */
22 #include "router.h"
23 #include "switch.h"
24 #include "zone.h"
25 #include "utils.h"
26
27 agl_router *agl_router_init (struct userdata *u)
28 {
29         agl_router *router;
30         size_t num_classes;
31
32         num_classes = agl_application_class_end;
33
34         router = pa_xnew0 (agl_router, 1);
35         router->rtgroups.input = pa_hashmap_new (pa_idxset_string_hash_func,
36                                                  pa_idxset_string_compare_func);
37         router->rtgroups.output = pa_hashmap_new (pa_idxset_string_hash_func,
38                                                   pa_idxset_string_compare_func);
39         router->maplen = num_classes;
40         router->priormap = pa_xnew0 (int, num_classes);
41
42         AGL_DLIST_INIT (router->nodlist);
43         AGL_DLIST_INIT (router->connlist);
44
45         return router;
46 }
47
48 void agl_router_done (struct userdata *u)
49 {
50         agl_router *router;
51         agl_node *e,*n;
52         agl_connection *conn, *c;
53         agl_rtgroup *rtg;
54         agl_rtgroup **map;
55         int i;
56
57         if (u && (router = u->router)) {
58                 AGL_DLIST_FOR_EACH_SAFE(agl_node, rtprilist, e,n, &router->nodlist)
59                         AGL_DLIST_UNLINK(agl_node, rtprilist, e);
60                 AGL_DLIST_FOR_EACH_SAFE(agl_connection, link, conn,c, &router->connlist) {
61                         AGL_DLIST_UNLINK(agl_connection, link, conn);
62                         pa_xfree (conn);
63                 }
64                 /*
65                 PA_HASHMAP_FOREACH(rtg, router->rtgroups.input, state) {
66                         rtgroup_destroy(u, rtg);
67                 }
68                 PA_HASHMAP_FOREACH(rtg, router->rtgroups.output, state) {
69                         rtgroup_destroy(u, rtg);
70                 }*/
71                 pa_hashmap_free (router->rtgroups.input);
72                 pa_hashmap_free (router->rtgroups.output);
73
74                 for (i = 0;  i < AGL_ZONE_MAX;  i++) {
75                         if ((map = router->classmap.input[i]))
76                                 pa_xfree(map);
77                         if ((map = router->classmap.output[i]))
78                                 pa_xfree(map);
79                 }
80
81                 pa_xfree (router->priormap);
82                 pa_xfree (router);
83
84                 u->router = NULL;
85         }
86 }
87
88 bool agl_router_default_accept (struct userdata *u, agl_rtgroup *rtg, agl_node *node)
89 {
90         /* TODO */
91         return true;
92 }
93
94 bool agl_router_phone_accept (struct userdata *u, agl_rtgroup *rtg, agl_node *node)
95 {
96         /* TODO */
97         return true;
98 }
99
100 int agl_router_default_effect (struct userdata *u, agl_rtgroup *rtg, agl_node *node, bool new)
101 {
102         /* TODO */
103         return 1;
104 }
105
106 int agl_router_phone_effect (struct userdata *u, agl_rtgroup *rtg, agl_node *node, bool new)
107 {
108         pa_assert (u);
109         pa_assert (node);
110
111         if (new)
112                 agl_utils_volume_ramp (u, node->nullsink, false);
113         else
114                 agl_utils_volume_ramp (u, node->nullsink, true);
115
116         return 1;
117 }
118
119 agl_rtgroup *agl_router_create_rtgroup (struct userdata *u, agl_direction type, const char *name, const char *node_desc, agl_rtgroup_accept_t accept, agl_rtgroup_effect_t effect)
120 {
121         agl_router *router;
122         agl_rtgroup *rtg;
123         agl_nodeset *nodeset;
124         agl_node *node;
125         pa_hashmap *table;
126
127         pa_assert (u);
128         pa_assert (type == agl_input || type == agl_output);
129         pa_assert (name);
130         pa_assert_se (router = u->router);
131         pa_assert_se (nodeset = u->nodeset);
132
133         if (type == agl_input)
134                 table = router->rtgroups.input;
135         else
136                 table = router->rtgroups.output;
137         pa_assert (table);
138
139         rtg = pa_xnew0 (agl_rtgroup, 1);
140         rtg->name = pa_xstrdup (name);
141         rtg->accept = accept;
142         rtg->effect = effect;
143         AGL_DLIST_INIT(rtg->entries);
144         /* associate an agl_output node for an agl_input routing group */
145         if (type == agl_input) {
146                 node = agl_node_create (u, NULL);
147                 node->direction = agl_output;
148                 node->implement = agl_device;
149                 node->visible = true;
150                 node->available = true;
151                 node->paname = pa_xstrdup (node_desc);
152                  /* add to global nodeset */
153                 pa_idxset_put (nodeset->nodes, node, &node->index);
154                 rtg->node = node;
155         } else {
156                 rtg->node = NULL;
157         }
158         
159
160         pa_hashmap_put (table, rtg->name, rtg);
161
162         pa_log_debug ("routing group '%s' created", name);
163
164         return rtg;
165 }
166
167 void agl_router_destroy_rtgroup (struct userdata *u, agl_direction type, const char *name)
168 {
169         agl_router *router;
170         agl_rtgroup *rtg;
171         pa_hashmap *table;
172
173         pa_assert (u);
174         pa_assert (name);
175         pa_assert_se (router = u->router);
176
177         if (type == agl_input)
178                 table = router->rtgroups.input;
179         else
180                 table = router->rtgroups.output;
181         pa_assert (table);
182
183         rtg = pa_hashmap_remove (table, name);
184         if (!rtg) {
185                 pa_log_debug ("can't destroy routing group '%s': not found", name);
186         } else {
187                 //rtgroup_destroy (u, rtg);
188                 pa_log_debug ("routing group '%s' destroyed", name);
189         }
190 }
191
192 bool agl_router_assign_class_to_rtgroup (struct userdata *u, agl_node_type class, uint32_t zone, agl_direction type, const char *name)
193 {
194         agl_router *router;
195         pa_hashmap *rtable;
196         agl_rtgroup ***classmap;
197         agl_rtgroup **zonemap;
198         const char *classname;
199         const char *direction;
200         agl_rtgroup *rtg;
201         agl_zone *rzone;
202
203         pa_assert (u);
204         pa_assert (zone < AGL_ZONE_MAX);
205         pa_assert (type == agl_input || type == agl_output);
206         pa_assert (name);
207         pa_assert_se (router = u->router);
208
209         if (type == agl_input) {
210                 rtable = router->rtgroups.input;
211                 classmap = router->classmap.input;
212         } else {
213                 rtable = router->rtgroups.output;
214                 classmap = router->classmap.output;
215         }
216
217         if (class < 0 || class >= router->maplen) {
218                 pa_log_debug ("Cannot assign class to routing group '%s': "
219                               "id %d out of range (0 - %d)",
220                               name, class, router->maplen);
221                 return false;
222         }
223
224         classname = agl_node_type_str (class); /* "Player", "Radio"... */
225         direction = agl_node_direction_str (type); /* "input", "output" */
226
227         rtg = pa_hashmap_get (rtable, name);
228         if (!rtg) {
229                 pa_log_debug ("Cannot assign class to routing group '%s': "
230                               "router group not found", name);
231                 return false;
232         }
233
234         zonemap = classmap[zone];
235         if (!zonemap) {
236                 zonemap = pa_xnew0 (agl_rtgroup *, router->maplen);
237                 classmap[zone] = zonemap;
238         }
239
240         zonemap[class] = rtg;
241
242          /* try to get zone name for logging, if fails, only print id number */
243         rzone = agl_zoneset_get_zone_by_index (u, zone);
244         if (rzone) {
245                 pa_log_debug ("class '%s'@'%s' assigned to routing group '%s'",
246                               classname, rzone->name, name); 
247         } else {
248                 pa_log_debug ("class '%s'@zone%d assigned to routing group '%s'",
249                               classname, zone, name);
250         }
251
252         return true;
253 }
254
255 agl_rtgroup * agl_router_get_rtgroup_from_class (struct userdata *u, agl_node_type class, uint32_t zone, agl_direction type)
256 {
257         agl_router *router;
258         pa_hashmap *rtable;
259         agl_rtgroup ***classmap;
260         agl_rtgroup **zonemap;
261         agl_rtgroup * rtg;
262
263         pa_assert (u);
264         pa_assert_se (router = u->router);
265         pa_assert (class >= 0 && class < router->maplen);
266         pa_assert (zone < AGL_ZONE_MAX);
267         pa_assert (type == agl_input || type == agl_output);
268
269         if (type == agl_input) {
270                 rtable = router->rtgroups.input;
271                 classmap = router->classmap.input;
272         } else {
273                 rtable = router->rtgroups.output;
274                 classmap = router->classmap.output;
275         }
276
277         zonemap = classmap[zone];
278         rtg = zonemap[class];
279
280         return rtg;
281 }
282
283 void agl_router_assign_class_priority (struct userdata *u, agl_node_type class, int priority)
284 {
285         agl_router *router;
286         int *priormap;
287
288         pa_assert (u);
289         pa_assert_se (router = u->router);
290         pa_assert_se (priormap = router->priormap);
291
292         if (class > 0 && class < router->maplen) {
293                 pa_log_debug ("assigning priority %d to class '%s'",
294                               priority, agl_node_type_str (class));
295                 priormap[class] = priority;
296         }
297 }
298
299 int agl_router_get_node_priority (struct userdata *u, agl_node *node)
300 {
301         agl_router *router;
302         int class;
303
304         pa_assert (u);
305         pa_assert (node);
306         pa_assert_se (router = u->router);
307
308         class = node->type;
309
310         if (class < 0 || class >= (int)router->maplen)
311                 return 0;
312
313         return router->priormap[class];
314 }
315
316 bool agl_router_apply_node_priority_effect (struct userdata *u, agl_node *node, bool new)
317 {
318         agl_router *router;
319         agl_rtgroup *rtg;
320         agl_nodeset *nodeset;
321         agl_node *n;
322         pa_sink *sink;
323         int priority;
324         uint32_t index;
325
326         pa_assert (u);
327         pa_assert (node);
328         pa_assert_se (router = u->router);
329         pa_assert_se (nodeset = u->nodeset);
330
331          /* do we have a routing group associated with this node ? It may have a custom effect */
332         if (node->direction == agl_input)
333                 rtg = pa_hashmap_get (router->rtgroups.input, agl_node_type_str (node->type));
334         else
335                 rtg = pa_hashmap_get (router->rtgroups.output, agl_node_type_str (node->type));
336
337         /* now let us compare priorities, and apply effect if needed */
338         /* "new" case */
339         if (new) {
340                 priority = agl_router_get_node_priority (u, node);
341                 PA_IDXSET_FOREACH(n, nodeset->nodes, index) {
342                         if (n->nullsink && (priority > agl_router_get_node_priority (u, n))) {
343                                 sink = agl_utils_get_null_sink (u, n->nullsink);
344                                 if (sink) {
345                                         /* do we have a custom effect ? otherwise, just mute it */
346                                         if (rtg && rtg->effect)
347                                                 rtg->effect (u, rtg, n, new);
348                                         else
349                                                 pa_sink_set_mute (sink, new, false);
350                                 }
351                         }
352                 }
353         } else {
354         /* "old" case */
355                 if (!agl_node_has_highest_priority (u, node))
356                         return true;
357                 PA_IDXSET_FOREACH(n, nodeset->nodes, index) {
358                         if (n->nullsink) {
359                                 sink = agl_utils_get_null_sink (u, n->nullsink);
360                                 if (sink) {
361                                         /* do we have a custom effect ? otherwise, just unmute it */
362                                         if (rtg && rtg->effect)
363                                                 rtg->effect (u, rtg, n, new);
364                                         else
365                                                 pa_sink_set_mute (sink, new, false);
366                                 }
367                         }
368                 }
369         }
370
371         return true;
372 }
373
374 void agl_router_register_node (struct userdata *u, agl_node *node)
375 {
376         agl_router *router;
377         agl_rtgroup *rtg;
378
379         pa_assert (u);
380         pa_assert (node);
381         pa_assert_se (router = u->router);
382
383         /* we try to discover node routing group from the configuration, "Phone" for instance,
384          * see defaults in "config.c. Otherwise we just say NULL, a.k.a. default */
385         if (node->direction == agl_input) {
386                 rtg = pa_hashmap_get (router->rtgroups.input, agl_node_type_str (node->type));
387                 if (rtg)
388                         implement_default_route (u, node, rtg->node, agl_utils_new_stamp ());
389                 else
390                         implement_default_route (u, node, NULL, agl_utils_new_stamp ());
391         } else {
392                 rtg = pa_hashmap_get (router->rtgroups.output, agl_node_type_str (node->type));
393                 if (rtg)
394                         implement_default_route (u, rtg->node, node, agl_utils_new_stamp ());
395                 else
396                         implement_default_route (u, NULL, node, agl_utils_new_stamp ());
397         }
398 }
399
400 void agl_router_unregister_node (struct userdata *u, agl_node *node)
401 {
402         pa_assert (u);
403         pa_assert (node);
404
405         remove_routes (u, node, NULL, agl_utils_new_stamp ());
406 }
407
408 agl_node *agl_router_make_prerouting (struct userdata *u, agl_node *data)
409 {
410         agl_router *router;
411         int priority;
412         static bool done_prerouting;
413         uint32_t stamp;
414         agl_node *start, *end;
415         agl_node *target;
416
417         pa_assert (u);
418         pa_assert_se (router = u->router);
419         pa_assert_se (data->implement == agl_stream);
420
421         //priority = node_priority (u, data);
422
423         done_prerouting = false;
424         target = NULL;
425         stamp = agl_utils_new_stamp ();
426
427         //make_explicit_routes (u, stamp);
428
429         //pa_audiomgr_delete_default_routes(u);
430
431         AGL_DLIST_FOR_EACH_BACKWARDS(agl_node, rtprilist, start, &router->nodlist) {
432                 //if ((start->implement == agl_device) &&
433                 //    (!start->loop))   /* only manage looped real devices */
434                 //      continue;
435
436                 /*if (priority >= node_priority (u, start)) {
437                         target = find_default_route (u, data, stamp);
438                         if (target)
439                                 implement_preroute (u, data, target, stamp);
440                         else
441                                 done_prerouting = true;
442                 }*/
443
444                 if (start->stamp >= stamp)
445                         continue;
446
447                 //end = find_default_route (u, start, stamp);
448                 //if (end)
449                 //      implement_default_route(u, start, end, stamp);
450         }
451
452         if (!done_prerouting) {
453                 pa_log_debug ("Prerouting failed, trying to find default route as last resort");
454
455                 //target = find_default_route (u, data, stamp);
456                 //if (target)
457                 //      implement_preroute (u, data, target, stamp);
458         }
459
460         return target;
461 }
462
463 void agl_router_make_routing (struct userdata *u)
464 {
465         agl_router *router;
466         static bool ongoing_routing;    /* true while we are actively routing */
467         uint32_t stamp;
468         agl_node *start, *end;
469
470         pa_assert (u);
471         pa_assert_se (router = u->router);
472
473         if (ongoing_routing)            /* already routing, canceling */
474                 return;
475         ongoing_routing = true;
476         stamp = agl_utils_new_stamp ();
477
478         pa_log_debug("stamp for routing: %d", stamp);
479
480         // make_explicit_routes (u, stamp);
481
482         // pa_audiomgr_delete_default_routes (u);
483
484         AGL_DLIST_FOR_EACH_BACKWARDS(agl_node, rtprilist, start, &router->nodlist) {
485                 //if ((start->implement == agl_device) &&
486                 //   (!start->loop))    /* only manage looped real devices */
487                 //      continue;
488
489                 if (start->stamp >= stamp)
490                         continue;
491
492                 end = find_default_route (u, start, stamp);
493                 if (end)
494                         implement_default_route (u, start, end, stamp);
495         }
496
497         // pa_audiomgr_send_default_routes (u);
498
499         ongoing_routing = false;
500 }
501
502 void implement_default_route (struct userdata *u,
503                               agl_node *start, agl_node *end,
504                               uint32_t stamp)
505 {
506         if (start->direction == agl_input)
507                 agl_switch_setup_link (u, start, end, false);
508         else
509                 agl_switch_setup_link (u, end, start, false);
510 }
511
512 agl_node *find_default_route (struct userdata *u, agl_node *start, uint32_t stamp)
513 {
514         /* TODO */
515
516         return NULL;
517 }
518
519 void remove_routes (struct userdata *u, agl_node *start, agl_node *end, uint32_t stamp)
520 {
521         if (start->direction == agl_input) {
522                 agl_switch_teardown_link (u, start, end);
523         } else {
524                 agl_switch_teardown_link (u, end, start);
525         }
526 }