utils: index is unsigned type
[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 void agl_router_assign_class_priority (struct userdata *u, agl_node_type class, int priority)
256 {
257         agl_router *router;
258         int *priormap;
259
260         pa_assert (u);
261         pa_assert_se (router = u->router);
262         pa_assert_se (priormap = router->priormap);
263
264         if (class > 0 && class < router->maplen) {
265                 pa_log_debug ("assigning priority %d to class '%s'",
266                               priority, agl_node_type_str (class));
267                 priormap[class] = priority;
268         }
269 }
270
271 int agl_router_get_node_priority (struct userdata *u, agl_node *node)
272 {
273         agl_router *router;
274         int class;
275
276         pa_assert (u);
277         pa_assert (node);
278         pa_assert_se (router = u->router);
279
280         class = node->type;
281
282         if (class < 0 || class >= (int)router->maplen)
283                 return 0;
284
285         return router->priormap[class];
286 }
287
288 bool agl_router_apply_node_priority_effect (struct userdata *u, agl_node *node, bool new)
289 {
290         agl_router *router;
291         agl_rtgroup *rtg;
292         agl_nodeset *nodeset;
293         agl_node *n;
294         pa_sink *sink;
295         int priority;
296         uint32_t index;
297
298         pa_assert (u);
299         pa_assert (node);
300         pa_assert_se (router = u->router);
301         pa_assert_se (nodeset = u->nodeset);
302
303          /* do we have a routing group associated with this node ? It may have a custom effect */
304         if (node->direction == agl_input)
305                 rtg = pa_hashmap_get (router->rtgroups.input, agl_node_type_str (node->type));
306         else
307                 rtg = pa_hashmap_get (router->rtgroups.output, agl_node_type_str (node->type));
308
309         /* now let us compare priorities, and apply effect if needed */
310         /* "new" case */
311         if (new) {
312                 priority = agl_router_get_node_priority (u, node);
313                 PA_IDXSET_FOREACH(n, nodeset->nodes, index) {
314                         if (n->nullsink && (priority > agl_router_get_node_priority (u, n))) {
315                                 sink = agl_utils_get_null_sink (u, n->nullsink);
316                                 if (sink) {
317                                         /* do we have a custom effect ? otherwise, just mute it */
318                                         if (rtg && rtg->effect)
319                                                 rtg->effect (u, rtg, n, new);
320                                         else
321                                                 pa_sink_set_mute (sink, new, false);
322                                 }
323                         }
324                 }
325         } else {
326         /* "old" case */
327                 if (!agl_node_has_highest_priority (u, node))
328                         return true;
329                 PA_IDXSET_FOREACH(n, nodeset->nodes, index) {
330                         if (n->nullsink) {
331                                 sink = agl_utils_get_null_sink (u, n->nullsink);
332                                 if (sink) {
333                                         /* do we have a custom effect ? otherwise, just unmute it */
334                                         if (rtg && rtg->effect)
335                                                 rtg->effect (u, rtg, n, new);
336                                         else
337                                                 pa_sink_set_mute (sink, new, false);
338                                 }
339                         }
340                 }
341         }
342
343         return true;
344 }
345
346 void agl_router_register_node (struct userdata *u, agl_node *node)
347 {
348         agl_router *router;
349         agl_rtgroup *rtg;
350
351         pa_assert (u);
352         pa_assert (node);
353         pa_assert_se (router = u->router);
354
355         /* we try to discover node routing group from the configuration, "Phone" for instance,
356          * see defaults in "config.c. Otherwise we just say NULL, a.k.a. default */
357         if (node->direction == agl_input) {
358                 rtg = pa_hashmap_get (router->rtgroups.input, agl_node_type_str (node->type));
359                 if (rtg)
360                         implement_default_route (u, node, rtg->node, agl_utils_new_stamp ());
361                 else
362                         implement_default_route (u, node, NULL, agl_utils_new_stamp ());
363         } else {
364                 rtg = pa_hashmap_get (router->rtgroups.output, agl_node_type_str (node->type));
365                 if (rtg)
366                         implement_default_route (u, rtg->node, node, agl_utils_new_stamp ());
367                 else
368                         implement_default_route (u, NULL, node, agl_utils_new_stamp ());
369         }
370 }
371
372 void agl_router_unregister_node (struct userdata *u, agl_node *node)
373 {
374         pa_assert (u);
375         pa_assert (node);
376
377         remove_routes (u, node, NULL, agl_utils_new_stamp ());
378 }
379
380 agl_node *agl_router_make_prerouting (struct userdata *u, agl_node *data)
381 {
382         agl_router *router;
383         int priority;
384         static bool done_prerouting;
385         uint32_t stamp;
386         agl_node *start, *end;
387         agl_node *target;
388
389         pa_assert (u);
390         pa_assert_se (router = u->router);
391         pa_assert_se (data->implement == agl_stream);
392
393         //priority = node_priority (u, data);
394
395         done_prerouting = false;
396         target = NULL;
397         stamp = agl_utils_new_stamp ();
398
399         //make_explicit_routes (u, stamp);
400
401         //pa_audiomgr_delete_default_routes(u);
402
403         AGL_DLIST_FOR_EACH_BACKWARDS(agl_node, rtprilist, start, &router->nodlist) {
404                 //if ((start->implement == agl_device) &&
405                 //    (!start->loop))   /* only manage looped real devices */
406                 //      continue;
407
408                 /*if (priority >= node_priority (u, start)) {
409                         target = find_default_route (u, data, stamp);
410                         if (target)
411                                 implement_preroute (u, data, target, stamp);
412                         else
413                                 done_prerouting = true;
414                 }*/
415
416                 if (start->stamp >= stamp)
417                         continue;
418
419                 //end = find_default_route (u, start, stamp);
420                 //if (end)
421                 //      implement_default_route(u, start, end, stamp);
422         }
423
424         if (!done_prerouting) {
425                 pa_log_debug ("Prerouting failed, trying to find default route as last resort");
426
427                 //target = find_default_route (u, data, stamp);
428                 //if (target)
429                 //      implement_preroute (u, data, target, stamp);
430         }
431
432         return target;
433 }
434
435 void agl_router_make_routing (struct userdata *u)
436 {
437         agl_router *router;
438         static bool ongoing_routing;    /* true while we are actively routing */
439         uint32_t stamp;
440         agl_node *start, *end;
441
442         pa_assert (u);
443         pa_assert_se (router = u->router);
444
445         if (ongoing_routing)            /* already routing, canceling */
446                 return;
447         ongoing_routing = true;
448         stamp = agl_utils_new_stamp ();
449
450         pa_log_debug("stamp for routing: %d", stamp);
451
452         // make_explicit_routes (u, stamp);
453
454         // pa_audiomgr_delete_default_routes (u);
455
456         AGL_DLIST_FOR_EACH_BACKWARDS(agl_node, rtprilist, start, &router->nodlist) {
457                 //if ((start->implement == agl_device) &&
458                 //   (!start->loop))    /* only manage looped real devices */
459                 //      continue;
460
461                 if (start->stamp >= stamp)
462                         continue;
463
464                 end = find_default_route (u, start, stamp);
465                 if (end)
466                         implement_default_route (u, start, end, stamp);
467         }
468
469         // pa_audiomgr_send_default_routes (u);
470
471         ongoing_routing = false;
472 }
473
474 void implement_default_route (struct userdata *u,
475                               agl_node *start, agl_node *end,
476                               uint32_t stamp)
477 {
478         if (start->direction == agl_input)
479                 agl_switch_setup_link (u, start, end, false);
480         else
481                 agl_switch_setup_link (u, end, start, false);
482 }
483
484 agl_node *find_default_route (struct userdata *u, agl_node *start, uint32_t stamp)
485 {
486         /* TODO */
487
488         return NULL;
489 }
490
491 void remove_routes (struct userdata *u, agl_node *start, agl_node *end, uint32_t stamp)
492 {
493         if (start->direction == agl_input) {
494                 agl_switch_teardown_link (u, start, end);
495         } else {
496                 agl_switch_teardown_link (u, end, start);
497         }
498 }